diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index a756bbf0185ad..0851fd7e0de82 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -106,6 +106,19 @@ TEST_F(AiksTest, CanRenderClips) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_F(AiksTest, CanRenderNestedClips) { + Canvas canvas; + Paint paint; + paint.color = Color::Fuchsia(); + canvas.Save(); + canvas.ClipPath(PathBuilder{}.AddCircle({200, 400}, 300).TakePath()); + canvas.Restore(); + canvas.ClipPath(PathBuilder{}.AddCircle({600, 400}, 300).TakePath()); + canvas.ClipPath(PathBuilder{}.AddCircle({400, 600}, 300).TakePath()); + canvas.DrawRect(Rect::MakeXYWH(200, 200, 400, 400), paint); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + TEST_F(AiksTest, CanSaveLayerStandalone) { Canvas canvas; diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 3af66e13e8918..0825d5c2bac4d 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -45,7 +45,14 @@ bool Canvas::Restore() { current_pass_ = GetCurrentPass().GetSuperpass(); FML_DCHECK(current_pass_); } + + bool contains_clips = xformation_stack_.back().contains_clips; xformation_stack_.pop_back(); + + if (contains_clips) { + RestoreClip(); + } + return true; } @@ -129,8 +136,6 @@ void Canvas::SaveLayer(Paint paint, std::optional bounds) { } void Canvas::ClipPath(Path path) { - IncrementStencilDepth(); - Entity entity; entity.SetTransformation(GetCurrentTransformation()); entity.SetPath(std::move(path)); @@ -139,6 +144,22 @@ void Canvas::ClipPath(Path path) { entity.SetAddsToCoverage(false); GetCurrentPass().AddEntity(std::move(entity)); + + ++xformation_stack_.back().stencil_depth; + xformation_stack_.back().contains_clips = true; +} + +void Canvas::RestoreClip() { + Entity entity; + entity.SetTransformation(GetCurrentTransformation()); + // This path is empty because ClipRestoreContents just generates a quad that + // takes up the full render target. + entity.SetPath({}); + entity.SetContents(std::make_shared()); + entity.SetStencilDepth(GetStencilDepth()); + entity.SetAddsToCoverage(false); + + GetCurrentPass().AddEntity(std::move(entity)); } void Canvas::DrawShadow(Path path, Color color, Scalar elevation) {} @@ -213,10 +234,6 @@ EntityPass& Canvas::GetCurrentPass() { return *current_pass_; } -void Canvas::IncrementStencilDepth() { - ++xformation_stack_.back().stencil_depth; -} - size_t Canvas::GetStencilDepth() const { return xformation_stack_.back().stencil_depth; } diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 2b00151f585a0..367669eccbdb8 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -87,12 +87,12 @@ class Canvas { EntityPass& GetCurrentPass(); - void IncrementStencilDepth(); - size_t GetStencilDepth() const; void Save(bool create_subpass); + void RestoreClip(); + FML_DISALLOW_COPY_AND_ASSIGN(Canvas); }; diff --git a/impeller/entity/content_context.cc b/impeller/entity/content_context.cc index 5a628518df3ab..53fd91745f6ac 100644 --- a/impeller/entity/content_context.cc +++ b/impeller/entity/content_context.cc @@ -23,25 +23,44 @@ ContentContext::ContentContext(std::shared_ptr context) std::make_unique(*context_); // Pipelines that are variants of the base pipelines with custom descriptors. + // TODO(98684): Rework this API to allow fetching the descriptor without + // waiting for the pipeline to build. if (auto solid_fill_pipeline = solid_fill_pipelines_[{}]->WaitAndGet()) { - auto clip_pipeline_descriptor = solid_fill_pipeline->GetDescriptor(); - clip_pipeline_descriptor.SetLabel("Clip Pipeline"); - // Write to the stencil buffer. - StencilAttachmentDescriptor stencil0; - stencil0.stencil_compare = CompareFunction::kGreaterEqual; - stencil0.depth_stencil_pass = StencilOperation::kSetToReferenceValue; - clip_pipeline_descriptor.SetStencilAttachmentDescriptors(stencil0); - // Disable write to all color attachments. - auto color_attachments = - clip_pipeline_descriptor.GetColorAttachmentDescriptors(); - for (auto& color_attachment : color_attachments) { - color_attachment.second.write_mask = - static_cast(ColorWriteMask::kNone); + // Clip pipeline. + { + auto clip_pipeline_descriptor = solid_fill_pipeline->GetDescriptor(); + clip_pipeline_descriptor.SetLabel("Clip Pipeline"); + // Write to the stencil buffer. + StencilAttachmentDescriptor stencil0; + stencil0.stencil_compare = CompareFunction::kEqual; + stencil0.depth_stencil_pass = StencilOperation::kIncrementClamp; + clip_pipeline_descriptor.SetStencilAttachmentDescriptors(stencil0); + // Disable write to all color attachments. + auto color_attachments = + clip_pipeline_descriptor.GetColorAttachmentDescriptors(); + for (auto& color_attachment : color_attachments) { + color_attachment.second.write_mask = + static_cast(ColorWriteMask::kNone); + } + clip_pipeline_descriptor.SetColorAttachmentDescriptors( + std::move(color_attachments)); + clip_pipelines_[{}] = + std::make_unique(*context_, clip_pipeline_descriptor); + } + + // Clip restoration pipeline. + { + auto clip_pipeline_descriptor = + clip_pipelines_[{}]->WaitAndGet()->GetDescriptor(); + clip_pipeline_descriptor.SetLabel("Clip Restoration Pipeline"); + // Write to the stencil buffer. + StencilAttachmentDescriptor stencil0; + stencil0.stencil_compare = CompareFunction::kLess; + stencil0.depth_stencil_pass = StencilOperation::kSetToReferenceValue; + clip_pipeline_descriptor.SetStencilAttachmentDescriptors(stencil0); + clip_restoration_pipelines_[{}] = std::make_unique( + *context_, std::move(clip_pipeline_descriptor)); } - clip_pipeline_descriptor.SetColorAttachmentDescriptors( - std::move(color_attachments)); - clip_pipelines_[{}] = std::make_unique( - *context_, std::move(clip_pipeline_descriptor)); } else { return; } diff --git a/impeller/entity/content_context.h b/impeller/entity/content_context.h index 09870f775d402..78d21e5c6340a 100644 --- a/impeller/entity/content_context.h +++ b/impeller/entity/content_context.h @@ -77,6 +77,10 @@ class ContentContext { return GetPipeline(clip_pipelines_, opts); } + std::shared_ptr GetClipRestorePipeline(Options opts) const { + return GetPipeline(clip_restoration_pipelines_, opts); + } + std::shared_ptr GetContext() const; private: @@ -94,6 +98,7 @@ class ContentContext { mutable Variants texture_pipelines_; mutable Variants solid_stroke_pipelines_; mutable Variants clip_pipelines_; + mutable Variants clip_restoration_pipelines_; static void ApplyOptionsToDescriptor(PipelineDescriptor& desc, const Options& options) { diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 91130debc04e6..79b943721ff9a 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -15,6 +15,7 @@ #include "impeller/renderer/sampler_library.h" #include "impeller/renderer/surface.h" #include "impeller/renderer/tessellator.h" +#include "impeller/renderer/vertex_buffer.h" #include "impeller/renderer/vertex_buffer_builder.h" namespace impeller { @@ -385,7 +386,7 @@ bool ClipContents::Render(const ContentContext& renderer, Command cmd; cmd.label = "Clip"; cmd.pipeline = renderer.GetClipPipeline(OptionsFromPass(pass)); - cmd.stencil_reference = entity.GetStencilDepth() + 1u; + cmd.stencil_reference = entity.GetStencilDepth(); cmd.BindVertices( CreateSolidFillVertices(entity.GetPath(), pass.GetTransientsBuffer())); @@ -400,4 +401,46 @@ bool ClipContents::Render(const ContentContext& renderer, return true; } +/******************************************************************************* + ******* ClipRestoreContents + ******************************************************************************/ + +ClipRestoreContents::ClipRestoreContents() = default; + +ClipRestoreContents::~ClipRestoreContents() = default; + +bool ClipRestoreContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + using VS = ClipPipeline::VertexShader; + + Command cmd; + cmd.label = "Clip Restore"; + cmd.pipeline = renderer.GetClipRestorePipeline(OptionsFromPass(pass)); + cmd.stencil_reference = entity.GetStencilDepth(); + + // Create a rect that covers the whole render target. + auto size = pass.GetRenderTargetSize(); + VertexBufferBuilder vtx_builder; + vtx_builder.AddVertices({ + {Point(0.0, 0.0)}, + {Point(size.width, 0.0)}, + {Point(size.width, size.height)}, + {Point(0.0, 0.0)}, + {Point(size.width, size.height)}, + {Point(0.0, size.height)}, + }); + cmd.BindVertices(vtx_builder.CreateVertexBuffer(pass.GetTransientsBuffer())); + + VS::FrameInfo info; + // The color really doesn't matter. + info.color = Color::SkyBlue(); + info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()); + + VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(info)); + + pass.AddCommand(std::move(cmd)); + return true; +} + } // namespace impeller diff --git a/impeller/entity/contents.h b/impeller/entity/contents.h index 126dd2bf671c2..f80b2375bf952 100644 --- a/impeller/entity/contents.h +++ b/impeller/entity/contents.h @@ -152,4 +152,19 @@ class ClipContents final : public Contents { FML_DISALLOW_COPY_AND_ASSIGN(ClipContents); }; +class ClipRestoreContents final : public Contents { + public: + ClipRestoreContents(); + + ~ClipRestoreContents(); + + // |Contents| + bool Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const override; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(ClipRestoreContents); +}; + } // namespace impeller diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index 1e179915cf3da..cff63df7ac68a 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -73,6 +73,7 @@ struct CanvasStackEntry { Matrix xformation; size_t stencil_depth = 0u; bool is_subpass = false; + bool contains_clips = false; }; } // namespace impeller diff --git a/impeller/renderer/pipeline_builder.h b/impeller/renderer/pipeline_builder.h index 9002c803aa5dd..090cd813b79b3 100644 --- a/impeller/renderer/pipeline_builder.h +++ b/impeller/renderer/pipeline_builder.h @@ -8,6 +8,7 @@ #include "flutter/fml/macros.h" #include "impeller/base/base.h" #include "impeller/renderer/context.h" +#include "impeller/renderer/formats.h" #include "impeller/renderer/pipeline_descriptor.h" #include "impeller/renderer/shader_library.h" #include "impeller/renderer/vertex_descriptor.h" @@ -107,7 +108,7 @@ struct PipelineBuilder { // Setup default stencil buffer descriptions. { StencilAttachmentDescriptor stencil0; - stencil0.stencil_compare = CompareFunction::kLessEqual; + stencil0.stencil_compare = CompareFunction::kEqual; desc.SetStencilAttachmentDescriptors(stencil0); desc.SetStencilPixelFormat(PixelFormat::kDefaultStencil); }