From 60b868ed1c80a8ab8645d9f27d5165464a581100 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Sun, 24 Mar 2024 13:26:40 -0700 Subject: [PATCH 1/7] [Impeller] fix unbalanced restores. --- impeller/entity/BUILD.gn | 1 + impeller/entity/entity_pass.cc | 8 +++- impeller/entity/entity_pass_unittests.cc | 52 ++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 impeller/entity/entity_pass_unittests.cc diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index b13e8f81526d8..a917b91b781cf 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -272,6 +272,7 @@ impeller_component("entity_unittests") { "contents/tiled_texture_contents_unittests.cc", "contents/vertices_contents_unittests.cc", "entity_pass_target_unittests.cc", + "entity_pass_unittests.cc", "entity_playground.cc", "entity_playground.h", "entity_unittests.cc", diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 353fccf8cd5f9..b470189a242a9 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -869,7 +869,9 @@ bool EntityPass::RenderElement(Entity& element_entity, if constexpr (ContentContext::kEnableStencilThenCover) { // Skip all clip restores when stencil-then-cover is enabled. - clip_replay_->RecordEntity(element_entity, clip_coverage.type); + if (clip_coverage_stack.back().coverage.has_value()) { + clip_replay_->RecordEntity(element_entity, clip_coverage.type); + } return true; } @@ -1269,7 +1271,9 @@ void EntityPassClipRecorder::RecordEntity(const Entity& entity, rendered_clip_entities_.push_back(entity.Clone()); break; case Contents::ClipCoverage::Type::kRestore: - rendered_clip_entities_.pop_back(); + if (!rendered_clip_entities_.empty()) { + rendered_clip_entities_.pop_back(); + } break; } } diff --git a/impeller/entity/entity_pass_unittests.cc b/impeller/entity/entity_pass_unittests.cc new file mode 100644 index 0000000000000..d1f05448e938b --- /dev/null +++ b/impeller/entity/entity_pass_unittests.cc @@ -0,0 +1,52 @@ +// 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 "flutter/testing/testing.h" +#include "gtest/gtest.h" +#include "impeller/entity/entity_pass.h" + +namespace impeller { +namespace testing { + +TEST(EntityPassClipRecorderTest, CanPushAndPopEntities) { + EntityPassClipRecorder recorder = EntityPassClipRecorder(); + + EXPECT_TRUE(recorder.GetReplayEntities().empty()); + + Entity entity; + recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kAppend); + EXPECT_EQ(recorder.GetReplayEntities().size(), 1u); + + recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kAppend); + EXPECT_EQ(recorder.GetReplayEntities().size(), 2u); + + recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kRestore); + EXPECT_EQ(recorder.GetReplayEntities().size(), 1u); + + recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kRestore); + EXPECT_TRUE(recorder.GetReplayEntities().empty()); +} + +TEST(EntityPassClipRecorderTest, CanPopEntitiesSafely) { + EntityPassClipRecorder recorder = EntityPassClipRecorder(); + + EXPECT_TRUE(recorder.GetReplayEntities().empty()); + + Entity entity; + recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kRestore); + EXPECT_TRUE(recorder.GetReplayEntities().empty()); +} + +TEST(EntityPassClipRecorderTest, CanAppendNoChange) { + EntityPassClipRecorder recorder = EntityPassClipRecorder(); + + EXPECT_TRUE(recorder.GetReplayEntities().empty()); + + Entity entity; + recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kNoChange); + EXPECT_TRUE(recorder.GetReplayEntities().empty()); +} + +} // namespace testing +} // namespace impeller From 7d56494982e807eb1dd134e94bf6fa1c5e41a4e7 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Mon, 25 Mar 2024 08:56:19 -0700 Subject: [PATCH 2/7] update licenses --- ci/licenses_golden/excluded_files | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index e89bbf56616b0..c9c50cea46b57 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -152,6 +152,7 @@ ../../../flutter/impeller/entity/contents/tiled_texture_contents_unittests.cc ../../../flutter/impeller/entity/contents/vertices_contents_unittests.cc ../../../flutter/impeller/entity/entity_pass_target_unittests.cc +../../../flutter/impeller/entity/entity_pass_unittests.cc ../../../flutter/impeller/entity/entity_unittests.cc ../../../flutter/impeller/entity/geometry/geometry_unittests.cc ../../../flutter/impeller/entity/render_target_cache_unittests.cc From 0a60ba8480ad1d4b531de4043fd378e38b22e06c Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Mon, 25 Mar 2024 11:15:07 -0700 Subject: [PATCH 3/7] [Impeller] refactor clip stack and record/replay into separate class. --- impeller/entity/BUILD.gn | 2 + impeller/entity/entity_pass.cc | 134 +++---------------- impeller/entity/entity_pass.h | 47 ++----- impeller/entity/entity_pass_clip_stack.cc | 150 ++++++++++++++++++++++ impeller/entity/entity_pass_clip_stack.h | 61 +++++++++ impeller/entity/entity_pass_delegate.h | 1 - impeller/entity/entity_pass_unittests.cc | 142 +++++++++++++++++++- 7 files changed, 374 insertions(+), 163 deletions(-) create mode 100644 impeller/entity/entity_pass_clip_stack.cc create mode 100644 impeller/entity/entity_pass_clip_stack.h diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index a917b91b781cf..9e1a56a21a3c5 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -188,6 +188,8 @@ impeller_component("entity") { "entity.h", "entity_pass.cc", "entity_pass.h", + "entity_pass_clip_stack.cc", + "entity_pass_clip_stack.h", "entity_pass_delegate.cc", "entity_pass_delegate.h", "entity_pass_target.cc", diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index b470189a242a9..f016be78dbffd 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -15,7 +15,6 @@ #include "impeller/base/strings.h" #include "impeller/base/validation.h" #include "impeller/core/formats.h" -#include "impeller/entity/contents/clip_contents.h" #include "impeller/entity/contents/content_context.h" #include "impeller/entity/contents/filters/color_filter_contents.h" #include "impeller/entity/contents/filters/inputs/filter_input.h" @@ -409,9 +408,8 @@ bool EntityPass::Render(ContentContext& renderer, return true; }); - ClipCoverageStack clip_coverage_stack = {ClipCoverageLayer{ - .coverage = Rect::MakeSize(root_render_target.GetRenderTargetSize()), - .clip_depth = 0}}; + clip_replay_->Initialize( + Rect::MakeSize(root_render_target.GetRenderTargetSize())); bool reads_from_onscreen_backdrop = GetTotalPassReads(renderer) > 0; // In this branch path, we need to render everything to an offscreen texture @@ -430,8 +428,7 @@ bool EntityPass::Render(ContentContext& renderer, offscreen_target, // pass_target Point(), // global_pass_position Point(), // local_pass_position - 0, // pass_depth - clip_coverage_stack // clip_coverage_stack + 0 // pass_depth )) { // Validation error messages are triggered for all `OnRender()` failure // cases. @@ -536,8 +533,7 @@ bool EntityPass::Render(ContentContext& renderer, pass_target, // pass_target Point(), // global_pass_position Point(), // local_pass_position - 0, // pass_depth - clip_coverage_stack); // clip_coverage_stack + 0); // pass_depth } EntityPass::EntityResult EntityPass::GetEntityForElement( @@ -548,7 +544,6 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( ISize root_pass_size, Point global_pass_position, uint32_t pass_depth, - ClipCoverageStack& clip_coverage_stack, size_t clip_depth_floor) const { //-------------------------------------------------------------------------- /// Setup entity element. @@ -590,7 +585,6 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( global_pass_position, // global_pass_position Point(), // local_pass_position pass_depth, // pass_depth - clip_coverage_stack, // clip_coverage_stack clip_depth_, // clip_depth_floor nullptr, // backdrop_filter_contents pass_context.GetRenderPass(pass_depth) // collapsed_parent_pass @@ -625,13 +619,13 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( pass_context.EndPass(); } - if (clip_coverage_stack.empty()) { + if (!clip_replay_->HasCoverage()) { // The current clip is empty. This means the pass texture won't be // visible, so skip it. capture.CreateChild("Subpass Entity (Skipped: Empty clip A)"); return EntityPass::EntityResult::Skip(); } - auto clip_coverage_back = clip_coverage_stack.back().coverage; + auto clip_coverage_back = clip_replay_->CurrentClipCoverage(); if (!clip_coverage_back.has_value()) { capture.CreateChild("Subpass Entity (Skipped: Empty clip B)"); return EntityPass::EntityResult::Skip(); @@ -690,8 +684,7 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( // save layers may transform the subpass texture after it's rendered, // causing parent clip coverage to get misaligned with the actual area that // the subpass will affect in the parent pass. - ClipCoverageStack subpass_clip_coverage_stack = {ClipCoverageLayer{ - .coverage = subpass_coverage, .clip_depth = subpass->clip_depth_}}; + clip_replay_->PushSubpass(subpass_coverage, subpass->clip_depth_); // Stencil textures aren't shared between EntityPasses (as much of the // time they are transient). @@ -704,7 +697,6 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( subpass_coverage->GetOrigin() - global_pass_position, // local_pass_position ++pass_depth, // pass_depth - subpass_clip_coverage_stack, // clip_coverage_stack subpass->clip_depth_, // clip_depth_floor subpass_backdrop_filter_contents // backdrop_filter_contents )) { @@ -712,6 +704,7 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( // cases. return EntityPass::EntityResult::Failure(); } + clip_replay_->PopSubpass(); // The subpass target's texture may have changed during OnRender. auto subpass_texture = @@ -757,7 +750,6 @@ bool EntityPass::RenderElement(Entity& element_entity, InlinePassContext& pass_context, int32_t pass_depth, ContentContext& renderer, - ClipCoverageStack& clip_coverage_stack, Point global_pass_position) const { auto result = pass_context.GetRenderPass(pass_depth); if (!result.pass) { @@ -801,7 +793,7 @@ bool EntityPass::RenderElement(Entity& element_entity, } } - auto current_clip_coverage = clip_coverage_stack.back().coverage; + auto current_clip_coverage = clip_replay_->CurrentClipCoverage(); if (current_clip_coverage.has_value()) { // Entity transforms are relative to the current pass position, so we need // to check clip coverage in the same space. @@ -826,81 +818,12 @@ bool EntityPass::RenderElement(Entity& element_entity, element_entity.GetContents()->SetCoverageHint( Rect::Intersection(element_coverage_hint, current_clip_coverage)); - switch (clip_coverage.type) { - case Contents::ClipCoverage::Type::kNoChange: - break; - case Contents::ClipCoverage::Type::kAppend: { - auto op = clip_coverage_stack.back().coverage; - clip_coverage_stack.push_back( - ClipCoverageLayer{.coverage = clip_coverage.coverage, - .clip_depth = element_entity.GetClipDepth() + 1}); - FML_DCHECK(clip_coverage_stack.back().clip_depth == - clip_coverage_stack.front().clip_depth + - clip_coverage_stack.size() - 1); - - if (!op.has_value()) { - // Running this append op won't impact the clip buffer because the - // whole screen is already being clipped, so skip it. - return true; - } - } break; - case Contents::ClipCoverage::Type::kRestore: { - if (clip_coverage_stack.back().clip_depth <= - element_entity.GetClipDepth()) { - // Drop clip restores that will do nothing. - return true; - } - - auto restoration_index = element_entity.GetClipDepth() - - clip_coverage_stack.front().clip_depth; - FML_DCHECK(restoration_index < clip_coverage_stack.size()); - - // We only need to restore the area that covers the coverage of the - // clip rect at target depth + 1. - std::optional restore_coverage = - (restoration_index + 1 < clip_coverage_stack.size()) - ? clip_coverage_stack[restoration_index + 1].coverage - : std::nullopt; - if (restore_coverage.has_value()) { - // Make the coverage rectangle relative to the current pass. - restore_coverage = restore_coverage->Shift(-global_pass_position); - } - clip_coverage_stack.resize(restoration_index + 1); - - if constexpr (ContentContext::kEnableStencilThenCover) { - // Skip all clip restores when stencil-then-cover is enabled. - if (clip_coverage_stack.back().coverage.has_value()) { - clip_replay_->RecordEntity(element_entity, clip_coverage.type); - } - return true; - } - - if (!clip_coverage_stack.back().coverage.has_value()) { - // Running this restore op won't make anything renderable, so skip it. - return true; - } - - auto restore_contents = - static_cast(element_entity.GetContents().get()); - restore_contents->SetRestoreCoverage(restore_coverage); - - } break; - } - -#ifdef IMPELLER_ENABLE_CAPTURE - { - auto element_entity_coverage = element_entity.GetCoverage(); - if (element_entity_coverage.has_value()) { - element_entity_coverage = - element_entity_coverage->Shift(global_pass_position); - element_entity.GetCapture().AddRect("Coverage", *element_entity_coverage, - {.readonly = true}); - } + if (!clip_replay_->AppendClipCoverage(clip_coverage, element_entity, + clip_depth_floor, + global_pass_position)) { + return true; } -#endif - element_entity.SetClipDepth(element_entity.GetClipDepth() - clip_depth_floor); - clip_replay_->RecordEntity(element_entity, clip_coverage.type); if (!element_entity.Render(renderer, *result.pass)) { VALIDATION_LOG << "Failed to render entity."; return false; @@ -916,7 +839,6 @@ bool EntityPass::OnRender( Point global_pass_position, Point local_pass_position, uint32_t pass_depth, - ClipCoverageStack& clip_coverage_stack, size_t clip_depth_floor, std::shared_ptr backdrop_filter_contents, const std::optional& @@ -964,7 +886,7 @@ bool EntityPass::OnRender( backdrop_entity.SetNewClipDepth(std::numeric_limits::max()); RenderElement(backdrop_entity, clip_depth_floor, pass_context, pass_depth, - renderer, clip_coverage_stack, global_pass_position); + renderer, global_pass_position); } bool is_collapsing_clear_colors = !collapsed_parent_pass && @@ -990,7 +912,6 @@ bool EntityPass::OnRender( root_pass_size, // root_pass_size global_pass_position, // global_pass_position pass_depth, // pass_depth - clip_coverage_stack, // clip_coverage_stack clip_depth_floor); // clip_depth_floor switch (result.status) { @@ -1058,8 +979,7 @@ bool EntityPass::OnRender( /// Render the Element. /// if (!RenderElement(result.entity, clip_depth_floor, pass_context, - pass_depth, renderer, clip_coverage_stack, - global_pass_position)) { + pass_depth, renderer, global_pass_position)) { // Specific validation logs are handled in `render_element()`. return false; } @@ -1256,30 +1176,8 @@ void EntityPass::SetEnableOffscreenCheckerboard(bool enabled) { enable_offscreen_debug_checkerboard_ = enabled; } -const EntityPassClipRecorder& EntityPass::GetEntityPassClipRecorder() const { +const EntityPassClipStack& EntityPass::GetEntityPassClipRecorder() const { return *clip_replay_; } -EntityPassClipRecorder::EntityPassClipRecorder() {} - -void EntityPassClipRecorder::RecordEntity(const Entity& entity, - Contents::ClipCoverage::Type type) { - switch (type) { - case Contents::ClipCoverage::Type::kNoChange: - return; - case Contents::ClipCoverage::Type::kAppend: - rendered_clip_entities_.push_back(entity.Clone()); - break; - case Contents::ClipCoverage::Type::kRestore: - if (!rendered_clip_entities_.empty()) { - rendered_clip_entities_.pop_back(); - } - break; - } -} - -const std::vector& EntityPassClipRecorder::GetReplayEntities() const { - return rendered_clip_entities_; -} - } // namespace impeller diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index 4198bccd0fbba..f6525ca95558a 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -14,6 +14,7 @@ #include "impeller/entity/contents/contents.h" #include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/entity.h" +#include "impeller/entity/entity_pass_clip_stack.h" #include "impeller/entity/entity_pass_delegate.h" #include "impeller/entity/inline_pass_context.h" #include "impeller/renderer/render_target.h" @@ -21,7 +22,6 @@ namespace impeller { class ContentContext; -class EntityPassClipRecorder; /// Specifies how much to trust the bounds rectangle provided for a list /// of contents. Used by both |EntityPass| and |Canvas::SaveLayer|. @@ -60,12 +60,12 @@ class EntityPass { const Matrix& effect_transform, Entity::RenderingMode rendering_mode)>; - struct ClipCoverageLayer { - std::optional coverage; - size_t clip_depth; - }; + // struct ClipCoverageLayer { + // std::optional coverage; + // size_t clip_depth; + // }; - using ClipCoverageStack = std::vector; + // using ClipCoverageStack = std::vector; EntityPass(); @@ -221,7 +221,7 @@ class EntityPass { std::optional coverage_limit) const; /// Exposed for testing purposes only. - const EntityPassClipRecorder& GetEntityPassClipRecorder() const; + const EntityPassClipStack& GetEntityPassClipRecorder() const; private: struct EntityResult { @@ -252,7 +252,6 @@ class EntityPass { InlinePassContext& pass_context, int32_t pass_depth, ContentContext& renderer, - ClipCoverageStack& clip_coverage_stack, Point global_pass_position) const; EntityResult GetEntityForElement(const EntityPass::Element& element, @@ -262,7 +261,6 @@ class EntityPass { ISize root_pass_size, Point global_pass_position, uint32_t pass_depth, - ClipCoverageStack& clip_coverage_stack, size_t clip_depth_floor) const; //---------------------------------------------------------------------------- @@ -297,12 +295,6 @@ class EntityPass { /// and debugging purposes. This can vary /// depending on whether passes are /// collapsed or not. - /// @param[in] clip_coverage_stack A global stack of coverage rectangles - /// for the clip 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] clip_depth_floor The clip depth that a value of /// zero corresponds to in the given /// `pass_target` clip buffer. @@ -329,7 +321,6 @@ class EntityPass { Point global_pass_position, Point local_pass_position, uint32_t pass_depth, - ClipCoverageStack& clip_coverage_stack, size_t clip_depth_floor = 0, std::shared_ptr backdrop_filter_contents = nullptr, const std::optional& @@ -354,8 +345,8 @@ class EntityPass { bool enable_offscreen_debug_checkerboard_ = false; std::optional bounds_limit_; ContentBoundsPromise bounds_promise_ = ContentBoundsPromise::kUnknown; - std::unique_ptr clip_replay_ = - std::make_unique(); + std::unique_ptr clip_replay_ = + std::make_unique(); int32_t required_mip_count_ = 1; /// These values are incremented whenever something is added to the pass that @@ -381,26 +372,6 @@ class EntityPass { EntityPass& operator=(const EntityPass&) = delete; }; -/// @brief A class that tracks all clips that have been recorded in the current -/// entity pass stencil. -/// -/// These clips are replayed when restoring the backdrop so that the -/// stencil buffer is left in an identical state. -class EntityPassClipRecorder { - public: - EntityPassClipRecorder(); - - ~EntityPassClipRecorder() = default; - - /// @brief Record the entity based on the provided coverage [type]. - void RecordEntity(const Entity& entity, Contents::ClipCoverage::Type type); - - const std::vector& GetReplayEntities() const; - - private: - std::vector rendered_clip_entities_; -}; - } // namespace impeller #endif // FLUTTER_IMPELLER_ENTITY_ENTITY_PASS_H_ diff --git a/impeller/entity/entity_pass_clip_stack.cc b/impeller/entity/entity_pass_clip_stack.cc new file mode 100644 index 0000000000000..f156015b842d5 --- /dev/null +++ b/impeller/entity/entity_pass_clip_stack.cc @@ -0,0 +1,150 @@ +// 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/entity_pass_clip_stack.h" +#include "impeller/entity/contents/clip_contents.h" +#include "impeller/entity/contents/content_context.h" +#include "impeller/entity/entity.h" + +namespace impeller { + +EntityPassClipStack::EntityPassClipStack() {} + +std::optional EntityPassClipStack::CurrentClipCoverage() const { + return clip_coverage_.back().back().coverage; +} + +bool EntityPassClipStack::HasCoverage() const { + return !clip_coverage_.back().empty(); +} + +void EntityPassClipStack::Initialize(const Rect& rect) { + clip_coverage_.clear(); + clip_coverage_.push_back({ClipCoverageLayer{ + .coverage = rect, + .clip_depth = 0, + }}); +} + +void EntityPassClipStack::PushSubpass(std::optional subpass_coverage, + size_t clip_depth) { + clip_coverage_.push_back({ + ClipCoverageLayer{.coverage = subpass_coverage, .clip_depth = clip_depth}, + }); +} + +void EntityPassClipStack::PopSubpass() { + clip_coverage_.pop_back(); +} + +const std::vector> +EntityPassClipStack::GetClipCoverageLayers() const { + return clip_coverage_; +} + +bool EntityPassClipStack::AppendClipCoverage( + Contents::ClipCoverage clip_coverage, + Entity& entity, + size_t clip_depth_floor, + Point global_pass_position) { + switch (clip_coverage.type) { + case Contents::ClipCoverage::Type::kNoChange: + break; + case Contents::ClipCoverage::Type::kAppend: { + auto op = CurrentClipCoverage(); + clip_coverage_.back().push_back( + ClipCoverageLayer{.coverage = clip_coverage.coverage, + .clip_depth = entity.GetClipDepth() + 1}); + + FML_DCHECK(clip_coverage_.back().back().clip_depth == + clip_coverage_.back().front().clip_depth + + clip_coverage_.back().size() - 1); + + if (!op.has_value()) { + // Running this append op won't impact the clip buffer because the + // whole screen is already being clipped, so skip it. + return false; + } + } break; + case Contents::ClipCoverage::Type::kRestore: { + if (clip_coverage_.back().back().clip_depth <= entity.GetClipDepth()) { + // Drop clip restores that will do nothing. + return false; + } + + auto restoration_index = + entity.GetClipDepth() - clip_coverage_.back().front().clip_depth; + FML_DCHECK(restoration_index < clip_coverage_.back().size()); + + // We only need to restore the area that covers the coverage of the + // clip rect at target depth + 1. + std::optional restore_coverage = + (restoration_index + 1 < clip_coverage_.back().size()) + ? clip_coverage_.back()[restoration_index + 1].coverage + : std::nullopt; + if (restore_coverage.has_value()) { + // Make the coverage rectangle relative to the current pass. + restore_coverage = restore_coverage->Shift(-global_pass_position); + } + clip_coverage_.back().resize(restoration_index + 1); + + if constexpr (ContentContext::kEnableStencilThenCover) { + // Skip all clip restores when stencil-then-cover is enabled. + if (clip_coverage_.back().back().coverage.has_value()) { + RecordEntity(entity, clip_coverage.type); + } + return false; + } + + if (!clip_coverage_.back().back().coverage.has_value()) { + // Running this restore op won't make anything renderable, so skip it. + return false; + } + + auto restore_contents = + static_cast(entity.GetContents().get()); + restore_contents->SetRestoreCoverage(restore_coverage); + + } break; + } + +#ifdef IMPELLER_ENABLE_CAPTURE + { + auto element_entity_coverage = entity.GetCoverage(); + if (element_entity_coverage.has_value()) { + element_entity_coverage = + element_entity_coverage->Shift(global_pass_position); + entity.GetCapture().AddRect("Coverage", *element_entity_coverage, + {.readonly = true}); + } + } +#endif + + entity.SetClipDepth(entity.GetClipDepth() - clip_depth_floor); + RecordEntity(entity, clip_coverage.type); + + return true; +} + +void EntityPassClipStack::RecordEntity(const Entity& entity, + Contents::ClipCoverage::Type type) { + switch (type) { + case Contents::ClipCoverage::Type::kNoChange: + return; + case Contents::ClipCoverage::Type::kAppend: + rendered_clip_entities_.push_back(entity.Clone()); + break; + case Contents::ClipCoverage::Type::kRestore: + if (!rendered_clip_entities_.empty()) { + rendered_clip_entities_.pop_back(); + } + break; + } +} + +const std::vector& EntityPassClipStack::GetReplayEntities() const { + return rendered_clip_entities_; +} + +} // namespace impeller diff --git a/impeller/entity/entity_pass_clip_stack.h b/impeller/entity/entity_pass_clip_stack.h new file mode 100644 index 0000000000000..0c29caab0d138 --- /dev/null +++ b/impeller/entity/entity_pass_clip_stack.h @@ -0,0 +1,61 @@ +// 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. + +#ifndef FLUTTER_IMPELLER_ENTITY_ENTITY_PASS_CLIP_STACK_H_ +#define FLUTTER_IMPELLER_ENTITY_ENTITY_PASS_CLIP_STACK_H_ + +#include "impeller/entity/contents/contents.h" + +namespace impeller { + +struct ClipCoverageLayer { + std::optional coverage; + size_t clip_depth; +}; + +/// @brief A class that tracks all clips that have been recorded in the current +/// entity pass stencil. +/// +/// These clips are replayed when restoring the backdrop so that the +/// stencil buffer is left in an identical state. +class EntityPassClipStack { + public: + EntityPassClipStack(); + + ~EntityPassClipStack() = default; + + void Initialize(const Rect& rect); + + std::optional CurrentClipCoverage() const; + + void PushSubpass(std::optional subpass_coverage, size_t clip_depth); + + void PopSubpass(); + + bool HasCoverage() const; + + /// Returns true if entity should be rendered. + bool AppendClipCoverage(Contents::ClipCoverage clip_coverage, + Entity& entity, + size_t clip_depth_floor, + Point global_pass_position); + + // Visible for testing. + void RecordEntity(const Entity& entity, Contents::ClipCoverage::Type type); + + // Visible for testing. + const std::vector& GetReplayEntities() const; + + // Visible for testing. + const std::vector> GetClipCoverageLayers() + const; + + private: + std::vector rendered_clip_entities_; + std::vector> clip_coverage_; +}; + +} // namespace impeller + +#endif // FLUTTER_IMPELLER_ENTITY_ENTITY_PASS_CLIP_STACK_H_ diff --git a/impeller/entity/entity_pass_delegate.h b/impeller/entity/entity_pass_delegate.h index 72a38af65eebc..04b96ba00782f 100644 --- a/impeller/entity/entity_pass_delegate.h +++ b/impeller/entity/entity_pass_delegate.h @@ -7,7 +7,6 @@ #include -#include "flutter/fml/macros.h" #include "impeller/core/texture.h" #include "impeller/entity/contents/contents.h" #include "impeller/entity/contents/filters/filter_contents.h" diff --git a/impeller/entity/entity_pass_unittests.cc b/impeller/entity/entity_pass_unittests.cc index d1f05448e938b..1e7e51d1a773d 100644 --- a/impeller/entity/entity_pass_unittests.cc +++ b/impeller/entity/entity_pass_unittests.cc @@ -9,8 +9,8 @@ namespace impeller { namespace testing { -TEST(EntityPassClipRecorderTest, CanPushAndPopEntities) { - EntityPassClipRecorder recorder = EntityPassClipRecorder(); +TEST(EntityPassClipStackTest, CanPushAndPopEntities) { + EntityPassClipStack recorder = EntityPassClipStack(); EXPECT_TRUE(recorder.GetReplayEntities().empty()); @@ -28,8 +28,8 @@ TEST(EntityPassClipRecorderTest, CanPushAndPopEntities) { EXPECT_TRUE(recorder.GetReplayEntities().empty()); } -TEST(EntityPassClipRecorderTest, CanPopEntitiesSafely) { - EntityPassClipRecorder recorder = EntityPassClipRecorder(); +TEST(EntityPassClipStackTest, CanPopEntitiesSafely) { + EntityPassClipStack recorder = EntityPassClipStack(); EXPECT_TRUE(recorder.GetReplayEntities().empty()); @@ -38,8 +38,8 @@ TEST(EntityPassClipRecorderTest, CanPopEntitiesSafely) { EXPECT_TRUE(recorder.GetReplayEntities().empty()); } -TEST(EntityPassClipRecorderTest, CanAppendNoChange) { - EntityPassClipRecorder recorder = EntityPassClipRecorder(); +TEST(EntityPassClipStackTest, CanAppendNoChange) { + EntityPassClipStack recorder = EntityPassClipStack(); EXPECT_TRUE(recorder.GetReplayEntities().empty()); @@ -48,5 +48,135 @@ TEST(EntityPassClipRecorderTest, CanAppendNoChange) { EXPECT_TRUE(recorder.GetReplayEntities().empty()); } +TEST(EntityPassClipStackTest, AppendCoverageNoChange) { + EntityPassClipStack recorder = EntityPassClipStack(); + recorder.Initialize(Rect::MakeSize(Size::MakeWH(100, 100))); + + ASSERT_EQ(recorder.GetClipCoverageLayers()[0].size(), 1u); + EXPECT_EQ(recorder.GetClipCoverageLayers()[0][0].coverage, + Rect::MakeSize(Size::MakeWH(100, 100))); + EXPECT_EQ(recorder.GetClipCoverageLayers()[0][0].clip_depth, 0u); + + Entity entity; + recorder.AppendClipCoverage( + Contents::ClipCoverage{ + .type = Contents::ClipCoverage::Type::kNoChange, + .coverage = std::nullopt, + }, + entity, 0, Point(0, 0)); + + ASSERT_EQ(recorder.GetClipCoverageLayers()[0].size(), 1u); + EXPECT_EQ(recorder.GetClipCoverageLayers()[0][0].coverage, + Rect::MakeSize(Size::MakeWH(100, 100))); + EXPECT_EQ(recorder.GetClipCoverageLayers()[0][0].clip_depth, 0u); +} + +TEST(EntityPassClipStackTest, AppendAndRestoreClipCoverage) { + EntityPassClipStack recorder = EntityPassClipStack(); + recorder.Initialize(Rect::MakeSize(Size::MakeWH(100, 100))); + + ASSERT_EQ(recorder.GetClipCoverageLayers()[0].size(), 1u); + + // Push a clip. + Entity entity; + entity.SetClipDepth(1); + recorder.AppendClipCoverage( + Contents::ClipCoverage{ + .type = Contents::ClipCoverage::Type::kAppend, + .coverage = Rect::MakeLTRB(50, 50, 55, 55), + }, + entity, 0, Point(0, 0)); + + ASSERT_EQ(recorder.GetClipCoverageLayers()[0].size(), 2u); + EXPECT_EQ(recorder.GetClipCoverageLayers()[0][1].coverage, + Rect::MakeLTRB(50, 50, 55, 55)); + EXPECT_EQ(recorder.GetClipCoverageLayers()[0][1].clip_depth, 2u); + EXPECT_EQ(recorder.GetReplayEntities().size(), 1u); + + // Restore the clip. + entity.SetClipDepth(0); + recorder.AppendClipCoverage( + Contents::ClipCoverage{ + .type = Contents::ClipCoverage::Type::kRestore, + .coverage = Rect::MakeLTRB(50, 50, 55, 55), + }, + entity, 0, Point(0, 0)); + + ASSERT_EQ(recorder.GetClipCoverageLayers()[0].size(), 1u); + EXPECT_EQ(recorder.GetClipCoverageLayers()[0][0].coverage, + Rect::MakeSize(Size::MakeWH(100, 100))); + EXPECT_EQ(recorder.GetClipCoverageLayers()[0][0].clip_depth, 0u); + EXPECT_EQ(recorder.GetReplayEntities().size(), 0u); +} + +TEST(EntityPassClipStackTest, UnbalancedRestore) { + EntityPassClipStack recorder = EntityPassClipStack(); + recorder.Initialize(Rect::MakeSize(Size::MakeWH(100, 100))); + + ASSERT_EQ(recorder.GetClipCoverageLayers()[0].size(), 1u); + + // Restore the clip. + Entity entity; + entity.SetClipDepth(0); + recorder.AppendClipCoverage( + Contents::ClipCoverage{ + .type = Contents::ClipCoverage::Type::kRestore, + .coverage = Rect::MakeLTRB(50, 50, 55, 55), + }, + entity, 0, Point(0, 0)); + + ASSERT_EQ(recorder.GetClipCoverageLayers()[0].size(), 1u); + EXPECT_EQ(recorder.GetClipCoverageLayers()[0][0].coverage, + Rect::MakeSize(Size::MakeWH(100, 100))); + EXPECT_EQ(recorder.GetClipCoverageLayers()[0][0].clip_depth, 0u); + EXPECT_EQ(recorder.GetReplayEntities().size(), 0u); +} + +TEST(EntityPassClipStackTest, ClipAndRestoreWithSubpasses) { + EntityPassClipStack recorder = EntityPassClipStack(); + recorder.Initialize(Rect::MakeSize(Size::MakeWH(100, 100))); + + ASSERT_EQ(recorder.GetClipCoverageLayers()[0].size(), 1u); + + // Push a clip. + Entity entity; + entity.SetClipDepth(1); + recorder.AppendClipCoverage( + Contents::ClipCoverage{ + .type = Contents::ClipCoverage::Type::kAppend, + .coverage = Rect::MakeLTRB(50, 50, 55, 55), + }, + entity, 0, Point(0, 0)); + + ASSERT_EQ(recorder.GetClipCoverageLayers()[0].size(), 2u); + EXPECT_EQ(recorder.GetClipCoverageLayers()[0][1].coverage, + Rect::MakeLTRB(50, 50, 55, 55)); + EXPECT_EQ(recorder.GetClipCoverageLayers()[0][1].clip_depth, 2u); + EXPECT_EQ(recorder.GetReplayEntities().size(), 1u); + + // Begin a subpass. + recorder.PushSubpass(Rect::MakeLTRB(50, 50, 55, 55), 0); + ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 2u); + EXPECT_EQ(recorder.GetClipCoverageLayers()[1][0].coverage, + Rect::MakeLTRB(50, 50, 55, 55)); + + entity.SetClipDepth(1); + recorder.AppendClipCoverage( + Contents::ClipCoverage{ + .type = Contents::ClipCoverage::Type::kAppend, + .coverage = Rect::MakeLTRB(54, 54, 55, 55), + }, + entity, 0, Point(0, 0)); + + EXPECT_EQ(recorder.GetClipCoverageLayers()[1][1].coverage, + Rect::MakeLTRB(54, 54, 55, 55)); + + // End subpass. + recorder.PopSubpass(); + + EXPECT_EQ(recorder.GetClipCoverageLayers()[0][1].coverage, + Rect::MakeLTRB(50, 50, 55, 55)); +} + } // namespace testing } // namespace impeller From 5cfbdb339e0eb6eff89b81c3b6ec2fef823471d6 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Mon, 25 Mar 2024 11:20:52 -0700 Subject: [PATCH 4/7] minor cleanups. --- impeller/entity/entity_pass.cc | 22 +++++++++++----------- impeller/entity/entity_pass.h | 9 +-------- impeller/entity/entity_pass_unittests.cc | 3 ++- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index f016be78dbffd..19f9d2149f5c5 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -408,7 +408,7 @@ bool EntityPass::Render(ContentContext& renderer, return true; }); - clip_replay_->Initialize( + clip_stack_->Initialize( Rect::MakeSize(root_render_target.GetRenderTargetSize())); bool reads_from_onscreen_backdrop = GetTotalPassReads(renderer) > 0; @@ -619,13 +619,13 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( pass_context.EndPass(); } - if (!clip_replay_->HasCoverage()) { + if (!clip_stack_->HasCoverage()) { // The current clip is empty. This means the pass texture won't be // visible, so skip it. capture.CreateChild("Subpass Entity (Skipped: Empty clip A)"); return EntityPass::EntityResult::Skip(); } - auto clip_coverage_back = clip_replay_->CurrentClipCoverage(); + auto clip_coverage_back = clip_stack_->CurrentClipCoverage(); if (!clip_coverage_back.has_value()) { capture.CreateChild("Subpass Entity (Skipped: Empty clip B)"); return EntityPass::EntityResult::Skip(); @@ -684,7 +684,7 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( // save layers may transform the subpass texture after it's rendered, // causing parent clip coverage to get misaligned with the actual area that // the subpass will affect in the parent pass. - clip_replay_->PushSubpass(subpass_coverage, subpass->clip_depth_); + clip_stack_->PushSubpass(subpass_coverage, subpass->clip_depth_); // Stencil textures aren't shared between EntityPasses (as much of the // time they are transient). @@ -704,7 +704,7 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( // cases. return EntityPass::EntityResult::Failure(); } - clip_replay_->PopSubpass(); + clip_stack_->PopSubpass(); // The subpass target's texture may have changed during OnRender. auto subpass_texture = @@ -762,7 +762,7 @@ bool EntityPass::RenderElement(Entity& element_entity, if (result.just_created) { // Restore any clips that were recorded before the backdrop filter was // applied. - auto& replay_entities = clip_replay_->GetReplayEntities(); + auto& replay_entities = clip_stack_->GetReplayEntities(); for (const auto& entity : replay_entities) { if (!entity.Render(renderer, *result.pass)) { VALIDATION_LOG << "Failed to render entity for clip restore."; @@ -793,7 +793,7 @@ bool EntityPass::RenderElement(Entity& element_entity, } } - auto current_clip_coverage = clip_replay_->CurrentClipCoverage(); + auto current_clip_coverage = clip_stack_->CurrentClipCoverage(); if (current_clip_coverage.has_value()) { // Entity transforms are relative to the current pass position, so we need // to check clip coverage in the same space. @@ -818,9 +818,9 @@ bool EntityPass::RenderElement(Entity& element_entity, element_entity.GetContents()->SetCoverageHint( Rect::Intersection(element_coverage_hint, current_clip_coverage)); - if (!clip_replay_->AppendClipCoverage(clip_coverage, element_entity, - clip_depth_floor, - global_pass_position)) { + if (!clip_stack_->AppendClipCoverage(clip_coverage, element_entity, + clip_depth_floor, + global_pass_position)) { return true; } @@ -1177,7 +1177,7 @@ void EntityPass::SetEnableOffscreenCheckerboard(bool enabled) { } const EntityPassClipStack& EntityPass::GetEntityPassClipRecorder() const { - return *clip_replay_; + return *clip_stack_; } } // namespace impeller diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index f6525ca95558a..1bcc0831117d1 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -60,13 +60,6 @@ class EntityPass { const Matrix& effect_transform, Entity::RenderingMode rendering_mode)>; - // struct ClipCoverageLayer { - // std::optional coverage; - // size_t clip_depth; - // }; - - // using ClipCoverageStack = std::vector; - EntityPass(); ~EntityPass(); @@ -345,7 +338,7 @@ class EntityPass { bool enable_offscreen_debug_checkerboard_ = false; std::optional bounds_limit_; ContentBoundsPromise bounds_promise_ = ContentBoundsPromise::kUnknown; - std::unique_ptr clip_replay_ = + std::unique_ptr clip_stack_ = std::make_unique(); int32_t required_mip_count_ = 1; diff --git a/impeller/entity/entity_pass_unittests.cc b/impeller/entity/entity_pass_unittests.cc index 1e7e51d1a773d..acc7b35f3f25a 100644 --- a/impeller/entity/entity_pass_unittests.cc +++ b/impeller/entity/entity_pass_unittests.cc @@ -4,7 +4,8 @@ #include "flutter/testing/testing.h" #include "gtest/gtest.h" -#include "impeller/entity/entity_pass.h" +#include "impeller/entity/entity.h" +#include "impeller/entity/entity_pass_clip_stack.h" namespace impeller { namespace testing { From 2c3fc3f76887e841869801817da5e3800b8b97d0 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Mon, 25 Mar 2024 13:52:35 -0700 Subject: [PATCH 5/7] make clip stack share state. --- impeller/aiks/aiks_unittests.cc | 17 ----- impeller/entity/entity_pass.cc | 49 ++++++++------ impeller/entity/entity_pass.h | 14 +++- impeller/entity/entity_pass_clip_stack.cc | 80 +++++++++++++---------- impeller/entity/entity_pass_clip_stack.h | 18 +++-- impeller/entity/entity_pass_unittests.cc | 73 +++++++++++---------- 6 files changed, 136 insertions(+), 115 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index b08f726ee090d..93768e8c8866c 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -3376,23 +3376,6 @@ TEST_P(AiksTest, CorrectClipDepthAssignedToEntities) { } } -TEST_P(AiksTest, EntityPassClipRecorderRestoresCancelOutClips) { - Canvas canvas; - canvas.Save(); - canvas.ClipRRect(Rect::MakeLTRB(0, 0, 50, 50), {10, 10}, {}); - canvas.DrawRRect(Rect::MakeLTRB(0, 0, 100, 100), {10, 10}, {}); - canvas.Restore(); - canvas.DrawRRect(Rect::MakeLTRB(0, 0, 50, 50), {10, 10}, {}); - - Picture picture = canvas.EndRecordingAsPicture(); - - AiksContext renderer(GetContext(), nullptr); - std::shared_ptr image = picture.ToImage(renderer, {300, 300}); - - EXPECT_EQ( - picture.pass->GetEntityPassClipRecorder().GetReplayEntities().size(), 0u); -} - } // namespace testing } // namespace impeller diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 19f9d2149f5c5..34a3eeac2cc19 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -15,12 +15,14 @@ #include "impeller/base/strings.h" #include "impeller/base/validation.h" #include "impeller/core/formats.h" +#include "impeller/entity/contents/clip_contents.h" #include "impeller/entity/contents/content_context.h" #include "impeller/entity/contents/filters/color_filter_contents.h" #include "impeller/entity/contents/filters/inputs/filter_input.h" #include "impeller/entity/contents/framebuffer_blend_contents.h" #include "impeller/entity/contents/texture_contents.h" #include "impeller/entity/entity.h" +#include "impeller/entity/entity_pass_clip_stack.h" #include "impeller/entity/inline_pass_context.h" #include "impeller/geometry/color.h" #include "impeller/geometry/rect.h" @@ -408,8 +410,9 @@ bool EntityPass::Render(ContentContext& renderer, return true; }); - clip_stack_->Initialize( - Rect::MakeSize(root_render_target.GetRenderTargetSize())); + std::unique_ptr clip_stack = + std::make_unique( + Rect::MakeSize(root_render_target.GetRenderTargetSize())); bool reads_from_onscreen_backdrop = GetTotalPassReads(renderer) > 0; // In this branch path, we need to render everything to an offscreen texture @@ -428,7 +431,8 @@ bool EntityPass::Render(ContentContext& renderer, offscreen_target, // pass_target Point(), // global_pass_position Point(), // local_pass_position - 0 // pass_depth + 0, // pass_depth + *clip_stack // clip_coverage_stack )) { // Validation error messages are triggered for all `OnRender()` failure // cases. @@ -533,7 +537,8 @@ bool EntityPass::Render(ContentContext& renderer, pass_target, // pass_target Point(), // global_pass_position Point(), // local_pass_position - 0); // pass_depth + 0, // pass_depth + *clip_stack); // clip_coverage_stack } EntityPass::EntityResult EntityPass::GetEntityForElement( @@ -544,6 +549,7 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( ISize root_pass_size, Point global_pass_position, uint32_t pass_depth, + EntityPassClipStack& clip_coverage_stack, size_t clip_depth_floor) const { //-------------------------------------------------------------------------- /// Setup entity element. @@ -585,6 +591,7 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( global_pass_position, // global_pass_position Point(), // local_pass_position pass_depth, // pass_depth + clip_coverage_stack, // clip_coverage_stack clip_depth_, // clip_depth_floor nullptr, // backdrop_filter_contents pass_context.GetRenderPass(pass_depth) // collapsed_parent_pass @@ -619,13 +626,13 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( pass_context.EndPass(); } - if (!clip_stack_->HasCoverage()) { + if (!clip_coverage_stack.HasCoverage()) { // The current clip is empty. This means the pass texture won't be // visible, so skip it. capture.CreateChild("Subpass Entity (Skipped: Empty clip A)"); return EntityPass::EntityResult::Skip(); } - auto clip_coverage_back = clip_stack_->CurrentClipCoverage(); + auto clip_coverage_back = clip_coverage_stack.CurrentClipCoverage(); if (!clip_coverage_back.has_value()) { capture.CreateChild("Subpass Entity (Skipped: Empty clip B)"); return EntityPass::EntityResult::Skip(); @@ -684,7 +691,7 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( // save layers may transform the subpass texture after it's rendered, // causing parent clip coverage to get misaligned with the actual area that // the subpass will affect in the parent pass. - clip_stack_->PushSubpass(subpass_coverage, subpass->clip_depth_); + clip_coverage_stack.PushSubpass(subpass_coverage, subpass->clip_depth_); // Stencil textures aren't shared between EntityPasses (as much of the // time they are transient). @@ -697,6 +704,7 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( subpass_coverage->GetOrigin() - global_pass_position, // local_pass_position ++pass_depth, // pass_depth + clip_coverage_stack, // clip_coverage_stack subpass->clip_depth_, // clip_depth_floor subpass_backdrop_filter_contents // backdrop_filter_contents )) { @@ -704,7 +712,8 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( // cases. return EntityPass::EntityResult::Failure(); } - clip_stack_->PopSubpass(); + + clip_coverage_stack.PopSubpass(); // The subpass target's texture may have changed during OnRender. auto subpass_texture = @@ -750,6 +759,7 @@ bool EntityPass::RenderElement(Entity& element_entity, InlinePassContext& pass_context, int32_t pass_depth, ContentContext& renderer, + EntityPassClipStack& clip_coverage_stack, Point global_pass_position) const { auto result = pass_context.GetRenderPass(pass_depth); if (!result.pass) { @@ -762,7 +772,7 @@ bool EntityPass::RenderElement(Entity& element_entity, if (result.just_created) { // Restore any clips that were recorded before the backdrop filter was // applied. - auto& replay_entities = clip_stack_->GetReplayEntities(); + auto& replay_entities = clip_coverage_stack.GetReplayEntities(); for (const auto& entity : replay_entities) { if (!entity.Render(renderer, *result.pass)) { VALIDATION_LOG << "Failed to render entity for clip restore."; @@ -793,7 +803,7 @@ bool EntityPass::RenderElement(Entity& element_entity, } } - auto current_clip_coverage = clip_stack_->CurrentClipCoverage(); + auto current_clip_coverage = clip_coverage_stack.CurrentClipCoverage(); if (current_clip_coverage.has_value()) { // Entity transforms are relative to the current pass position, so we need // to check clip coverage in the same space. @@ -818,9 +828,11 @@ bool EntityPass::RenderElement(Entity& element_entity, element_entity.GetContents()->SetCoverageHint( Rect::Intersection(element_coverage_hint, current_clip_coverage)); - if (!clip_stack_->AppendClipCoverage(clip_coverage, element_entity, - clip_depth_floor, - global_pass_position)) { + if (!clip_coverage_stack.AppendClipCoverage(clip_coverage, element_entity, + clip_depth_floor, + global_pass_position)) { + // If the entity's coverage change did not change the clip coverage, we + // don't need to render it. return true; } @@ -839,6 +851,7 @@ bool EntityPass::OnRender( Point global_pass_position, Point local_pass_position, uint32_t pass_depth, + EntityPassClipStack& clip_coverage_stack, size_t clip_depth_floor, std::shared_ptr backdrop_filter_contents, const std::optional& @@ -886,7 +899,7 @@ bool EntityPass::OnRender( backdrop_entity.SetNewClipDepth(std::numeric_limits::max()); RenderElement(backdrop_entity, clip_depth_floor, pass_context, pass_depth, - renderer, global_pass_position); + renderer, clip_coverage_stack, global_pass_position); } bool is_collapsing_clear_colors = !collapsed_parent_pass && @@ -912,6 +925,7 @@ bool EntityPass::OnRender( root_pass_size, // root_pass_size global_pass_position, // global_pass_position pass_depth, // pass_depth + clip_coverage_stack, // clip_coverage_stack clip_depth_floor); // clip_depth_floor switch (result.status) { @@ -979,7 +993,8 @@ bool EntityPass::OnRender( /// Render the Element. /// if (!RenderElement(result.entity, clip_depth_floor, pass_context, - pass_depth, renderer, global_pass_position)) { + pass_depth, renderer, clip_coverage_stack, + global_pass_position)) { // Specific validation logs are handled in `render_element()`. return false; } @@ -1176,8 +1191,4 @@ void EntityPass::SetEnableOffscreenCheckerboard(bool enabled) { enable_offscreen_debug_checkerboard_ = enabled; } -const EntityPassClipStack& EntityPass::GetEntityPassClipRecorder() const { - return *clip_stack_; -} - } // namespace impeller diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index 1bcc0831117d1..f7c0754be18fa 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -22,6 +22,7 @@ namespace impeller { class ContentContext; +class EntityPassClipRecorder; /// Specifies how much to trust the bounds rectangle provided for a list /// of contents. Used by both |EntityPass| and |Canvas::SaveLayer|. @@ -214,7 +215,7 @@ class EntityPass { std::optional coverage_limit) const; /// Exposed for testing purposes only. - const EntityPassClipStack& GetEntityPassClipRecorder() const; + const EntityPassClipRecorder& GetEntityPassClipRecorder() const; private: struct EntityResult { @@ -245,6 +246,7 @@ class EntityPass { InlinePassContext& pass_context, int32_t pass_depth, ContentContext& renderer, + EntityPassClipStack& clip_coverage_stack, Point global_pass_position) const; EntityResult GetEntityForElement(const EntityPass::Element& element, @@ -254,6 +256,7 @@ class EntityPass { ISize root_pass_size, Point global_pass_position, uint32_t pass_depth, + EntityPassClipStack& clip_coverage_stack, size_t clip_depth_floor) const; //---------------------------------------------------------------------------- @@ -288,6 +291,12 @@ class EntityPass { /// and debugging purposes. This can vary /// depending on whether passes are /// collapsed or not. + /// @param[in] clip_coverage_stack A global stack of coverage rectangles + /// for the clip 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] clip_depth_floor The clip depth that a value of /// zero corresponds to in the given /// `pass_target` clip buffer. @@ -314,6 +323,7 @@ class EntityPass { Point global_pass_position, Point local_pass_position, uint32_t pass_depth, + EntityPassClipStack& clip_coverage_stack, size_t clip_depth_floor = 0, std::shared_ptr backdrop_filter_contents = nullptr, const std::optional& @@ -338,8 +348,6 @@ class EntityPass { bool enable_offscreen_debug_checkerboard_ = false; std::optional bounds_limit_; ContentBoundsPromise bounds_promise_ = ContentBoundsPromise::kUnknown; - std::unique_ptr clip_stack_ = - std::make_unique(); int32_t required_mip_count_ = 1; /// These values are incremented whenever something is added to the pass that diff --git a/impeller/entity/entity_pass_clip_stack.cc b/impeller/entity/entity_pass_clip_stack.cc index f156015b842d5..5d126acdcbc94 100644 --- a/impeller/entity/entity_pass_clip_stack.cc +++ b/impeller/entity/entity_pass_clip_stack.cc @@ -9,38 +9,44 @@ namespace impeller { -EntityPassClipStack::EntityPassClipStack() {} +EntityPassClipStack::EntityPassClipStack(const Rect& initial_coverage_rect) { + subpass_state_.push_back(SubpassState{ + .clip_coverage = + { + {ClipCoverageLayer{ + .coverage = initial_coverage_rect, + .clip_depth = 0, + }}, + }, + }); +} std::optional EntityPassClipStack::CurrentClipCoverage() const { - return clip_coverage_.back().back().coverage; + return subpass_state_.back().clip_coverage.back().coverage; } bool EntityPassClipStack::HasCoverage() const { - return !clip_coverage_.back().empty(); -} - -void EntityPassClipStack::Initialize(const Rect& rect) { - clip_coverage_.clear(); - clip_coverage_.push_back({ClipCoverageLayer{ - .coverage = rect, - .clip_depth = 0, - }}); + return !subpass_state_.back().clip_coverage.empty(); } void EntityPassClipStack::PushSubpass(std::optional subpass_coverage, size_t clip_depth) { - clip_coverage_.push_back({ - ClipCoverageLayer{.coverage = subpass_coverage, .clip_depth = clip_depth}, + subpass_state_.push_back(SubpassState{ + .clip_coverage = + { + ClipCoverageLayer{.coverage = subpass_coverage, + .clip_depth = clip_depth}, + }, }); } void EntityPassClipStack::PopSubpass() { - clip_coverage_.pop_back(); + subpass_state_.pop_back(); } -const std::vector> +const std::vector EntityPassClipStack::GetClipCoverageLayers() const { - return clip_coverage_; + return subpass_state_.back().clip_coverage; } bool EntityPassClipStack::AppendClipCoverage( @@ -48,18 +54,19 @@ bool EntityPassClipStack::AppendClipCoverage( Entity& entity, size_t clip_depth_floor, Point global_pass_position) { + auto& subpass_state = GetCurrentSubpassState(); switch (clip_coverage.type) { case Contents::ClipCoverage::Type::kNoChange: break; case Contents::ClipCoverage::Type::kAppend: { auto op = CurrentClipCoverage(); - clip_coverage_.back().push_back( + subpass_state.clip_coverage.push_back( ClipCoverageLayer{.coverage = clip_coverage.coverage, .clip_depth = entity.GetClipDepth() + 1}); - FML_DCHECK(clip_coverage_.back().back().clip_depth == - clip_coverage_.back().front().clip_depth + - clip_coverage_.back().size() - 1); + FML_DCHECK(subpass_state.clip_coverage.back().clip_depth == + subpass_state.clip_coverage.front().clip_depth + + subpass_state.clip_coverage.size() - 1); if (!op.has_value()) { // Running this append op won't impact the clip buffer because the @@ -68,36 +75,37 @@ bool EntityPassClipStack::AppendClipCoverage( } } break; case Contents::ClipCoverage::Type::kRestore: { - if (clip_coverage_.back().back().clip_depth <= entity.GetClipDepth()) { + if (subpass_state.clip_coverage.back().clip_depth <= + entity.GetClipDepth()) { // Drop clip restores that will do nothing. return false; } - auto restoration_index = - entity.GetClipDepth() - clip_coverage_.back().front().clip_depth; - FML_DCHECK(restoration_index < clip_coverage_.back().size()); + auto restoration_index = entity.GetClipDepth() - + subpass_state.clip_coverage.front().clip_depth; + FML_DCHECK(restoration_index < subpass_state.clip_coverage.size()); // We only need to restore the area that covers the coverage of the // clip rect at target depth + 1. std::optional restore_coverage = - (restoration_index + 1 < clip_coverage_.back().size()) - ? clip_coverage_.back()[restoration_index + 1].coverage + (restoration_index + 1 < subpass_state.clip_coverage.size()) + ? subpass_state.clip_coverage[restoration_index + 1].coverage : std::nullopt; if (restore_coverage.has_value()) { // Make the coverage rectangle relative to the current pass. restore_coverage = restore_coverage->Shift(-global_pass_position); } - clip_coverage_.back().resize(restoration_index + 1); + subpass_state.clip_coverage.resize(restoration_index + 1); if constexpr (ContentContext::kEnableStencilThenCover) { // Skip all clip restores when stencil-then-cover is enabled. - if (clip_coverage_.back().back().coverage.has_value()) { + if (subpass_state.clip_coverage.back().coverage.has_value()) { RecordEntity(entity, clip_coverage.type); } return false; } - if (!clip_coverage_.back().back().coverage.has_value()) { + if (!subpass_state.clip_coverage.back().coverage.has_value()) { // Running this restore op won't make anything renderable, so skip it. return false; } @@ -129,22 +137,28 @@ bool EntityPassClipStack::AppendClipCoverage( void EntityPassClipStack::RecordEntity(const Entity& entity, Contents::ClipCoverage::Type type) { + auto& subpass_state = GetCurrentSubpassState(); switch (type) { case Contents::ClipCoverage::Type::kNoChange: return; case Contents::ClipCoverage::Type::kAppend: - rendered_clip_entities_.push_back(entity.Clone()); + subpass_state.rendered_clip_entities.push_back(entity.Clone()); break; case Contents::ClipCoverage::Type::kRestore: - if (!rendered_clip_entities_.empty()) { - rendered_clip_entities_.pop_back(); + if (!subpass_state.rendered_clip_entities.empty()) { + subpass_state.rendered_clip_entities.pop_back(); } break; } } +EntityPassClipStack::SubpassState& +EntityPassClipStack::GetCurrentSubpassState() { + return subpass_state_.back(); +} + const std::vector& EntityPassClipStack::GetReplayEntities() const { - return rendered_clip_entities_; + return subpass_state_.back().rendered_clip_entities; } } // namespace impeller diff --git a/impeller/entity/entity_pass_clip_stack.h b/impeller/entity/entity_pass_clip_stack.h index 0c29caab0d138..bfd7f5ba83b8e 100644 --- a/impeller/entity/entity_pass_clip_stack.h +++ b/impeller/entity/entity_pass_clip_stack.h @@ -21,12 +21,11 @@ struct ClipCoverageLayer { /// stencil buffer is left in an identical state. class EntityPassClipStack { public: - EntityPassClipStack(); + /// Create a new [EntityPassClipStack] with an initialized coverage rect. + explicit EntityPassClipStack(const Rect& initial_coverage_rect); ~EntityPassClipStack() = default; - void Initialize(const Rect& rect); - std::optional CurrentClipCoverage() const; void PushSubpass(std::optional subpass_coverage, size_t clip_depth); @@ -48,12 +47,17 @@ class EntityPassClipStack { const std::vector& GetReplayEntities() const; // Visible for testing. - const std::vector> GetClipCoverageLayers() - const; + const std::vector GetClipCoverageLayers() const; private: - std::vector rendered_clip_entities_; - std::vector> clip_coverage_; + struct SubpassState { + std::vector rendered_clip_entities; + std::vector clip_coverage; + }; + + SubpassState& GetCurrentSubpassState(); + + std::vector subpass_state_; }; } // namespace impeller diff --git a/impeller/entity/entity_pass_unittests.cc b/impeller/entity/entity_pass_unittests.cc index acc7b35f3f25a..b0a3583dca5c2 100644 --- a/impeller/entity/entity_pass_unittests.cc +++ b/impeller/entity/entity_pass_unittests.cc @@ -11,7 +11,8 @@ namespace impeller { namespace testing { TEST(EntityPassClipStackTest, CanPushAndPopEntities) { - EntityPassClipStack recorder = EntityPassClipStack(); + EntityPassClipStack recorder = + EntityPassClipStack(Rect::MakeLTRB(0, 0, 100, 100)); EXPECT_TRUE(recorder.GetReplayEntities().empty()); @@ -30,7 +31,8 @@ TEST(EntityPassClipStackTest, CanPushAndPopEntities) { } TEST(EntityPassClipStackTest, CanPopEntitiesSafely) { - EntityPassClipStack recorder = EntityPassClipStack(); + EntityPassClipStack recorder = + EntityPassClipStack(Rect::MakeLTRB(0, 0, 100, 100)); EXPECT_TRUE(recorder.GetReplayEntities().empty()); @@ -40,7 +42,8 @@ TEST(EntityPassClipStackTest, CanPopEntitiesSafely) { } TEST(EntityPassClipStackTest, CanAppendNoChange) { - EntityPassClipStack recorder = EntityPassClipStack(); + EntityPassClipStack recorder = + EntityPassClipStack(Rect::MakeLTRB(0, 0, 100, 100)); EXPECT_TRUE(recorder.GetReplayEntities().empty()); @@ -50,13 +53,12 @@ TEST(EntityPassClipStackTest, CanAppendNoChange) { } TEST(EntityPassClipStackTest, AppendCoverageNoChange) { - EntityPassClipStack recorder = EntityPassClipStack(); - recorder.Initialize(Rect::MakeSize(Size::MakeWH(100, 100))); + EntityPassClipStack recorder = + EntityPassClipStack(Rect::MakeLTRB(0, 0, 100, 100)); - ASSERT_EQ(recorder.GetClipCoverageLayers()[0].size(), 1u); - EXPECT_EQ(recorder.GetClipCoverageLayers()[0][0].coverage, + EXPECT_EQ(recorder.GetClipCoverageLayers()[0].coverage, Rect::MakeSize(Size::MakeWH(100, 100))); - EXPECT_EQ(recorder.GetClipCoverageLayers()[0][0].clip_depth, 0u); + EXPECT_EQ(recorder.GetClipCoverageLayers()[0].clip_depth, 0u); Entity entity; recorder.AppendClipCoverage( @@ -66,17 +68,16 @@ TEST(EntityPassClipStackTest, AppendCoverageNoChange) { }, entity, 0, Point(0, 0)); - ASSERT_EQ(recorder.GetClipCoverageLayers()[0].size(), 1u); - EXPECT_EQ(recorder.GetClipCoverageLayers()[0][0].coverage, + EXPECT_EQ(recorder.GetClipCoverageLayers()[0].coverage, Rect::MakeSize(Size::MakeWH(100, 100))); - EXPECT_EQ(recorder.GetClipCoverageLayers()[0][0].clip_depth, 0u); + EXPECT_EQ(recorder.GetClipCoverageLayers()[0].clip_depth, 0u); } TEST(EntityPassClipStackTest, AppendAndRestoreClipCoverage) { - EntityPassClipStack recorder = EntityPassClipStack(); - recorder.Initialize(Rect::MakeSize(Size::MakeWH(100, 100))); + EntityPassClipStack recorder = + EntityPassClipStack(Rect::MakeLTRB(0, 0, 100, 100)); - ASSERT_EQ(recorder.GetClipCoverageLayers()[0].size(), 1u); + ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 1u); // Push a clip. Entity entity; @@ -88,10 +89,10 @@ TEST(EntityPassClipStackTest, AppendAndRestoreClipCoverage) { }, entity, 0, Point(0, 0)); - ASSERT_EQ(recorder.GetClipCoverageLayers()[0].size(), 2u); - EXPECT_EQ(recorder.GetClipCoverageLayers()[0][1].coverage, + ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 2u); + EXPECT_EQ(recorder.GetClipCoverageLayers()[1].coverage, Rect::MakeLTRB(50, 50, 55, 55)); - EXPECT_EQ(recorder.GetClipCoverageLayers()[0][1].clip_depth, 2u); + EXPECT_EQ(recorder.GetClipCoverageLayers()[1].clip_depth, 2u); EXPECT_EQ(recorder.GetReplayEntities().size(), 1u); // Restore the clip. @@ -103,18 +104,18 @@ TEST(EntityPassClipStackTest, AppendAndRestoreClipCoverage) { }, entity, 0, Point(0, 0)); - ASSERT_EQ(recorder.GetClipCoverageLayers()[0].size(), 1u); - EXPECT_EQ(recorder.GetClipCoverageLayers()[0][0].coverage, + ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 1u); + EXPECT_EQ(recorder.GetClipCoverageLayers()[0].coverage, Rect::MakeSize(Size::MakeWH(100, 100))); - EXPECT_EQ(recorder.GetClipCoverageLayers()[0][0].clip_depth, 0u); + EXPECT_EQ(recorder.GetClipCoverageLayers()[0].clip_depth, 0u); EXPECT_EQ(recorder.GetReplayEntities().size(), 0u); } TEST(EntityPassClipStackTest, UnbalancedRestore) { - EntityPassClipStack recorder = EntityPassClipStack(); - recorder.Initialize(Rect::MakeSize(Size::MakeWH(100, 100))); + EntityPassClipStack recorder = + EntityPassClipStack(Rect::MakeLTRB(0, 0, 100, 100)); - ASSERT_EQ(recorder.GetClipCoverageLayers()[0].size(), 1u); + ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 1u); // Restore the clip. Entity entity; @@ -126,18 +127,18 @@ TEST(EntityPassClipStackTest, UnbalancedRestore) { }, entity, 0, Point(0, 0)); - ASSERT_EQ(recorder.GetClipCoverageLayers()[0].size(), 1u); - EXPECT_EQ(recorder.GetClipCoverageLayers()[0][0].coverage, + ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 1u); + EXPECT_EQ(recorder.GetClipCoverageLayers()[0].coverage, Rect::MakeSize(Size::MakeWH(100, 100))); - EXPECT_EQ(recorder.GetClipCoverageLayers()[0][0].clip_depth, 0u); + EXPECT_EQ(recorder.GetClipCoverageLayers()[0].clip_depth, 0u); EXPECT_EQ(recorder.GetReplayEntities().size(), 0u); } TEST(EntityPassClipStackTest, ClipAndRestoreWithSubpasses) { - EntityPassClipStack recorder = EntityPassClipStack(); - recorder.Initialize(Rect::MakeSize(Size::MakeWH(100, 100))); + EntityPassClipStack recorder = + EntityPassClipStack(Rect::MakeLTRB(0, 0, 100, 100)); - ASSERT_EQ(recorder.GetClipCoverageLayers()[0].size(), 1u); + ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 1u); // Push a clip. Entity entity; @@ -149,16 +150,16 @@ TEST(EntityPassClipStackTest, ClipAndRestoreWithSubpasses) { }, entity, 0, Point(0, 0)); - ASSERT_EQ(recorder.GetClipCoverageLayers()[0].size(), 2u); - EXPECT_EQ(recorder.GetClipCoverageLayers()[0][1].coverage, + ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 2u); + EXPECT_EQ(recorder.GetClipCoverageLayers()[1].coverage, Rect::MakeLTRB(50, 50, 55, 55)); - EXPECT_EQ(recorder.GetClipCoverageLayers()[0][1].clip_depth, 2u); + EXPECT_EQ(recorder.GetClipCoverageLayers()[1].clip_depth, 2u); EXPECT_EQ(recorder.GetReplayEntities().size(), 1u); // Begin a subpass. recorder.PushSubpass(Rect::MakeLTRB(50, 50, 55, 55), 0); - ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 2u); - EXPECT_EQ(recorder.GetClipCoverageLayers()[1][0].coverage, + ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 1u); + EXPECT_EQ(recorder.GetClipCoverageLayers()[0].coverage, Rect::MakeLTRB(50, 50, 55, 55)); entity.SetClipDepth(1); @@ -169,13 +170,13 @@ TEST(EntityPassClipStackTest, ClipAndRestoreWithSubpasses) { }, entity, 0, Point(0, 0)); - EXPECT_EQ(recorder.GetClipCoverageLayers()[1][1].coverage, + EXPECT_EQ(recorder.GetClipCoverageLayers()[1].coverage, Rect::MakeLTRB(54, 54, 55, 55)); // End subpass. recorder.PopSubpass(); - EXPECT_EQ(recorder.GetClipCoverageLayers()[0][1].coverage, + EXPECT_EQ(recorder.GetClipCoverageLayers()[1].coverage, Rect::MakeLTRB(50, 50, 55, 55)); } From 29e56fdc999abf26ac6fb02c5af8f3cb55bc0b4a Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Mon, 25 Mar 2024 13:54:31 -0700 Subject: [PATCH 6/7] remove unique ptr. --- impeller/entity/entity_pass.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 34a3eeac2cc19..c128dd06e9747 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -15,7 +15,6 @@ #include "impeller/base/strings.h" #include "impeller/base/validation.h" #include "impeller/core/formats.h" -#include "impeller/entity/contents/clip_contents.h" #include "impeller/entity/contents/content_context.h" #include "impeller/entity/contents/filters/color_filter_contents.h" #include "impeller/entity/contents/filters/inputs/filter_input.h" @@ -410,9 +409,8 @@ bool EntityPass::Render(ContentContext& renderer, return true; }); - std::unique_ptr clip_stack = - std::make_unique( - Rect::MakeSize(root_render_target.GetRenderTargetSize())); + EntityPassClipStack clip_stack = EntityPassClipStack( + Rect::MakeSize(root_render_target.GetRenderTargetSize())); bool reads_from_onscreen_backdrop = GetTotalPassReads(renderer) > 0; // In this branch path, we need to render everything to an offscreen texture @@ -432,7 +430,7 @@ bool EntityPass::Render(ContentContext& renderer, Point(), // global_pass_position Point(), // local_pass_position 0, // pass_depth - *clip_stack // clip_coverage_stack + clip_stack // clip_coverage_stack )) { // Validation error messages are triggered for all `OnRender()` failure // cases. @@ -538,7 +536,7 @@ bool EntityPass::Render(ContentContext& renderer, Point(), // global_pass_position Point(), // local_pass_position 0, // pass_depth - *clip_stack); // clip_coverage_stack + clip_stack); // clip_coverage_stack } EntityPass::EntityResult EntityPass::GetEntityForElement( From 769c95cecc00631161227913d41a2691d333a1d9 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Mon, 25 Mar 2024 15:00:01 -0700 Subject: [PATCH 7/7] fix clip values and add license files. --- ci/licenses_golden/licenses_flutter | 4 ++++ impeller/entity/entity_pass_unittests.cc | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index ee89116474024..b6d82a2305b12 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -39694,6 +39694,8 @@ ORIGIN: ../../../flutter/impeller/entity/entity.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/entity.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/entity_pass.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/entity_pass.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/entity/entity_pass_clip_stack.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/entity/entity_pass_clip_stack.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/entity_pass_delegate.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/entity_pass_delegate.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/entity_pass_target.cc + ../../../flutter/LICENSE @@ -42567,6 +42569,8 @@ FILE: ../../../flutter/impeller/entity/entity.cc FILE: ../../../flutter/impeller/entity/entity.h FILE: ../../../flutter/impeller/entity/entity_pass.cc FILE: ../../../flutter/impeller/entity/entity_pass.h +FILE: ../../../flutter/impeller/entity/entity_pass_clip_stack.cc +FILE: ../../../flutter/impeller/entity/entity_pass_clip_stack.h FILE: ../../../flutter/impeller/entity/entity_pass_delegate.cc FILE: ../../../flutter/impeller/entity/entity_pass_delegate.h FILE: ../../../flutter/impeller/entity/entity_pass_target.cc diff --git a/impeller/entity/entity_pass_unittests.cc b/impeller/entity/entity_pass_unittests.cc index b0a3583dca5c2..ce2a66a82dc7c 100644 --- a/impeller/entity/entity_pass_unittests.cc +++ b/impeller/entity/entity_pass_unittests.cc @@ -81,7 +81,7 @@ TEST(EntityPassClipStackTest, AppendAndRestoreClipCoverage) { // Push a clip. Entity entity; - entity.SetClipDepth(1); + entity.SetClipDepth(0); recorder.AppendClipCoverage( Contents::ClipCoverage{ .type = Contents::ClipCoverage::Type::kAppend, @@ -92,7 +92,7 @@ TEST(EntityPassClipStackTest, AppendAndRestoreClipCoverage) { ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 2u); EXPECT_EQ(recorder.GetClipCoverageLayers()[1].coverage, Rect::MakeLTRB(50, 50, 55, 55)); - EXPECT_EQ(recorder.GetClipCoverageLayers()[1].clip_depth, 2u); + EXPECT_EQ(recorder.GetClipCoverageLayers()[1].clip_depth, 1u); EXPECT_EQ(recorder.GetReplayEntities().size(), 1u); // Restore the clip. @@ -142,7 +142,7 @@ TEST(EntityPassClipStackTest, ClipAndRestoreWithSubpasses) { // Push a clip. Entity entity; - entity.SetClipDepth(1); + entity.SetClipDepth(0u); recorder.AppendClipCoverage( Contents::ClipCoverage{ .type = Contents::ClipCoverage::Type::kAppend, @@ -153,11 +153,11 @@ TEST(EntityPassClipStackTest, ClipAndRestoreWithSubpasses) { ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 2u); EXPECT_EQ(recorder.GetClipCoverageLayers()[1].coverage, Rect::MakeLTRB(50, 50, 55, 55)); - EXPECT_EQ(recorder.GetClipCoverageLayers()[1].clip_depth, 2u); + EXPECT_EQ(recorder.GetClipCoverageLayers()[1].clip_depth, 1u); EXPECT_EQ(recorder.GetReplayEntities().size(), 1u); // Begin a subpass. - recorder.PushSubpass(Rect::MakeLTRB(50, 50, 55, 55), 0); + recorder.PushSubpass(Rect::MakeLTRB(50, 50, 55, 55), 1); ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 1u); EXPECT_EQ(recorder.GetClipCoverageLayers()[0].coverage, Rect::MakeLTRB(50, 50, 55, 55));