Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit e28246c

Browse files
committed
[Impeller] new blur: fixed effect transform handling
1 parent 249fa5b commit e28246c

File tree

3 files changed

+132
-57
lines changed

3 files changed

+132
-57
lines changed

impeller/entity/contents/filters/gaussian_blur_filter_contents.cc

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -235,14 +235,14 @@ std::optional<Rect> GaussianBlurFilterContents::GetFilterCoverage(
235235
return {};
236236
}
237237

238-
Vector2 scaled_sigma = {ScaleSigma(sigma_x_), ScaleSigma(sigma_y_)};
239-
Vector2 blur_radius = {CalculateBlurRadius(scaled_sigma.x),
240-
CalculateBlurRadius(scaled_sigma.y)};
241-
Vector3 blur_radii =
242-
(inputs[0]->GetTransform(entity).Basis() * effect_transform.Basis() *
243-
Vector3{blur_radius.x, blur_radius.y, 0.0})
244-
.Abs();
245-
return input_coverage.value().Expand(Point(blur_radii.x, blur_radii.y));
238+
Vector2 scaled_sigma = (effect_transform.Basis() *
239+
Vector2(ScaleSigma(sigma_x_), ScaleSigma(sigma_y_)))
240+
.Abs();
241+
Vector2 blur_radius = Vector2(CalculateBlurRadius(scaled_sigma.x),
242+
CalculateBlurRadius(scaled_sigma.y));
243+
Vector2 padding(ceil(blur_radius.x), ceil(blur_radius.y));
244+
Vector2 local_padding = (entity.GetTransform().Basis() * padding).Abs();
245+
return input_coverage.value().Expand(Point(local_padding.x, local_padding.y));
246246
}
247247

248248
std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
@@ -256,13 +256,13 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
256256
return std::nullopt;
257257
}
258258

259-
Vector2 scaled_sigma = {ScaleSigma(sigma_x_), ScaleSigma(sigma_y_)};
260-
Vector2 blur_radius = {CalculateBlurRadius(scaled_sigma.x),
261-
CalculateBlurRadius(scaled_sigma.y)};
259+
Vector2 scaled_sigma = (effect_transform.Basis() *
260+
Vector2(ScaleSigma(sigma_x_), ScaleSigma(sigma_y_)))
261+
.Abs();
262+
Vector2 blur_radius = Vector2(CalculateBlurRadius(scaled_sigma.x),
263+
CalculateBlurRadius(scaled_sigma.y));
262264
Vector2 padding(ceil(blur_radius.x), ceil(blur_radius.y));
263-
Vector2 local_padding =
264-
(entity.GetTransform().Basis() * effect_transform.Basis() * padding)
265-
.Abs();
265+
Vector2 local_padding = (entity.GetTransform().Basis() * padding).Abs();
266266

