Skip to content

Commit 7f81e71

Browse files
authored
[Impeller] blur - cropped the downsample pass for backdrop filters (flutter#53562)
This should be no functional change, just makes the case of clipped backdrop filters take up less memory. fixes: flutter#150713 test coverage: - the framework's "backdrop filter" test - GaussianBlurAnimatedBackdrop [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent b097a62 commit 7f81e71

1 file changed

Lines changed: 131 additions & 54 deletions

File tree

impeller/entity/contents/filters/gaussian_blur_filter_contents.cc

Lines changed: 131 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,64 @@ std::optional<Snapshot> GetSnapshot(const std::shared_ptr<FilterInput>& input,
156156
return input_snapshot;
157157
}
158158

159+
/// Returns `rect` relative to `reference`, where Rect::MakeXYWH(0,0,1,1) will
160+
/// be returned when `rect` == `reference`.
161+
Rect MakeReferenceUVs(const Rect& reference, const Rect& rect) {
162+
Rect result = Rect::MakeOriginSize(rect.GetOrigin() - reference.GetOrigin(),
163+
rect.GetSize());
164+
return result.Scale(1.0f / Vector2(reference.GetSize()));
165+
}
166+
167+
Quad CalculateSnapshotUVs(
168+
const Snapshot& input_snapshot,
169+
const std::optional<Rect>& source_expanded_coverage_hint) {
170+
std::optional<Rect> input_snapshot_coverage = input_snapshot.GetCoverage();
171+
Quad blur_uvs = {Point(0, 0), Point(1, 0), Point(0, 1), Point(1, 1)};
172+
FML_DCHECK(input_snapshot.transform.IsTranslationScaleOnly());
173+
if (source_expanded_coverage_hint.has_value() &&
174+
input_snapshot_coverage.has_value()) {
175+
// Only process the uvs where the blur is happening, not the whole texture.
176+
std::optional<Rect> uvs =
177+
MakeReferenceUVs(input_snapshot_coverage.value(),
178+
source_expanded_coverage_hint.value())
179+
.Intersection(Rect::MakeSize(Size(1, 1)));
180+
FML_DCHECK(uvs.has_value());
181+
if (uvs.has_value()) {
182+
blur_uvs[0] = uvs->GetLeftTop();
183+
blur_uvs[1] = uvs->GetRightTop();
184+
blur_uvs[2] = uvs->GetLeftBottom();
185+
blur_uvs[3] = uvs->GetRightBottom();
186+
}
187+
}
188+
return blur_uvs;
189+
}
190+
191+
Scalar CeilToDivisible(Scalar val, Scalar divisor) {
192+
if (divisor == 0.0f) {
193+
return val;
194+
}
195+
196+
Scalar remainder = fmod(val, divisor);
197+
if (remainder != 0.0f) {
198+
return val + (divisor - remainder);
199+
} else {
200+
return val;
201+
}
202+
}
203+
204+
Scalar FloorToDivisible(Scalar val, Scalar divisor) {
205+
if (divisor == 0.0f) {
206+
return val;
207+
}
208+
209+
Scalar remainder = fmod(val, divisor);
210+
if (remainder != 0.0f) {
211+
return val - remainder;
212+
} else {
213+
return val;
214+
}
215+
}
216+
159217
struct DownsamplePassArgs {
160218
/// The output size of the down-sampling pass.
161219
ISize subpass_size;
@@ -166,13 +224,19 @@ struct DownsamplePassArgs {
166224
/// This isn't usually exactly as we'd calculate because it has to be rounded
167225
/// to integer boundaries for generating the texture for the output.
168226
Vector2 effective_scalar;
227+
/// Transforms from unrotated local space to position the output from the
228+
/// down-sample pass.
229+
/// This can differ if we request a coverage hint but it is rejected, as is
230+
/// the case with backdrop filters.
231+
Matrix transform;
169232
};
170233

171234
/// Calculates info required for the down-sampling pass.
172235
DownsamplePassArgs CalculateDownsamplePassArgs(
173236
Vector2 scaled_sigma,
174237
Vector2 padding,
175-
ISize input_snapshot_size,
238+
const Snapshot& input_snapshot,
239+
const std::optional<Rect>& source_expanded_coverage_hint,
176240
const std::shared_ptr<FilterInput>& input,
177241
const Entity& snapshot_entity) {
178242
Scalar desired_scalar =
@@ -182,8 +246,6 @@ DownsamplePassArgs CalculateDownsamplePassArgs(
182246
// gutter from the expanded_coverage_hint, we can skip the downsample pass.
183247
// pass.
184248
Vector2 downsample_scalar(desired_scalar, desired_scalar);
185-
Rect source_rect = Rect::MakeSize(input_snapshot_size);
186-
Rect source_rect_padded = source_rect.Expand(padding);
187249
// TODO(gaaclarke): The padding could be removed if we know it's not needed or
188250
// resized to account for the expanded_clip_coverage. There doesn't appear
189251
// to be the math to make those calculations though. The following
@@ -192,19 +254,68 @@ DownsamplePassArgs CalculateDownsamplePassArgs(
192254
//
193255
// !input_snapshot->GetCoverage()->Expand(-local_padding)
194256
// .Contains(coverage_hint.value()))
195-
Vector2 downsampled_size = source_rect_padded.GetSize() * downsample_scalar;
196-
ISize subpass_size =
197-
ISize(round(downsampled_size.x), round(downsampled_size.y));
198-
Vector2 effective_scalar =
199-
Vector2(subpass_size) / source_rect_padded.GetSize();
200-
201-
Quad uvs = GaussianBlurFilterContents::CalculateUVs(
202-
input, snapshot_entity, source_rect_padded, input_snapshot_size);
203-
return {
204-
.subpass_size = subpass_size,
205-
.uvs = uvs,
206-
.effective_scalar = effective_scalar,
207-
};
257+
258+
std::optional<Rect> snapshot_coverage = input_snapshot.GetCoverage();
259+
if (input_snapshot.transform.IsIdentity() &&
260+
source_expanded_coverage_hint.has_value() &&
261+
snapshot_coverage.has_value() &&
262+
snapshot_coverage->Contains(source_expanded_coverage_hint.value())) {
263+
// If the snapshot's transform is the identity transform and we have
264+
// coverage hint that fits inside of the snapshots coverage that means the
265+
// coverage hint was ignored so we will trim out the area we are interested
266+
// in the down-sample pass. This usually means we have a backdrop image
267+
// filter.
268+
//
269+
// The region we cut out will be aligned with the down-sample divisor to
270+
// avoid pixel alignment problems that create shimmering.
271+
int32_t divisor = std::round(1.0f / desired_scalar);
272+
Rect aligned_coverage_hint = Rect::MakeLTRB(
273+
FloorToDivisible(source_expanded_coverage_hint->GetLeft(), divisor),
274+
FloorToDivisible(source_expanded_coverage_hint->GetTop(), divisor),
275+
source_expanded_coverage_hint->GetRight(),
276+
source_expanded_coverage_hint->GetBottom());
277+
aligned_coverage_hint = Rect::MakeXYWH(
278+
aligned_coverage_hint.GetX(), aligned_coverage_hint.GetY(),
279+
CeilToDivisible(aligned_coverage_hint.GetWidth(), divisor),
280+
CeilToDivisible(aligned_coverage_hint.GetHeight(), divisor));
281+
ISize source_size = ISize(aligned_coverage_hint.GetSize().width,
282+
aligned_coverage_hint.GetSize().height);
283+
Vector2 downsampled_size = source_size * downsample_scalar;
284+
Scalar int_part;
285+
FML_DCHECK(std::modf(downsampled_size.x, &int_part) == 0.0f);
286+
FML_DCHECK(std::modf(downsampled_size.y, &int_part) == 0.0f);
287+
(void)int_part;
288+
ISize subpass_size = ISize(downsampled_size.x, downsampled_size.y);
289+
Vector2 effective_scalar = Vector2(subpass_size) / source_size;
290+
FML_DCHECK(effective_scalar == downsample_scalar);
291+
292+
Quad uvs = CalculateSnapshotUVs(input_snapshot, aligned_coverage_hint);
293+
return {
294+
.subpass_size = subpass_size,
295+
.uvs = uvs,
296+
.effective_scalar = effective_scalar,
297+
.transform = Matrix::MakeTranslation(
298+
{aligned_coverage_hint.GetX(), aligned_coverage_hint.GetY(), 0})};
299+
} else {
300+
//////////////////////////////////////////////////////////////////////////////
301+
auto input_snapshot_size = input_snapshot.texture->GetSize();
302+
Rect source_rect = Rect::MakeSize(input_snapshot_size);
303+
Rect source_rect_padded = source_rect.Expand(padding);
304+
Vector2 downsampled_size = source_rect_padded.GetSize() * downsample_scalar;
305+
ISize subpass_size =
306+
ISize(round(downsampled_size.x), round(downsampled_size.y));
307+
Vector2 effective_scalar =
308+
Vector2(subpass_size) / source_rect_padded.GetSize();
309+
Quad uvs = GaussianBlurFilterContents::CalculateUVs(
310+
input, snapshot_entity, source_rect_padded, input_snapshot_size);
311+
return {
312+
.subpass_size = subpass_size,
313+
.uvs = uvs,
314+
.effective_scalar = effective_scalar,
315+
.transform =
316+
input_snapshot.transform * Matrix::MakeTranslation(-padding),
317+
};
318+
}
208319
}
209320

210321
/// Makes a subpass that will render the scaled down input and add the
@@ -326,38 +437,6 @@ fml::StatusOr<RenderTarget> MakeBlurSubpass(
326437
}
327438
}
328439

329-
/// Returns `rect` relative to `reference`, where Rect::MakeXYWH(0,0,1,1) will
330-
/// be returned when `rect` == `reference`.
331-
Rect MakeReferenceUVs(const Rect& reference, const Rect& rect) {
332-
Rect result = Rect::MakeOriginSize(rect.GetOrigin() - reference.GetOrigin(),
333-
rect.GetSize());
334-
return result.Scale(1.0f / Vector2(reference.GetSize()));
335-
}
336-
337-
Quad CalculateBlurUVs(
338-
const Snapshot& input_snapshot,
339-
const std::optional<Rect>& source_expanded_coverage_hint) {
340-
std::optional<Rect> input_snapshot_coverage = input_snapshot.GetCoverage();
341-
Quad blur_uvs = {Point(0, 0), Point(1, 0), Point(0, 1), Point(1, 1)};
342-
FML_DCHECK(input_snapshot.transform.IsTranslationScaleOnly());
343-
if (source_expanded_coverage_hint.has_value() &&
344-
input_snapshot_coverage.has_value()) {
345-
// Only process the uvs where the blur is happening, not the whole texture.
346-
std::optional<Rect> uvs =
347-
MakeReferenceUVs(input_snapshot_coverage.value(),
348-
source_expanded_coverage_hint.value())
349-
.Intersection(Rect::MakeSize(Size(1, 1)));
350-
FML_DCHECK(uvs.has_value());
351-
if (uvs.has_value()) {
352-
blur_uvs[0] = uvs->GetLeftTop();
353-
blur_uvs[1] = uvs->GetRightTop();
354-
blur_uvs[2] = uvs->GetLeftBottom();
355-
blur_uvs[3] = uvs->GetRightBottom();
356-
}
357-
}
358-
return blur_uvs;
359-
}
360-
361440
int ScaleBlurRadius(Scalar radius, Scalar scalar) {
362441
return static_cast<int>(std::round(radius * scalar));
363442
}
@@ -597,8 +676,8 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
597676
}
598677

599678
DownsamplePassArgs downsample_pass_args = CalculateDownsamplePassArgs(
600-
blur_info.scaled_sigma, blur_info.padding,
601-
input_snapshot->texture->GetSize(), inputs[0], snapshot_entity);
679+
blur_info.scaled_sigma, blur_info.padding, input_snapshot.value(),
680+
source_expanded_coverage_hint, inputs[0], snapshot_entity);
602681

603682
fml::StatusOr<RenderTarget> pass1_out = MakeDownsampleSubpass(
604683
renderer, command_buffer, input_snapshot->texture,
@@ -611,8 +690,7 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
611690
Vector2 pass1_pixel_size =
612691
1.0 / Vector2(pass1_out.value().GetRenderTargetTexture()->GetSize());
613692

614-
Quad blur_uvs =
615-
CalculateBlurUVs(input_snapshot.value(), source_expanded_coverage_hint);
693+
Quad blur_uvs = {Point(0, 0), Point(1, 0), Point(0, 1), Point(1, 1)};
616694

617695
fml::StatusOr<RenderTarget> pass2_out = MakeBlurSubpass(
618696
renderer, command_buffer, /*input_pass=*/pass1_out.value(),
@@ -676,8 +754,7 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
676754
.transform =
677755
entity.GetTransform() * //
678756
Matrix::MakeScale(1.f / blur_info.source_space_scalar) * //
679-
input_snapshot->transform * //
680-
Matrix::MakeTranslation(-blur_info.padding) * //
757+
downsample_pass_args.transform * //
681758
Matrix::MakeScale(1 / downsample_pass_args.effective_scalar),
682759
.sampler_descriptor = sampler_desc,
683760
.opacity = input_snapshot->opacity},

0 commit comments

Comments
 (0)