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

Commit 75c60e6

Browse files
authored
[Impeller] Limit subpass textures and backdrop blurs to the current clip (#42039)
Part of flutter/flutter#126696. * Generalize the FilterContents coverage hint as a Contents property. * Incorporates the stencil coverage when creating subpass textures. * Set a coverage hint for backdrop filters. * Incorporate the coverage hint in the 2-pass Gaussian blur.
1 parent 3654548 commit 75c60e6

37 files changed

+306
-160
lines changed

impeller/aiks/aiks_unittests.cc

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2174,5 +2174,52 @@ TEST_P(AiksTest, CanRenderDestructiveSaveLayer) {
21742174
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
21752175
}
21762176

2177+
TEST_P(AiksTest, CanRenderBackdropBlurInteractive) {
2178+
auto callback = [&](AiksContext& renderer, RenderTarget& render_target) {
2179+
auto [a, b] = IMPELLER_PLAYGROUND_LINE(Point(50, 50), Point(300, 200), 30,
2180+
Color::White(), Color::White());
2181+
2182+
Canvas canvas;
2183+
canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
2184+
canvas.DrawCircle({300, 200}, 100, {.color = Color::GreenYellow()});
2185+
canvas.DrawCircle({140, 170}, 75, {.color = Color::DarkMagenta()});
2186+
canvas.DrawCircle({180, 120}, 100, {.color = Color::OrangeRed()});
2187+
canvas.ClipRRect(Rect::MakeLTRB(a.x, a.y, b.x, b.y), 20);
2188+
canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
2189+
[](const FilterInput::Ref& input,
2190+
const Matrix& effect_transform, bool is_subpass) {
2191+
return FilterContents::MakeGaussianBlur(
2192+
input, Sigma(20.0), Sigma(20.0),
2193+
FilterContents::BlurStyle::kNormal,
2194+
Entity::TileMode::kClamp, effect_transform);
2195+
});
2196+
canvas.Restore();
2197+
2198+
return renderer.Render(canvas.EndRecordingAsPicture(), render_target);
2199+
};
2200+
2201+
ASSERT_TRUE(OpenPlaygroundHere(callback));
2202+
}
2203+
2204+
TEST_P(AiksTest, CanRenderBackdropBlur) {
2205+
Canvas canvas;
2206+
canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
2207+
canvas.DrawCircle({300, 200}, 100, {.color = Color::GreenYellow()});
2208+
canvas.DrawCircle({140, 170}, 75, {.color = Color::DarkMagenta()});
2209+
canvas.DrawCircle({180, 120}, 100, {.color = Color::OrangeRed()});
2210+
canvas.ClipRRect(Rect::MakeLTRB(75, 50, 375, 275), 20);
2211+
canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
2212+
[](const FilterInput::Ref& input,
2213+
const Matrix& effect_transform, bool is_subpass) {
2214+
return FilterContents::MakeGaussianBlur(
2215+
input, Sigma(30.0), Sigma(30.0),
2216+
FilterContents::BlurStyle::kNormal,
2217+
Entity::TileMode::kClamp, effect_transform);
2218+
});
2219+
canvas.Restore();
2220+
2221+
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2222+
}
2223+
21772224
} // namespace testing
21782225
} // namespace impeller

impeller/entity/contents/contents.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,14 @@ bool Contents::ShouldRender(const Entity& entity,
124124
return stencil_coverage->IntersectsWithRect(coverage.value());
125125
}
126126

127+
void Contents::SetCoverageHint(std::optional<Rect> coverage_hint) {
128+
coverage_hint_ = coverage_hint;
129+
}
130+
131+
const std::optional<Rect>& Contents::GetCoverageHint() const {
132+
return coverage_hint_;
133+
}
134+
127135
std::optional<Size> Contents::GetColorSourceSize() const {
128136
return color_source_size_;
129137
};

