From 0e4963f6f0240f5c8556c4a9417f9a5cfd04fbe0 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 7 Jun 2022 08:37:14 -0700 Subject: [PATCH 01/13] Move inline renderpass state machine into a new object to simplify OnRender pass management --- impeller/entity/entity_pass.cc | 217 ++++++++++++++++++++------------- impeller/entity/entity_pass.h | 20 ++- 2 files changed, 146 insertions(+), 91 deletions(-) diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 58db9cdfec07d..087e4d0a75909 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -4,10 +4,12 @@ #include "impeller/entity/entity_pass.h" +#include #include #include "flutter/fml/logging.h" #include "flutter/fml/trace_event.h" +#include "fml/macros.h" #include "impeller/base/validation.h" #include "impeller/entity/contents/content_context.h" #include "impeller/entity/contents/filters/filter_contents.h" @@ -139,7 +141,7 @@ bool EntityPass::Render(ContentContext& renderer, "EntityPass", // StorageMode::kDevicePrivate, LoadAction::kClear, StoreAction::kStore, StorageMode::kDevicePrivate, LoadAction::kClear, StoreAction::kStore); - if (!RenderInternal(renderer, offscreen_target, Point(), 0)) { + if (!OnRender(renderer, offscreen_target, Point(), 0)) { return false; } @@ -174,40 +176,122 @@ bool EntityPass::Render(ContentContext& renderer, return true; } - return RenderInternal(renderer, render_target, Point(), 0); + return OnRender(renderer, render_target, Point(), 0); } -bool EntityPass::RenderInternal(ContentContext& renderer, - RenderTarget render_target, - Point position, - uint32_t pass_depth, - size_t stencil_depth_floor) const { - TRACE_EVENT0("impeller", "EntityPass::Render"); +class PassContext { + public: + PassContext(std::shared_ptr context, RenderTarget render_target) + : context_(context), render_target_(render_target) {} - auto context = renderer.GetContext(); + ~PassContext() { EndPass(); } + + bool IsValid() { return !render_target_.GetColorAttachments().empty(); } + + bool IsActive() { return pass_ != nullptr; } + + std::shared_ptr GetTexture() { + if (!IsValid()) { + return nullptr; + } + auto color0 = render_target_.GetColorAttachments().find(0)->second; + return color0.resolve_texture ? color0.resolve_texture : color0.texture; + } - std::shared_ptr command_buffer; - std::shared_ptr pass; - uint32_t pass_count = 0; + bool EndPass() { + if (!IsActive()) { + return true; + } - auto end_pass = [&command_buffer, &pass, &context]() { - if (!pass->EncodeCommands(context->GetTransientsAllocator())) { + if (!pass_->EncodeCommands(context_->GetTransientsAllocator())) { return false; } - if (!command_buffer->SubmitCommands()) { + if (!command_buffer_->SubmitCommands()) { return false; } + pass_ = nullptr; + command_buffer_ = nullptr; + return true; - }; + } + + RenderTarget GetRenderTarget() { return render_target_; } + + std::shared_ptr GetRenderPass(uint32_t pass_depth) { + // Create a new render pass if one isn't active. + if (!IsActive()) { + command_buffer_ = context_->CreateRenderCommandBuffer(); + if (!command_buffer_) { + return nullptr; + } + + command_buffer_->SetLabel( + "EntityPass Command Buffer: Depth=" + std::to_string(pass_depth) + + " Count=" + std::to_string(pass_count_)); + + // Never clear the texture for subsequent passes. + if (pass_count_ > 0) { + if (!render_target_.GetColorAttachments().empty()) { + auto color0 = render_target_.GetColorAttachments().find(0)->second; + color0.load_action = LoadAction::kLoad; + render_target_.SetColorAttachment(color0, 0); + } + + if (auto stencil = render_target_.GetStencilAttachment(); + stencil.has_value()) { + stencil->load_action = LoadAction::kLoad; + render_target_.SetStencilAttachment(stencil.value()); + } + } + + pass_ = command_buffer_->CreateRenderPass(render_target_); + if (!pass_) { + return nullptr; + } + + pass_->SetLabel( + "EntityPass Render Pass: Depth=" + std::to_string(pass_depth) + + " Count=" + std::to_string(pass_count_)); + + ++pass_count_; + } + + return pass_; + } + + private: + std::shared_ptr context_; + RenderTarget render_target_; + std::shared_ptr command_buffer_; + std::shared_ptr pass_; + uint32_t pass_count_ = 0; + + FML_DISALLOW_COPY_AND_ASSIGN(PassContext); +}; + +bool EntityPass::OnRender(ContentContext& renderer, + RenderTarget render_target, + Point position, + uint32_t pass_depth, + size_t stencil_depth_floor, + std::shared_ptr backdrop_texture) const { + TRACE_EVENT0("impeller", "EntityPass::OnRender"); + + auto context = renderer.GetContext(); + PassContext pass_context(context, render_target); + if (!pass_context.IsValid()) { + return false; + } for (const auto& element : elements_) { Entity element_entity; - // ========================================================================= - // Setup entity element for rendering ====================================== - // ========================================================================= + //-------------------------------------------------------------------------- + /// Setup entity element for rendering. + /// + if (const auto& entity = std::get_if(&element)) { element_entity = *entity; if (!position.IsZero()) { @@ -220,9 +304,10 @@ bool EntityPass::RenderInternal(ContentContext& renderer, } } - // ========================================================================= - // Setup subpass element for rendering ===================================== - // ========================================================================= + //-------------------------------------------------------------------------- + /// Setup subpass element for rendering + /// + else if (const auto& subpass_ptr = std::get_if>(&element)) { auto subpass = subpass_ptr->get(); @@ -233,8 +318,8 @@ 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, - pass_depth, stencil_depth_floor)) { + if (!subpass->OnRender(renderer, pass_context.GetRenderTarget(), + position, pass_depth, stencil_depth_floor)) { return false; } continue; @@ -289,9 +374,8 @@ bool EntityPass::RenderInternal(ContentContext& renderer, // Stencil textures aren't shared between EntityPasses (as much of the // time they are transient). - if (!subpass->RenderInternal(renderer, subpass_target, - subpass_coverage->origin, ++pass_depth, - subpass->stencil_depth_)) { + if (!subpass->OnRender(renderer, subpass_target, subpass_coverage->origin, + ++pass_depth, subpass->stencil_depth_)) { return false; } @@ -308,12 +392,12 @@ bool EntityPass::RenderInternal(ContentContext& renderer, FML_UNREACHABLE(); } - // ========================================================================= - // Configure the RenderPass ================================================ - // ========================================================================= + //-------------------------------------------------------------------------- + /// Setup advanced blends. + /// - if (pass && element_entity.GetBlendMode() > - Entity::BlendMode::kLastPipelineBlendMode) { + if (element_entity.GetBlendMode() > + Entity::BlendMode::kLastPipelineBlendMode) { // End the active pass and flush the buffer before rendering "advanced" // blends. Advanced blends work by binding the current render target // texture as an input ("destination"), blending with a second texture @@ -323,82 +407,39 @@ bool EntityPass::RenderInternal(ContentContext& renderer, // the render target texture so far need to execute before it's bound for // blending (otherwise the blend pass will end up executing before all the // previous commands in the active pass). - if (!end_pass()) { + if (!pass_context.EndPass()) { return false; } - // Resetting these handles triggers a new pass to get created below - pass = nullptr; - command_buffer = nullptr; - // Amend an advanced blend to the contents. - if (render_target.GetColorAttachments().empty()) { + // Amend an advanced blend filter to the contents, attaching the pass + // texture. + auto texture = pass_context.GetTexture(); + if (!texture) { return false; } - auto color0 = render_target.GetColorAttachments().find(0)->second; FilterInput::Vector inputs = { FilterInput::Make(element_entity.GetContents()), - FilterInput::Make( - color0.resolve_texture ? color0.resolve_texture : color0.texture, - element_entity.GetTransformation().Invert())}; + FilterInput::Make(texture, + element_entity.GetTransformation().Invert())}; element_entity.SetContents( FilterContents::MakeBlend(element_entity.GetBlendMode(), inputs)); element_entity.SetBlendMode(Entity::BlendMode::kSourceOver); } - // Create a new render pass to render the element if one isn't active. - if (!pass) { - command_buffer = context->CreateRenderCommandBuffer(); - if (!command_buffer) { - return false; - } - - command_buffer->SetLabel( - "EntityPass Command Buffer: Depth=" + std::to_string(pass_depth) + - " Count=" + std::to_string(pass_count)); - - // Never clear the texture for subsequent passes. - if (pass_count > 0) { - if (!render_target.GetColorAttachments().empty()) { - auto color0 = render_target.GetColorAttachments().find(0)->second; - color0.load_action = LoadAction::kLoad; - render_target.SetColorAttachment(color0, 0); - } - - if (auto stencil = render_target.GetStencilAttachment(); - stencil.has_value()) { - stencil->load_action = LoadAction::kLoad; - render_target.SetStencilAttachment(stencil.value()); - } - } - - pass = command_buffer->CreateRenderPass(render_target); - if (!pass) { - return false; - } - - pass->SetLabel( - "EntityPass Render Pass: Depth=" + std::to_string(pass_depth) + - " Count=" + std::to_string(pass_count)); - - ++pass_count; - } - - // ========================================================================= - // Render the element ====================================================== - // ========================================================================= + //-------------------------------------------------------------------------- + /// Render the Element. + /// element_entity.SetStencilDepth(element_entity.GetStencilDepth() - stencil_depth_floor); + auto pass = pass_context.GetRenderPass(pass_depth); if (!element_entity.Render(renderer, *pass)) { return false; } } - if (pass) { - return end_pass(); - } return true; } @@ -455,4 +496,8 @@ void EntityPass::SetBlendMode(Entity::BlendMode blend_mode) { blend_mode_ = blend_mode; } +void EntityPass::SetBackdropFilter(std::optional proc) { + backdrop_filter_proc_ = proc; +} + } // namespace impeller diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index 0566bd4b0f3b0..a1d2d7ae59605 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -4,12 +4,14 @@ #pragma once +#include #include #include #include #include "flutter/fml/macros.h" #include "impeller/entity/contents/contents.h" +#include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/entity.h" #include "impeller/entity/entity_pass_delegate.h" #include "impeller/renderer/render_target.h" @@ -22,6 +24,8 @@ class ContentContext; class EntityPass { public: using Element = std::variant>; + using BackdropFilterProc = + std::function(FilterInput::Ref)>; EntityPass(); @@ -53,16 +57,19 @@ class EntityPass { void SetBlendMode(Entity::BlendMode blend_mode); + void SetBackdropFilter(std::optional proc); + std::optional GetSubpassCoverage(const EntityPass& subpass) const; std::optional GetElementsCoverage() const; private: - bool RenderInternal(ContentContext& renderer, - RenderTarget render_target, - Point position, - uint32_t pass_depth, - size_t stencil_depth_floor = 0) const; + bool OnRender(ContentContext& renderer, + RenderTarget render_target, + Point position, + uint32_t pass_depth, + size_t stencil_depth_floor = 0, + std::shared_ptr backdrop_texture = nullptr) const; std::vector elements_; @@ -71,6 +78,9 @@ class EntityPass { size_t stencil_depth_ = 0u; Entity::BlendMode blend_mode_ = Entity::BlendMode::kSourceOver; bool contains_advanced_blends_ = false; + + std::optional backdrop_filter_proc_ = std::nullopt; + std::unique_ptr delegate_ = EntityPassDelegate::MakeDefault(); std::shared_ptr lazy_glyph_atlas_ = From 9bbbed65c33a2ce9b7ad16ca17157521ee85e412 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 7 Jun 2022 09:48:14 -0700 Subject: [PATCH 02/13] Separate Element->Entity resolution into its own routine --- impeller/entity/entity_pass.cc | 360 ++++++++++++++++++--------------- impeller/entity/entity_pass.h | 42 ++++ 2 files changed, 234 insertions(+), 168 deletions(-) diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 087e4d0a75909..73734c749310b 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -179,97 +179,212 @@ bool EntityPass::Render(ContentContext& renderer, return OnRender(renderer, render_target, Point(), 0); } -class PassContext { - public: - PassContext(std::shared_ptr context, RenderTarget render_target) - : context_(context), render_target_(render_target) {} +EntityPass::EntityPassContext::EntityPassContext( + std::shared_ptr context, + RenderTarget render_target) + : context_(context), render_target_(render_target) {} - ~PassContext() { EndPass(); } +EntityPass::EntityPassContext::~EntityPassContext() { + EndPass(); +} - bool IsValid() { return !render_target_.GetColorAttachments().empty(); } +bool EntityPass::EntityPassContext::IsValid() const { + return !render_target_.GetColorAttachments().empty(); +} - bool IsActive() { return pass_ != nullptr; } +bool EntityPass::EntityPassContext::IsActive() const { + return pass_ != nullptr; +} - std::shared_ptr GetTexture() { - if (!IsValid()) { - return nullptr; - } - auto color0 = render_target_.GetColorAttachments().find(0)->second; - return color0.resolve_texture ? color0.resolve_texture : color0.texture; +std::shared_ptr EntityPass::EntityPassContext::GetTexture() { + if (!IsValid()) { + return nullptr; + } + auto color0 = render_target_.GetColorAttachments().find(0)->second; + return color0.resolve_texture ? color0.resolve_texture : color0.texture; +} + +bool EntityPass::EntityPassContext::EndPass() { + if (!IsActive()) { + return true; + } + + if (!pass_->EncodeCommands(context_->GetTransientsAllocator())) { + return false; + } + + if (!command_buffer_->SubmitCommands()) { + return false; } - bool EndPass() { - if (!IsActive()) { - return true; + pass_ = nullptr; + command_buffer_ = nullptr; + + return true; +} + +RenderTarget EntityPass::EntityPassContext::GetRenderTarget() const { + return render_target_; +} + +std::shared_ptr EntityPass::EntityPassContext::GetRenderPass( + uint32_t pass_depth) { + // Create a new render pass if one isn't active. + if (!IsActive()) { + command_buffer_ = context_->CreateRenderCommandBuffer(); + if (!command_buffer_) { + return nullptr; } - if (!pass_->EncodeCommands(context_->GetTransientsAllocator())) { - return false; + command_buffer_->SetLabel( + "EntityPass Command Buffer: Depth=" + std::to_string(pass_depth) + + " Count=" + std::to_string(pass_count_)); + + // Never clear the texture for subsequent passes. + if (pass_count_ > 0) { + if (!render_target_.GetColorAttachments().empty()) { + auto color0 = render_target_.GetColorAttachments().find(0)->second; + color0.load_action = LoadAction::kLoad; + render_target_.SetColorAttachment(color0, 0); + } + + if (auto stencil = render_target_.GetStencilAttachment(); + stencil.has_value()) { + stencil->load_action = LoadAction::kLoad; + render_target_.SetStencilAttachment(stencil.value()); + } } - if (!command_buffer_->SubmitCommands()) { - return false; + pass_ = command_buffer_->CreateRenderPass(render_target_); + if (!pass_) { + return nullptr; } - pass_ = nullptr; - command_buffer_ = nullptr; + pass_->SetLabel( + "EntityPass Render Pass: Depth=" + std::to_string(pass_depth) + + " Count=" + std::to_string(pass_count_)); - return true; + ++pass_count_; } - RenderTarget GetRenderTarget() { return render_target_; } + return pass_; +} - std::shared_ptr GetRenderPass(uint32_t pass_depth) { - // Create a new render pass if one isn't active. - if (!IsActive()) { - command_buffer_ = context_->CreateRenderCommandBuffer(); - if (!command_buffer_) { - return nullptr; - } +EntityPass::EntityResult EntityPass::GetElementEntity( + const EntityPass::Element& element, + ContentContext& renderer, + EntityPassContext& pass_context, + Point position, + uint32_t pass_depth, + size_t stencil_depth_floor) const { + Entity element_entity; + + //-------------------------------------------------------------------------- + /// Setup entity element. + /// + + if (const auto& entity = std::get_if(&element)) { + element_entity = *entity; + if (!position.IsZero()) { + // If the pass image is going to be rendered with a non-zero position, + // apply the negative translation to entity copies before rendering them + // so that they'll end up rendering to the correct on-screen position. + element_entity.SetTransformation( + Matrix::MakeTranslation(Vector3(-position)) * + element_entity.GetTransformation()); + } + } - command_buffer_->SetLabel( - "EntityPass Command Buffer: Depth=" + std::to_string(pass_depth) + - " Count=" + std::to_string(pass_count_)); - - // Never clear the texture for subsequent passes. - if (pass_count_ > 0) { - if (!render_target_.GetColorAttachments().empty()) { - auto color0 = render_target_.GetColorAttachments().find(0)->second; - color0.load_action = LoadAction::kLoad; - render_target_.SetColorAttachment(color0, 0); - } - - if (auto stencil = render_target_.GetStencilAttachment(); - stencil.has_value()) { - stencil->load_action = LoadAction::kLoad; - render_target_.SetStencilAttachment(stencil.value()); - } - } + //-------------------------------------------------------------------------- + /// Setup subpass element. + /// + + else if (const auto& subpass_ptr = + std::get_if>(&element)) { + auto subpass = subpass_ptr->get(); + + if (subpass->delegate_->CanElide()) { + EntityPass::EntityResult::Empty(); + } - pass_ = command_buffer_->CreateRenderPass(render_target_); - if (!pass_) { - return nullptr; + if (subpass->delegate_->CanCollapseIntoParentPass()) { + // Directly render into the parent target and move on. + if (!subpass->OnRender(renderer, pass_context.GetRenderTarget(), position, + pass_depth, stencil_depth_floor)) { + return EntityPass::EntityResult::Failure(); } + EntityPass::EntityResult::Empty(); + } - pass_->SetLabel( - "EntityPass Render Pass: Depth=" + std::to_string(pass_depth) + - " Count=" + std::to_string(pass_count_)); + const auto subpass_coverage = GetSubpassCoverage(*subpass); + if (!subpass_coverage.has_value()) { + EntityPass::EntityResult::Empty(); + } - ++pass_count_; + if (subpass_coverage->size.IsEmpty()) { + // It is not an error to have an empty subpass. But subpasses that can't + // create their intermediates must trip errors. + EntityPass::EntityResult::Empty(); } - return pass_; - } + RenderTarget subpass_target; + if (subpass->contains_advanced_blends_) { + subpass_target = RenderTarget::CreateOffscreen( + *renderer.GetContext(), ISize::Ceil(subpass_coverage->size), + "EntityPass", StorageMode::kDevicePrivate, LoadAction::kClear, + StoreAction::kStore, StorageMode::kDevicePrivate, LoadAction::kClear, + StoreAction::kStore); + } else { + subpass_target = RenderTarget::CreateOffscreen( + *renderer.GetContext(), ISize::Ceil(subpass_coverage->size), + "EntityPass", StorageMode::kDevicePrivate, LoadAction::kClear, + StoreAction::kStore, StorageMode::kDeviceTransient, + LoadAction::kClear, StoreAction::kDontCare); + } - private: - std::shared_ptr context_; - RenderTarget render_target_; - std::shared_ptr command_buffer_; - std::shared_ptr pass_; - uint32_t pass_count_ = 0; + auto subpass_texture = subpass_target.GetRenderTargetTexture(); - FML_DISALLOW_COPY_AND_ASSIGN(PassContext); -}; + if (!subpass_texture) { + return EntityPass::EntityResult::Failure(); + } + + auto offscreen_texture_contents = + subpass->delegate_->CreateContentsForSubpassTarget(subpass_texture); + + if (!offscreen_texture_contents) { + // This is an error because the subpass delegate said the pass couldn't + // be collapsed into its parent. Yet, when asked how it want's to + // postprocess the offscreen texture, it couldn't give us an answer. + // + // Theoretically, we could collapse the pass now. But that would be + // wasteful as we already have the offscreen texture and we don't want + // to discard it without ever using it. Just make the delegate do the + // right thing. + return EntityPass::EntityResult::Failure(); + } + + // Stencil textures aren't shared between EntityPasses (as much of the + // time they are transient). + if (!subpass->OnRender(renderer, subpass_target, subpass_coverage->origin, + ++pass_depth, subpass->stencil_depth_)) { + return EntityPass::EntityResult::Failure(); + } + + element_entity.SetContents(std::move(offscreen_texture_contents)); + 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 + // parameters are transformed by the `xformation_` matrix, while + // continuing to apply only the subpass offset to the offscreen texture. + element_entity.SetTransformation( + Matrix::MakeTranslation(Vector3(subpass_coverage->origin - position))); + } else { + FML_UNREACHABLE(); + } + + return EntityPass::EntityResult::Success(element_entity); +} bool EntityPass::OnRender(ContentContext& renderer, RenderTarget render_target, @@ -280,118 +395,27 @@ bool EntityPass::OnRender(ContentContext& renderer, TRACE_EVENT0("impeller", "EntityPass::OnRender"); auto context = renderer.GetContext(); - PassContext pass_context(context, render_target); + EntityPassContext pass_context(context, render_target); if (!pass_context.IsValid()) { return false; } for (const auto& element : elements_) { - Entity element_entity; - - //-------------------------------------------------------------------------- - /// Setup entity element for rendering. - /// + EntityResult result = + GetElementEntity(element, renderer, pass_context, position, pass_depth, + stencil_depth_floor); - if (const auto& entity = std::get_if(&element)) { - element_entity = *entity; - if (!position.IsZero()) { - // If the pass image is going to be rendered with a non-zero position, - // apply the negative translation to entity copies before rendering them - // so that they'll end up rendering to the correct on-screen position. - element_entity.SetTransformation( - Matrix::MakeTranslation(Vector3(-position)) * - element_entity.GetTransformation()); - } + if (!result.success) { + return false; } - //-------------------------------------------------------------------------- - /// Setup subpass element for rendering - /// - - else if (const auto& subpass_ptr = - std::get_if>(&element)) { - auto subpass = subpass_ptr->get(); - - if (subpass->delegate_->CanElide()) { - continue; - } - - if (subpass->delegate_->CanCollapseIntoParentPass()) { - // Directly render into the parent target and move on. - if (!subpass->OnRender(renderer, pass_context.GetRenderTarget(), - position, pass_depth, stencil_depth_floor)) { - return false; - } - continue; - } - - const auto subpass_coverage = GetSubpassCoverage(*subpass); - if (!subpass_coverage.has_value()) { - continue; - } - - if (subpass_coverage->size.IsEmpty()) { - // It is not an error to have an empty subpass. But subpasses that can't - // create their intermediates must trip errors. - continue; - } - - RenderTarget subpass_target; - if (subpass->contains_advanced_blends_) { - subpass_target = RenderTarget::CreateOffscreen( - *context, ISize::Ceil(subpass_coverage->size), "EntityPass", - StorageMode::kDevicePrivate, LoadAction::kClear, - StoreAction::kStore, StorageMode::kDevicePrivate, - LoadAction::kClear, StoreAction::kStore); - } else { - subpass_target = RenderTarget::CreateOffscreen( - *context, ISize::Ceil(subpass_coverage->size), "EntityPass", - StorageMode::kDevicePrivate, LoadAction::kClear, - StoreAction::kStore, StorageMode::kDeviceTransient, - LoadAction::kClear, StoreAction::kDontCare); - } - - auto subpass_texture = subpass_target.GetRenderTargetTexture(); - - if (!subpass_texture) { - return false; - } - - auto offscreen_texture_contents = - subpass->delegate_->CreateContentsForSubpassTarget(subpass_texture); - - if (!offscreen_texture_contents) { - // This is an error because the subpass delegate said the pass couldn't - // be collapsed into its parent. Yet, when asked how it want's to - // postprocess the offscreen texture, it couldn't give us an answer. - // - // Theoretically, we could collapse the pass now. But that would be - // wasteful as we already have the offscreen texture and we don't want - // to discard it without ever using it. Just make the delegate do the - // right thing. - return false; - } - - // Stencil textures aren't shared between EntityPasses (as much of the - // time they are transient). - if (!subpass->OnRender(renderer, subpass_target, subpass_coverage->origin, - ++pass_depth, subpass->stencil_depth_)) { - return false; - } - - element_entity.SetContents(std::move(offscreen_texture_contents)); - 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 - // parameters are transformed by the `xformation_` matrix, while - // continuing to apply only the subpass offset to the offscreen texture. - element_entity.SetTransformation(Matrix::MakeTranslation( - Vector3(subpass_coverage->origin - position))); - } else { - FML_UNREACHABLE(); + if (!result.entity.has_value()) { + // Nothing to render. + continue; } + Entity element_entity = result.entity.value(); + //-------------------------------------------------------------------------- /// Setup advanced blends. /// diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index a1d2d7ae59605..3677e07699524 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -64,6 +64,48 @@ class EntityPass { std::optional GetElementsCoverage() const; private: + class EntityPassContext { + public: + EntityPassContext(std::shared_ptr context, RenderTarget render_target); + ~EntityPassContext(); + + bool IsValid() const; + bool IsActive() const; + std::shared_ptr GetTexture(); + bool EndPass(); + RenderTarget GetRenderTarget() const; + std::shared_ptr GetRenderPass(uint32_t pass_depth); + + private: + std::shared_ptr context_; + RenderTarget render_target_; + std::shared_ptr command_buffer_; + std::shared_ptr pass_; + uint32_t pass_count_ = 0; + + FML_DISALLOW_COPY_AND_ASSIGN(EntityPassContext); + }; + + struct EntityResult { + /// @brief The resulting entity that should be rendered. If `std::nullopt`, + /// there is nothing to render. + std::optional entity = std::nullopt; + /// @brief This is set to `false` if there was an unexpected rendering + /// error while resolving the Entity. + bool success = false; + + static EntityResult Success(Entity e) { return {e, true}; } + static EntityResult Failure() { return {std::nullopt, false}; } + static EntityResult Empty() { return {std::nullopt, true}; } + }; + + EntityResult GetElementEntity(const EntityPass::Element& element, + ContentContext& renderer, + EntityPassContext& pass_context, + Point position, + uint32_t pass_depth, + size_t stencil_depth_floor) const; + bool OnRender(ContentContext& renderer, RenderTarget render_target, Point position, From 068ac3d302be55662e1a91d742736c64d53bbe9d Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 7 Jun 2022 09:57:42 -0700 Subject: [PATCH 03/13] Move pass context into its own TU --- impeller/entity/BUILD.gn | 2 + impeller/entity/entity_pass.cc | 96 +---------------------- impeller/entity/entity_pass.h | 25 +----- impeller/entity/inline_pass_context.cc | 102 +++++++++++++++++++++++++ impeller/entity/inline_pass_context.h | 36 +++++++++ 5 files changed, 145 insertions(+), 116 deletions(-) create mode 100644 impeller/entity/inline_pass_context.cc create mode 100644 impeller/entity/inline_pass_context.h diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 8ac34f093497e..05e0b591624c7 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -89,6 +89,8 @@ impeller_component("entity") { "entity_pass.h", "entity_pass_delegate.cc", "entity_pass_delegate.h", + "inline_pass_context.cc", + "inline_pass_context.h", ] public_deps = [ diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 73734c749310b..f7713cce1e3b2 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -15,6 +15,7 @@ #include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/contents/filters/inputs/filter_input.h" #include "impeller/entity/contents/texture_contents.h" +#include "impeller/entity/inline_pass_context.h" #include "impeller/geometry/path_builder.h" #include "impeller/renderer/allocator.h" #include "impeller/renderer/command.h" @@ -179,101 +180,10 @@ bool EntityPass::Render(ContentContext& renderer, return OnRender(renderer, render_target, Point(), 0); } -EntityPass::EntityPassContext::EntityPassContext( - std::shared_ptr context, - RenderTarget render_target) - : context_(context), render_target_(render_target) {} - -EntityPass::EntityPassContext::~EntityPassContext() { - EndPass(); -} - -bool EntityPass::EntityPassContext::IsValid() const { - return !render_target_.GetColorAttachments().empty(); -} - -bool EntityPass::EntityPassContext::IsActive() const { - return pass_ != nullptr; -} - -std::shared_ptr EntityPass::EntityPassContext::GetTexture() { - if (!IsValid()) { - return nullptr; - } - auto color0 = render_target_.GetColorAttachments().find(0)->second; - return color0.resolve_texture ? color0.resolve_texture : color0.texture; -} - -bool EntityPass::EntityPassContext::EndPass() { - if (!IsActive()) { - return true; - } - - if (!pass_->EncodeCommands(context_->GetTransientsAllocator())) { - return false; - } - - if (!command_buffer_->SubmitCommands()) { - return false; - } - - pass_ = nullptr; - command_buffer_ = nullptr; - - return true; -} - -RenderTarget EntityPass::EntityPassContext::GetRenderTarget() const { - return render_target_; -} - -std::shared_ptr EntityPass::EntityPassContext::GetRenderPass( - uint32_t pass_depth) { - // Create a new render pass if one isn't active. - if (!IsActive()) { - command_buffer_ = context_->CreateRenderCommandBuffer(); - if (!command_buffer_) { - return nullptr; - } - - command_buffer_->SetLabel( - "EntityPass Command Buffer: Depth=" + std::to_string(pass_depth) + - " Count=" + std::to_string(pass_count_)); - - // Never clear the texture for subsequent passes. - if (pass_count_ > 0) { - if (!render_target_.GetColorAttachments().empty()) { - auto color0 = render_target_.GetColorAttachments().find(0)->second; - color0.load_action = LoadAction::kLoad; - render_target_.SetColorAttachment(color0, 0); - } - - if (auto stencil = render_target_.GetStencilAttachment(); - stencil.has_value()) { - stencil->load_action = LoadAction::kLoad; - render_target_.SetStencilAttachment(stencil.value()); - } - } - - pass_ = command_buffer_->CreateRenderPass(render_target_); - if (!pass_) { - return nullptr; - } - - pass_->SetLabel( - "EntityPass Render Pass: Depth=" + std::to_string(pass_depth) + - " Count=" + std::to_string(pass_count_)); - - ++pass_count_; - } - - return pass_; -} - EntityPass::EntityResult EntityPass::GetElementEntity( const EntityPass::Element& element, ContentContext& renderer, - EntityPassContext& pass_context, + InlinePassContext& pass_context, Point position, uint32_t pass_depth, size_t stencil_depth_floor) const { @@ -395,7 +305,7 @@ bool EntityPass::OnRender(ContentContext& renderer, TRACE_EVENT0("impeller", "EntityPass::OnRender"); auto context = renderer.GetContext(); - EntityPassContext pass_context(context, render_target); + InlinePassContext pass_context(context, render_target); if (!pass_context.IsValid()) { return false; } diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index 3677e07699524..287ff554ef91d 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -14,6 +14,7 @@ #include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/entity.h" #include "impeller/entity/entity_pass_delegate.h" +#include "impeller/entity/inline_pass_context.h" #include "impeller/renderer/render_target.h" #include "impeller/typographer/lazy_glyph_atlas.h" @@ -64,28 +65,6 @@ class EntityPass { std::optional GetElementsCoverage() const; private: - class EntityPassContext { - public: - EntityPassContext(std::shared_ptr context, RenderTarget render_target); - ~EntityPassContext(); - - bool IsValid() const; - bool IsActive() const; - std::shared_ptr GetTexture(); - bool EndPass(); - RenderTarget GetRenderTarget() const; - std::shared_ptr GetRenderPass(uint32_t pass_depth); - - private: - std::shared_ptr context_; - RenderTarget render_target_; - std::shared_ptr command_buffer_; - std::shared_ptr pass_; - uint32_t pass_count_ = 0; - - FML_DISALLOW_COPY_AND_ASSIGN(EntityPassContext); - }; - struct EntityResult { /// @brief The resulting entity that should be rendered. If `std::nullopt`, /// there is nothing to render. @@ -101,7 +80,7 @@ class EntityPass { EntityResult GetElementEntity(const EntityPass::Element& element, ContentContext& renderer, - EntityPassContext& pass_context, + InlinePassContext& pass_context, Point position, uint32_t pass_depth, size_t stencil_depth_floor) const; diff --git a/impeller/entity/inline_pass_context.cc b/impeller/entity/inline_pass_context.cc new file mode 100644 index 0000000000000..cae60818880ad --- /dev/null +++ b/impeller/entity/inline_pass_context.cc @@ -0,0 +1,102 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/inline_pass_context.h" + +#include "impeller/renderer/command_buffer.h" + +namespace impeller { + +InlinePassContext::InlinePassContext( + std::shared_ptr context, + RenderTarget render_target) + : context_(context), render_target_(render_target) {} + +InlinePassContext::~InlinePassContext() { + EndPass(); +} + +bool InlinePassContext::IsValid() const { + return !render_target_.GetColorAttachments().empty(); +} + +bool InlinePassContext::IsActive() const { + return pass_ != nullptr; +} + +std::shared_ptr InlinePassContext::GetTexture() { + if (!IsValid()) { + return nullptr; + } + auto color0 = render_target_.GetColorAttachments().find(0)->second; + return color0.resolve_texture ? color0.resolve_texture : color0.texture; +} + +bool InlinePassContext::EndPass() { + if (!IsActive()) { + return true; + } + + if (!pass_->EncodeCommands(context_->GetTransientsAllocator())) { + return false; + } + + if (!command_buffer_->SubmitCommands()) { + return false; + } + + pass_ = nullptr; + command_buffer_ = nullptr; + + return true; +} + +RenderTarget InlinePassContext::GetRenderTarget() const { + return render_target_; +} + +std::shared_ptr InlinePassContext::GetRenderPass( + uint32_t pass_depth) { + // Create a new render pass if one isn't active. + if (!IsActive()) { + command_buffer_ = context_->CreateRenderCommandBuffer(); + if (!command_buffer_) { + return nullptr; + } + + command_buffer_->SetLabel( + "EntityPass Command Buffer: Depth=" + std::to_string(pass_depth) + + " Count=" + std::to_string(pass_count_)); + + // Never clear the texture for subsequent passes. + if (pass_count_ > 0) { + if (!render_target_.GetColorAttachments().empty()) { + auto color0 = render_target_.GetColorAttachments().find(0)->second; + color0.load_action = LoadAction::kLoad; + render_target_.SetColorAttachment(color0, 0); + } + + if (auto stencil = render_target_.GetStencilAttachment(); + stencil.has_value()) { + stencil->load_action = LoadAction::kLoad; + render_target_.SetStencilAttachment(stencil.value()); + } + } + + pass_ = command_buffer_->CreateRenderPass(render_target_); + if (!pass_) { + return nullptr; + } + + pass_->SetLabel( + "EntityPass Render Pass: Depth=" + std::to_string(pass_depth) + + " Count=" + std::to_string(pass_count_)); + + ++pass_count_; + } + + return pass_; +} + +} diff --git a/impeller/entity/inline_pass_context.h b/impeller/entity/inline_pass_context.h new file mode 100644 index 0000000000000..dcae129ce0ad3 --- /dev/null +++ b/impeller/entity/inline_pass_context.h @@ -0,0 +1,36 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "impeller/renderer/context.h" +#include "impeller/renderer/render_pass.h" +#include "impeller/renderer/render_target.h" + +namespace impeller { + +class InlinePassContext { + public: + InlinePassContext(std::shared_ptr context, + RenderTarget render_target); + ~InlinePassContext(); + + bool IsValid() const; + bool IsActive() const; + std::shared_ptr GetTexture(); + bool EndPass(); + RenderTarget GetRenderTarget() const; + std::shared_ptr GetRenderPass(uint32_t pass_depth); + + private: + std::shared_ptr context_; + RenderTarget render_target_; + std::shared_ptr command_buffer_; + std::shared_ptr pass_; + uint32_t pass_count_ = 0; + + FML_DISALLOW_COPY_AND_ASSIGN(InlinePassContext); +}; + +} // namespace impeller From fa09ec7aacb39853ad54bdff68be988812bdeb88 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 7 Jun 2022 14:03:12 -0700 Subject: [PATCH 04/13] Add backdrop filter support --- impeller/aiks/canvas.cc | 5 +- impeller/aiks/canvas.h | 5 +- .../display_list/display_list_dispatcher.cc | 25 ++-- .../display_list/display_list_unittests.cc | 39 +++++++ impeller/entity/entity_pass.cc | 107 +++++++++++++----- impeller/entity/entity_pass.h | 23 ++-- impeller/entity/inline_pass_context.cc | 9 +- impeller/entity/inline_pass_context.h | 3 +- 8 files changed, 164 insertions(+), 52 deletions(-) diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index b5001edaa1056..c106d49931653 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -265,12 +265,15 @@ size_t Canvas::GetStencilDepth() const { return xformation_stack_.back().stencil_depth; } -void Canvas::SaveLayer(Paint paint, std::optional bounds) { +void Canvas::SaveLayer(Paint paint, + std::optional bounds, + std::optional backdrop_filter) { Save(true, paint.blend_mode); auto& new_layer_pass = GetCurrentPass(); new_layer_pass.SetDelegate( std::make_unique(paint, bounds)); + new_layer_pass.SetBackdropFilter(backdrop_filter); if (bounds.has_value()) { // Render target switches due to a save layer can be elided. In such cases diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 974085fb4ef59..a58794a9ac6b3 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -35,7 +35,10 @@ class Canvas { void Save(); - void SaveLayer(Paint paint, std::optional bounds = std::nullopt); + void SaveLayer( + Paint paint, + std::optional bounds = std::nullopt, + std::optional backdrop_filter = std::nullopt); bool Restore(); diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 40f7b4986919f..b698500335bbd 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -324,9 +324,12 @@ void DisplayListDispatcher::setMaskFilter(const flutter::DlMaskFilter* filter) { } } -// |flutter::Dispatcher| -void DisplayListDispatcher::setImageFilter( +static std::optional ToImageFilterProc( const flutter::DlImageFilter* filter) { + if (filter == nullptr) { + return std::nullopt; + } + switch (filter->type()) { case flutter::DlImageFilterType::kBlur: { auto blur = filter->asBlur(); @@ -338,7 +341,7 @@ void DisplayListDispatcher::setImageFilter( UNIMPLEMENTED; } - paint_.image_filter = [sigma_x, sigma_y](FilterInput::Ref input) { + return [sigma_x, sigma_y](FilterInput::Ref input) { return FilterContents::MakeGaussianBlur(input, sigma_x, sigma_y); }; @@ -350,11 +353,16 @@ void DisplayListDispatcher::setImageFilter( case flutter::DlImageFilterType::kComposeFilter: case flutter::DlImageFilterType::kColorFilter: case flutter::DlImageFilterType::kUnknown: - UNIMPLEMENTED; - break; + return std::nullopt; } } +// |flutter::Dispatcher| +void DisplayListDispatcher::setImageFilter( + const flutter::DlImageFilter* filter) { + paint_.image_filter = ToImageFilterProc(filter); +} + // |flutter::Dispatcher| void DisplayListDispatcher::save() { canvas_.Save(); @@ -371,11 +379,12 @@ static std::optional ToRect(const SkRect* rect) { void DisplayListDispatcher::saveLayer(const SkRect* bounds, const flutter::SaveLayerOptions options, const flutter::DlImageFilter* backdrop) { + auto paint = options.renders_with_attributes() ? paint_ : Paint{}; if (backdrop) { - UNIMPLEMENTED; + canvas_.SaveLayer(paint, ToRect(bounds), ToImageFilterProc(backdrop)); + } else { + canvas_.SaveLayer(paint, ToRect(bounds)); } - canvas_.SaveLayer(options.renders_with_attributes() ? paint_ : Paint{}, - ToRect(bounds)); } // |flutter::Dispatcher| diff --git a/impeller/display_list/display_list_unittests.cc b/impeller/display_list/display_list_unittests.cc index 81d67c138fcc6..150cc730d6356 100644 --- a/impeller/display_list/display_list_unittests.cc +++ b/impeller/display_list/display_list_unittests.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "display_list/display_list_blend_mode.h" +#include "display_list/display_list_color.h" #include "display_list/display_list_color_filter.h" #include "display_list/display_list_image_filter.h" #include "display_list/display_list_tile_mode.h" @@ -285,5 +286,43 @@ TEST_P(DisplayListTest, CanDrawWithImageBlurFilter) { ASSERT_TRUE(OpenPlaygroundHere(callback)); } +TEST_P(DisplayListTest, CanDrawBackdropFilter) { + auto texture = CreateTextureForFixture("embarcadero.jpg"); + + bool first_frame = true; + auto callback = [&]() { + if (first_frame) { + first_frame = false; + ImGui::SetNextWindowSize({400, 100}); + ImGui::SetNextWindowPos({300, 550}); + } + + static float sigma[] = {10, 10}; + + ImGui::Begin("Controls"); + ImGui::SliderFloat2("Sigma", sigma, 0, 100); + ImGui::End(); + + flutter::DisplayListBuilder builder; + + auto filter = flutter::DlBlurImageFilter(sigma[0], sigma[1], + flutter::DlTileMode::kClamp); + + flutter::DlPaint paint; + paint.setColor(flutter::DlColor::kRed()); + builder.drawCircle({100, 100}, 100, paint); + + auto bounds = SkRect::MakeXYWH(100, 100, 100, 100); + builder.saveLayer(&bounds, &paint, &filter); + builder.setImageFilter(&filter); + builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(200, 200), + SkSamplingOptions{}, true); + + return builder.Build(); + }; + + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + } // namespace testing } // namespace impeller diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index f7713cce1e3b2..2dcd547854fdf 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -39,7 +39,7 @@ void EntityPass::SetDelegate(std::unique_ptr delegate) { void EntityPass::AddEntity(Entity entity) { if (entity.GetBlendMode() > Entity::BlendMode::kLastPipelineBlendMode) { - contains_advanced_blends_ = true; + reads_from_pass_texture_ = true; } elements_.emplace_back(std::move(entity)); @@ -125,8 +125,9 @@ EntityPass* EntityPass::AddSubpass(std::unique_ptr pass) { FML_DCHECK(pass->superpass_ == nullptr); pass->superpass_ = this; - if (pass->blend_mode_ > Entity::BlendMode::kLastPipelineBlendMode) { - contains_advanced_blends_ = true; + if (pass->blend_mode_ > Entity::BlendMode::kLastPipelineBlendMode || + pass->backdrop_filter_proc_.has_value()) { + reads_from_pass_texture_ = true; } auto subpass_pointer = pass.get(); @@ -136,13 +137,13 @@ EntityPass* EntityPass::AddSubpass(std::unique_ptr pass) { bool EntityPass::Render(ContentContext& renderer, RenderTarget render_target) const { - if (contains_advanced_blends_) { + if (reads_from_pass_texture_) { auto offscreen_target = RenderTarget::CreateOffscreen( *renderer.GetContext(), render_target.GetRenderTargetSize(), "EntityPass", // StorageMode::kDevicePrivate, LoadAction::kClear, StoreAction::kStore, StorageMode::kDevicePrivate, LoadAction::kClear, StoreAction::kStore); - if (!OnRender(renderer, offscreen_target, Point(), 0)) { + if (!OnRender(renderer, offscreen_target, Point(), Point(), 0)) { return false; } @@ -177,10 +178,10 @@ bool EntityPass::Render(ContentContext& renderer, return true; } - return OnRender(renderer, render_target, Point(), 0); + return OnRender(renderer, render_target, Point(), Point(), 0); } -EntityPass::EntityResult EntityPass::GetElementEntity( +EntityPass::EntityResult EntityPass::GetEntityForElement( const EntityPass::Element& element, ContentContext& renderer, InlinePassContext& pass_context, @@ -214,31 +215,41 @@ EntityPass::EntityResult EntityPass::GetElementEntity( auto subpass = subpass_ptr->get(); if (subpass->delegate_->CanElide()) { - EntityPass::EntityResult::Empty(); + return EntityPass::EntityResult::Skip(); } - if (subpass->delegate_->CanCollapseIntoParentPass()) { + if (subpass->delegate_->CanCollapseIntoParentPass() && + !subpass->backdrop_filter_proc_.has_value()) { // Directly render into the parent target and move on. if (!subpass->OnRender(renderer, pass_context.GetRenderTarget(), position, - pass_depth, stencil_depth_floor)) { + position, stencil_depth_floor)) { return EntityPass::EntityResult::Failure(); } - EntityPass::EntityResult::Empty(); + return EntityPass::EntityResult::Skip(); } - const auto subpass_coverage = GetSubpassCoverage(*subpass); - if (!subpass_coverage.has_value()) { - EntityPass::EntityResult::Empty(); - } + std::optional subpass_coverage; + + if (subpass->elements_.empty() && backdrop_filter_proc_.has_value()) { + // An empty subpass with a backdrop filter should render the backdrop over + // the entire coverage of the current pass. + subpass_coverage = Rect( + position, Size(pass_context.GetRenderTarget().GetRenderTargetSize())); + } else { + subpass_coverage = GetSubpassCoverage(*subpass); + if (!subpass_coverage.has_value()) { + return EntityPass::EntityResult::Skip(); + } - if (subpass_coverage->size.IsEmpty()) { - // It is not an error to have an empty subpass. But subpasses that can't - // create their intermediates must trip errors. - EntityPass::EntityResult::Empty(); + if (subpass_coverage->size.IsEmpty()) { + // It is not an error to have an empty subpass. But subpasses that can't + // create their intermediates must trip errors. + return EntityPass::EntityResult::Skip(); + } } RenderTarget subpass_target; - if (subpass->contains_advanced_blends_) { + if (subpass->reads_from_pass_texture_) { subpass_target = RenderTarget::CreateOffscreen( *renderer.GetContext(), ISize::Ceil(subpass_coverage->size), "EntityPass", StorageMode::kDevicePrivate, LoadAction::kClear, @@ -273,10 +284,21 @@ EntityPass::EntityResult EntityPass::GetElementEntity( return EntityPass::EntityResult::Failure(); } + std::shared_ptr backdrop_texture = nullptr; + if (subpass->backdrop_filter_proc_.has_value()) { + backdrop_texture = pass_context.GetTexture(); + + // The subpass will need to read from the current pass texture when + // rendering the backdrop, so if there's an active pass, end it prior to + // rendering the subpass. + pass_context.EndPass(); + } + // Stencil textures aren't shared between EntityPasses (as much of the // time they are transient). if (!subpass->OnRender(renderer, subpass_target, subpass_coverage->origin, - ++pass_depth, subpass->stencil_depth_)) { + position, ++pass_depth, subpass->stencil_depth_, + backdrop_texture)) { return EntityPass::EntityResult::Failure(); } @@ -299,6 +321,7 @@ EntityPass::EntityResult EntityPass::GetElementEntity( bool EntityPass::OnRender(ContentContext& renderer, RenderTarget render_target, Point position, + Point parent_position, uint32_t pass_depth, size_t stencil_depth_floor, std::shared_ptr backdrop_texture) const { @@ -310,10 +333,39 @@ bool EntityPass::OnRender(ContentContext& renderer, return false; } + auto render_element = [&stencil_depth_floor, &pass_context, &pass_depth, + &renderer](Entity element_entity) { + element_entity.SetStencilDepth(element_entity.GetStencilDepth() - + stencil_depth_floor); + + auto pass = pass_context.GetRenderPass(pass_depth); + if (!element_entity.Render(renderer, *pass)) { + return false; + } + return true; + }; + + if (backdrop_filter_proc_.has_value()) { + if (!backdrop_texture) { + return false; + } + + // Render the backdrop texture before any of the pass elements. + const auto& proc = backdrop_filter_proc_.value(); + auto contents = proc(FilterInput::Make(std::move(backdrop_texture))); + + Entity backdrop_entity; + backdrop_entity.SetContents(std::move(contents)); + backdrop_entity.SetTransformation( + Matrix::MakeTranslation(Vector3(parent_position - position))); + + render_element(backdrop_entity); + } + for (const auto& element : elements_) { EntityResult result = - GetElementEntity(element, renderer, pass_context, position, pass_depth, - stencil_depth_floor); + GetEntityForElement(element, renderer, pass_context, position, + pass_depth, stencil_depth_floor); if (!result.success) { return false; @@ -365,11 +417,7 @@ bool EntityPass::OnRender(ContentContext& renderer, /// Render the Element. /// - element_entity.SetStencilDepth(element_entity.GetStencilDepth() - - stencil_depth_floor); - - auto pass = pass_context.GetRenderPass(pass_depth); - if (!element_entity.Render(renderer, *pass)) { + if (!render_element(element_entity)) { return false; } } @@ -432,6 +480,9 @@ void EntityPass::SetBlendMode(Entity::BlendMode blend_mode) { void EntityPass::SetBackdropFilter(std::optional proc) { backdrop_filter_proc_ = proc; + if (superpass_) { + superpass_->reads_from_pass_texture_ = true; + } } } // namespace impeller diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index 287ff554ef91d..46b21b53be896 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -75,19 +75,20 @@ class EntityPass { static EntityResult Success(Entity e) { return {e, true}; } static EntityResult Failure() { return {std::nullopt, false}; } - static EntityResult Empty() { return {std::nullopt, true}; } + static EntityResult Skip() { return {std::nullopt, true}; } }; - EntityResult GetElementEntity(const EntityPass::Element& element, - ContentContext& renderer, - InlinePassContext& pass_context, - Point position, - uint32_t pass_depth, - size_t stencil_depth_floor) const; + EntityResult GetEntityForElement(const EntityPass::Element& element, + ContentContext& renderer, + InlinePassContext& pass_context, + Point position, + uint32_t pass_depth, + size_t stencil_depth_floor) const; bool OnRender(ContentContext& renderer, RenderTarget render_target, Point position, + Point parent_position, uint32_t pass_depth, size_t stencil_depth_floor = 0, std::shared_ptr backdrop_texture = nullptr) const; @@ -98,7 +99,13 @@ class EntityPass { Matrix xformation_; size_t stencil_depth_ = 0u; Entity::BlendMode blend_mode_ = Entity::BlendMode::kSourceOver; - bool contains_advanced_blends_ = false; + + /// This flag is set to `true` whenever an entity is added to the pass that + /// requires reading the pass texture during rendering. This can happen in the + /// following scenarios: + /// 1. An entity with an "advanced blend" is added to the pass. + /// 2. A subpass with a backdrop filter is added to the pass. + bool reads_from_pass_texture_ = false; std::optional backdrop_filter_proc_ = std::nullopt; diff --git a/impeller/entity/inline_pass_context.cc b/impeller/entity/inline_pass_context.cc index cae60818880ad..0b41e8687e2d7 100644 --- a/impeller/entity/inline_pass_context.cc +++ b/impeller/entity/inline_pass_context.cc @@ -8,9 +8,8 @@ namespace impeller { -InlinePassContext::InlinePassContext( - std::shared_ptr context, - RenderTarget render_target) +InlinePassContext::InlinePassContext(std::shared_ptr context, + RenderTarget render_target) : context_(context), render_target_(render_target) {} InlinePassContext::~InlinePassContext() { @@ -52,7 +51,7 @@ bool InlinePassContext::EndPass() { return true; } -RenderTarget InlinePassContext::GetRenderTarget() const { +const RenderTarget& InlinePassContext::GetRenderTarget() const { return render_target_; } @@ -99,4 +98,4 @@ std::shared_ptr InlinePassContext::GetRenderPass( return pass_; } -} +} // namespace impeller diff --git a/impeller/entity/inline_pass_context.h b/impeller/entity/inline_pass_context.h index dcae129ce0ad3..cde3aad13b16f 100644 --- a/impeller/entity/inline_pass_context.h +++ b/impeller/entity/inline_pass_context.h @@ -20,7 +20,8 @@ class InlinePassContext { bool IsActive() const; std::shared_ptr GetTexture(); bool EndPass(); - RenderTarget GetRenderTarget() const; + const RenderTarget& GetRenderTarget() const; + std::shared_ptr GetRenderPass(uint32_t pass_depth); private: From 74f34d1588d8a021033fbcd038dfb748f14a7fcb Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 7 Jun 2022 15:31:33 -0700 Subject: [PATCH 05/13] Beef up the test --- .../display_list/display_list_unittests.cc | 36 +++++++++++++++---- impeller/entity/entity_pass.cc | 2 +- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/impeller/display_list/display_list_unittests.cc b/impeller/display_list/display_list_unittests.cc index 150cc730d6356..ce5c4ceff5786 100644 --- a/impeller/display_list/display_list_unittests.cc +++ b/impeller/display_list/display_list_unittests.cc @@ -6,6 +6,7 @@ #include "display_list/display_list_color.h" #include "display_list/display_list_color_filter.h" #include "display_list/display_list_image_filter.h" +#include "display_list/display_list_paint.h" #include "display_list/display_list_tile_mode.h" #include "gtest/gtest.h" #include "third_party/imgui/imgui.h" @@ -294,29 +295,50 @@ TEST_P(DisplayListTest, CanDrawBackdropFilter) { if (first_frame) { first_frame = false; ImGui::SetNextWindowSize({400, 100}); - ImGui::SetNextWindowPos({300, 550}); + ImGui::SetNextWindowPos({300, 650}); } static float sigma[] = {10, 10}; + static bool use_bounds = true; + static bool draw_circle = true; ImGui::Begin("Controls"); ImGui::SliderFloat2("Sigma", sigma, 0, 100); + ImGui::Checkbox("Use SaveLayer bounds", &use_bounds); + ImGui::Checkbox("Draw child element", &draw_circle); ImGui::End(); flutter::DisplayListBuilder builder; + Vector2 scale = GetContentScale(); + builder.scale(scale.x, scale.y); + auto filter = flutter::DlBlurImageFilter(sigma[0], sigma[1], flutter::DlTileMode::kClamp); - flutter::DlPaint paint; - paint.setColor(flutter::DlColor::kRed()); - builder.drawCircle({100, 100}, 100, paint); + std::optional bounds; + if (use_bounds) { + auto [p1, p2] = IMPELLER_PLAYGROUND_LINE( + Point(250, 150), Point(800, 600), 20, Color::White(), Color::White()); + bounds = SkRect::MakeLTRB(p1.x, p1.y, p2.x, p2.y); + } - auto bounds = SkRect::MakeXYWH(100, 100, 100, 100); - builder.saveLayer(&bounds, &paint, &filter); - builder.setImageFilter(&filter); builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(200, 200), SkSamplingOptions{}, true); + builder.saveLayer(bounds.has_value() ? &bounds.value() : nullptr, nullptr, + &filter); + + if (draw_circle) { + auto circle_center = + IMPELLER_PLAYGROUND_POINT(Point(500, 400), 20, Color::Red()); + + builder.setStyle(flutter::DlDrawStyle::kStroke); + builder.setStrokeCap(flutter::DlStrokeCap::kButt); + builder.setStrokeJoin(flutter::DlStrokeJoin::kBevel); + builder.setStrokeWidth(10); + builder.setColor(flutter::DlColor::kRed().withAlpha(100)); + builder.drawCircle({circle_center.x, circle_center.y}, 100); + } return builder.Build(); }; diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 2dcd547854fdf..67081dd51aac1 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -8,8 +8,8 @@ #include #include "flutter/fml/logging.h" +#include "flutter/fml/macros.h" #include "flutter/fml/trace_event.h" -#include "fml/macros.h" #include "impeller/base/validation.h" #include "impeller/entity/contents/content_context.h" #include "impeller/entity/contents/filters/filter_contents.h" From 64fb54a28fdba72f89950d04af377819e099d246 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 7 Jun 2022 16:24:11 -0700 Subject: [PATCH 06/13] Fix coverage behavior --- impeller/entity/entity_pass.cc | 47 ++++++++++++++++++---------------- impeller/entity/entity_pass.h | 4 ++- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 67081dd51aac1..72975ef408503 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -91,8 +91,12 @@ std::optional EntityPass::GetElementsCoverage() const { } std::optional EntityPass::GetSubpassCoverage( - const EntityPass& subpass) const { - auto entities_coverage = subpass.GetElementsCoverage(); + const EntityPass& subpass, + std::optional backdrop_coverage) const { + auto entities_coverage = backdrop_coverage.has_value() + ? backdrop_coverage + : subpass.GetElementsCoverage(); + // The entities don't cover anything. There is nothing to do. if (!entities_coverage.has_value()) { return std::nullopt; @@ -228,24 +232,23 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( return EntityPass::EntityResult::Skip(); } - std::optional subpass_coverage; - - if (subpass->elements_.empty() && backdrop_filter_proc_.has_value()) { - // An empty subpass with a backdrop filter should render the backdrop over - // the entire coverage of the current pass. - subpass_coverage = Rect( + std::optional backdrop_coverage; + if (subpass->backdrop_filter_proc_.has_value()) { + backdrop_coverage = Rect( position, Size(pass_context.GetRenderTarget().GetRenderTargetSize())); - } else { - subpass_coverage = GetSubpassCoverage(*subpass); - if (!subpass_coverage.has_value()) { - return EntityPass::EntityResult::Skip(); - } + } - if (subpass_coverage->size.IsEmpty()) { - // It is not an error to have an empty subpass. But subpasses that can't - // create their intermediates must trip errors. - return EntityPass::EntityResult::Skip(); - } + std::optional subpass_coverage = + GetSubpassCoverage(*subpass, backdrop_coverage); + + if (!subpass_coverage.has_value()) { + return EntityPass::EntityResult::Skip(); + } + + if (subpass_coverage->size.IsEmpty()) { + // It is not an error to have an empty subpass. But subpasses that can't + // create their intermediates must trip errors. + return EntityPass::EntityResult::Skip(); } RenderTarget subpass_target; @@ -389,10 +392,10 @@ bool EntityPass::OnRender(ContentContext& renderer, // texture as an input ("destination"), blending with a second texture // input ("source"), writing the result to an intermediate texture, and // finally copying the data from the intermediate texture back to the - // render target texture. And so all of the commands that have written to - // the render target texture so far need to execute before it's bound for - // blending (otherwise the blend pass will end up executing before all the - // previous commands in the active pass). + // render target texture. And so all of the commands that have written + // to the render target texture so far need to execute before it's bound + // for blending (otherwise the blend pass will end up executing before + // all the previous commands in the active pass). if (!pass_context.EndPass()) { return false; } diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index 46b21b53be896..2de0d3e22354e 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -60,7 +60,9 @@ class EntityPass { void SetBackdropFilter(std::optional proc); - std::optional GetSubpassCoverage(const EntityPass& subpass) const; + std::optional GetSubpassCoverage( + const EntityPass& subpass, + std::optional backdrop_coverage = std::nullopt) const; std::optional GetElementsCoverage() const; From d808f61d417abce251133ddbe22086458a493ea8 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 7 Jun 2022 16:28:56 -0700 Subject: [PATCH 07/13] Address comment --- impeller/display_list/display_list_dispatcher.cc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index b698500335bbd..3cf40c8d2bc6c 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -380,11 +380,8 @@ void DisplayListDispatcher::saveLayer(const SkRect* bounds, const flutter::SaveLayerOptions options, const flutter::DlImageFilter* backdrop) { auto paint = options.renders_with_attributes() ? paint_ : Paint{}; - if (backdrop) { - canvas_.SaveLayer(paint, ToRect(bounds), ToImageFilterProc(backdrop)); - } else { - canvas_.SaveLayer(paint, ToRect(bounds)); - } + canvas_.SaveLayer(paint, ToRect(bounds), + backdrop ? ToImageFilterProc(backdrop) : std::nullopt); } // |flutter::Dispatcher| From 30f3f215b93edee0f44743523ab2800a047e2a30 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 7 Jun 2022 16:29:57 -0700 Subject: [PATCH 08/13] Licenses --- ci/licenses_golden/licenses_flutter | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 42a60f1fb1cdd..170f019182b98 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -568,6 +568,8 @@ FILE: ../../../flutter/impeller/entity/entity_pass_delegate.h FILE: ../../../flutter/impeller/entity/entity_playground.cc FILE: ../../../flutter/impeller/entity/entity_playground.h FILE: ../../../flutter/impeller/entity/entity_unittests.cc +FILE: ../../../flutter/impeller/entity/inline_pass_context.cc +FILE: ../../../flutter/impeller/entity/inline_pass_context.h FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend.glsl FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend.vert FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_color.frag From 813764d4e402294d3347e0c05e8c4820c4008ab2 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 7 Jun 2022 16:39:11 -0700 Subject: [PATCH 09/13] Address comment --- impeller/display_list/display_list_dispatcher.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 3cf40c8d2bc6c..4acc0adaf07b6 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -380,8 +380,7 @@ void DisplayListDispatcher::saveLayer(const SkRect* bounds, const flutter::SaveLayerOptions options, const flutter::DlImageFilter* backdrop) { auto paint = options.renders_with_attributes() ? paint_ : Paint{}; - canvas_.SaveLayer(paint, ToRect(bounds), - backdrop ? ToImageFilterProc(backdrop) : std::nullopt); + canvas_.SaveLayer(paint, ToRect(bounds), ToImageFilterProc(backdrop)); } // |flutter::Dispatcher| From d91a750c67483876a29e22d04c1b2af898e2e5cb Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 7 Jun 2022 17:14:40 -0700 Subject: [PATCH 10/13] Fix coverage again --- impeller/aiks/canvas.cc | 2 +- impeller/entity/entity_pass.cc | 34 +++++++++++++++------------------- impeller/entity/entity_pass.h | 4 +--- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index c106d49931653..c7b317c08127b 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -275,7 +275,7 @@ void Canvas::SaveLayer(Paint paint, std::make_unique(paint, bounds)); new_layer_pass.SetBackdropFilter(backdrop_filter); - if (bounds.has_value()) { + if (bounds.has_value() && !backdrop_filter.has_value()) { // Render target switches due to a save layer can be elided. In such cases // where passes are collapsed into their parent, the clipping effect to // the size of the render target that would have been allocated will be diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 72975ef408503..b458969e534c7 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -91,12 +91,8 @@ std::optional EntityPass::GetElementsCoverage() const { } std::optional EntityPass::GetSubpassCoverage( - const EntityPass& subpass, - std::optional backdrop_coverage) const { - auto entities_coverage = backdrop_coverage.has_value() - ? backdrop_coverage - : subpass.GetElementsCoverage(); - + const EntityPass& subpass) const { + auto entities_coverage = subpass.GetElementsCoverage(); // The entities don't cover anything. There is nothing to do. if (!entities_coverage.has_value()) { return std::nullopt; @@ -232,23 +228,23 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( return EntityPass::EntityResult::Skip(); } - std::optional backdrop_coverage; + std::optional subpass_coverage; + if (subpass->backdrop_filter_proc_.has_value()) { - backdrop_coverage = Rect( + subpass_coverage = Rect( position, Size(pass_context.GetRenderTarget().GetRenderTargetSize())); - } - - std::optional subpass_coverage = - GetSubpassCoverage(*subpass, backdrop_coverage); + } else { + subpass_coverage = GetSubpassCoverage(*subpass); - if (!subpass_coverage.has_value()) { - return EntityPass::EntityResult::Skip(); - } + if (!subpass_coverage.has_value()) { + return EntityPass::EntityResult::Skip(); + } - if (subpass_coverage->size.IsEmpty()) { - // It is not an error to have an empty subpass. But subpasses that can't - // create their intermediates must trip errors. - return EntityPass::EntityResult::Skip(); + if (subpass_coverage->size.IsEmpty()) { + // It is not an error to have an empty subpass. But subpasses that can't + // create their intermediates must trip errors. + return EntityPass::EntityResult::Skip(); + } } RenderTarget subpass_target; diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index 2de0d3e22354e..46b21b53be896 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -60,9 +60,7 @@ class EntityPass { void SetBackdropFilter(std::optional proc); - std::optional GetSubpassCoverage( - const EntityPass& subpass, - std::optional backdrop_coverage = std::nullopt) const; + std::optional GetSubpassCoverage(const EntityPass& subpass) const; std::optional GetElementsCoverage() const; From 364882834b0db76d0b508f43c2d92cd55ee3b297 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 8 Jun 2022 12:45:47 -0700 Subject: [PATCH 11/13] Incorporate post-filter coverage --- impeller/entity/entity_pass.cc | 42 ++++++++++++++++------------------ impeller/entity/entity_pass.h | 2 +- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index b458969e534c7..bf987fde5a72b 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -228,11 +228,23 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( return EntityPass::EntityResult::Skip(); } - std::optional subpass_coverage; - + std::shared_ptr backdrop_contents = nullptr; if (subpass->backdrop_filter_proc_.has_value()) { - subpass_coverage = Rect( - position, Size(pass_context.GetRenderTarget().GetRenderTargetSize())); + auto texture = pass_context.GetTexture(); + // Render the backdrop texture before any of the pass elements. + const auto& proc = subpass->backdrop_filter_proc_.value(); + backdrop_contents = proc(FilterInput::Make(std::move(texture))); + + // The subpass will need to read from the current pass texture when + // rendering the backdrop, so if there's an active pass, end it prior to + // rendering the subpass. + pass_context.EndPass(); + } + + std::optional subpass_coverage; + if (backdrop_contents) { + subpass_coverage = backdrop_contents->GetCoverage(Entity{}); + subpass_coverage->origin += position; } else { subpass_coverage = GetSubpassCoverage(*subpass); @@ -283,21 +295,11 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( return EntityPass::EntityResult::Failure(); } - std::shared_ptr backdrop_texture = nullptr; - if (subpass->backdrop_filter_proc_.has_value()) { - backdrop_texture = pass_context.GetTexture(); - - // The subpass will need to read from the current pass texture when - // rendering the backdrop, so if there's an active pass, end it prior to - // rendering the subpass. - pass_context.EndPass(); - } - // Stencil textures aren't shared between EntityPasses (as much of the // time they are transient). if (!subpass->OnRender(renderer, subpass_target, subpass_coverage->origin, position, ++pass_depth, subpass->stencil_depth_, - backdrop_texture)) { + backdrop_contents)) { return EntityPass::EntityResult::Failure(); } @@ -323,7 +325,7 @@ bool EntityPass::OnRender(ContentContext& renderer, Point parent_position, uint32_t pass_depth, size_t stencil_depth_floor, - std::shared_ptr backdrop_texture) const { + std::shared_ptr backdrop_contents) const { TRACE_EVENT0("impeller", "EntityPass::OnRender"); auto context = renderer.GetContext(); @@ -345,16 +347,12 @@ bool EntityPass::OnRender(ContentContext& renderer, }; if (backdrop_filter_proc_.has_value()) { - if (!backdrop_texture) { + if (!backdrop_contents) { return false; } - // Render the backdrop texture before any of the pass elements. - const auto& proc = backdrop_filter_proc_.value(); - auto contents = proc(FilterInput::Make(std::move(backdrop_texture))); - Entity backdrop_entity; - backdrop_entity.SetContents(std::move(contents)); + backdrop_entity.SetContents(std::move(backdrop_contents)); backdrop_entity.SetTransformation( Matrix::MakeTranslation(Vector3(parent_position - position))); diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index 46b21b53be896..7100e4b68d256 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -91,7 +91,7 @@ class EntityPass { Point parent_position, uint32_t pass_depth, size_t stencil_depth_floor = 0, - std::shared_ptr backdrop_texture = nullptr) const; + std::shared_ptr backdrop_contents = nullptr) const; std::vector elements_; From 3a960494bdf93f1493e8637bfe89bc821350f33f Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Fri, 10 Jun 2022 15:52:24 -0700 Subject: [PATCH 12/13] Always use maximal coverage --- impeller/entity/entity_pass.cc | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index bf987fde5a72b..7227a1b342c0c 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -241,22 +241,29 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( pass_context.EndPass(); } - std::optional subpass_coverage; - if (backdrop_contents) { - subpass_coverage = backdrop_contents->GetCoverage(Entity{}); - subpass_coverage->origin += position; - } else { - subpass_coverage = GetSubpassCoverage(*subpass); + auto subpass_coverage = GetSubpassCoverage(*subpass); - if (!subpass_coverage.has_value()) { - return EntityPass::EntityResult::Skip(); + if (backdrop_contents) { + auto backdrop_coverage = backdrop_contents->GetCoverage(Entity{}); + if (backdrop_coverage.has_value()) { + backdrop_coverage->origin += position; + + if (subpass_coverage.has_value()) { + subpass_coverage = subpass_coverage->Union(backdrop_coverage.value()); + } else { + subpass_coverage = backdrop_coverage; + } } + } - if (subpass_coverage->size.IsEmpty()) { - // It is not an error to have an empty subpass. But subpasses that can't - // create their intermediates must trip errors. - return EntityPass::EntityResult::Skip(); - } + if (!subpass_coverage.has_value()) { + return EntityPass::EntityResult::Skip(); + } + + if (subpass_coverage->size.IsEmpty()) { + // It is not an error to have an empty subpass. But subpasses that can't + // create their intermediates must trip errors. + return EntityPass::EntityResult::Skip(); } RenderTarget subpass_target; From eb372e6193ef303c3098ec4842ec569732f67f3e Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 15 Jun 2022 11:37:26 -0700 Subject: [PATCH 13/13] Use enum --- impeller/entity/entity_pass.cc | 32 +++++++++++++++----------------- impeller/entity/entity_pass.h | 20 +++++++++++++++----- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 7227a1b342c0c..c61b4db7fddb1 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -371,22 +371,20 @@ bool EntityPass::OnRender(ContentContext& renderer, GetEntityForElement(element, renderer, pass_context, position, pass_depth, stencil_depth_floor); - if (!result.success) { - return false; - } - - if (!result.entity.has_value()) { - // Nothing to render. - continue; - } - - Entity element_entity = result.entity.value(); + switch (result.status) { + case EntityResult::kSuccess: + break; + case EntityResult::kFailure: + return false; + case EntityResult::kSkip: + continue; + }; //-------------------------------------------------------------------------- /// Setup advanced blends. /// - if (element_entity.GetBlendMode() > + if (result.entity.GetBlendMode() > Entity::BlendMode::kLastPipelineBlendMode) { // End the active pass and flush the buffer before rendering "advanced" // blends. Advanced blends work by binding the current render target @@ -409,19 +407,19 @@ bool EntityPass::OnRender(ContentContext& renderer, } FilterInput::Vector inputs = { - FilterInput::Make(element_entity.GetContents()), + FilterInput::Make(result.entity.GetContents()), FilterInput::Make(texture, - element_entity.GetTransformation().Invert())}; - element_entity.SetContents( - FilterContents::MakeBlend(element_entity.GetBlendMode(), inputs)); - element_entity.SetBlendMode(Entity::BlendMode::kSourceOver); + result.entity.GetTransformation().Invert())}; + result.entity.SetContents( + FilterContents::MakeBlend(result.entity.GetBlendMode(), inputs)); + result.entity.SetBlendMode(Entity::BlendMode::kSourceOver); } //-------------------------------------------------------------------------- /// Render the Element. /// - if (!render_element(element_entity)) { + if (!render_element(result.entity)) { return false; } } diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index 7100e4b68d256..9954fe5898ea1 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -66,16 +66,26 @@ class EntityPass { private: struct EntityResult { + enum Status { + /// The entity was successfully resolved and can be rendered. + kSuccess, + /// An unexpected rendering error occurred while resolving the Entity. + kFailure, + /// The entity should be skipped because rendering it will contribute + /// nothing to the frame. + kSkip, + }; + /// @brief The resulting entity that should be rendered. If `std::nullopt`, /// there is nothing to render. - std::optional entity = std::nullopt; + Entity entity; /// @brief This is set to `false` if there was an unexpected rendering /// error while resolving the Entity. - bool success = false; + Status status = kFailure; - static EntityResult Success(Entity e) { return {e, true}; } - static EntityResult Failure() { return {std::nullopt, false}; } - static EntityResult Skip() { return {std::nullopt, true}; } + static EntityResult Success(Entity e) { return {e, kSuccess}; } + static EntityResult Failure() { return {{}, kFailure}; } + static EntityResult Skip() { return {{}, kSkip}; } }; EntityResult GetEntityForElement(const EntityPass::Element& element,