267267
// Apply as much of the desired padding as possible from the source. This may
268268
// be ignored so must be accounted for in the downsample pass by adding a
@@ -437,7 +437,8 @@ KernelPipeline::FragmentShader::KernelSamples GenerateBlurInfo(
437437
KernelPipeline::FragmentShader::KernelSamples result;
438438
result.sample_count =
439439
((2 * parameters.blur_radius) / parameters.step_size) + 1;
440-
FML_CHECK(result.sample_count < 24);
440+
// 32 comes from kernel.glsl.
441+
FML_CHECK(result.sample_count < 32);
441442

442443
// Chop off the last samples if the radius >= 3 where they account for < 1.56%
443444
// of the result.

impeller/entity/contents/filters/gaussian_blur_filter_contents_unittests.cc

Lines changed: 115 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,43 @@ namespace {
1919
// Use newtonian method to give the closest answer to target where
2020
// f(x) is less than the target. We do this because the value is `ceil`'d to
2121
// grab fractional pixels.
22-
float LowerBoundNewtonianMethod(const std::function<float(float)>& func,
23-
float target,
24-
float guess,
25-
float tolerance) {
26-
const float delta = 1e-6;
27-
float x = guess;
28-
float fx;
22+
fml::StatusOr<float> LowerBoundNewtonianMethod(
23+
const std::function<float(float)>& func,
24+
float target,
25+
float guess,
26+
float tolerance) {
27+
const double delta = 1e-6;
28+
double x = guess;
29+
double fx;
30+
static const int kMaxIterations = 1000;
31+
int count = 0;
2932

3033
do {
3134
fx = func(x) - target;
32-
float derivative = (func(x + delta) - func(x)) / delta;
35+
double derivative = (func(x + delta) - func(x)) / delta;
3336
x = x - fx / derivative;
34-
37+
if (++count > kMaxIterations) {
38+
return fml::Status(fml::StatusCode::kDeadlineExceeded,
39+
"Did not converge on answer.");
40+
}
3541
} while (std::abs(fx) > tolerance ||
3642
fx < 0.0); // fx < 0.0 makes this lower bound.
3743

3844
return x;
3945
}
4046

41-
Scalar CalculateSigmaForBlurRadius(Scalar radius) {
42-
auto f = [](Scalar x) -> Scalar {
43-
return GaussianBlurFilterContents::CalculateBlurRadius(
44-
GaussianBlurFilterContents::ScaleSigma(x));
47+
fml::StatusOr<Scalar> CalculateSigmaForBlurRadius(
48+
Scalar radius,
49+
const Matrix& effect_transform) {
50+
auto f = [effect_transform](Scalar x) -> Scalar {
51+
Vector2 scaled_sigma = (effect_transform.Basis() *
52+
Vector2(GaussianBlurFilterContents::ScaleSigma(x),
53+
GaussianBlurFilterContents::ScaleSigma(x)))
54+
.Abs();
55+
Vector2 blur_radius = Vector2(
56+
GaussianBlurFilterContents::CalculateBlurRadius(scaled_sigma.x),
57+
GaussianBlurFilterContents::CalculateBlurRadius(scaled_sigma.y));
58+
return std::max(blur_radius.x, blur_radius.y);
4559
};
4660
// The newtonian method is used here since inverting the function is
4761
// non-trivial because of conditional logic and would be fragile to changes.
@@ -90,9 +104,11 @@ TEST(GaussianBlurFilterContentsTest, CoverageSimple) {
90104
}
91105

92106
TEST(GaussianBlurFilterContentsTest, CoverageWithSigma) {
93-
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
94-
GaussianBlurFilterContents contents(/*sigma_x=*/sigma_radius_1,
95-
/*sigma_y=*/sigma_radius_1,
107+
fml::StatusOr<Scalar> sigma_radius_1 =
108+
CalculateSigmaForBlurRadius(1.0, Matrix());
109+
ASSERT_TRUE(sigma_radius_1.ok());
110+
GaussianBlurFilterContents contents(/*sigma_x=*/sigma_radius_1.value(),
111+
/*sigma_y=*/sigma_radius_1.value(),
96112
Entity::TileMode::kDecal);
97113
FilterInput::Vector inputs = {
98114
FilterInput::Make(Rect::MakeLTRB(100, 100, 200, 200))};
@@ -111,9 +127,11 @@ TEST_P(GaussianBlurFilterContentsTest, CoverageWithTexture) {
111127
.format = PixelFormat::kB8G8R8A8UNormInt,
112128
.size = ISize(100, 100),
113129
};
114-
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
115-
GaussianBlurFilterContents contents(/*sigma_X=*/sigma_radius_1,
116-
/*sigma_y=*/sigma_radius_1,
130+
fml::StatusOr<Scalar> sigma_radius_1 =
131+
CalculateSigmaForBlurRadius(1.0, Matrix());
132+
ASSERT_TRUE(sigma_radius_1.ok());
133+
GaussianBlurFilterContents contents(/*sigma_X=*/sigma_radius_1.value(),
134+
/*sigma_y=*/sigma_radius_1.value(),
117135
Entity::TileMode::kDecal);
118136
std::shared_ptr<Texture> texture =
119137
GetContentContext()->GetContext()->GetResourceAllocator()->CreateTexture(
@@ -135,33 +153,39 @@ TEST_P(GaussianBlurFilterContentsTest, CoverageWithEffectTransform) {
135153
.format = PixelFormat::kB8G8R8A8UNormInt,
136154
.size = ISize(100, 100),
137155
};
138-
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
139-
GaussianBlurFilterContents contents(/*sigma_x=*/sigma_radius_1,
140-
/*sigma_y=*/sigma_radius_1,
156+
Matrix effect_transform = Matrix::MakeScale({2.0, 2.0, 1.0});
157+
fml::StatusOr<Scalar> sigma_radius_1 =
158+
CalculateSigmaForBlurRadius(1.0, effect_transform);
159+
ASSERT_TRUE(sigma_radius_1.ok());
160+
GaussianBlurFilterContents contents(/*sigma_x=*/sigma_radius_1.value(),
161+
/*sigma_y=*/sigma_radius_1.value(),
141162
Entity::TileMode::kDecal);
142163
std::shared_ptr<Texture> texture =
143164
GetContentContext()->GetContext()->GetResourceAllocator()->CreateTexture(
144165
desc);
145166
FilterInput::Vector inputs = {FilterInput::Make(texture)};
146167
Entity entity;
147168
entity.SetTransform(Matrix::MakeTranslation({100, 100, 0}));
148-
std::optional<Rect> coverage = contents.GetFilterCoverage(
149-
inputs, entity, /*effect_transform=*/Matrix::MakeScale({2.0, 2.0, 1.0}));
169+
std::optional<Rect> coverage =
170+
contents.GetFilterCoverage(inputs, entity, effect_transform);
150171
EXPECT_TRUE(coverage.has_value());
151172
if (coverage.has_value()) {
152173
EXPECT_RECT_NEAR(coverage.value(),
153-
Rect::MakeLTRB(100 - 2, 100 - 2, 200 + 2, 200 + 2));
174+
Rect::MakeLTRB(100 - 1, 100 - 1, 200 + 1, 200 + 1));
154175
}
155176
}
156177

157178
TEST(GaussianBlurFilterContentsTest, FilterSourceCoverage) {
158-
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
179+
fml::StatusOr<Scalar> sigma_radius_1 =
180+
CalculateSigmaForBlurRadius(1.0, Matrix());
181+
ASSERT_TRUE(sigma_radius_1.ok());
159182
auto contents = std::make_unique<GaussianBlurFilterContents>(
160-
sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal);
183+
sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal);
161184
std::optional<Rect> coverage = contents->GetFilterSourceCoverage(
162185
/*effect_transform=*/Matrix::MakeScale({2.0, 2.0, 1.0}),
163186
/*output_limit=*/Rect::MakeLTRB(100, 100, 200, 200));
164-
ASSERT_EQ(coverage, Rect::MakeLTRB(100 - 2, 100 - 2, 200 + 2, 200 + 2));
187+
EXPECT_RECT_NEAR(coverage.value(),
188+
Rect::MakeLTRB(100 - 2, 100 - 2, 200 + 2, 200 + 2));
165189
}
166190

167191
TEST(GaussianBlurFilterContentsTest, CalculateSigmaValues) {
@@ -180,9 +204,11 @@ TEST_P(GaussianBlurFilterContentsTest, RenderCoverageMatchesGetCoverage) {
180204
.size = ISize(100, 100),
181205
};
182206
std::shared_ptr<Texture> texture = MakeTexture(desc);
183-
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
207+
fml::StatusOr<Scalar> sigma_radius_1 =
208+
CalculateSigmaForBlurRadius(1.0, Matrix());
209+
ASSERT_TRUE(sigma_radius_1.ok());
184210
auto contents = std::make_unique<GaussianBlurFilterContents>(
185-
sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal);
211+
sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal);
186212
contents->SetInputs({FilterInput::Make(texture)});
187213
std::shared_ptr<ContentContext> renderer = GetContentContext();
188214

@@ -213,9 +239,11 @@ TEST_P(GaussianBlurFilterContentsTest,
213239
.size = ISize(100, 100),
214240
};
215241
std::shared_ptr<Texture> texture = MakeTexture(desc);
216-
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
242+
fml::StatusOr<Scalar> sigma_radius_1 =
243+
CalculateSigmaForBlurRadius(1.0, Matrix());
244+
ASSERT_TRUE(sigma_radius_1.ok());
217245
auto contents = std::make_unique<GaussianBlurFilterContents>(
218-
sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal);
246+
sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal);
219247
contents->SetInputs({FilterInput::Make(texture)});
220248
std::shared_ptr<ContentContext> renderer = GetContentContext();
221249

@@ -248,9 +276,10 @@ TEST_P(GaussianBlurFilterContentsTest,
248276
.size = ISize(400, 300),
249277
};
250278
std::shared_ptr<Texture> texture = MakeTexture(desc);
251-
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
279+
fml::StatusOr<Scalar> sigma_radius_1 =
280+
CalculateSigmaForBlurRadius(1.0, Matrix());
252281
auto contents = std::make_unique<GaussianBlurFilterContents>(
253-
sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal);
282+
sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal);
254283
contents->SetInputs({FilterInput::Make(texture)});
255284
std::shared_ptr<ContentContext> renderer = GetContentContext();
256285

@@ -308,9 +337,10 @@ TEST_P(GaussianBlurFilterContentsTest, TextureContentsWithDestinationRect) {
308337
texture_contents->SetDestinationRect(Rect::MakeXYWH(
309338
50, 40, texture->GetSize().width, texture->GetSize().height));
310339

311-
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
340+
fml::StatusOr<Scalar> sigma_radius_1 =
341+
CalculateSigmaForBlurRadius(1.0, Matrix());
312342
auto contents = std::make_unique<GaussianBlurFilterContents>(
313-
sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal);
343+
sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal);
314344
contents->SetInputs({FilterInput::Make(texture_contents)});
315345
std::shared_ptr<ContentContext> renderer = GetContentContext();
316346

@@ -347,9 +377,10 @@ TEST_P(GaussianBlurFilterContentsTest,
347377
texture_contents->SetDestinationRect(Rect::MakeXYWH(
348378
50, 40, texture->GetSize().width, texture->GetSize().height));
349379

350-
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
380+
fml::StatusOr<Scalar> sigma_radius_1 =
381+
CalculateSigmaForBlurRadius(1.0, Matrix());
351382
auto contents = std::make_unique<GaussianBlurFilterContents>(
352-
sigma_radius_1, sigma_radius_1, Entity::TileMode::kDecal);
383+
sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal);
353384
contents->SetInputs({FilterInput::Make(texture_contents)});
354385
std::shared_ptr<ContentContext> renderer = GetContentContext();
355386

@@ -372,13 +403,56 @@ TEST_P(GaussianBlurFilterContentsTest,
372403
}
373404
}
374405

406+
TEST_P(GaussianBlurFilterContentsTest, TextureContentsWithEffectTransform) {
407+
TextureDescriptor desc = {
408+
.storage_mode = StorageMode::kDevicePrivate,
409+
.format = PixelFormat::kB8G8R8A8UNormInt,
410+
.size = ISize(100, 100),
411+
};
412+
413+
Matrix effect_transform = Matrix::MakeScale({2.0, 2.0, 1.0});
414+
std::shared_ptr<Texture> texture = MakeTexture(desc);
415+
auto texture_contents = std::make_shared<TextureContents>();
416+
texture_contents->SetSourceRect(Rect::MakeSize(texture->GetSize()));
417+
texture_contents->SetTexture(texture);
418+
texture_contents->SetDestinationRect(Rect::MakeXYWH(
419+
50, 40, texture->GetSize().width, texture->GetSize().height));
420+
421+
fml::StatusOr<Scalar> sigma_radius_1 =
422+
CalculateSigmaForBlurRadius(1.0, effect_transform);
423+
ASSERT_TRUE(sigma_radius_1.ok());
424+
auto contents = std::make_unique<GaussianBlurFilterContents>(
425+
sigma_radius_1.value(), sigma_radius_1.value(), Entity::TileMode::kDecal);
426+
contents->SetInputs({FilterInput::Make(texture_contents)});
427+
contents->SetEffectTransform(effect_transform);
428+
std::shared_ptr<ContentContext> renderer = GetContentContext();
429+
430+
Entity entity;
431+
std::optional<Entity> result =
432+
contents->GetEntity(*renderer, entity, /*coverage_hint=*/{});
433+
EXPECT_TRUE(result.has_value());
434+
if (result.has_value()) {
435+
EXPECT_EQ(result.value().GetBlendMode(), BlendMode::kSourceOver);
436+
std::optional<Rect> result_coverage = result.value().GetCoverage();
437+
std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
438+
EXPECT_TRUE(result_coverage.has_value());
439+
EXPECT_TRUE(contents_coverage.has_value());
440+
if (result_coverage.has_value() && contents_coverage.has_value()) {
441+
EXPECT_TRUE(RectNear(result_coverage.value(), contents_coverage.value()));
442+
EXPECT_TRUE(RectNear(contents_coverage.value(),
443+
Rect::MakeXYWH(49.f, 39.f, 102.f, 102.f)));
444+
}
445+
}
446+
}
447+
375448
TEST(GaussianBlurFilterContentsTest, CalculateSigmaForBlurRadius) {
376449
Scalar sigma = 1.0;
377450
Scalar radius = GaussianBlurFilterContents::CalculateBlurRadius(
378451
GaussianBlurFilterContents::ScaleSigma(sigma));
379-
Scalar derived_sigma = CalculateSigmaForBlurRadius(radius);
380-
381-
EXPECT_NEAR(sigma, derived_sigma, 0.01f);
452+
fml::StatusOr<Scalar> derived_sigma =
453+
CalculateSigmaForBlurRadius(radius, Matrix());
454+
ASSERT_TRUE(derived_sigma.ok());
455+
EXPECT_NEAR(sigma, derived_sigma.value(), 0.01f);
382456
}
383457

384458
TEST(GaussianBlurFilterContentsTest, Coefficients) {

impeller/entity/shaders/gaussian_blur/kernel.glsl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ struct KernelSample {
1616

1717
uniform KernelSamples {
1818
int sample_count;
19-
KernelSample samples[24];
19+
KernelSample samples[32];
2020
}
2121
blur_info;
2222

0 commit comments

Comments
 (0)