impeller/entity/contents/contents.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ class Contents {
5656
/// @brief Get the screen space bounding rectangle that this contents affects.
5757
virtual std::optional<Rect> GetCoverage(const Entity& entity) const = 0;
5858

59+
/// @brief Hint that specifies the coverage area of this Contents that will
60+
/// actually be used during rendering. This is for optimization
61+
/// purposes only and can not be relied on as a clip. May optionally
62+
/// affect the result of `GetCoverage()`.
63+
void SetCoverageHint(std::optional<Rect> coverage_hint);
64+
65+
const std::optional<Rect>& GetCoverageHint() const;
66+
5967
/// @brief Whether this Contents only emits opaque source colors from the
6068
/// fragment stage. This value does not account for any entity
6169
/// properties (e.g. the blend mode), clips/visibility culling, or
@@ -110,6 +118,7 @@ class Contents {
110118
virtual void SetInheritedOpacity(Scalar opacity);
111119

112120
private:
121+
std::optional<Rect> coverage_hint_;
113122
std::optional<Size> color_source_size_;
114123

115124
FML_DISALLOW_COPY_AND_ASSIGN(Contents);

impeller/entity/contents/filters/blend_filter_contents.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,8 @@ std::optional<Entity> BlendFilterContents::RenderFilter(
649649
const ContentContext& renderer,
650650
const Entity& entity,
651651
const Matrix& effect_transform,
652-
const Rect& coverage) const {
652+
const Rect& coverage,
653+
const std::optional<Rect>& coverage_hint) const {
653654
if (inputs.empty()) {
654655
return std::nullopt;
655656
}

impeller/entity/contents/filters/blend_filter_contents.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,13 @@ class BlendFilterContents : public ColorFilterContents {
3232

3333
private:
3434
// |FilterContents|
35-
std::optional<Entity> RenderFilter(const FilterInput::Vector& inputs,
36-
const ContentContext& renderer,
37-
const Entity& entity,
38-
const Matrix& effect_transform,
39-
const Rect& coverage) const override;
35+
std::optional<Entity> RenderFilter(
36+
const FilterInput::Vector& inputs,
37+
const ContentContext& renderer,
38+
const Entity& entity,
39+
const Matrix& effect_transform,
40+
const Rect& coverage,
41+
const std::optional<Rect>& coverage_hint) const override;
4042

4143
/// @brief Optimized advanced blend that avoids a second subpass when there is
4244
/// only a single input and a foreground color.

impeller/entity/contents/filters/border_mask_blur_filter_contents.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ std::optional<Entity> BorderMaskBlurFilterContents::RenderFilter(
5353
const ContentContext& renderer,
5454
const Entity& entity,
5555
const Matrix& effect_transform,
56-
const Rect& coverage) const {
56+
const Rect& coverage,
57+
const std::optional<Rect>& coverage_hint) const {
5758
using VS = BorderMaskBlurPipeline::VertexShader;
5859
using FS = BorderMaskBlurPipeline::FragmentShader;
5960

impeller/entity/contents/filters/border_mask_blur_filter_contents.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@ class BorderMaskBlurFilterContents final : public FilterContents {
2929

3030
private:
3131
// |FilterContents|
32-
std::optional<Entity> RenderFilter(const FilterInput::Vector& input_textures,
33-
const ContentContext& renderer,
34-
const Entity& entity,
35-
const Matrix& effect_transform,
36-
const Rect& coverage) const override;
32+
std::optional<Entity> RenderFilter(
33+
const FilterInput::Vector& input_textures,
34+
const ContentContext& renderer,
35+
const Entity& entity,
36+
const Matrix& effect_transform,
37+
const Rect& coverage,
38+
const std::optional<Rect>& coverage_hint) const override;
3739

3840
Sigma sigma_x_;
3941
Sigma sigma_y_;

impeller/entity/contents/filters/color_matrix_filter_contents.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ std::optional<Entity> ColorMatrixFilterContents::RenderFilter(
2929
const ContentContext& renderer,
3030
const Entity& entity,
3131
const Matrix& effect_transform,
32-
const Rect& coverage) const {
32+
const Rect& coverage,
33+
const std::optional<Rect>& coverage_hint) const {
3334
using VS = ColorMatrixColorFilterPipeline::VertexShader;
3435
using FS = ColorMatrixColorFilterPipeline::FragmentShader;
3536

impeller/entity/contents/filters/color_matrix_filter_contents.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ class ColorMatrixFilterContents final : public ColorFilterContents {
2424

2525
private:
2626
// |FilterContents|
27-
std::optional<Entity> RenderFilter(const FilterInput::Vector& input_textures,
28-
const ContentContext& renderer,
29-
const Entity& entity,
30-
const Matrix& effect_transform,
31-
const Rect& coverage) const override;
27+
std::optional<Entity> RenderFilter(
28+
const FilterInput::Vector& input_textures,
29+
const ContentContext& renderer,
30+
const Entity& entity,
31+
const Matrix& effect_transform,
32+
const Rect& coverage,
33+
const std::optional<Rect>& coverage_hint) const override;
3234

3335
ColorMatrix matrix_;
3436

impeller/entity/contents/filters/filter_contents.cc

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,6 @@ void FilterContents::SetInputs(FilterInput::Vector inputs) {
153153
inputs_ = std::move(inputs);
154154
}
155155

156-
void FilterContents::SetCoverageCrop(std::optional<Rect> coverage_crop) {
157-
coverage_crop_ = coverage_crop;
158-
}
159-
160156
void FilterContents::SetEffectTransform(Matrix effect_transform) {
161157
effect_transform_ = effect_transform;
162158
}
@@ -171,7 +167,7 @@ bool FilterContents::Render(const ContentContext& renderer,
171167

172168
// Run the filter.
173169

174-
auto maybe_entity = GetEntity(renderer, entity);
170+
auto maybe_entity = GetEntity(renderer, entity, GetCoverageHint());
175171
if (!maybe_entity.has_value()) {
176172
return true;
177173
}
@@ -181,8 +177,8 @@ bool FilterContents::Render(const ContentContext& renderer,
181177
std::optional<Rect> FilterContents::GetLocalCoverage(
182178
const Entity& local_entity) const {
183179
auto coverage = GetFilterCoverage(inputs_, local_entity, effect_transform_);
184-
if (coverage_crop_.has_value() && coverage.has_value()) {
185-
coverage = coverage->Intersection(coverage_crop_.value());
180+
if (GetCoverageHint().has_value() && coverage.has_value()) {
181+
coverage = coverage->Intersection(*GetCoverageHint());
186182
}
187183

188184
return coverage;
@@ -223,8 +219,10 @@ std::optional<Rect> FilterContents::GetFilterCoverage(
223219
return result;
224220
}
225221

226-
std::optional<Entity> FilterContents::GetEntity(const ContentContext& renderer,
227-
const Entity& entity) const {
222+
std::optional<Entity> FilterContents::GetEntity(
223+
const ContentContext& renderer,
224+
const Entity& entity,
225+
const std::optional<Rect>& coverage_hint) const {
228226
Entity entity_with_local_transform = entity;
229227
entity_with_local_transform.SetTransformation(
230228
GetTransform(entity.GetTransformation()));
@@ -235,7 +233,7 @@ std::optional<Entity> FilterContents::GetEntity(const ContentContext& renderer,
235233
}
236234

237235
return RenderFilter(inputs_, renderer, entity_with_local_transform,
238-
effect_transform_, coverage.value());
236+
effect_transform_, coverage.value(), coverage_hint);
239237
}
240238

241239
std::optional<Snapshot> FilterContents::RenderToSnapshot(
@@ -247,12 +245,13 @@ std::optional<Snapshot> FilterContents::RenderToSnapshot(
247245
const std::string& label) const {
248246
// Resolve the render instruction (entity) from the filter and render it to a
249247
// snapshot.
250-
if (std::optional<Entity> result = GetEntity(renderer, entity);
248+
if (std::optional<Entity> result =
249+
GetEntity(renderer, entity, coverage_limit);
251250
result.has_value()) {
252251
return result->GetContents()->RenderToSnapshot(
253252
renderer, // renderer
254253
result.value(), // entity
255-
std::nullopt, // coverage_limit
254+
coverage_limit, // coverage_limit
256255
std::nullopt, // sampler_descriptor
257256
true, // msaa_enabled
258257
label); // label

impeller/entity/contents/filters/filter_contents.h

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -102,16 +102,15 @@ class FilterContents : public Contents {
102102
/// particular filter's implementation.
103103
void SetInputs(FilterInput::Vector inputs);
104104

105-
/// @brief Screen space bounds to use for cropping the filter output.
106-
void SetCoverageCrop(std::optional<Rect> coverage_crop);
107-
108105
/// @brief Sets the transform which gets appended to the effect of this
109106
/// filter. Note that this is in addition to the entity's transform.
110107
void SetEffectTransform(Matrix effect_transform);
111108

112109
/// @brief Create an Entity that renders this filter's output.
113-
std::optional<Entity> GetEntity(const ContentContext& renderer,
114-
const Entity& entity) const;
110+
std::optional<Entity> GetEntity(
111+
const ContentContext& renderer,
112+
const Entity& entity,
113+
const std::optional<Rect>& coverage_hint) const;
115114

116115
// |Contents|
117116
bool Render(const ContentContext& renderer,
@@ -141,16 +140,17 @@ class FilterContents : public Contents {
141140
const Matrix& effect_transform) const;
142141

143142
/// @brief Converts zero or more filter inputs into a render instruction.
144-
virtual std::optional<Entity> RenderFilter(const FilterInput::Vector& inputs,
145-
const ContentContext& renderer,
146-
const Entity& entity,
147-
const Matrix& effect_transform,
148-
const Rect& coverage) const = 0;
143+
virtual std::optional<Entity> RenderFilter(
144+
const FilterInput::Vector& inputs,
145+
const ContentContext& renderer,
146+
const Entity& entity,
147+
const Matrix& effect_transform,
148+
const Rect& coverage,
149+
const std::optional<Rect>& coverage_hint) const = 0;
149150

150151
std::optional<Rect> GetLocalCoverage(const Entity& local_entity) const;
151152

152153
FilterInput::Vector inputs_;
153-
std::optional<Rect> coverage_crop_;
154154
Matrix effect_transform_;
155155

156156
FML_DISALLOW_COPY_AND_ASSIGN(FilterContents);

0 commit comments

Comments
 (0)