From 46616a4dd0d43cb7767a6194391d590b17d88c8a Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Sun, 26 Mar 2023 01:05:02 -0700 Subject: [PATCH] [Impeller] Document EntityPass, make positioning less confusing --- impeller/entity/entity_pass.cc | 101 ++++++++++++++++++++++----------- impeller/entity/entity_pass.h | 77 +++++++++++++++++++++++-- 2 files changed, 140 insertions(+), 38 deletions(-) diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index e7b94ef7e34ca..ea30b55b76452 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -211,9 +211,15 @@ bool EntityPass::Render(ContentContext& renderer, auto offscreen_target = CreateRenderTarget(renderer, render_target.GetRenderTargetSize(), true); - if (!OnRender( - renderer, offscreen_target.GetRenderTarget().GetRenderTargetSize(), - offscreen_target, Point(), Point(), 0, stencil_coverage_stack)) { + if (!OnRender(renderer, // renderer + offscreen_target.GetRenderTarget() + .GetRenderTargetSize(), // root_pass_size + offscreen_target, // pass_target + Point(), // global_pass_position + Point(), // local_pass_position + 0, // pass_depth + stencil_coverage_stack // stencil_coverage_stack + )) { return false; } @@ -267,8 +273,14 @@ bool EntityPass::Render(ContentContext& renderer, render_target, renderer.GetDeviceCapabilities().SupportsReadFromResolve()); - return OnRender(renderer, render_target.GetRenderTargetSize(), pass_target, - Point(), Point(), 0, stencil_coverage_stack); + return OnRender( // + renderer, // renderer + render_target.GetRenderTargetSize(), // root_pass_size + pass_target, // pass_target + Point(), // global_pass_position + Point(), // local_pass_position + 0, // pass_depth + stencil_coverage_stack); // stencil_coverage_stack } EntityPass::EntityResult EntityPass::GetEntityForElement( @@ -276,7 +288,7 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( ContentContext& renderer, InlinePassContext& pass_context, ISize root_pass_size, - Point position, + Point global_pass_position, uint32_t pass_depth, StencilCoverageStack& stencil_coverage_stack, size_t stencil_depth_floor) const { @@ -288,12 +300,12 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( if (const auto& entity = std::get_if(&element)) { element_entity = *entity; - if (!position.IsZero()) { + if (!global_pass_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)) * + Matrix::MakeTranslation(Vector3(-global_pass_position)) * element_entity.GetTransformation()); } } @@ -313,10 +325,18 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( if (!subpass->backdrop_filter_proc_.has_value() && subpass->delegate_->CanCollapseIntoParentPass(subpass)) { // Directly render into the parent target and move on. - if (!subpass->OnRender(renderer, root_pass_size, - pass_context.GetPassTarget(), position, position, - pass_depth, stencil_coverage_stack, stencil_depth_, - nullptr, pass_context.GetRenderPass(pass_depth))) { + if (!subpass->OnRender( + renderer, // renderer + root_pass_size, // root_pass_size + pass_context.GetPassTarget(), // pass_target + global_pass_position, // global_pass_position + Point(), // local_pass_position + pass_depth, // pass_depth + stencil_coverage_stack, // stencil_coverage_stack + stencil_depth_, // stencil_depth_floor + nullptr, // backdrop_filter_contents + pass_context.GetRenderPass(pass_depth) // collapsed_parent_pass + )) { return EntityPass::EntityResult::Failure(); } return EntityPass::EntityResult::Skip(); @@ -340,14 +360,15 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( auto subpass_coverage = GetSubpassCoverage(*subpass, Rect::MakeSize(root_pass_size)); if (subpass->cover_whole_screen_) { - subpass_coverage = Rect(position, Size(pass_context.GetPassTarget() - .GetRenderTarget() - .GetRenderTargetSize())); + subpass_coverage = + Rect(global_pass_position, Size(pass_context.GetPassTarget() + .GetRenderTarget() + .GetRenderTargetSize())); } if (backdrop_filter_contents) { auto backdrop_coverage = backdrop_filter_contents->GetCoverage(Entity{}); if (backdrop_coverage.has_value()) { - backdrop_coverage->origin += position; + backdrop_coverage->origin += global_pass_position; subpass_coverage = subpass_coverage.has_value() @@ -379,7 +400,8 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( auto offscreen_texture_contents = subpass->delegate_->CreateContentsForSubpassTarget( subpass_texture, - Matrix::MakeTranslation(Vector3{-position}) * subpass->xformation_); + Matrix::MakeTranslation(Vector3{-global_pass_position}) * + subpass->xformation_); if (!offscreen_texture_contents) { // This is an error because the subpass delegate said the pass couldn't @@ -395,18 +417,25 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( // Stencil textures aren't shared between EntityPasses (as much of the // time they are transient). - if (!subpass->OnRender(renderer, root_pass_size, subpass_target, - subpass_coverage->origin, position, ++pass_depth, - stencil_coverage_stack, subpass->stencil_depth_, - backdrop_filter_contents)) { + if (!subpass->OnRender(renderer, // renderer + root_pass_size, // root_pass_size + subpass_target, // pass_target + subpass_coverage->origin, // global_pass_position + subpass_coverage->origin - + global_pass_position, // local_pass_position + ++pass_depth, // pass_depth + stencil_coverage_stack, // stencil_coverage_stack + subpass->stencil_depth_, // stencil_depth_floor + backdrop_filter_contents // backdrop_filter_contents + )) { return EntityPass::EntityResult::Failure(); } element_entity.SetContents(std::move(offscreen_texture_contents)); element_entity.SetStencilDepth(subpass->stencil_depth_); element_entity.SetBlendMode(subpass->blend_mode_); - element_entity.SetTransformation( - Matrix::MakeTranslation(Vector3(subpass_coverage->origin - position))); + element_entity.SetTransformation(Matrix::MakeTranslation( + Vector3(subpass_coverage->origin - global_pass_position))); } else { FML_UNREACHABLE(); } @@ -417,8 +446,8 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( bool EntityPass::OnRender(ContentContext& renderer, ISize root_pass_size, EntityPassTarget& pass_target, - Point position, - Point parent_position, + Point global_pass_position, + Point local_pass_position, uint32_t pass_depth, StencilCoverageStack& stencil_coverage_stack, size_t stencil_depth_floor, @@ -437,7 +466,7 @@ bool EntityPass::OnRender(ContentContext& renderer, auto render_element = [&stencil_depth_floor, &pass_context, &pass_depth, &renderer, &stencil_coverage_stack, - &position](Entity& element_entity) { + &global_pass_position](Entity& element_entity) { auto result = pass_context.GetRenderPass(pass_depth); if (!result.pass) { @@ -469,7 +498,7 @@ bool EntityPass::OnRender(ContentContext& renderer, if (current_stencil_coverage.has_value()) { // Entity transforms are relative to the current pass position, so we need // to check stencil coverage in the same space. - current_stencil_coverage->origin -= position; + current_stencil_coverage->origin -= global_pass_position; } if (!element_entity.ShouldRender(current_stencil_coverage)) { @@ -479,7 +508,7 @@ bool EntityPass::OnRender(ContentContext& renderer, auto stencil_coverage = element_entity.GetStencilCoverage(current_stencil_coverage); if (stencil_coverage.coverage.has_value()) { - stencil_coverage.coverage->origin += position; + stencil_coverage.coverage->origin += global_pass_position; } switch (stencil_coverage.type) { @@ -517,7 +546,7 @@ bool EntityPass::OnRender(ContentContext& renderer, : std::nullopt; if (restore_coverage.has_value()) { // Make the coverage rectangle relative to the current pass. - restore_coverage->origin -= position; + restore_coverage->origin -= global_pass_position; } stencil_coverage_stack.resize(restoration_depth + 1); @@ -549,16 +578,22 @@ bool EntityPass::OnRender(ContentContext& renderer, Entity backdrop_entity; backdrop_entity.SetContents(std::move(backdrop_filter_contents)); backdrop_entity.SetTransformation( - Matrix::MakeTranslation(Vector3(parent_position - position))); + Matrix::MakeTranslation(Vector3(local_pass_position))); backdrop_entity.SetStencilDepth(stencil_depth_floor); render_element(backdrop_entity); } for (const auto& element : elements_) { - EntityResult result = GetEntityForElement( - element, renderer, pass_context, root_pass_size, position, pass_depth, - stencil_coverage_stack, stencil_depth_floor); + EntityResult result = + GetEntityForElement(element, // element + renderer, // renderer + pass_context, // pass_context + root_pass_size, // root_pass_size + global_pass_position, // global_pass_position + pass_depth, // pass_depth + stencil_coverage_stack, // stencil_coverage_stack + stencil_depth_floor); // stencil_depth_floor switch (result.status) { case EntityResult::kSuccess: diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index b276748f8f009..b448a93cb2017 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -25,7 +25,17 @@ class ContentContext; class EntityPass { public: + /// Elements are renderable items in the `EntityPass`. Each can either be an + /// `Entity` or a child `EntityPass`. + /// + /// When the element is a child `EntityPass`, it may be rendered to an + /// offscreen texture and converted into an `Entity` that draws the texture + /// into the current pass, or its children may be collapsed into the current + /// + /// `EntityPass`. Elements are converted to Entities in + /// `GetEntityForElement()`. using Element = std::variant>; + using BackdropFilterProc = std::function( FilterInput::Ref, const Matrix& effect_transform, @@ -104,7 +114,7 @@ class EntityPass { /// error while resolving the Entity. Status status = kFailure; - static EntityResult Success(Entity e) { return {e, kSuccess}; } + static EntityResult Success(const Entity& e) { return {e, kSuccess}; } static EntityResult Failure() { return {{}, kFailure}; } static EntityResult Skip() { return {{}, kSkip}; } }; @@ -113,16 +123,71 @@ class EntityPass { ContentContext& renderer, InlinePassContext& pass_context, ISize root_pass_size, - Point position, + Point global_pass_position, uint32_t pass_depth, StencilCoverageStack& stencil_coverage_stack, size_t stencil_depth_floor) const; + /// @brief OnRender is the internal command recording routine for + /// `EntityPass`. Its job is to walk through each `Element` which + /// was appended to the scene (either an `Entity` via `AddEntity()` + /// or a child `EntityPass` via `AddSubpass()`) and render them to + /// the given `pass_target`. + /// @param[in] renderer The Contents context, which manages + /// pipeline state. + /// @param[in] root_pass_size The size of the texture being + /// rendered into at the root of the + /// `EntityPass` tree. This is the size + /// of the `RenderTarget` color + /// attachment passed to the public + /// `EntityPass::Render` method. + /// @param[out] pass_target Stores the render target that should + /// be used for rendering. + /// @param[in] global_pass_position The position that this `EntityPass` + /// will be drawn to the parent pass + /// relative to the root pass origin. + /// Used for offsetting drawn `Element`s, + /// whose origins are all in root + /// pass/screen space, + /// @param[in] local_pass_position The position that this `EntityPass` + /// will be drawn to the parent pass + /// relative to the parent pass origin. + /// Used for positioning backdrop + /// filters. + /// @param[in] pass_depth The tree depth of the `EntityPass` at + /// render time. Only used for labeling + /// and debugging purposes. This can vary + /// depending on whether passes are + /// collapsed or not. + /// @param[in] stencil_coverage_stack A global stack of coverage rectangles + /// for the stencil buffer at each depth. + /// Higher depths are more restrictive. + /// Used to cull Elements that we + /// know won't result in a visible + /// change. + /// @param[in] stencil_depth_floor The stencil depth that a value of + /// zero corresponds to in the given + /// `pass_target` stencil buffer. + /// When new `pass_target`s are created + /// for subpasses, their stencils are + /// initialized at zero, and so this + /// value is used to offset Entity clip + /// depths to match the stencil. + /// @param[in] backdrop_filter_contents Optional. Is supplied, this contents + /// is rendered prior to anything else in + /// the `EntityPass`, offset by the + /// `local_pass_position`. + /// @param[in] collapsed_parent_pass Optional. If supplied, this + /// `InlinePassContext` state is used to + /// begin rendering elements instead of + /// creating a new `RenderPass`. This + /// "collapses" the Elements into the + /// parent pass. bool OnRender(ContentContext& renderer, ISize root_pass_size, - EntityPassTarget& render_target, - Point position, - Point parent_position, + EntityPassTarget& pass_target, + Point global_pass_position, + Point local_pass_position, uint32_t pass_depth, StencilCoverageStack& stencil_coverage_stack, size_t stencil_depth_floor = 0, @@ -130,6 +195,8 @@ class EntityPass { std::optional collapsed_parent_pass = std::nullopt) const; + /// The list of renderable items in the scene. Each of these items is + /// evaluated and recorded to an `EntityPassTarget` by the `OnRender` method. std::vector elements_; EntityPass* superpass_ = nullptr;