diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 33ee6347b3976..079e28bae3dd5 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -2174,5 +2174,52 @@ TEST_P(AiksTest, CanRenderDestructiveSaveLayer) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_P(AiksTest, CanRenderBackdropBlurInteractive) { + auto callback = [&](AiksContext& renderer, RenderTarget& render_target) { + auto [a, b] = IMPELLER_PLAYGROUND_LINE(Point(50, 50), Point(300, 200), 30, + Color::White(), Color::White()); + + Canvas canvas; + canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()}); + canvas.DrawCircle({300, 200}, 100, {.color = Color::GreenYellow()}); + canvas.DrawCircle({140, 170}, 75, {.color = Color::DarkMagenta()}); + canvas.DrawCircle({180, 120}, 100, {.color = Color::OrangeRed()}); + canvas.ClipRRect(Rect::MakeLTRB(a.x, a.y, b.x, b.y), 20); + canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt, + [](const FilterInput::Ref& input, + const Matrix& effect_transform, bool is_subpass) { + return FilterContents::MakeGaussianBlur( + input, Sigma(20.0), Sigma(20.0), + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kClamp, effect_transform); + }); + canvas.Restore(); + + return renderer.Render(canvas.EndRecordingAsPicture(), render_target); + }; + + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + +TEST_P(AiksTest, CanRenderBackdropBlur) { + Canvas canvas; + canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()}); + canvas.DrawCircle({300, 200}, 100, {.color = Color::GreenYellow()}); + canvas.DrawCircle({140, 170}, 75, {.color = Color::DarkMagenta()}); + canvas.DrawCircle({180, 120}, 100, {.color = Color::OrangeRed()}); + canvas.ClipRRect(Rect::MakeLTRB(75, 50, 375, 275), 20); + canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt, + [](const FilterInput::Ref& input, + const Matrix& effect_transform, bool is_subpass) { + return FilterContents::MakeGaussianBlur( + input, Sigma(30.0), Sigma(30.0), + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kClamp, effect_transform); + }); + canvas.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + } // namespace testing } // namespace impeller diff --git a/impeller/entity/contents/contents.cc b/impeller/entity/contents/contents.cc index e81ace0d18646..975b45b77e2d3 100644 --- a/impeller/entity/contents/contents.cc +++ b/impeller/entity/contents/contents.cc @@ -124,6 +124,14 @@ bool Contents::ShouldRender(const Entity& entity, return stencil_coverage->IntersectsWithRect(coverage.value()); } +void Contents::SetCoverageHint(std::optional coverage_hint) { + coverage_hint_ = coverage_hint; +} + +const std::optional& Contents::GetCoverageHint() const { + return coverage_hint_; +} + std::optional Contents::GetColorSourceSize() const { return color_source_size_; }; diff --git a/impeller/entity/contents/contents.h b/impeller/entity/contents/contents.h index 42f52925a4a98..9d42fb55fcd03 100644 --- a/impeller/entity/contents/contents.h +++ b/impeller/entity/contents/contents.h @@ -56,6 +56,14 @@ class Contents { /// @brief Get the screen space bounding rectangle that this contents affects. virtual std::optional GetCoverage(const Entity& entity) const = 0; + /// @brief Hint that specifies the coverage area of this Contents that will + /// actually be used during rendering. This is for optimization + /// purposes only and can not be relied on as a clip. May optionally + /// affect the result of `GetCoverage()`. + void SetCoverageHint(std::optional coverage_hint); + + const std::optional& GetCoverageHint() const; + /// @brief Whether this Contents only emits opaque source colors from the /// fragment stage. This value does not account for any entity /// properties (e.g. the blend mode), clips/visibility culling, or @@ -110,6 +118,7 @@ class Contents { virtual void SetInheritedOpacity(Scalar opacity); private: + std::optional coverage_hint_; std::optional color_source_size_; FML_DISALLOW_COPY_AND_ASSIGN(Contents); diff --git a/impeller/entity/contents/filters/blend_filter_contents.cc b/impeller/entity/contents/filters/blend_filter_contents.cc index 015197dd15d37..bedf6543c25c5 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.cc +++ b/impeller/entity/contents/filters/blend_filter_contents.cc @@ -649,7 +649,8 @@ std::optional BlendFilterContents::RenderFilter( const ContentContext& renderer, const Entity& entity, const Matrix& effect_transform, - const Rect& coverage) const { + const Rect& coverage, + const std::optional& coverage_hint) const { if (inputs.empty()) { return std::nullopt; } diff --git a/impeller/entity/contents/filters/blend_filter_contents.h b/impeller/entity/contents/filters/blend_filter_contents.h index 099880db47baf..01078e9aba143 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.h +++ b/impeller/entity/contents/filters/blend_filter_contents.h @@ -32,11 +32,13 @@ class BlendFilterContents : public ColorFilterContents { private: // |FilterContents| - std::optional RenderFilter(const FilterInput::Vector& inputs, - const ContentContext& renderer, - const Entity& entity, - const Matrix& effect_transform, - const Rect& coverage) const override; + std::optional RenderFilter( + const FilterInput::Vector& inputs, + const ContentContext& renderer, + const Entity& entity, + const Matrix& effect_transform, + const Rect& coverage, + const std::optional& coverage_hint) const override; /// @brief Optimized advanced blend that avoids a second subpass when there is /// only a single input and a foreground color. diff --git a/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc b/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc index 05db0f0443e02..5ede46259dda1 100644 --- a/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc @@ -53,7 +53,8 @@ std::optional BorderMaskBlurFilterContents::RenderFilter( const ContentContext& renderer, const Entity& entity, const Matrix& effect_transform, - const Rect& coverage) const { + const Rect& coverage, + const std::optional& coverage_hint) const { using VS = BorderMaskBlurPipeline::VertexShader; using FS = BorderMaskBlurPipeline::FragmentShader; diff --git a/impeller/entity/contents/filters/border_mask_blur_filter_contents.h b/impeller/entity/contents/filters/border_mask_blur_filter_contents.h index 06dc386704fb9..7f8d12693984a 100644 --- a/impeller/entity/contents/filters/border_mask_blur_filter_contents.h +++ b/impeller/entity/contents/filters/border_mask_blur_filter_contents.h @@ -29,11 +29,13 @@ class BorderMaskBlurFilterContents final : public FilterContents { private: // |FilterContents| - std::optional RenderFilter(const FilterInput::Vector& input_textures, - const ContentContext& renderer, - const Entity& entity, - const Matrix& effect_transform, - const Rect& coverage) const override; + std::optional RenderFilter( + const FilterInput::Vector& input_textures, + const ContentContext& renderer, + const Entity& entity, + const Matrix& effect_transform, + const Rect& coverage, + const std::optional& coverage_hint) const override; Sigma sigma_x_; Sigma sigma_y_; diff --git a/impeller/entity/contents/filters/color_matrix_filter_contents.cc b/impeller/entity/contents/filters/color_matrix_filter_contents.cc index 2e007dabc71b0..600d7a4df5bba 100644 --- a/impeller/entity/contents/filters/color_matrix_filter_contents.cc +++ b/impeller/entity/contents/filters/color_matrix_filter_contents.cc @@ -29,7 +29,8 @@ std::optional ColorMatrixFilterContents::RenderFilter( const ContentContext& renderer, const Entity& entity, const Matrix& effect_transform, - const Rect& coverage) const { + const Rect& coverage, + const std::optional& coverage_hint) const { using VS = ColorMatrixColorFilterPipeline::VertexShader; using FS = ColorMatrixColorFilterPipeline::FragmentShader; diff --git a/impeller/entity/contents/filters/color_matrix_filter_contents.h b/impeller/entity/contents/filters/color_matrix_filter_contents.h index fb3a32058f2b9..f5aeb1dbf9dd5 100644 --- a/impeller/entity/contents/filters/color_matrix_filter_contents.h +++ b/impeller/entity/contents/filters/color_matrix_filter_contents.h @@ -24,11 +24,13 @@ class ColorMatrixFilterContents final : public ColorFilterContents { private: // |FilterContents| - std::optional RenderFilter(const FilterInput::Vector& input_textures, - const ContentContext& renderer, - const Entity& entity, - const Matrix& effect_transform, - const Rect& coverage) const override; + std::optional RenderFilter( + const FilterInput::Vector& input_textures, + const ContentContext& renderer, + const Entity& entity, + const Matrix& effect_transform, + const Rect& coverage, + const std::optional& coverage_hint) const override; ColorMatrix matrix_; diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc index 3c6f2cc059d71..d9e566c50a24d 100644 --- a/impeller/entity/contents/filters/filter_contents.cc +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -153,10 +153,6 @@ void FilterContents::SetInputs(FilterInput::Vector inputs) { inputs_ = std::move(inputs); } -void FilterContents::SetCoverageCrop(std::optional coverage_crop) { - coverage_crop_ = coverage_crop; -} - void FilterContents::SetEffectTransform(Matrix effect_transform) { effect_transform_ = effect_transform; } @@ -171,7 +167,7 @@ bool FilterContents::Render(const ContentContext& renderer, // Run the filter. - auto maybe_entity = GetEntity(renderer, entity); + auto maybe_entity = GetEntity(renderer, entity, GetCoverageHint()); if (!maybe_entity.has_value()) { return true; } @@ -181,8 +177,8 @@ bool FilterContents::Render(const ContentContext& renderer, std::optional FilterContents::GetLocalCoverage( const Entity& local_entity) const { auto coverage = GetFilterCoverage(inputs_, local_entity, effect_transform_); - if (coverage_crop_.has_value() && coverage.has_value()) { - coverage = coverage->Intersection(coverage_crop_.value()); + if (GetCoverageHint().has_value() && coverage.has_value()) { + coverage = coverage->Intersection(*GetCoverageHint()); } return coverage; @@ -223,8 +219,10 @@ std::optional FilterContents::GetFilterCoverage( return result; } -std::optional FilterContents::GetEntity(const ContentContext& renderer, - const Entity& entity) const { +std::optional FilterContents::GetEntity( + const ContentContext& renderer, + const Entity& entity, + const std::optional& coverage_hint) const { Entity entity_with_local_transform = entity; entity_with_local_transform.SetTransformation( GetTransform(entity.GetTransformation())); @@ -235,7 +233,7 @@ std::optional FilterContents::GetEntity(const ContentContext& renderer, } return RenderFilter(inputs_, renderer, entity_with_local_transform, - effect_transform_, coverage.value()); + effect_transform_, coverage.value(), coverage_hint); } std::optional FilterContents::RenderToSnapshot( @@ -247,12 +245,13 @@ std::optional FilterContents::RenderToSnapshot( const std::string& label) const { // Resolve the render instruction (entity) from the filter and render it to a // snapshot. - if (std::optional result = GetEntity(renderer, entity); + if (std::optional result = + GetEntity(renderer, entity, coverage_limit); result.has_value()) { return result->GetContents()->RenderToSnapshot( renderer, // renderer result.value(), // entity - std::nullopt, // coverage_limit + coverage_limit, // coverage_limit std::nullopt, // sampler_descriptor true, // msaa_enabled label); // label diff --git a/impeller/entity/contents/filters/filter_contents.h b/impeller/entity/contents/filters/filter_contents.h index 90605b7aee571..706abfa826fd1 100644 --- a/impeller/entity/contents/filters/filter_contents.h +++ b/impeller/entity/contents/filters/filter_contents.h @@ -102,16 +102,15 @@ class FilterContents : public Contents { /// particular filter's implementation. void SetInputs(FilterInput::Vector inputs); - /// @brief Screen space bounds to use for cropping the filter output. - void SetCoverageCrop(std::optional coverage_crop); - /// @brief Sets the transform which gets appended to the effect of this /// filter. Note that this is in addition to the entity's transform. void SetEffectTransform(Matrix effect_transform); /// @brief Create an Entity that renders this filter's output. - std::optional GetEntity(const ContentContext& renderer, - const Entity& entity) const; + std::optional GetEntity( + const ContentContext& renderer, + const Entity& entity, + const std::optional& coverage_hint) const; // |Contents| bool Render(const ContentContext& renderer, @@ -141,16 +140,17 @@ class FilterContents : public Contents { const Matrix& effect_transform) const; /// @brief Converts zero or more filter inputs into a render instruction. - virtual std::optional RenderFilter(const FilterInput::Vector& inputs, - const ContentContext& renderer, - const Entity& entity, - const Matrix& effect_transform, - const Rect& coverage) const = 0; + virtual std::optional RenderFilter( + const FilterInput::Vector& inputs, + const ContentContext& renderer, + const Entity& entity, + const Matrix& effect_transform, + const Rect& coverage, + const std::optional& coverage_hint) const = 0; std::optional GetLocalCoverage(const Entity& local_entity) const; FilterInput::Vector inputs_; - std::optional coverage_crop_; Matrix effect_transform_; FML_DISALLOW_COPY_AND_ASSIGN(FilterContents); diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index 6e33eefccadb9..d565414006682 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -86,10 +86,13 @@ std::optional DirectionalGaussianBlurFilterContents::RenderFilter( const ContentContext& renderer, const Entity& entity, const Matrix& effect_transform, - const Rect& coverage) const { + const Rect& coverage, + const std::optional& coverage_hint) const { using VS = GaussianBlurAlphaDecalPipeline::VertexShader; using FS = GaussianBlurAlphaDecalPipeline::FragmentShader; + bool is_first_pass = !source_override_; + //---------------------------------------------------------------------------- /// Handle inputs. /// @@ -98,10 +101,20 @@ std::optional DirectionalGaussianBlurFilterContents::RenderFilter( return std::nullopt; } + auto radius = Radius{blur_sigma_}.radius; + // Input 0 snapshot. - auto input_snapshot = - inputs[0]->GetSnapshot("GaussianBlur", renderer, entity); + std::optional expanded_coverage_hint; + if (coverage_hint.has_value()) { + auto r = Size(radius, radius).Abs(); + expanded_coverage_hint = + is_first_pass ? Rect(coverage_hint.value().origin - r, + Size(coverage_hint.value().size + r * 2)) + : coverage_hint; + } + auto input_snapshot = inputs[0]->GetSnapshot("GaussianBlur", renderer, entity, + expanded_coverage_hint); if (!input_snapshot.has_value()) { return std::nullopt; } @@ -112,8 +125,6 @@ std::optional DirectionalGaussianBlurFilterContents::RenderFilter( entity.GetStencilDepth()); // No blur to render. } - auto radius = Radius{blur_sigma_}.radius; - auto transform = entity.GetTransformation() * effect_transform.Basis(); auto transformed_blur_radius = transform.TransformDirection(blur_direction_ * radius); @@ -146,11 +157,21 @@ std::optional DirectionalGaussianBlurFilterContents::RenderFilter( pass_texture_rect.origin.x -= transformed_blur_radius_length; pass_texture_rect.size.width += transformed_blur_radius_length * 2; + // Crop the pass texture with the rotated coverage hint if one was given. + if (expanded_coverage_hint.has_value()) { + auto maybe_pass_texture_rect = pass_texture_rect.Intersection( + expanded_coverage_hint->TransformBounds(texture_rotate)); + if (!maybe_pass_texture_rect.has_value()) { + return std::nullopt; + } + pass_texture_rect = *maybe_pass_texture_rect; + } + // Source override snapshot. auto source = source_override_ ? source_override_ : inputs[0]; - auto source_snapshot = - source->GetSnapshot("GaussianBlur(Override)", renderer, entity); + auto source_snapshot = source->GetSnapshot("GaussianBlur(Override)", renderer, + entity, GetCoverageHint()); if (!source_snapshot.has_value()) { return std::nullopt; } @@ -172,8 +193,9 @@ std::optional DirectionalGaussianBlurFilterContents::RenderFilter( /// Render to texture. /// - ContentContext::SubpassCallback callback = [&](const ContentContext& renderer, - RenderPass& pass) { + ContentContext::SubpassCallback subpass_callback = [&](const ContentContext& + renderer, + RenderPass& pass) { auto& host_buffer = pass.GetTransientsBuffer(); VertexBufferBuilder vtx_builder; @@ -294,8 +316,8 @@ std::optional DirectionalGaussianBlurFilterContents::RenderFilter( { scale.x = scale_curve(transformed_blur_radius_length); - Scalar y_radius = std::abs(pass_transform.GetDirectionScale(Vector2( - 0, source_override_ ? Radius{secondary_blur_sigma_}.radius : 1))); + Scalar y_radius = std::abs(pass_transform.GetDirectionScale( + Vector2(0, !is_first_pass ? Radius{secondary_blur_sigma_}.radius : 1))); scale.y = scale_curve(y_radius); } @@ -303,7 +325,7 @@ std::optional DirectionalGaussianBlurFilterContents::RenderFilter( ISize floored_size = ISize(scaled_size.x, scaled_size.y); auto out_texture = renderer.MakeSubpass("Directional Gaussian Blur Filter", - floored_size, callback); + floored_size, subpass_callback); if (!out_texture) { return std::nullopt; @@ -339,7 +361,7 @@ std::optional DirectionalGaussianBlurFilterContents::GetFilterCoverage( auto transform = inputs[0]->GetTransform(entity) * effect_transform.Basis(); auto transformed_blur_vector = - transform.TransformDirection(blur_direction_* Radius{blur_sigma_}.radius) + transform.TransformDirection(blur_direction_ * Radius{blur_sigma_}.radius) .Abs(); auto extent = coverage->size + transformed_blur_vector * 2; return Rect(coverage->origin - transformed_blur_vector, diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h index b69f6627e1485..1f4ef1f450605 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h @@ -37,11 +37,13 @@ class DirectionalGaussianBlurFilterContents final : public FilterContents { private: // |FilterContents| - std::optional RenderFilter(const FilterInput::Vector& input_textures, - const ContentContext& renderer, - const Entity& entity, - const Matrix& effect_transform, - const Rect& coverage) const override; + std::optional RenderFilter( + const FilterInput::Vector& input_textures, + const ContentContext& renderer, + const Entity& entity, + const Matrix& effect_transform, + const Rect& coverage, + const std::optional& coverage_hint) const override; Sigma blur_sigma_; Sigma secondary_blur_sigma_; Vector2 blur_direction_; diff --git a/impeller/entity/contents/filters/inputs/contents_filter_input.cc b/impeller/entity/contents/filters/inputs/contents_filter_input.cc index b2babf4cd23fd..d50788e67f183 100644 --- a/impeller/entity/contents/filters/inputs/contents_filter_input.cc +++ b/impeller/entity/contents/filters/inputs/contents_filter_input.cc @@ -24,14 +24,15 @@ FilterInput::Variant ContentsFilterInput::GetInput() const { std::optional ContentsFilterInput::GetSnapshot( const std::string& label, const ContentContext& renderer, - const Entity& entity) const { + const Entity& entity, + std::optional coverage_limit) const { if (!snapshot_.has_value()) { snapshot_ = contents_->RenderToSnapshot( - renderer, // renderer - entity, // entity - std::nullopt, // coverage_limit - std::nullopt, // sampler_descriptor - msaa_enabled_, // msaa_enabled + renderer, // renderer + entity, // entity + coverage_limit, // coverage_limit + std::nullopt, // sampler_descriptor + msaa_enabled_, // msaa_enabled SPrintF("Contents to %s Filter Snapshot", label.c_str())); // label } return snapshot_; diff --git a/impeller/entity/contents/filters/inputs/contents_filter_input.h b/impeller/entity/contents/filters/inputs/contents_filter_input.h index 85a975416cc71..288cfe9a812a9 100644 --- a/impeller/entity/contents/filters/inputs/contents_filter_input.h +++ b/impeller/entity/contents/filters/inputs/contents_filter_input.h @@ -16,9 +16,11 @@ class ContentsFilterInput final : public FilterInput { Variant GetInput() const override; // |FilterInput| - std::optional GetSnapshot(const std::string& label, - const ContentContext& renderer, - const Entity& entity) const override; + std::optional GetSnapshot( + const std::string& label, + const ContentContext& renderer, + const Entity& entity, + std::optional coverage_limit) const override; // |FilterInput| std::optional GetCoverage(const Entity& entity) const override; diff --git a/impeller/entity/contents/filters/inputs/filter_contents_filter_input.cc b/impeller/entity/contents/filters/inputs/filter_contents_filter_input.cc index d92e7a0ed9cbd..96b5f7e75c5f4 100644 --- a/impeller/entity/contents/filters/inputs/filter_contents_filter_input.cc +++ b/impeller/entity/contents/filters/inputs/filter_contents_filter_input.cc @@ -24,14 +24,15 @@ FilterInput::Variant FilterContentsFilterInput::GetInput() const { std::optional FilterContentsFilterInput::GetSnapshot( const std::string& label, const ContentContext& renderer, - const Entity& entity) const { + const Entity& entity, + std::optional coverage_limit) const { if (!snapshot_.has_value()) { snapshot_ = filter_->RenderToSnapshot( - renderer, // renderer - entity, // entity - std::nullopt, // coverage_limit - std::nullopt, // sampler_descriptor - true, // msaa_enabled + renderer, // renderer + entity, // entity + coverage_limit, // coverage_limit + std::nullopt, // sampler_descriptor + true, // msaa_enabled SPrintF("Filter to %s Filter Snapshot", label.c_str())); // label } return snapshot_; diff --git a/impeller/entity/contents/filters/inputs/filter_contents_filter_input.h b/impeller/entity/contents/filters/inputs/filter_contents_filter_input.h index 778ca1e69eaf8..c546b85fa1b00 100644 --- a/impeller/entity/contents/filters/inputs/filter_contents_filter_input.h +++ b/impeller/entity/contents/filters/inputs/filter_contents_filter_input.h @@ -16,9 +16,11 @@ class FilterContentsFilterInput final : public FilterInput { Variant GetInput() const override; // |FilterInput| - std::optional GetSnapshot(const std::string& label, - const ContentContext& renderer, - const Entity& entity) const override; + std::optional GetSnapshot( + const std::string& label, + const ContentContext& renderer, + const Entity& entity, + std::optional coverage_limit) const override; // |FilterInput| std::optional GetCoverage(const Entity& entity) const override; diff --git a/impeller/entity/contents/filters/inputs/filter_input.h b/impeller/entity/contents/filters/inputs/filter_input.h index 07e185cca7fa9..7eb228851f531 100644 --- a/impeller/entity/contents/filters/inputs/filter_input.h +++ b/impeller/entity/contents/filters/inputs/filter_input.h @@ -45,9 +45,11 @@ class FilterInput { virtual Variant GetInput() const = 0; - virtual std::optional GetSnapshot(const std::string& label, - const ContentContext& renderer, - const Entity& entity) const = 0; + virtual std::optional GetSnapshot( + const std::string& label, + const ContentContext& renderer, + const Entity& entity, + std::optional coverage_limit = std::nullopt) const = 0; std::optional GetLocalCoverage(const Entity& entity) const; diff --git a/impeller/entity/contents/filters/inputs/texture_filter_input.cc b/impeller/entity/contents/filters/inputs/texture_filter_input.cc index 6134ca059a722..903bca976b597 100644 --- a/impeller/entity/contents/filters/inputs/texture_filter_input.cc +++ b/impeller/entity/contents/filters/inputs/texture_filter_input.cc @@ -23,7 +23,8 @@ FilterInput::Variant TextureFilterInput::GetInput() const { std::optional TextureFilterInput::GetSnapshot( const std::string& label, const ContentContext& renderer, - const Entity& entity) const { + const Entity& entity, + std::optional coverage_limit) const { auto snapshot = Snapshot{.texture = texture_, .transform = GetTransform(entity)}; if (texture_->GetMipCount() > 1) { diff --git a/impeller/entity/contents/filters/inputs/texture_filter_input.h b/impeller/entity/contents/filters/inputs/texture_filter_input.h index 29f15c2e6fa51..dd4443e14ac8a 100644 --- a/impeller/entity/contents/filters/inputs/texture_filter_input.h +++ b/impeller/entity/contents/filters/inputs/texture_filter_input.h @@ -18,9 +18,11 @@ class TextureFilterInput final : public FilterInput { Variant GetInput() const override; // |FilterInput| - std::optional GetSnapshot(const std::string& label, - const ContentContext& renderer, - const Entity& entity) const override; + std::optional GetSnapshot( + const std::string& label, + const ContentContext& renderer, + const Entity& entity, + std::optional coverage_limit) const override; // |FilterInput| std::optional GetCoverage(const Entity& entity) const override; diff --git a/impeller/entity/contents/filters/linear_to_srgb_filter_contents.cc b/impeller/entity/contents/filters/linear_to_srgb_filter_contents.cc index 7f5dc0bd05c68..54a54854a8b4d 100644 --- a/impeller/entity/contents/filters/linear_to_srgb_filter_contents.cc +++ b/impeller/entity/contents/filters/linear_to_srgb_filter_contents.cc @@ -23,7 +23,8 @@ std::optional LinearToSrgbFilterContents::RenderFilter( const ContentContext& renderer, const Entity& entity, const Matrix& effect_transform, - const Rect& coverage) const { + const Rect& coverage, + const std::optional& coverage_hint) const { if (inputs.empty()) { return std::nullopt; } diff --git a/impeller/entity/contents/filters/linear_to_srgb_filter_contents.h b/impeller/entity/contents/filters/linear_to_srgb_filter_contents.h index ee2390c5980c3..9c712b96b2b21 100644 --- a/impeller/entity/contents/filters/linear_to_srgb_filter_contents.h +++ b/impeller/entity/contents/filters/linear_to_srgb_filter_contents.h @@ -17,11 +17,13 @@ class LinearToSrgbFilterContents final : public ColorFilterContents { private: // |FilterContents| - std::optional RenderFilter(const FilterInput::Vector& input_textures, - const ContentContext& renderer, - const Entity& entity, - const Matrix& effect_transform, - const Rect& coverage) const override; + std::optional RenderFilter( + const FilterInput::Vector& input_textures, + const ContentContext& renderer, + const Entity& entity, + const Matrix& effect_transform, + const Rect& coverage, + const std::optional& coverage_hint) const override; FML_DISALLOW_COPY_AND_ASSIGN(LinearToSrgbFilterContents); }; diff --git a/impeller/entity/contents/filters/local_matrix_filter_contents.cc b/impeller/entity/contents/filters/local_matrix_filter_contents.cc index 1cdce565db516..709af709fc739 100644 --- a/impeller/entity/contents/filters/local_matrix_filter_contents.cc +++ b/impeller/entity/contents/filters/local_matrix_filter_contents.cc @@ -24,7 +24,8 @@ std::optional LocalMatrixFilterContents::RenderFilter( const ContentContext& renderer, const Entity& entity, const Matrix& effect_transform, - const Rect& coverage) const { + const Rect& coverage, + const std::optional& coverage_hint) const { return Entity::FromSnapshot( inputs[0]->GetSnapshot("LocalMatrix", renderer, entity), entity.GetBlendMode(), entity.GetStencilDepth()); diff --git a/impeller/entity/contents/filters/local_matrix_filter_contents.h b/impeller/entity/contents/filters/local_matrix_filter_contents.h index 824f1c36e3bd5..e243b8584fb3d 100644 --- a/impeller/entity/contents/filters/local_matrix_filter_contents.h +++ b/impeller/entity/contents/filters/local_matrix_filter_contents.h @@ -22,11 +22,13 @@ class LocalMatrixFilterContents final : public FilterContents { private: // |FilterContents| - std::optional RenderFilter(const FilterInput::Vector& input_textures, - const ContentContext& renderer, - const Entity& entity, - const Matrix& effect_transform, - const Rect& coverage) const override; + std::optional RenderFilter( + const FilterInput::Vector& input_textures, + const ContentContext& renderer, + const Entity& entity, + const Matrix& effect_transform, + const Rect& coverage, + const std::optional& coverage_hint) const override; Matrix matrix_; diff --git a/impeller/entity/contents/filters/matrix_filter_contents.cc b/impeller/entity/contents/filters/matrix_filter_contents.cc index b64d91bddcb67..b9c9c2424df7d 100644 --- a/impeller/entity/contents/filters/matrix_filter_contents.cc +++ b/impeller/entity/contents/filters/matrix_filter_contents.cc @@ -27,7 +27,8 @@ std::optional MatrixFilterContents::RenderFilter( const ContentContext& renderer, const Entity& entity, const Matrix& effect_transform, - const Rect& coverage) const { + const Rect& coverage, + const std::optional& coverage_hint) const { auto snapshot = inputs[0]->GetSnapshot("Matrix", renderer, entity); if (!snapshot.has_value()) { return std::nullopt; diff --git a/impeller/entity/contents/filters/matrix_filter_contents.h b/impeller/entity/contents/filters/matrix_filter_contents.h index bf2e49b2904d2..902f6a8c98d1a 100644 --- a/impeller/entity/contents/filters/matrix_filter_contents.h +++ b/impeller/entity/contents/filters/matrix_filter_contents.h @@ -29,11 +29,13 @@ class MatrixFilterContents final : public FilterContents { private: // |FilterContents| - std::optional RenderFilter(const FilterInput::Vector& input_textures, - const ContentContext& renderer, - const Entity& entity, - const Matrix& effect_transform, - const Rect& coverage) const override; + std::optional RenderFilter( + const FilterInput::Vector& input_textures, + const ContentContext& renderer, + const Entity& entity, + const Matrix& effect_transform, + const Rect& coverage, + const std::optional& coverage_hint) const override; Matrix matrix_; SamplerDescriptor sampler_descriptor_ = {}; diff --git a/impeller/entity/contents/filters/morphology_filter_contents.cc b/impeller/entity/contents/filters/morphology_filter_contents.cc index 4a94e1ce6cc65..16b5cc5daf5b5 100644 --- a/impeller/entity/contents/filters/morphology_filter_contents.cc +++ b/impeller/entity/contents/filters/morphology_filter_contents.cc @@ -39,7 +39,8 @@ std::optional DirectionalMorphologyFilterContents::RenderFilter( const ContentContext& renderer, const Entity& entity, const Matrix& effect_transform, - const Rect& coverage) const { + const Rect& coverage, + const std::optional& coverage_hint) const { using VS = MorphologyFilterPipeline::VertexShader; using FS = MorphologyFilterPipeline::FragmentShader; diff --git a/impeller/entity/contents/filters/morphology_filter_contents.h b/impeller/entity/contents/filters/morphology_filter_contents.h index 6c4ac20a561d8..7a37f544801da 100644 --- a/impeller/entity/contents/filters/morphology_filter_contents.h +++ b/impeller/entity/contents/filters/morphology_filter_contents.h @@ -31,11 +31,13 @@ class DirectionalMorphologyFilterContents final : public FilterContents { private: // |FilterContents| - std::optional RenderFilter(const FilterInput::Vector& input_textures, - const ContentContext& renderer, - const Entity& entity, - const Matrix& effect_transform, - const Rect& coverage) const override; + std::optional RenderFilter( + const FilterInput::Vector& input_textures, + const ContentContext& renderer, + const Entity& entity, + const Matrix& effect_transform, + const Rect& coverage, + const std::optional& coverage_hint) const override; Radius radius_; Vector2 direction_; diff --git a/impeller/entity/contents/filters/srgb_to_linear_filter_contents.cc b/impeller/entity/contents/filters/srgb_to_linear_filter_contents.cc index 0060c96e0a68e..da5181681497d 100644 --- a/impeller/entity/contents/filters/srgb_to_linear_filter_contents.cc +++ b/impeller/entity/contents/filters/srgb_to_linear_filter_contents.cc @@ -23,7 +23,8 @@ std::optional SrgbToLinearFilterContents::RenderFilter( const ContentContext& renderer, const Entity& entity, const Matrix& effect_transform, - const Rect& coverage) const { + const Rect& coverage, + const std::optional& coverage_hint) const { if (inputs.empty()) { return std::nullopt; } diff --git a/impeller/entity/contents/filters/srgb_to_linear_filter_contents.h b/impeller/entity/contents/filters/srgb_to_linear_filter_contents.h index 6568e9db43308..2b077510f1dc7 100644 --- a/impeller/entity/contents/filters/srgb_to_linear_filter_contents.h +++ b/impeller/entity/contents/filters/srgb_to_linear_filter_contents.h @@ -17,11 +17,13 @@ class SrgbToLinearFilterContents final : public ColorFilterContents { private: // |FilterContents| - std::optional RenderFilter(const FilterInput::Vector& input_textures, - const ContentContext& renderer, - const Entity& entity, - const Matrix& effect_transform, - const Rect& coverage) const override; + std::optional RenderFilter( + const FilterInput::Vector& input_textures, + const ContentContext& renderer, + const Entity& entity, + const Matrix& effect_transform, + const Rect& coverage, + const std::optional& coverage_hint) const override; FML_DISALLOW_COPY_AND_ASSIGN(SrgbToLinearFilterContents); }; diff --git a/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.cc b/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.cc index 4041171ad76a6..fa10a460ee1e6 100644 --- a/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.cc +++ b/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.cc @@ -42,7 +42,8 @@ std::optional YUVToRGBFilterContents::RenderFilter( const ContentContext& renderer, const Entity& entity, const Matrix& effect_transform, - const Rect& coverage) const { + const Rect& coverage, + const std::optional& coverage_hint) const { if (inputs.size() < 2) { return std::nullopt; } diff --git a/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.h b/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.h index 45ac58cfbe3d3..4de9e5c3319ea 100644 --- a/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.h +++ b/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.h @@ -18,11 +18,13 @@ class YUVToRGBFilterContents final : public FilterContents { private: // |FilterContents| - std::optional RenderFilter(const FilterInput::Vector& input_textures, - const ContentContext& renderer, - const Entity& entity, - const Matrix& effect_transform, - const Rect& coverage) const override; + std::optional RenderFilter( + const FilterInput::Vector& input_textures, + const ContentContext& renderer, + const Entity& entity, + const Matrix& effect_transform, + const Rect& coverage, + const std::optional& coverage_hint) const override; YUVColorSpace yuv_color_space_ = YUVColorSpace::kBT601LimitedRange; diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 78684a3da8b3c..8ffd24390d9b9 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -75,7 +75,7 @@ size_t EntityPass::GetSubpassesDepth() const { } std::optional EntityPass::GetElementsCoverage( - std::optional coverage_crop) const { + std::optional coverage_limit) const { std::optional result; for (const auto& element : elements_) { std::optional coverage; @@ -83,12 +83,12 @@ std::optional EntityPass::GetElementsCoverage( if (auto entity = std::get_if(&element)) { coverage = entity->GetCoverage(); - if (coverage.has_value() && coverage_crop.has_value()) { - coverage = coverage->Intersection(coverage_crop.value()); + if (coverage.has_value() && coverage_limit.has_value()) { + coverage = coverage->Intersection(coverage_limit.value()); } } else if (auto subpass = std::get_if>(&element)) { - coverage = GetSubpassCoverage(*subpass->get(), coverage_crop); + coverage = GetSubpassCoverage(*subpass->get(), coverage_limit); } else { FML_UNREACHABLE(); } @@ -107,8 +107,8 @@ std::optional EntityPass::GetElementsCoverage( std::optional EntityPass::GetSubpassCoverage( const EntityPass& subpass, - std::optional coverage_clip) const { - auto entities_coverage = subpass.GetElementsCoverage(coverage_clip); + std::optional coverage_limit) const { + auto entities_coverage = subpass.GetElementsCoverage(coverage_limit); // The entities don't cover anything. There is nothing to do. if (!entities_coverage.has_value()) { return std::nullopt; @@ -434,50 +434,56 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( pass_context.EndPass(); } - auto subpass_coverage = - GetSubpassCoverage(*subpass, Rect::MakeSize(root_pass_size)); - if (subpass->cover_whole_screen_) { - subpass_coverage = - Rect(global_pass_position, Size(pass_context.GetPassTarget() - .GetRenderTarget() - .GetRenderTargetSize())); + if (stencil_coverage_stack.empty() || + !stencil_coverage_stack.back().coverage.has_value()) { + // The current clip is empty. This means the pass texture won't be + // visible, so skip it. + return EntityPass::EntityResult::Skip(); } - if (backdrop_filter_contents) { - auto backdrop_coverage = backdrop_filter_contents->GetCoverage(Entity{}); - if (backdrop_coverage.has_value()) { - backdrop_coverage->origin += global_pass_position; - subpass_coverage = - subpass_coverage.has_value() - ? subpass_coverage->Union(backdrop_coverage.value()) - : backdrop_coverage; - } + // The maximum coverage of the subpass. Subpasses textures should never + // extend outside the parent pass texture or the current clip coverage. + auto coverage_limit = + Rect(global_pass_position, Size(pass_context.GetPassTarget() + .GetRenderTarget() + .GetRenderTargetSize())) + .Intersection(*stencil_coverage_stack.back().coverage); + if (!coverage_limit.has_value()) { + return EntityPass::EntityResult::Skip(); } + coverage_limit = + coverage_limit->Intersection(Rect::MakeSize(root_pass_size)); + if (!coverage_limit.has_value()) { + return EntityPass::EntityResult::Skip(); + } + + auto subpass_coverage = (subpass->flood_clip_ || backdrop_filter_contents) + ? coverage_limit + : GetSubpassCoverage(*subpass, coverage_limit); if (!subpass_coverage.has_value()) { - // The subpass doesn't contain anything visible, so skip it. return EntityPass::EntityResult::Skip(); } - subpass_coverage = - subpass_coverage->Intersection(Rect::MakeSize(root_pass_size)); - if (!subpass_coverage.has_value() || - ISize(subpass_coverage->size).IsEmpty()) { - // The subpass doesn't contain anything visible, so skip it. + auto subpass_size = ISize(subpass_coverage->size); + if (subpass_size.IsEmpty()) { return EntityPass::EntityResult::Skip(); } - auto subpass_target = - CreateRenderTarget(renderer, // - ISize(subpass_coverage->size), // - subpass->GetTotalPassReads(renderer) > 0, // - clear_color_.Premultiply()); + auto subpass_target = CreateRenderTarget( + renderer, // renderer + subpass_size, // size + subpass->GetTotalPassReads(renderer) > 0, // readable + clear_color_.Premultiply()); // clear_color if (!subpass_target.IsValid()) { VALIDATION_LOG << "Subpass render target is invalid."; return EntityPass::EntityResult::Failure(); } + FML_LOG(ERROR) << "Origin: " << subpass_coverage->origin + << " Size: " << subpass_coverage->size; + // Stencil textures aren't shared between EntityPasses (as much of the // time they are transient). if (!subpass->OnRender(renderer, // renderer @@ -682,10 +688,21 @@ bool EntityPass::OnRender( return false; } + // Tell the backdrop contents which portion of the rendered output will + // actually be used. The contents may optionally use this hint to avoid + // unnecessary rendering work. + if (!stencil_coverage_stack.empty() && + stencil_coverage_stack.back().coverage.has_value()) { + auto coverage_hint = Rect( + stencil_coverage_stack.back().coverage->origin - global_pass_position, + stencil_coverage_stack.back().coverage->size); + backdrop_filter_contents->SetCoverageHint(coverage_hint); + } + Entity backdrop_entity; backdrop_entity.SetContents(std::move(backdrop_filter_contents)); backdrop_entity.SetTransformation( - Matrix::MakeTranslation(Vector3(local_pass_position))); + Matrix::MakeTranslation(Vector3(-local_pass_position))); backdrop_entity.SetStencilDepth(stencil_depth_floor); render_element(backdrop_entity); @@ -758,7 +775,7 @@ bool EntityPass::OnRender( FilterInput::Make(result.entity.GetContents())}; auto contents = ColorFilterContents::MakeBlend( result.entity.GetBlendMode(), inputs); - contents->SetCoverageCrop(result.entity.GetCoverage()); + contents->SetCoverageHint(result.entity.GetCoverage()); result.entity.SetContents(std::move(contents)); result.entity.SetBlendMode(BlendMode::kSource); } @@ -876,7 +893,7 @@ void EntityPass::SetStencilDepth(size_t stencil_depth) { void EntityPass::SetBlendMode(BlendMode blend_mode) { blend_mode_ = blend_mode; - cover_whole_screen_ = Entity::IsBlendModeDestructive(blend_mode); + flood_clip_ = Entity::IsBlendModeDestructive(blend_mode); } void EntityPass::SetClearColor(Color clear_color) { diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index 649151de0a14b..673e162c57881 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -96,10 +96,10 @@ class EntityPass { std::optional GetSubpassCoverage( const EntityPass& subpass, - std::optional coverage_crop) const; + std::optional coverage_limit) const; std::optional GetElementsCoverage( - std::optional coverage_crop) const; + std::optional coverage_limit) const; private: struct EntityResult { @@ -209,7 +209,7 @@ class EntityPass { Matrix xformation_; size_t stencil_depth_ = 0u; BlendMode blend_mode_ = BlendMode::kSourceOver; - bool cover_whole_screen_ = false; + bool flood_clip_ = false; Color clear_color_ = Color::BlackTransparent(); bool enable_offscreen_debug_checkerboard_ = false; diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 007664bbf3ae1..999d1f0132071 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -202,7 +202,7 @@ TEST_P(EntityTest, FilterCoverageRespectsCropRect) { // With the crop rect. { auto expected = Rect::MakeLTRB(50, 50, 100, 100); - filter->SetCoverageCrop(expected); + filter->SetCoverageHint(expected); auto actual = filter->GetCoverage({}); ASSERT_TRUE(actual.has_value()); diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index 146f198f3b864..680e2056608c2 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -82,6 +82,8 @@ struct TSize { }; } + constexpr TSize Abs() const { return {std::fabs(width), std::fabs(height)}; } + constexpr TSize Floor() const { return {std::floor(width), std::floor(height)}; } diff --git a/impeller/golden_tests/golden_playground_test_mac.cc b/impeller/golden_tests/golden_playground_test_mac.cc index 409ee630d9951..eea27721207a1 100644 --- a/impeller/golden_tests/golden_playground_test_mac.cc +++ b/impeller/golden_tests/golden_playground_test_mac.cc @@ -23,6 +23,8 @@ static const std::vector kSkipTests = { "impeller_Play_AiksTest_CanRenderRadialGradient_Vulkan", "impeller_Play_AiksTest_CanRenderRadialGradientManyColors_Metal", "impeller_Play_AiksTest_CanRenderRadialGradientManyColors_Vulkan", + "impeller_Play_AiksTest_CanRenderBackdropBlurInteractive_Metal", + "impeller_Play_AiksTest_CanRenderBackdropBlurInteractive_Vulkan", "impeller_Play_AiksTest_TextFrameSubpixelAlignment_Metal", "impeller_Play_AiksTest_TextFrameSubpixelAlignment_Vulkan", "impeller_Play_AiksTest_ColorWheel_Metal",