From 1a8ff96a0d69eac6d256baee698ade4fc00dcd46 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 19 May 2022 01:13:21 -0700 Subject: [PATCH] [Impeller] Keep track of a stencil depth floor for non-collapsed subpasses (flutter/engine#33473) --- .../flutter/impeller/aiks/aiks_unittests.cc | 24 ++++++++++++++++ engine/src/flutter/impeller/aiks/canvas.cc | 3 +- .../flutter/impeller/entity/entity_pass.cc | 28 +++++++++++++------ .../src/flutter/impeller/entity/entity_pass.h | 3 +- 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/engine/src/flutter/impeller/aiks/aiks_unittests.cc b/engine/src/flutter/impeller/aiks/aiks_unittests.cc index dbe9e60f26a..08e035ecde2 100644 --- a/engine/src/flutter/impeller/aiks/aiks_unittests.cc +++ b/engine/src/flutter/impeller/aiks/aiks_unittests.cc @@ -805,5 +805,29 @@ TEST_P(AiksTest, SiblingSaveLayerBoundsAreRespected) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_P(AiksTest, CanRenderClippedLayers) { + Canvas canvas; + + canvas.DrawPaint({.color = Color::White()}); + + // Draw a green circle on the screen. + { + // Increase the clip depth for the savelayer to contend with. + canvas.ClipPath(PathBuilder{}.AddCircle({100, 100}, 50).TakePath()); + + canvas.SaveLayer({}, Rect::MakeXYWH(50, 50, 100, 100)); + + // Fill the layer with white. + canvas.DrawRect(Rect::MakeSize({400, 400}), {.color = Color::White()}); + // Fill the layer with green, but do so with a color blend that can't be + // collapsed into the parent pass. + canvas.DrawRect( + Rect::MakeSize({400, 400}), + {.color = Color::Green(), .blend_mode = Entity::BlendMode::kColorBurn}); + } + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + } // namespace testing } // namespace impeller diff --git a/engine/src/flutter/impeller/aiks/canvas.cc b/engine/src/flutter/impeller/aiks/canvas.cc index e4e4f7fd3cb..05620e38cfe 100644 --- a/engine/src/flutter/impeller/aiks/canvas.cc +++ b/engine/src/flutter/impeller/aiks/canvas.cc @@ -266,7 +266,8 @@ size_t Canvas::GetStencilDepth() const { void Canvas::SaveLayer(Paint paint, std::optional bounds) { Save(true, paint.blend_mode); - GetCurrentPass().SetDelegate( + auto& new_layer_pass = GetCurrentPass(); + new_layer_pass.SetDelegate( std::make_unique(paint, bounds)); if (bounds.has_value()) { diff --git a/engine/src/flutter/impeller/entity/entity_pass.cc b/engine/src/flutter/impeller/entity/entity_pass.cc index 7bb12a1bdcc..58db9cdfec0 100644 --- a/engine/src/flutter/impeller/entity/entity_pass.cc +++ b/engine/src/flutter/impeller/entity/entity_pass.cc @@ -180,7 +180,8 @@ bool EntityPass::Render(ContentContext& renderer, bool EntityPass::RenderInternal(ContentContext& renderer, RenderTarget render_target, Point position, - uint32_t depth) const { + uint32_t pass_depth, + size_t stencil_depth_floor) const { TRACE_EVENT0("impeller", "EntityPass::Render"); auto context = renderer.GetContext(); @@ -233,7 +234,7 @@ bool EntityPass::RenderInternal(ContentContext& renderer, if (subpass->delegate_->CanCollapseIntoParentPass()) { // Directly render into the parent target and move on. if (!subpass->RenderInternal(renderer, render_target, position, - depth)) { + pass_depth, stencil_depth_floor)) { return false; } continue; @@ -286,13 +287,16 @@ bool EntityPass::RenderInternal(ContentContext& renderer, return false; } + // Stencil textures aren't shared between EntityPasses (as much of the + // time they are transient). if (!subpass->RenderInternal(renderer, subpass_target, - subpass_coverage->origin, ++depth)) { + subpass_coverage->origin, ++pass_depth, + subpass->stencil_depth_)) { return false; } element_entity.SetContents(std::move(offscreen_texture_contents)); - element_entity.SetStencilDepth(stencil_depth_); + element_entity.SetStencilDepth(subpass->stencil_depth_); element_entity.SetBlendMode(subpass->blend_mode_); // Once we have filters being applied for SaveLayer, some special sauce // may be needed here (or in PaintPassDelegate) to ensure the filter @@ -305,7 +309,7 @@ bool EntityPass::RenderInternal(ContentContext& renderer, } // ========================================================================= - // Render the element ====================================================== + // Configure the RenderPass ================================================ // ========================================================================= if (pass && element_entity.GetBlendMode() > @@ -350,7 +354,7 @@ bool EntityPass::RenderInternal(ContentContext& renderer, } command_buffer->SetLabel( - "EntityPass Command Buffer: Depth=" + std::to_string(depth) + + "EntityPass Command Buffer: Depth=" + std::to_string(pass_depth) + " Count=" + std::to_string(pass_count)); // Never clear the texture for subsequent passes. @@ -373,12 +377,20 @@ bool EntityPass::RenderInternal(ContentContext& renderer, return false; } - pass->SetLabel("EntityPass Render Pass: Depth=" + std::to_string(depth) + - " Count=" + std::to_string(pass_count)); + pass->SetLabel( + "EntityPass Render Pass: Depth=" + std::to_string(pass_depth) + + " Count=" + std::to_string(pass_count)); ++pass_count; } + // ========================================================================= + // Render the element ====================================================== + // ========================================================================= + + element_entity.SetStencilDepth(element_entity.GetStencilDepth() - + stencil_depth_floor); + if (!element_entity.Render(renderer, *pass)) { return false; } diff --git a/engine/src/flutter/impeller/entity/entity_pass.h b/engine/src/flutter/impeller/entity/entity_pass.h index fe37a719585..0566bd4b0f3 100644 --- a/engine/src/flutter/impeller/entity/entity_pass.h +++ b/engine/src/flutter/impeller/entity/entity_pass.h @@ -61,7 +61,8 @@ class EntityPass { bool RenderInternal(ContentContext& renderer, RenderTarget render_target, Point position, - uint32_t depth) const; + uint32_t pass_depth, + size_t stencil_depth_floor = 0) const; std::vector elements_;