diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 7f143b41e80ea..c225182016aa6 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -777,9 +777,6 @@ FILE: ../../../flutter/flow/layers/layer.cc FILE: ../../../flutter/flow/layers/layer.h FILE: ../../../flutter/flow/layers/layer_raster_cache_item.cc FILE: ../../../flutter/flow/layers/layer_raster_cache_item.h -FILE: ../../../flutter/flow/layers/layer_state_stack.cc -FILE: ../../../flutter/flow/layers/layer_state_stack.h -FILE: ../../../flutter/flow/layers/layer_state_stack_unittests.cc FILE: ../../../flutter/flow/layers/layer_tree.cc FILE: ../../../flutter/flow/layers/layer_tree.h FILE: ../../../flutter/flow/layers/layer_tree_unittests.cc diff --git a/display_list/display_list_builder.cc b/display_list/display_list_builder.cc index 7540b1e159875..ccef2769aeaaf 100644 --- a/display_list/display_list_builder.cc +++ b/display_list/display_list_builder.cc @@ -709,21 +709,6 @@ SkRect DisplayListBuilder::getLocalClipBounds() { return kMaxCullRect_; } -bool DisplayListBuilder::quickReject(const SkRect& bounds) const { - if (bounds.isEmpty()) { - return true; - } - SkMatrix matrix = getTransform(); - // We don't need the inverse, but this method tells us if the matrix - // is singular in which case we can reject all rendering. - if (!matrix.invert(nullptr)) { - return true; - } - SkRect dev_bounds; - matrix.mapRect(bounds).roundOut(&dev_bounds); - return !current_layer_->clip_bounds.intersects(dev_bounds); -} - void DisplayListBuilder::drawPaint() { Push(0, 1); CheckLayerOpacityCompatibility(); diff --git a/display_list/display_list_builder.h b/display_list/display_list_builder.h index 3b575153d7ded..8bcc3ffa56db6 100644 --- a/display_list/display_list_builder.h +++ b/display_list/display_list_builder.h @@ -206,7 +206,7 @@ class DisplayListBuilder final : public virtual Dispatcher, /// Returns the 3x3 partial perspective transform representing all transform /// operations executed so far in this DisplayList within the enclosing /// save stack. - SkMatrix getTransform() const { return current_layer_->matrix.asM33(); } + SkMatrix getTransform() { return current_layer_->matrix.asM33(); } void clipRect(const SkRect& rect, SkClipOp clip_op, bool is_aa) override; void clipRRect(const SkRRect& rrect, SkClipOp clip_op, bool is_aa) override; @@ -221,11 +221,6 @@ class DisplayListBuilder final : public virtual Dispatcher, /// recorded rendering operations are interpreted. SkRect getLocalClipBounds(); - /// Return true iff the supplied bounds are easily shown to be outside - /// of the current clip bounds. This method may conservatively return - /// false if it cannot make the determination. - bool quickReject(const SkRect& bounds) const; - void drawPaint() override; void drawPaint(const DlPaint& paint); void drawColor(DlColor color, DlBlendMode mode) override; diff --git a/display_list/display_list_canvas_unittests.cc b/display_list/display_list_canvas_unittests.cc index aebb618d424a1..0c285c1fde4e4 100644 --- a/display_list/display_list_canvas_unittests.cc +++ b/display_list/display_list_canvas_unittests.cc @@ -2040,9 +2040,6 @@ class CanvasCompareTester { const uint32_t* test_row = test_pixels->addr32(0, y); for (int x = 0; x < test_pixels->width(); x++) { if (ref_row[x] != test_row[x]) { - if (should_match) { - FML_LOG(ERROR) << std::hex << ref_row[x] << " != " << test_row[x]; - } pixels_different++; } } @@ -3241,176 +3238,5 @@ TEST_F(DisplayListCanvas, DrawShadowDpr) { CanvasCompareTester::DefaultTolerance.addBoundsPadding(3, 3)); } -TEST_F(DisplayListCanvas, SaveLayerConsolidation) { - float commutable_color_matrix[]{ - // clang-format off - 0, 1, 0, 0, 0, - 0, 0, 1, 0, 0, - 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, - // clang-format on - }; - float non_commutable_color_matrix[]{ - // clang-format off - 0, 1, 0, .1, 0, - 0, 0, 1, .1, 0, - 1, 0, 0, .1, 0, - 0, 0, 0, .7, 0, - // clang-format on - }; - SkMatrix contract_matrix; - contract_matrix.setScale(0.9f, 0.9f, kRenderCenterX, kRenderCenterY); - - std::vector opacities = { - 0, - 0.5f, - SK_Scalar1, - }; - std::vector> color_filters = { - std::make_shared(DlColor::kCyan(), - DlBlendMode::kSrcATop), - std::make_shared(commutable_color_matrix), - std::make_shared(non_commutable_color_matrix), - DlSrgbToLinearGammaColorFilter::instance, - DlLinearToSrgbGammaColorFilter::instance, - }; - std::vector> image_filters = { - std::make_shared(5.0f, 5.0f, DlTileMode::kDecal), - std::make_shared(5.0f, 5.0f), - std::make_shared(5.0f, 5.0f), - std::make_shared(contract_matrix, - DlImageSampling::kLinear), - }; - RenderEnvironment env = RenderEnvironment::MakeN32(); - - auto render_content = [](DisplayListBuilder& builder) { - builder.drawRect( - SkRect{kRenderLeft, kRenderTop, kRenderCenterX, kRenderCenterY}, - DlPaint().setColor(DlColor::kYellow())); - builder.drawRect( - SkRect{kRenderCenterX, kRenderTop, kRenderRight, kRenderCenterY}, - DlPaint().setColor(DlColor::kRed())); - builder.drawRect( - SkRect{kRenderLeft, kRenderCenterY, kRenderCenterX, kRenderBottom}, - DlPaint().setColor(DlColor::kBlue())); - builder.drawRect( - SkRect{kRenderCenterX, kRenderCenterY, kRenderRight, kRenderBottom}, - DlPaint().setColor(DlColor::kRed().modulateOpacity(0.5f))); - }; - - // clang-format off - auto test_attributes = [&env, render_content] - (DlPaint& paint1, DlPaint& paint2, const DlPaint& paint_both, - bool same, bool rev_same, const std::string& desc1, - const std::string& desc2) { - // clang-format on - DisplayListBuilder nested_builder; - nested_builder.saveLayer(&kTestBounds, &paint1); - nested_builder.saveLayer(&kTestBounds, &paint2); - render_content(nested_builder); - auto nested_surface = env.MakeSurface(); - nested_builder.Build()->RenderTo(nested_surface->canvas()); - - DisplayListBuilder reverse_builder; - reverse_builder.saveLayer(&kTestBounds, &paint2); - reverse_builder.saveLayer(&kTestBounds, &paint1); - render_content(reverse_builder); - auto reverse_surface = env.MakeSurface(); - reverse_builder.Build()->RenderTo(reverse_surface->canvas()); - - DisplayListBuilder combined_builder; - combined_builder.saveLayer(&kTestBounds, &paint_both); - render_content(combined_builder); - auto combined_surface = env.MakeSurface(); - combined_builder.Build()->RenderTo(combined_surface->canvas()); - - // Set this boolean to true to test if combinations that are marked - // as incompatible actually are compatible despite our predictions. - // Some of the combinations that we treat as incompatible actually - // are compatible with swapping the order of the operations, but - // it would take a bit of new infrastructure to really identify - // those combinations. The only hard constraint to test here is - // when we claim that they are compatible and they aren't. - const bool always = false; - - if (always || same) { - CanvasCompareTester::quickCompareToReference( - nested_surface->pixmap(), combined_surface->pixmap(), same, - "nested " + desc1 + " then " + desc2); - } - if (always || rev_same) { - CanvasCompareTester::quickCompareToReference( - reverse_surface->pixmap(), combined_surface->pixmap(), rev_same, - "nested " + desc2 + " then " + desc1); - } - }; - - // CF then Opacity should always work. - // The reverse sometimes works. - for (size_t cfi = 0; cfi < color_filters.size(); cfi++) { - auto color_filter = color_filters[cfi]; - std::string cf_desc = "color filter #" + std::to_string(cfi + 1); - DlPaint nested_paint1 = DlPaint().setColorFilter(color_filter); - - for (size_t oi = 0; oi < opacities.size(); oi++) { - SkScalar opacity = opacities[oi]; - std::string op_desc = "opacity " + std::to_string(opacity); - DlPaint nested_paint2 = DlPaint().setOpacity(opacity); - - DlPaint combined_paint = nested_paint1; - combined_paint.setOpacity(opacity); - - bool op_then_cf_works = opacity <= 0.0 || opacity >= 1.0 || - color_filter->can_commute_with_opacity(); - - test_attributes(nested_paint1, nested_paint2, combined_paint, true, - op_then_cf_works, cf_desc, op_desc); - } - } - - // Opacity then IF should always work. - // The reverse can also work for some values of opacity. - // The reverse should also theoretically work for some IFs, but we - // get some rounding errors that are more than just trivial. - for (size_t oi = 0; oi < opacities.size(); oi++) { - SkScalar opacity = opacities[oi]; - std::string op_desc = "opacity " + std::to_string(opacity); - DlPaint nested_paint1 = DlPaint().setOpacity(opacity); - - for (size_t ifi = 0; ifi < image_filters.size(); ifi++) { - auto image_filter = image_filters[ifi]; - std::string if_desc = "image filter #" + std::to_string(ifi + 1); - DlPaint nested_paint2 = DlPaint().setImageFilter(image_filter); - - DlPaint combined_paint = nested_paint1; - combined_paint.setImageFilter(image_filter); - - bool if_then_op_works = opacity <= 0.0 || opacity >= 1.0; - test_attributes(nested_paint1, nested_paint2, combined_paint, true, - if_then_op_works, op_desc, if_desc); - } - } - - // CF then IF should always work. - // The reverse might work, but we lack the infrastructure to check it. - for (size_t cfi = 0; cfi < color_filters.size(); cfi++) { - auto color_filter = color_filters[cfi]; - std::string cf_desc = "color filter #" + std::to_string(cfi + 1); - DlPaint nested_paint1 = DlPaint().setColorFilter(color_filter); - - for (size_t ifi = 0; ifi < image_filters.size(); ifi++) { - auto image_filter = image_filters[ifi]; - std::string if_desc = "image filter #" + std::to_string(ifi + 1); - DlPaint nested_paint2 = DlPaint().setImageFilter(image_filter); - - DlPaint combined_paint = nested_paint1; - combined_paint.setImageFilter(image_filter); - - test_attributes(nested_paint1, nested_paint2, combined_paint, true, false, - cf_desc, if_desc); - } - } -} - } // namespace testing } // namespace flutter diff --git a/display_list/display_list_color.h b/display_list/display_list_color.h index d84ccd4342a93..38ad332bd36bf 100644 --- a/display_list/display_list_color.h +++ b/display_list/display_list_color.h @@ -14,9 +14,6 @@ struct DlColor { constexpr DlColor() : argb(0xFF000000) {} constexpr DlColor(uint32_t argb) : argb(argb) {} - static constexpr uint8_t toAlpha(SkScalar opacity) { return toC(opacity); } - static constexpr SkScalar toOpacity(uint8_t alpha) { return toF(alpha); } - // clang-format off static constexpr DlColor kTransparent() {return 0x00000000;}; static constexpr DlColor kBlack() {return 0xFF000000;}; diff --git a/display_list/display_list_color_filter.h b/display_list/display_list_color_filter.h index ee536b30eb215..7f03fbf5cb1df 100644 --- a/display_list/display_list_color_filter.h +++ b/display_list/display_list_color_filter.h @@ -57,11 +57,6 @@ class DlColorFilter // pixels non-transparent and therefore expand the bounds. virtual bool modifies_transparent_black() const = 0; - // Return a boolean indicating whether the color filtering operation can - // be applied either before or after modulating the pixels with an opacity - // value without changing the operation. - virtual bool can_commute_with_opacity() const { return false; } - // Return a DlBlendColorFilter pointer to this object iff it is a Blend // type of ColorFilter, otherwise return nullptr. virtual const DlBlendColorFilter* asBlend() const { return nullptr; } @@ -155,12 +150,6 @@ class DlMatrixColorFilter final : public DlColorFilter { sk_filter->filterColor(SK_ColorTRANSPARENT) != SK_ColorTRANSPARENT; } - bool can_commute_with_opacity() const override { - return matrix_[3] == 0 && matrix_[8] == 0 && matrix_[13] == 0 && - matrix_[15] == 0 && matrix_[16] == 0 && matrix_[17] == 0 && - (matrix_[18] >= 0.0 && matrix_[18] <= 1.0) && matrix_[19] == 0; - } - std::shared_ptr shared() const override { return std::make_shared(this); } @@ -204,7 +193,6 @@ class DlSrgbToLinearGammaColorFilter final : public DlColorFilter { } size_t size() const override { return sizeof(*this); } bool modifies_transparent_black() const override { return false; } - bool can_commute_with_opacity() const override { return true; } std::shared_ptr shared() const override { return instance; } sk_sp skia_object() const override { return sk_filter_; } @@ -237,7 +225,6 @@ class DlLinearToSrgbGammaColorFilter final : public DlColorFilter { } size_t size() const override { return sizeof(*this); } bool modifies_transparent_black() const override { return false; } - bool can_commute_with_opacity() const override { return true; } std::shared_ptr shared() const override { return instance; } sk_sp skia_object() const override { return sk_filter_; } diff --git a/display_list/display_list_paint.h b/display_list/display_list_paint.h index 9eb0ad0de2b2a..b260180294eba 100644 --- a/display_list/display_list_paint.h +++ b/display_list/display_list_paint.h @@ -104,10 +104,6 @@ class DlPaint { color_.argb = alpha << 24 | (color_.argb & 0x00FFFFFF); return *this; } - DlPaint& setOpacity(SkScalar opacity) { - setAlpha(SkScalarRoundToInt(opacity * 0xff)); - return *this; - } DlBlendMode getBlendMode() const { return static_cast(blendMode_); @@ -170,7 +166,7 @@ class DlPaint { return colorFilter_; } const DlColorFilter* getColorFilterPtr() const { return colorFilter_.get(); } - DlPaint& setColorFilter(const std::shared_ptr filter) { + DlPaint& setColorFilter(std::shared_ptr filter) { colorFilter_ = filter ? filter->shared() : nullptr; return *this; } @@ -183,7 +179,7 @@ class DlPaint { return imageFilter_; } const DlImageFilter* getImageFilterPtr() const { return imageFilter_.get(); } - DlPaint& setImageFilter(const std::shared_ptr filter) { + DlPaint& setImageFilter(std::shared_ptr filter) { imageFilter_ = filter; return *this; } diff --git a/flow/BUILD.gn b/flow/BUILD.gn index 09ed594157cab..c42fa8e7ab18c 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -45,8 +45,6 @@ source_set("flow") { "layers/layer.h", "layers/layer_raster_cache_item.cc", "layers/layer_raster_cache_item.h", - "layers/layer_state_stack.cc", - "layers/layer_state_stack.h", "layers/layer_tree.cc", "layers/layer_tree.h", "layers/offscreen_surface.cc", @@ -158,7 +156,6 @@ if (enable_unittests) { "layers/container_layer_unittests.cc", "layers/display_list_layer_unittests.cc", "layers/image_filter_layer_unittests.cc", - "layers/layer_state_stack_unittests.cc", "layers/layer_tree_unittests.cc", "layers/offscreen_surface_unittests.cc", "layers/opacity_layer_unittests.cc", @@ -173,6 +170,7 @@ if (enable_unittests) { "rtree_unittests.cc", "skia_gpu_object_unittests.cc", "surface_frame_unittests.cc", + "testing/auto_save_layer_unittests.cc", "testing/mock_layer_unittests.cc", "testing/mock_texture_unittests.cc", "texture_unittests.cc", diff --git a/flow/embedded_view_params_unittests.cc b/flow/embedded_view_params_unittests.cc index fb301d0009946..aa9604cae9922 100644 --- a/flow/embedded_view_params_unittests.cc +++ b/flow/embedded_view_params_unittests.cc @@ -56,6 +56,7 @@ TEST(EmbeddedViewParams, GetBoundingRectAfterMutationsWithRotation90) { EmbeddedViewParams params(matrix, SkSize::Make(1, 1), stack); const SkRect& rect = params.finalBoundingRect(); + FML_DLOG(ERROR) << rect.x(); ASSERT_TRUE(SkScalarNearlyEqual(rect.x(), -1)); ASSERT_TRUE(SkScalarNearlyEqual(rect.y(), 0)); ASSERT_TRUE(SkScalarNearlyEqual(rect.width(), 1)); diff --git a/flow/embedded_views.cc b/flow/embedded_views.cc index 2982cb40824b9..ebc1812755488 100644 --- a/flow/embedded_views.cc +++ b/flow/embedded_views.cc @@ -109,12 +109,6 @@ void MutatorsStack::Pop() { vector_.pop_back(); }; -void MutatorsStack::PopTo(size_t stack_count) { - while (vector_.size() > stack_count) { - Pop(); - } -} - const std::vector>::const_reverse_iterator MutatorsStack::Top() const { return vector_.rend(); diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 4627c7d880a06..14ca2ba12b348 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -160,8 +160,6 @@ class MutatorsStack { // and destroys it. void Pop(); - void PopTo(size_t stack_count); - // Returns a reverse iterator pointing to the top of the stack, which is the // mutator that is furtherest from the leaf node. const std::vector>::const_reverse_iterator Top() @@ -180,7 +178,6 @@ class MutatorsStack { const std::vector>::const_iterator End() const; bool is_empty() const { return vector_.empty(); } - size_t stack_count() const { return vector_.size(); } bool operator==(const MutatorsStack& other) const { if (vector_.size() != other.vector_.size()) { diff --git a/flow/layers/backdrop_filter_layer.cc b/flow/layers/backdrop_filter_layer.cc index 07a596662d730..2c9f673a5a4f0 100644 --- a/flow/layers/backdrop_filter_layer.cc +++ b/flow/layers/backdrop_filter_layer.cc @@ -39,26 +39,48 @@ void BackdropFilterLayer::Diff(DiffContext* context, const Layer* old_layer) { context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); } -void BackdropFilterLayer::Preroll(PrerollContext* context) { +void BackdropFilterLayer::Preroll(PrerollContext* context, + const SkMatrix& matrix) { Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context, true, bool(filter_)); if (context->view_embedder != nullptr) { context->view_embedder->PushFilterToVisitedPlatformViews(filter_); } SkRect child_paint_bounds = SkRect::MakeEmpty(); - PrerollChildren(context, &child_paint_bounds); - child_paint_bounds.join(context->state_stack.local_cull_rect()); + PrerollChildren(context, matrix, &child_paint_bounds); + child_paint_bounds.join(context->cull_rect); set_paint_bounds(child_paint_bounds); - context->renderable_state_flags = kSaveLayerRenderFlags; + context->subtree_can_inherit_opacity = true; } void BackdropFilterLayer::Paint(PaintContext& context) const { FML_DCHECK(needs_painting(context)); - auto mutator = context.state_stack.save(); - mutator.applyBackdropFilter(paint_bounds(), filter_, blend_mode_); + AutoCachePaint save_paint(context); + save_paint.setBlendMode(blend_mode_); + if (context.leaf_nodes_builder) { + // Note that we perform a saveLayer directly on the + // leaf_nodes_builder here similar to how the SkCanvas + // path specifies the kLeafNodesCanvas below. + // See https:://flutter.dev/go/backdrop-filter-with-overlay-canvas + context.leaf_nodes_builder->saveLayer(&paint_bounds(), + save_paint.dl_paint(), filter_.get()); - PaintChildren(context); + PaintChildren(context); + + context.leaf_nodes_builder->restore(); + } else { + auto sk_filter = filter_ ? filter_->skia_object() : nullptr; + Layer::AutoSaveLayer save = Layer::AutoSaveLayer::Create( + context, + SkCanvas::SaveLayerRec{&paint_bounds(), save_paint.sk_paint(), + sk_filter.get(), 0}, + // BackdropFilter should only happen on the leaf nodes canvas. + // See https:://flutter.dev/go/backdrop-filter-with-overlay-canvas + AutoSaveLayer::SaveMode::kLeafNodesCanvas); + + PaintChildren(context); + } } } // namespace flutter diff --git a/flow/layers/backdrop_filter_layer.h b/flow/layers/backdrop_filter_layer.h index d17fed110d5e9..8584413698a75 100644 --- a/flow/layers/backdrop_filter_layer.h +++ b/flow/layers/backdrop_filter_layer.h @@ -17,7 +17,7 @@ class BackdropFilterLayer : public ContainerLayer { void Diff(DiffContext* context, const Layer* old_layer) override; - void Preroll(PrerollContext* context) override; + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/backdrop_filter_layer_unittests.cc b/flow/layers/backdrop_filter_layer_unittests.cc index 398ee2133333b..49a214bd76e73 100644 --- a/flow/layers/backdrop_filter_layer_unittests.cc +++ b/flow/layers/backdrop_filter_layer_unittests.cc @@ -28,7 +28,7 @@ TEST_F(BackdropFilterLayerTest, PaintingEmptyLayerDies) { auto parent = std::make_shared(kEmptyRect, Clip::hardEdge); parent->Add(layer); - parent->Preroll(preroll_context()); + parent->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); EXPECT_FALSE(layer->needs_painting(paint_context())); @@ -65,8 +65,7 @@ TEST_F(BackdropFilterLayerTest, EmptyFilter) { auto parent = std::make_shared(child_bounds, Clip::hardEdge); parent->Add(layer); - preroll_context()->state_stack.set_initial_transform(initial_transform); - parent->Preroll(preroll_context()); + parent->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); @@ -96,8 +95,7 @@ TEST_F(BackdropFilterLayerTest, SimpleFilter) { auto parent = std::make_shared(child_bounds, Clip::hardEdge); parent->Add(layer); - preroll_context()->state_stack.set_initial_transform(initial_transform); - parent->Preroll(preroll_context()); + parent->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); @@ -127,8 +125,7 @@ TEST_F(BackdropFilterLayerTest, NonSrcOverBlend) { auto parent = std::make_shared(child_bounds, Clip::hardEdge); parent->Add(layer); - preroll_context()->state_stack.set_initial_transform(initial_transform); - parent->Preroll(preroll_context()); + parent->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); @@ -169,8 +166,7 @@ TEST_F(BackdropFilterLayerTest, MultipleChildren) { std::make_shared(children_bounds, Clip::hardEdge); parent->Add(layer); - preroll_context()->state_stack.set_initial_transform(initial_transform); - parent->Preroll(preroll_context()); + parent->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer->paint_bounds(), children_bounds); @@ -219,9 +215,7 @@ TEST_F(BackdropFilterLayerTest, Nested) { std::make_shared(children_bounds, Clip::hardEdge); parent->Add(layer1); - preroll_context()->state_stack.set_initial_transform(initial_transform); - parent->Preroll(preroll_context()); - + parent->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer1->paint_bounds(), children_bounds); @@ -259,20 +253,19 @@ TEST_F(BackdropFilterLayerTest, Readback) { auto layer1 = std::make_shared(layer_filter.shared(), DlBlendMode::kSrcOver); preroll_context()->surface_needs_readback = false; - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer1->Preroll(preroll_context()); + layer1->Preroll(preroll_context(), initial_transform); EXPECT_TRUE(preroll_context()->surface_needs_readback); // BDF with no filter does not read from surface itself auto layer2 = std::make_shared(no_filter, DlBlendMode::kSrcOver); preroll_context()->surface_needs_readback = false; - layer2->Preroll(preroll_context()); + layer2->Preroll(preroll_context(), initial_transform); EXPECT_FALSE(preroll_context()->surface_needs_readback); // BDF with no filter does not block prior readback value preroll_context()->surface_needs_readback = true; - layer2->Preroll(preroll_context()); + layer2->Preroll(preroll_context(), initial_transform); EXPECT_TRUE(preroll_context()->surface_needs_readback); // BDF with no filter blocks child with readback @@ -280,7 +273,7 @@ TEST_F(BackdropFilterLayerTest, Readback) { mock_layer->set_fake_reads_surface(true); layer2->Add(mock_layer); preroll_context()->surface_needs_readback = false; - layer2->Preroll(preroll_context()); + layer2->Preroll(preroll_context(), initial_transform); EXPECT_FALSE(preroll_context()->surface_needs_readback); } @@ -299,7 +292,7 @@ TEST_F(BackdropFilterLayerTest, OpacityInheritance) { parent->Add(layer); clip->Add(parent); - clip->Preroll(preroll_context()); + clip->Preroll(preroll_context(), SkMatrix::I()); clip->Paint(display_list_paint_context()); diff --git a/flow/layers/cacheable_layer.h b/flow/layers/cacheable_layer.h index c4aa392460cb4..aac445b4bf054 100644 --- a/flow/layers/cacheable_layer.h +++ b/flow/layers/cacheable_layer.h @@ -27,7 +27,7 @@ class AutoCache { inline bool IsCacheEnabled(); RasterCacheItem* raster_cache_item_ = nullptr; PrerollContext* context_ = nullptr; - const SkMatrix matrix_; + const SkMatrix& matrix_; }; class CacheableContainerLayer : public ContainerLayer { diff --git a/flow/layers/checkerboard_layertree_unittests.cc b/flow/layers/checkerboard_layertree_unittests.cc index 2b176b97a8e82..1923b77278334 100644 --- a/flow/layers/checkerboard_layertree_unittests.cc +++ b/flow/layers/checkerboard_layertree_unittests.cc @@ -18,35 +18,40 @@ namespace testing { using CheckerBoardLayerTest = LayerTest; #ifndef NDEBUG -TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerCheckBoard) { +TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerNotCheckBoard) { const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); - const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); + const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 2.0, 4.0); + const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); const SkPath child_path = SkPath().addRect(child_bounds); const SkPaint child_paint = SkPaint(SkColors::kYellow); + const SkPaint clip_paint; + auto mock_layer = std::make_shared(child_path, child_paint); auto layer = std::make_shared(layer_bounds, Clip::antiAliasWithSaveLayer); layer->Add(mock_layer); - preroll_context()->state_stack.set_initial_transform(initial_matrix); - layer->Preroll(preroll_context()); - - // Untouched - EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), kGiantRect); - EXPECT_TRUE(preroll_context()->state_stack.is_empty()); + SkRect intersect_bounds = layer_bounds; + SkRect child_intersect_bounds = layer_bounds; + intersect_bounds.intersect(cull_bounds); + child_intersect_bounds.intersect(child_bounds); + preroll_context()->cull_rect = cull_bounds; // Cull child + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); - EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); - EXPECT_EQ(layer->child_paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); - EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); + EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_bounds)})); layer->Paint(paint_context()); + EXPECT_EQ( mock_canvas().draw_calls(), std::vector( @@ -55,17 +60,49 @@ TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerCheckBoard) { 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, MockCanvas::kSoft_ClipEdgeStyle}}, MockCanvas::DrawCall{ - 1, - MockCanvas::SaveLayerData{child_bounds, SkPaint(), nullptr, 2}}, + 1, MockCanvas::SaveLayerData{layer->paint_bounds(), clip_paint, + nullptr, 2}}, MockCanvas::DrawCall{ 2, MockCanvas::DrawPathData{child_path, child_paint}}, MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} - mock_canvas().reset_draw_calls(); +TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerCheckBoard) { + const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); + const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 2.0, 4.0); + const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + const SkPaint clip_paint; - layer->Paint(checkerboard_context()); - EXPECT_EQ( + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_bounds, + Clip::antiAliasWithSaveLayer); + layer->Add(mock_layer); + + SkRect intersect_bounds = layer_bounds; + SkRect child_intersect_bounds = layer_bounds; + intersect_bounds.intersect(cull_bounds); + child_intersect_bounds.intersect(child_bounds); + preroll_context()->cull_rect = cull_bounds; // Cull child + + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); + EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), + std::vector({Mutator(layer_bounds)})); + + layer->Paint(check_board_context()); + + EXPECT_NE( mock_canvas().draw_calls(), std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, @@ -73,25 +110,20 @@ TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerCheckBoard) { 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, MockCanvas::kSoft_ClipEdgeStyle}}, MockCanvas::DrawCall{ - 1, - MockCanvas::SaveLayerData{child_bounds, SkPaint(), nullptr, 2}}, + 1, MockCanvas::SaveLayerData{layer->paint_bounds(), clip_paint, + nullptr, 2}}, MockCanvas::DrawCall{ 2, MockCanvas::DrawPathData{child_path, child_paint}}, - // start DrawCheckerboard calls - MockCanvas::DrawCall{ - 2, MockCanvas::DrawRectData{child_bounds, checkerboard_paint()}}, - // end DrawCheckerboard calls MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } -TEST_F(CheckerBoardLayerTest, ClipPathSaveLayerCheckBoard) { +TEST_F(CheckerBoardLayerTest, ClipPathSaveLayerNotCheckBoard) { const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); const SkPath child_path = SkPath().addRect(child_bounds); - const SkPath layer_path = - SkPath().addRect(layer_bounds).addRect(layer_bounds); + const SkPath layer_path = SkPath().addRect(layer_bounds); const SkPaint child_paint = SkPaint(SkColors::kYellow); const SkPaint clip_paint; auto mock_layer = std::make_shared(child_path, child_paint); @@ -99,13 +131,9 @@ TEST_F(CheckerBoardLayerTest, ClipPathSaveLayerCheckBoard) { std::make_shared(layer_path, Clip::antiAliasWithSaveLayer); layer->Add(mock_layer); - preroll_context()->state_stack.set_initial_transform(initial_matrix); - layer->Preroll(preroll_context()); - - // Untouched - EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), kGiantRect); - EXPECT_TRUE(preroll_context()->state_stack.is_empty()); - + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); @@ -120,7 +148,7 @@ TEST_F(CheckerBoardLayerTest, ClipPathSaveLayerCheckBoard) { std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, MockCanvas::DrawCall{ - 1, MockCanvas::ClipPathData{layer_path, SkClipOp::kIntersect, + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, MockCanvas::kSoft_ClipEdgeStyle}}, MockCanvas::DrawCall{ 1, @@ -129,36 +157,55 @@ TEST_F(CheckerBoardLayerTest, ClipPathSaveLayerCheckBoard) { 2, MockCanvas::DrawPathData{child_path, child_paint}}, MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} - mock_canvas().reset_draw_calls(); +TEST_F(CheckerBoardLayerTest, ClipPathSaveLayerCheckBoard) { + const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPath layer_path = SkPath().addRect(layer_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + const SkPaint clip_paint; + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = + std::make_shared(layer_path, Clip::antiAliasWithSaveLayer); + layer->Add(mock_layer); - layer->Paint(checkerboard_context()); - EXPECT_EQ( + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); + EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); + + layer->Paint(check_board_context()); + EXPECT_NE( mock_canvas().draw_calls(), std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, MockCanvas::DrawCall{ - 1, MockCanvas::ClipPathData{layer_path, SkClipOp::kIntersect, + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, MockCanvas::kSoft_ClipEdgeStyle}}, MockCanvas::DrawCall{ 1, MockCanvas::SaveLayerData{child_bounds, clip_paint, nullptr, 2}}, MockCanvas::DrawCall{ 2, MockCanvas::DrawPathData{child_path, child_paint}}, - // start DrawCheckerboard calls - MockCanvas::DrawCall{ - 2, MockCanvas::DrawRectData{child_bounds, checkerboard_paint()}}, - // end DrawCheckerboard calls MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } -TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerCheckBoard) { +TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerNotCheckBoard) { const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); const SkPath child_path = SkPath().addRect(child_bounds); - const SkRRect layer_rrect = SkRRect::MakeRectXY(layer_bounds, .1, .1); + const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); const SkPaint child_paint = SkPaint(SkColors::kYellow); const SkPaint clip_paint; auto mock_layer = std::make_shared(child_path, child_paint); @@ -166,13 +213,9 @@ TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerCheckBoard) { Clip::antiAliasWithSaveLayer); layer->Add(mock_layer); - preroll_context()->state_stack.set_initial_transform(initial_matrix); - layer->Preroll(preroll_context()); - - // Untouched - EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), kGiantRect); - EXPECT_TRUE(preroll_context()->state_stack.is_empty()); - + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); @@ -187,8 +230,8 @@ TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerCheckBoard) { std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, MockCanvas::DrawCall{ - 1, MockCanvas::ClipRRectData{layer_rrect, SkClipOp::kIntersect, - MockCanvas::kSoft_ClipEdgeStyle}}, + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kSoft_ClipEdgeStyle}}, MockCanvas::DrawCall{ 1, MockCanvas::SaveLayerData{child_bounds, clip_paint, nullptr, 2}}, @@ -196,31 +239,50 @@ TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerCheckBoard) { 2, MockCanvas::DrawPathData{child_path, child_paint}}, MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} - mock_canvas().reset_draw_calls(); +TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerCheckBoard) { + const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + const SkPaint clip_paint; + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = std::make_shared(layer_rrect, + Clip::antiAliasWithSaveLayer); + layer->Add(mock_layer); - layer->Paint(checkerboard_context()); - EXPECT_EQ( + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); + EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); + + layer->Paint(check_board_context()); + EXPECT_NE( mock_canvas().draw_calls(), std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, MockCanvas::DrawCall{ - 1, MockCanvas::ClipRRectData{layer_rrect, SkClipOp::kIntersect, - MockCanvas::kSoft_ClipEdgeStyle}}, + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kSoft_ClipEdgeStyle}}, MockCanvas::DrawCall{ 1, MockCanvas::SaveLayerData{child_bounds, clip_paint, nullptr, 2}}, MockCanvas::DrawCall{ 2, MockCanvas::DrawPathData{child_path, child_paint}}, - // start DrawCheckerboard calls - MockCanvas::DrawCall{ - 2, MockCanvas::DrawRectData{child_bounds, checkerboard_paint()}}, - // end DrawCheckerboard calls MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } -TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerCheckBoard) { +TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerNotCheckBoard) { constexpr float initial_elevation = 20.0f; SkPath layer_path; layer_path.addRect(0, 0, 8, 8).close(); @@ -228,7 +290,7 @@ TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerCheckBoard) { SK_ColorGREEN, SK_ColorBLACK, initial_elevation, layer_path, Clip::antiAliasWithSaveLayer); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and // their shadows , so we do not do any painting there. EXPECT_EQ(layer->paint_bounds(), @@ -257,11 +319,32 @@ TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerCheckBoard) { MockCanvas::DrawCall{2, MockCanvas::DrawPaint{layer_paint}}, MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} - mock_canvas().reset_draw_calls(); +TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerCheckBoard) { + constexpr float initial_elevation = 20.0f; + SkPath layer_path; + layer_path.addRect(0, 0, 8, 8).close(); + auto layer = std::make_shared( + SK_ColorGREEN, SK_ColorBLACK, initial_elevation, layer_path, + Clip::antiAliasWithSaveLayer); - layer->Paint(checkerboard_context()); - EXPECT_EQ( + layer->Preroll(preroll_context(), SkMatrix()); + // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and + // their shadows , so we do not do any painting there. + EXPECT_EQ(layer->paint_bounds(), + DisplayListCanvasDispatcher::ComputeShadowBounds( + layer_path, initial_elevation, 1.0f, SkMatrix())); + EXPECT_TRUE(layer->needs_painting(paint_context())); + EXPECT_EQ(layer->elevation(), initial_elevation); + + const SkRect paint_bounds = SkRect::MakeXYWH(0, 0, 8, 8); + const SkPaint clip_paint; + SkPaint layer_paint; + layer_paint.setColor(SK_ColorGREEN); + layer_paint.setAntiAlias(true); + layer->Paint(check_board_context()); + EXPECT_NE( mock_canvas().draw_calls(), std::vector( {MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}}, @@ -273,11 +356,6 @@ TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerCheckBoard) { 1, MockCanvas::SaveLayerData{layer->paint_bounds(), clip_paint, nullptr, 2}}, MockCanvas::DrawCall{2, MockCanvas::DrawPaint{layer_paint}}, - // start DrawCheckerboard calls - MockCanvas::DrawCall{2, - MockCanvas::DrawRectData{layer->paint_bounds(), - checkerboard_paint()}}, - // end DrawCheckerboard calls MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } diff --git a/flow/layers/clip_path_layer.cc b/flow/layers/clip_path_layer.cc index 5a8961fced9c6..65c7f898ea211 100644 --- a/flow/layers/clip_path_layer.cc +++ b/flow/layers/clip_path_layer.cc @@ -9,12 +9,25 @@ namespace flutter { ClipPathLayer::ClipPathLayer(const SkPath& clip_path, Clip clip_behavior) : ClipShapeLayer(clip_path, clip_behavior) {} +void ClipPathLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { + ClipShapeLayer::Preroll(context, matrix); +} + +void ClipPathLayer::Paint(PaintContext& context) const { + ClipShapeLayer::Paint(context); +} + const SkRect& ClipPathLayer::clip_shape_bounds() const { return clip_shape().getBounds(); } -void ClipPathLayer::ApplyClip(LayerStateStack::MutatorContext& mutator) const { - mutator.clipPath(clip_shape(), clip_behavior() != Clip::hardEdge); +void ClipPathLayer::OnMutatorsStackPushClipShape( + MutatorsStack& mutators_stack) { + mutators_stack.PushClipPath(clip_shape()); +} + +void ClipPathLayer::OnCanvasClipShape(SkCanvas* canvas) const { + canvas->clipPath(clip_shape(), clip_behavior() != Clip::hardEdge); } } // namespace flutter diff --git a/flow/layers/clip_path_layer.h b/flow/layers/clip_path_layer.h index 5a1cbffcb6f92..c8ff1bad743a6 100644 --- a/flow/layers/clip_path_layer.h +++ b/flow/layers/clip_path_layer.h @@ -14,10 +14,16 @@ class ClipPathLayer : public ClipShapeLayer { explicit ClipPathLayer(const SkPath& clip_path, Clip clip_behavior = Clip::antiAlias); + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; + + void Paint(PaintContext& context) const override; + protected: const SkRect& clip_shape_bounds() const override; - void ApplyClip(LayerStateStack::MutatorContext& mutator) const override; + void OnMutatorsStackPushClipShape(MutatorsStack& mutators_stack) override; + + void OnCanvasClipShape(SkCanvas* canvas) const override; private: FML_DISALLOW_COPY_AND_ASSIGN(ClipPathLayer); diff --git a/flow/layers/clip_path_layer_unittests.cc b/flow/layers/clip_path_layer_unittests.cc index adc52b6549e50..4408185aed5e2 100644 --- a/flow/layers/clip_path_layer_unittests.cc +++ b/flow/layers/clip_path_layer_unittests.cc @@ -29,12 +29,9 @@ TEST_F(ClipPathLayerTest, ClipNoneBehaviorDies) { TEST_F(ClipPathLayerTest, PaintingEmptyLayerDies) { auto layer = std::make_shared(SkPath(), Clip::hardEdge); - layer->Preroll(preroll_context()); - - // Untouched - EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), kGiantRect); - EXPECT_TRUE(preroll_context()->state_stack.is_empty()); - + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(layer->paint_bounds(), kEmptyRect); EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); EXPECT_FALSE(layer->needs_painting(paint_context())); @@ -66,15 +63,11 @@ TEST_F(ClipPathLayerTest, PaintingCulledLayerDies) { auto layer = std::make_shared(layer_path, Clip::hardEdge); layer->Add(mock_layer); - // Cull these children - preroll_context()->state_stack.set_initial_state(distant_bounds, - initial_matrix); - layer->Preroll(preroll_context()); - - // Untouched - EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), distant_bounds); - EXPECT_TRUE(preroll_context()->state_stack.is_empty()); + preroll_context()->cull_rect = distant_bounds; // Cull these children + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, distant_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); @@ -84,8 +77,7 @@ TEST_F(ClipPathLayerTest, PaintingCulledLayerDies) { EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); - auto mutator = paint_context().state_stack.save(); - mutator.clipRect(distant_bounds, false); + paint_context().internal_nodes_canvas->clipRect(distant_bounds, false); EXPECT_FALSE(mock_layer->needs_painting(paint_context())); EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), @@ -95,47 +87,45 @@ TEST_F(ClipPathLayerTest, PaintingCulledLayerDies) { TEST_F(ClipPathLayerTest, ChildOutsideBounds) { const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); - const SkRect local_cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 2.0, 4.0); - const SkRect device_cull_bounds = initial_matrix.mapRect(local_cull_bounds); + const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 2.0, 4.0); const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); - const SkRect clip_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); const SkPath child_path = SkPath().addRect(child_bounds); - const SkPath clip_path = SkPath().addRect(clip_bounds); + const SkPath layer_path = SkPath().addRect(layer_bounds); const SkPaint child_paint = SkPaint(SkColors::kYellow); auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(clip_path, Clip::hardEdge); + auto layer = std::make_shared(layer_path, Clip::hardEdge); layer->Add(mock_layer); - SkRect clip_cull_rect = local_cull_bounds; - ASSERT_TRUE(clip_cull_rect.intersect(clip_bounds)); - SkRect clip_layer_bounds = child_bounds; - ASSERT_TRUE(clip_layer_bounds.intersect(clip_bounds)); - - // Set up both contexts to cull clipped child - preroll_context()->state_stack.set_initial_state(device_cull_bounds, - initial_matrix); - paint_context().state_stack.set_initial_state(device_cull_bounds, - initial_matrix); - - layer->Preroll(preroll_context()); - // Untouched - EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), - device_cull_bounds); - EXPECT_EQ(preroll_context()->state_stack.local_cull_rect(), - local_cull_bounds); - EXPECT_TRUE(preroll_context()->state_stack.is_empty()); + SkRect intersect_bounds = layer_bounds; + SkRect child_intersect_bounds = layer_bounds; + intersect_bounds.intersect(cull_bounds); + child_intersect_bounds.intersect(child_bounds); + preroll_context()->cull_rect = cull_bounds; // Cull child + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); - EXPECT_EQ(layer->paint_bounds(), clip_layer_bounds); + EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); - EXPECT_EQ(mock_layer->parent_cull_rect(), clip_cull_rect); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); + EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); - EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(clip_path)})); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); - EXPECT_FALSE(layer->needs_painting(paint_context())); - EXPECT_FALSE(mock_layer->needs_painting(paint_context())); - // Top level layer not visible so calling layer->Paint() - // would trip an FML_DCHECK + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } TEST_F(ClipPathLayerTest, FullyContainedChild) { @@ -149,13 +139,9 @@ TEST_F(ClipPathLayerTest, FullyContainedChild) { auto layer = std::make_shared(layer_path, Clip::hardEdge); layer->Add(mock_layer); - preroll_context()->state_stack.set_initial_transform(initial_matrix); - layer->Preroll(preroll_context()); - - // Untouched - EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), kGiantRect); - EXPECT_TRUE(preroll_context()->state_stack.is_empty()); - + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); @@ -180,42 +166,33 @@ TEST_F(ClipPathLayerTest, FullyContainedChild) { TEST_F(ClipPathLayerTest, PartiallyContainedChild) { const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); - const SkRect local_cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 4.0, 5.5); - const SkRect device_cull_bounds = initial_matrix.mapRect(local_cull_bounds); + const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 4.0, 5.5); const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); - const SkRect clip_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); const SkPath child_path = SkPath().addRect(child_bounds); - const SkPath clip_path = SkPath().addRect(clip_bounds); + const SkPath layer_path = SkPath().addRect(layer_bounds); const SkPaint child_paint = SkPaint(SkColors::kYellow); auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(clip_path, Clip::hardEdge); + auto layer = std::make_shared(layer_path, Clip::hardEdge); layer->Add(mock_layer); - SkRect clip_cull_rect = local_cull_bounds; - ASSERT_TRUE(clip_cull_rect.intersect(clip_bounds)); - SkRect clip_layer_bounds = child_bounds; - ASSERT_TRUE(clip_layer_bounds.intersect(clip_bounds)); - - // Cull child - preroll_context()->state_stack.set_initial_state(device_cull_bounds, - initial_matrix); - layer->Preroll(preroll_context()); - - // Untouched - EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), - device_cull_bounds); - EXPECT_EQ(preroll_context()->state_stack.local_cull_rect(), - local_cull_bounds); - EXPECT_TRUE(preroll_context()->state_stack.is_empty()); + SkRect intersect_bounds = layer_bounds; + SkRect child_intersect_bounds = layer_bounds; + intersect_bounds.intersect(cull_bounds); + child_intersect_bounds.intersect(child_bounds); + preroll_context()->cull_rect = cull_bounds; // Cull child + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); - EXPECT_EQ(layer->paint_bounds(), clip_layer_bounds); + EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); - EXPECT_EQ(mock_layer->parent_cull_rect(), clip_cull_rect); + EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); - EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(clip_path)})); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); layer->Paint(paint_context()); EXPECT_EQ( @@ -223,7 +200,7 @@ TEST_F(ClipPathLayerTest, PartiallyContainedChild) { std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, MockCanvas::DrawCall{ - 1, MockCanvas::ClipRectData{clip_bounds, SkClipOp::kIntersect, + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, MockCanvas::kHard_ClipEdgeStyle}}, MockCanvas::DrawCall{ 1, MockCanvas::DrawPathData{child_path, child_paint}}, @@ -234,6 +211,7 @@ static bool ReadbackResult(PrerollContext* context, Clip clip_behavior, const std::shared_ptr& child, bool before) { + const SkMatrix initial_matrix = SkMatrix(); const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); const SkPath layer_path = SkPath().addRect(layer_bounds); auto layer = std::make_shared(layer_path, clip_behavior); @@ -241,7 +219,7 @@ static bool ReadbackResult(PrerollContext* context, layer->Add(child); } context->surface_needs_readback = before; - layer->Preroll(context); + layer->Preroll(context, initial_matrix); return context->surface_needs_readback; } @@ -302,9 +280,9 @@ TEST_F(ClipPathLayerTest, OpacityInheritance) { // ClipRectLayer will pass through compatibility from a compatible child PrerollContext* context = preroll_context(); - clip_path_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + clip_path_layer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); auto path2 = SkPath().addRect({40, 40, 50, 50}); auto mock2 = MockLayer::MakeOpacityCompatible(path2); @@ -312,9 +290,9 @@ TEST_F(ClipPathLayerTest, OpacityInheritance) { // ClipRectLayer will pass through compatibility from multiple // non-overlapping compatible children - clip_path_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + clip_path_layer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); auto path3 = SkPath().addRect({20, 20, 40, 40}); auto mock3 = MockLayer::MakeOpacityCompatible(path3); @@ -322,8 +300,9 @@ TEST_F(ClipPathLayerTest, OpacityInheritance) { // ClipRectLayer will not pass through compatibility from multiple // overlapping children even if they are individually compatible - clip_path_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, 0); + context->subtree_can_inherit_opacity = false; + clip_path_layer->Preroll(context, SkMatrix::I()); + EXPECT_FALSE(context->subtree_can_inherit_opacity); { // ClipRectLayer(aa with saveLayer) will always be compatible @@ -333,13 +312,15 @@ TEST_F(ClipPathLayerTest, OpacityInheritance) { clip_path_savelayer->Add(mock2); // Double check first two children are compatible and non-overlapping - clip_path_savelayer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags); + context->subtree_can_inherit_opacity = false; + clip_path_savelayer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); // Now add the overlapping child and test again, should still be compatible clip_path_savelayer->Add(mock3); - clip_path_savelayer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags); + context->subtree_can_inherit_opacity = false; + clip_path_savelayer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); } // An incompatible, but non-overlapping child for the following tests @@ -354,16 +335,17 @@ TEST_F(ClipPathLayerTest, OpacityInheritance) { clip_path_bad_child->Add(mock2); // Double check first two children are compatible and non-overlapping - clip_path_bad_child->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + clip_path_bad_child->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); clip_path_bad_child->Add(mock4); // The third child is non-overlapping, but not compatible so the // TransformLayer should end up incompatible - clip_path_bad_child->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, 0); + context->subtree_can_inherit_opacity = false; + clip_path_bad_child->Preroll(context, SkMatrix::I()); + EXPECT_FALSE(context->subtree_can_inherit_opacity); } { @@ -374,13 +356,15 @@ TEST_F(ClipPathLayerTest, OpacityInheritance) { clip_path_savelayer_bad_child->Add(mock2); // Double check first two children are compatible and non-overlapping - clip_path_savelayer_bad_child->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags); + context->subtree_can_inherit_opacity = false; + clip_path_savelayer_bad_child->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); // Now add the incompatible child and test again, should still be compatible clip_path_savelayer_bad_child->Add(mock4); - clip_path_savelayer_bad_child->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags); + context->subtree_can_inherit_opacity = false; + clip_path_savelayer_bad_child->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); } } @@ -391,7 +375,7 @@ TEST_F(ClipPathLayerTest, OpacityInheritancePainting) { auto mock2 = MockLayer::MakeOpacityCompatible(path2); auto layer_clip = SkPath() .addRect(SkRect::MakeLTRB(5, 5, 25, 25)) - .addOval(SkRect::MakeLTRB(45, 45, 55, 55)); + .addOval(SkRect::MakeLTRB(20, 20, 40, 50)); auto clip_path_layer = std::make_shared(layer_clip, Clip::antiAlias); clip_path_layer->Add(mock1); @@ -400,15 +384,16 @@ TEST_F(ClipPathLayerTest, OpacityInheritancePainting) { // ClipRectLayer will pass through compatibility from multiple // non-overlapping compatible children PrerollContext* context = preroll_context(); - clip_path_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + clip_path_layer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); int opacity_alpha = 0x7F; SkPoint offset = SkPoint::Make(10, 10); auto opacity_layer = std::make_shared(opacity_alpha, offset); opacity_layer->Add(clip_path_layer); - opacity_layer->Preroll(context); + context->subtree_can_inherit_opacity = false; + opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); DisplayListBuilder expected_builder; @@ -420,10 +405,22 @@ TEST_F(ClipPathLayerTest, OpacityInheritancePainting) { expected_builder.save(); expected_builder.clipPath(layer_clip, SkClipOp::kIntersect, true); /* child layer1 paint */ { - expected_builder.drawPath(path1, DlPaint().setAlpha(opacity_alpha)); + expected_builder.setColor(opacity_alpha << 24); + expected_builder.saveLayer(&path1.getBounds(), true); + { + expected_builder.setColor(0xFF000000); + expected_builder.drawPath(path1); + } + expected_builder.restore(); } /* child layer2 paint */ { - expected_builder.drawPath(path2, DlPaint().setAlpha(opacity_alpha)); + expected_builder.setColor(opacity_alpha << 24); + expected_builder.saveLayer(&path2.getBounds(), true); + { + expected_builder.setColor(0xFF000000); + expected_builder.drawPath(path2); + } + expected_builder.restore(); } expected_builder.restore(); } @@ -432,7 +429,7 @@ TEST_F(ClipPathLayerTest, OpacityInheritancePainting) { } opacity_layer->Paint(display_list_paint_context()); - EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); + EXPECT_TRUE(DisplayListsEQ_Verbose(expected_builder.Build(), display_list())); } TEST_F(ClipPathLayerTest, OpacityInheritanceSaveLayerPainting) { @@ -453,14 +450,16 @@ TEST_F(ClipPathLayerTest, OpacityInheritanceSaveLayerPainting) { // ClipRectLayer will pass through compatibility from multiple // non-overlapping compatible children PrerollContext* context = preroll_context(); - clip_path_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags); + context->subtree_can_inherit_opacity = false; + clip_path_layer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); int opacity_alpha = 0x7F; SkPoint offset = SkPoint::Make(10, 10); auto opacity_layer = std::make_shared(opacity_alpha, offset); opacity_layer->Add(clip_path_layer); - opacity_layer->Preroll(context); + context->subtree_can_inherit_opacity = false; + opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); DisplayListBuilder expected_builder; @@ -506,24 +505,23 @@ TEST_F(ClipPathLayerTest, LayerCached) { cache_canvas.setMatrix(cache_ctm); use_mock_raster_cache(); - preroll_context()->state_stack.set_initial_transform(initial_transform); const auto* clip_cache_item = layer->raster_cache_item(); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); EXPECT_EQ(clip_cache_item->cache_state(), diff --git a/flow/layers/clip_rect_layer.cc b/flow/layers/clip_rect_layer.cc index b141ec83e3a01..6b7ff2002f0a8 100644 --- a/flow/layers/clip_rect_layer.cc +++ b/flow/layers/clip_rect_layer.cc @@ -9,12 +9,25 @@ namespace flutter { ClipRectLayer::ClipRectLayer(const SkRect& clip_rect, Clip clip_behavior) : ClipShapeLayer(clip_rect, clip_behavior) {} +void ClipRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { + ClipShapeLayer::Preroll(context, matrix); +} + +void ClipRectLayer::Paint(PaintContext& context) const { + ClipShapeLayer::Paint(context); +} + const SkRect& ClipRectLayer::clip_shape_bounds() const { return clip_shape(); } -void ClipRectLayer::ApplyClip(LayerStateStack::MutatorContext& mutator) const { - mutator.clipRect(clip_shape(), clip_behavior() != Clip::hardEdge); +void ClipRectLayer::OnMutatorsStackPushClipShape( + MutatorsStack& mutators_stack) { + mutators_stack.PushClipRect(clip_shape()); +} + +void ClipRectLayer::OnCanvasClipShape(SkCanvas* canvas) const { + canvas->clipRect(clip_shape(), clip_behavior() != Clip::hardEdge); } } // namespace flutter diff --git a/flow/layers/clip_rect_layer.h b/flow/layers/clip_rect_layer.h index 5f8f11d2bf023..967bd9b349cd7 100644 --- a/flow/layers/clip_rect_layer.h +++ b/flow/layers/clip_rect_layer.h @@ -13,10 +13,16 @@ class ClipRectLayer : public ClipShapeLayer { public: ClipRectLayer(const SkRect& clip_rect, Clip clip_behavior); + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; + + void Paint(PaintContext& context) const override; + protected: const SkRect& clip_shape_bounds() const override; - void ApplyClip(LayerStateStack::MutatorContext& mutator) const override; + void OnMutatorsStackPushClipShape(MutatorsStack& mutators_stack) override; + + void OnCanvasClipShape(SkCanvas* canvas) const override; private: FML_DISALLOW_COPY_AND_ASSIGN(ClipRectLayer); diff --git a/flow/layers/clip_rect_layer_unittests.cc b/flow/layers/clip_rect_layer_unittests.cc index cd152f6218380..2b11cb588dd65 100644 --- a/flow/layers/clip_rect_layer_unittests.cc +++ b/flow/layers/clip_rect_layer_unittests.cc @@ -26,12 +26,9 @@ TEST_F(ClipRectLayerTest, ClipNoneBehaviorDies) { TEST_F(ClipRectLayerTest, PaintingEmptyLayerDies) { auto layer = std::make_shared(kEmptyRect, Clip::hardEdge); - layer->Preroll(preroll_context()); - - // Untouched - EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), kGiantRect); - EXPECT_TRUE(preroll_context()->state_stack.is_empty()); - + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(layer->paint_bounds(), kEmptyRect); EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); EXPECT_FALSE(layer->needs_painting(paint_context())); @@ -61,15 +58,11 @@ TEST_F(ClipRectLayerTest, PaintingCulledLayerDies) { auto layer = std::make_shared(layer_bounds, Clip::hardEdge); layer->Add(mock_layer); - // Cull these children - preroll_context()->state_stack.set_initial_state(distant_bounds, - initial_matrix); - layer->Preroll(preroll_context()); - - // Untouched - EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), distant_bounds); - EXPECT_TRUE(preroll_context()->state_stack.is_empty()); + preroll_context()->cull_rect = distant_bounds; // Cull these children + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, distant_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); @@ -80,8 +73,7 @@ TEST_F(ClipRectLayerTest, PaintingCulledLayerDies) { EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_bounds)})); - auto mutator = paint_context().state_stack.save(); - mutator.clipRect(distant_bounds, false); + paint_context().internal_nodes_canvas->clipRect(distant_bounds, false); EXPECT_FALSE(mock_layer->needs_painting(paint_context())); EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), @@ -91,46 +83,45 @@ TEST_F(ClipRectLayerTest, PaintingCulledLayerDies) { TEST_F(ClipRectLayerTest, ChildOutsideBounds) { const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); - const SkRect local_cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 2.0, 4.0); - const SkRect device_cull_bounds = initial_matrix.mapRect(local_cull_bounds); + const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 2.0, 4.0); const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); - const SkRect clip_rect = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); const SkPath child_path = SkPath().addRect(child_bounds); const SkPaint child_paint = SkPaint(SkColors::kYellow); auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(clip_rect, Clip::hardEdge); + auto layer = std::make_shared(layer_bounds, Clip::hardEdge); layer->Add(mock_layer); - SkRect clip_cull_rect = local_cull_bounds; - ASSERT_TRUE(clip_cull_rect.intersect(clip_rect)); - SkRect clip_layer_bounds = child_bounds; - ASSERT_TRUE(clip_layer_bounds.intersect(clip_rect)); - - // Set up both contexts to cull clipped child - preroll_context()->state_stack.set_initial_state(device_cull_bounds, - initial_matrix); - paint_context().state_stack.set_initial_state(device_cull_bounds, - initial_matrix); - - layer->Preroll(preroll_context()); - // Untouched - EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), - device_cull_bounds); - EXPECT_EQ(preroll_context()->state_stack.local_cull_rect(), - local_cull_bounds); - EXPECT_TRUE(preroll_context()->state_stack.is_empty()); + SkRect intersect_bounds = layer_bounds; + SkRect child_intersect_bounds = layer_bounds; + intersect_bounds.intersect(cull_bounds); + child_intersect_bounds.intersect(child_bounds); + preroll_context()->cull_rect = cull_bounds; // Cull child + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); - EXPECT_EQ(layer->paint_bounds(), clip_layer_bounds); + EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); - EXPECT_EQ(mock_layer->parent_cull_rect(), clip_cull_rect); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); + EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); - EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(clip_rect)})); + EXPECT_EQ(mock_layer->parent_mutators(), + std::vector({Mutator(layer_bounds)})); - EXPECT_FALSE(layer->needs_painting(paint_context())); - EXPECT_FALSE(mock_layer->needs_painting(paint_context())); - // Top level layer not visible so calling layer->Paint() - // would trip an FML_DCHECK + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } TEST_F(ClipRectLayerTest, FullyContainedChild) { @@ -143,13 +134,9 @@ TEST_F(ClipRectLayerTest, FullyContainedChild) { auto layer = std::make_shared(layer_bounds, Clip::hardEdge); layer->Add(mock_layer); - preroll_context()->state_stack.set_initial_transform(initial_matrix); - layer->Preroll(preroll_context()); - - // Untouched - EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), kGiantRect); - EXPECT_TRUE(preroll_context()->state_stack.is_empty()); - + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); @@ -175,49 +162,41 @@ TEST_F(ClipRectLayerTest, FullyContainedChild) { TEST_F(ClipRectLayerTest, PartiallyContainedChild) { const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); - const SkRect local_cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 4.0, 5.5); - const SkRect device_cull_bounds = initial_matrix.mapRect(local_cull_bounds); + const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 4.0, 5.5); const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); - const SkRect clip_rect = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); const SkPath child_path = SkPath().addRect(child_bounds); const SkPaint child_paint = SkPaint(SkColors::kYellow); auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(clip_rect, Clip::hardEdge); + auto layer = std::make_shared(layer_bounds, Clip::hardEdge); layer->Add(mock_layer); - SkRect clip_cull_rect = clip_rect; - ASSERT_TRUE(clip_cull_rect.intersect(local_cull_bounds)); - SkRect clip_layer_bounds = clip_rect; - ASSERT_TRUE(clip_layer_bounds.intersect(child_bounds)); - - // Cull child - preroll_context()->state_stack.set_initial_state(device_cull_bounds, - initial_matrix); - layer->Preroll(preroll_context()); - - // Untouched - EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), - device_cull_bounds); - EXPECT_TRUE(preroll_context()->state_stack.is_empty()); + SkRect intersect_bounds = layer_bounds; + SkRect child_intersect_bounds = layer_bounds; + intersect_bounds.intersect(cull_bounds); + child_intersect_bounds.intersect(child_bounds); + preroll_context()->cull_rect = cull_bounds; // Cull child + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); - EXPECT_EQ(layer->paint_bounds(), clip_layer_bounds); + EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); - EXPECT_EQ(mock_layer->parent_cull_rect(), clip_cull_rect); + EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); - EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(clip_rect)})); + EXPECT_EQ(mock_layer->parent_mutators(), + std::vector({Mutator(layer_bounds)})); - paint_context().state_stack.set_initial_state(device_cull_bounds, - initial_matrix); layer->Paint(paint_context()); EXPECT_EQ( mock_canvas().draw_calls(), std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, MockCanvas::DrawCall{ - 1, MockCanvas::ClipRectData{clip_rect, SkClipOp::kIntersect, + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, MockCanvas::kHard_ClipEdgeStyle}}, MockCanvas::DrawCall{ 1, MockCanvas::DrawPathData{child_path, child_paint}}, @@ -228,13 +207,14 @@ static bool ReadbackResult(PrerollContext* context, Clip clip_behavior, const std::shared_ptr& child, bool before) { + const SkMatrix initial_matrix = SkMatrix(); const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); auto layer = std::make_shared(layer_bounds, clip_behavior); if (child != nullptr) { layer->Add(child); } context->surface_needs_readback = before; - layer->Preroll(context); + layer->Preroll(context, initial_matrix); return context->surface_needs_readback; } @@ -293,9 +273,9 @@ TEST_F(ClipRectLayerTest, OpacityInheritance) { // ClipRectLayer will pass through compatibility from a compatible child PrerollContext* context = preroll_context(); - clip_rect_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + clip_rect_layer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); auto path2 = SkPath().addRect({40, 40, 50, 50}); auto mock2 = MockLayer::MakeOpacityCompatible(path2); @@ -303,9 +283,9 @@ TEST_F(ClipRectLayerTest, OpacityInheritance) { // ClipRectLayer will pass through compatibility from multiple // non-overlapping compatible children - clip_rect_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + clip_rect_layer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); auto path3 = SkPath().addRect({20, 20, 40, 40}); auto mock3 = MockLayer::MakeOpacityCompatible(path3); @@ -313,8 +293,9 @@ TEST_F(ClipRectLayerTest, OpacityInheritance) { // ClipRectLayer will not pass through compatibility from multiple // overlapping children even if they are individually compatible - clip_rect_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, 0); + context->subtree_can_inherit_opacity = false; + clip_rect_layer->Preroll(context, SkMatrix::I()); + EXPECT_FALSE(context->subtree_can_inherit_opacity); { // ClipRectLayer(aa with saveLayer) will always be compatible @@ -324,13 +305,15 @@ TEST_F(ClipRectLayerTest, OpacityInheritance) { clip_path_savelayer->Add(mock2); // Double check first two children are compatible and non-overlapping - clip_path_savelayer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags); + context->subtree_can_inherit_opacity = false; + clip_path_savelayer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); // Now add the overlapping child and test again, should still be compatible clip_path_savelayer->Add(mock3); - clip_path_savelayer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags); + context->subtree_can_inherit_opacity = false; + clip_path_savelayer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); } // An incompatible, but non-overlapping child for the following tests @@ -345,16 +328,17 @@ TEST_F(ClipRectLayerTest, OpacityInheritance) { clip_rect_bad_child->Add(mock2); // Double check first two children are compatible and non-overlapping - clip_rect_bad_child->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + clip_rect_bad_child->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); clip_rect_bad_child->Add(mock4); // The third child is non-overlapping, but not compatible so the // TransformLayer should end up incompatible - clip_rect_bad_child->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, 0); + context->subtree_can_inherit_opacity = false; + clip_rect_bad_child->Preroll(context, SkMatrix::I()); + EXPECT_FALSE(context->subtree_can_inherit_opacity); } { @@ -365,13 +349,15 @@ TEST_F(ClipRectLayerTest, OpacityInheritance) { clip_path_savelayer_bad_child->Add(mock2); // Double check first two children are compatible and non-overlapping - clip_path_savelayer_bad_child->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags); + context->subtree_can_inherit_opacity = false; + clip_path_savelayer_bad_child->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); // Now add the incompatible child and test again, should still be compatible clip_path_savelayer_bad_child->Add(mock4); - clip_path_savelayer_bad_child->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags); + context->subtree_can_inherit_opacity = false; + clip_path_savelayer_bad_child->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); } } @@ -389,15 +375,16 @@ TEST_F(ClipRectLayerTest, OpacityInheritancePainting) { // ClipRectLayer will pass through compatibility from multiple // non-overlapping compatible children PrerollContext* context = preroll_context(); - clip_rect_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + clip_rect_layer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); int opacity_alpha = 0x7F; SkPoint offset = SkPoint::Make(10, 10); auto opacity_layer = std::make_shared(opacity_alpha, offset); opacity_layer->Add(clip_rect_layer); - opacity_layer->Preroll(context); + context->subtree_can_inherit_opacity = false; + opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); DisplayListBuilder expected_builder; @@ -409,10 +396,22 @@ TEST_F(ClipRectLayerTest, OpacityInheritancePainting) { expected_builder.save(); expected_builder.clipRect(clip_rect, SkClipOp::kIntersect, true); /* child layer1 paint */ { - expected_builder.drawPath(path1, DlPaint().setAlpha(opacity_alpha)); + expected_builder.setColor(opacity_alpha << 24); + expected_builder.saveLayer(&path1.getBounds(), true); + { + expected_builder.setColor(0xFF000000); + expected_builder.drawPath(path1); + } + expected_builder.restore(); } /* child layer2 paint */ { - expected_builder.drawPath(path2, DlPaint().setAlpha(opacity_alpha)); + expected_builder.setColor(opacity_alpha << 24); + expected_builder.saveLayer(&path2.getBounds(), true); + { + expected_builder.setColor(0xFF000000); + expected_builder.drawPath(path2); + } + expected_builder.restore(); } expected_builder.restore(); } @@ -440,14 +439,16 @@ TEST_F(ClipRectLayerTest, OpacityInheritanceSaveLayerPainting) { // ClipRectLayer will pass through compatibility from multiple // non-overlapping compatible children PrerollContext* context = preroll_context(); - clip_rect_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags); + context->subtree_can_inherit_opacity = false; + clip_rect_layer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); int opacity_alpha = 0x7F; SkPoint offset = SkPoint::Make(10, 10); auto opacity_layer = std::make_shared(opacity_alpha, offset); opacity_layer->Add(clip_rect_layer); - opacity_layer->Preroll(context); + context->subtree_can_inherit_opacity = false; + opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); DisplayListBuilder expected_builder; @@ -491,22 +492,21 @@ TEST_F(ClipRectLayerTest, LayerCached) { cache_canvas.setMatrix(cache_ctm); use_mock_raster_cache(); - preroll_context()->state_stack.set_initial_transform(initial_transform); const auto* clip_cache_item = layer->raster_cache_item(); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); EXPECT_EQ(clip_cache_item->cache_state(), diff --git a/flow/layers/clip_rrect_layer.cc b/flow/layers/clip_rrect_layer.cc index 735d027203303..2bf5b069332f0 100644 --- a/flow/layers/clip_rrect_layer.cc +++ b/flow/layers/clip_rrect_layer.cc @@ -9,12 +9,25 @@ namespace flutter { ClipRRectLayer::ClipRRectLayer(const SkRRect& clip_rrect, Clip clip_behavior) : ClipShapeLayer(clip_rrect, clip_behavior) {} +void ClipRRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { + ClipShapeLayer::Preroll(context, matrix); +} + +void ClipRRectLayer::Paint(PaintContext& context) const { + ClipShapeLayer::Paint(context); +} + const SkRect& ClipRRectLayer::clip_shape_bounds() const { return clip_shape().getBounds(); } -void ClipRRectLayer::ApplyClip(LayerStateStack::MutatorContext& mutator) const { - mutator.clipRRect(clip_shape(), clip_behavior() != Clip::hardEdge); +void ClipRRectLayer::OnMutatorsStackPushClipShape( + MutatorsStack& mutators_stack) { + mutators_stack.PushClipRRect(clip_shape()); +} + +void ClipRRectLayer::OnCanvasClipShape(SkCanvas* canvas) const { + canvas->clipRRect(clip_shape(), clip_behavior() != Clip::hardEdge); } } // namespace flutter diff --git a/flow/layers/clip_rrect_layer.h b/flow/layers/clip_rrect_layer.h index 53b240abe2975..15c71a732e467 100644 --- a/flow/layers/clip_rrect_layer.h +++ b/flow/layers/clip_rrect_layer.h @@ -13,10 +13,16 @@ class ClipRRectLayer : public ClipShapeLayer { public: ClipRRectLayer(const SkRRect& clip_rrect, Clip clip_behavior); + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; + + void Paint(PaintContext& context) const override; + protected: const SkRect& clip_shape_bounds() const override; - void ApplyClip(LayerStateStack::MutatorContext& mutator) const override; + void OnMutatorsStackPushClipShape(MutatorsStack& mutators_stack) override; + + void OnCanvasClipShape(SkCanvas* canvas) const override; private: FML_DISALLOW_COPY_AND_ASSIGN(ClipRRectLayer); diff --git a/flow/layers/clip_rrect_layer_unittests.cc b/flow/layers/clip_rrect_layer_unittests.cc index abb9b600099d7..918c36ec47e44 100644 --- a/flow/layers/clip_rrect_layer_unittests.cc +++ b/flow/layers/clip_rrect_layer_unittests.cc @@ -28,12 +28,9 @@ TEST_F(ClipRRectLayerTest, PaintingEmptyLayerDies) { const SkRRect layer_rrect = SkRRect::MakeEmpty(); auto layer = std::make_shared(layer_rrect, Clip::hardEdge); - layer->Preroll(preroll_context()); - - // Untouched - EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), kGiantRect); - EXPECT_TRUE(preroll_context()->state_stack.is_empty()); - + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(layer->paint_bounds(), kEmptyRect); EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); EXPECT_FALSE(layer->needs_painting(paint_context())); @@ -66,15 +63,11 @@ TEST_F(ClipRRectLayerTest, PaintingCulledLayerDies) { auto layer = std::make_shared(layer_rrect, Clip::hardEdge); layer->Add(mock_layer); - // Cull these children - preroll_context()->state_stack.set_initial_state(distant_bounds, - initial_matrix); - layer->Preroll(preroll_context()); - - // Untouched - EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), distant_bounds); - EXPECT_TRUE(preroll_context()->state_stack.is_empty()); + preroll_context()->cull_rect = distant_bounds; // Cull these children + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, distant_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); @@ -84,8 +77,7 @@ TEST_F(ClipRRectLayerTest, PaintingCulledLayerDies) { EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); - auto mutator = paint_context().state_stack.save(); - mutator.clipRect(distant_bounds, false); + paint_context().internal_nodes_canvas->clipRect(distant_bounds, false); EXPECT_FALSE(mock_layer->needs_painting(paint_context())); EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), @@ -95,47 +87,45 @@ TEST_F(ClipRRectLayerTest, PaintingCulledLayerDies) { TEST_F(ClipRRectLayerTest, ChildOutsideBounds) { const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); - const SkRect local_cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 2.0, 4.0); - const SkRect device_cull_bounds = initial_matrix.mapRect(local_cull_bounds); + const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 2.0, 4.0); const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); - const SkRect clip_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); const SkPath child_path = SkPath().addRect(child_bounds); - const SkRRect clip_rrect = SkRRect::MakeRect(clip_bounds); + const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); const SkPaint child_paint = SkPaint(SkColors::kYellow); auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(clip_rrect, Clip::hardEdge); + auto layer = std::make_shared(layer_rrect, Clip::hardEdge); layer->Add(mock_layer); - SkRect clip_cull_rect = clip_bounds; - ASSERT_TRUE(clip_cull_rect.intersect(local_cull_bounds)); - SkRect clip_layer_bounds = child_bounds; - ASSERT_TRUE(clip_layer_bounds.intersect(clip_bounds)); - - // Set up both contexts to cull clipped child - preroll_context()->state_stack.set_initial_state(device_cull_bounds, - initial_matrix); - paint_context().state_stack.set_initial_state(device_cull_bounds, - initial_matrix); - - layer->Preroll(preroll_context()); - // Untouched - EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), - device_cull_bounds); - EXPECT_EQ(preroll_context()->state_stack.local_cull_rect(), - local_cull_bounds); - EXPECT_TRUE(preroll_context()->state_stack.is_empty()); + SkRect intersect_bounds = layer_bounds; + SkRect child_intersect_bounds = layer_bounds; + intersect_bounds.intersect(cull_bounds); + child_intersect_bounds.intersect(child_bounds); + preroll_context()->cull_rect = cull_bounds; // Cull child + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); - EXPECT_EQ(layer->paint_bounds(), clip_layer_bounds); + EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); - EXPECT_EQ(mock_layer->parent_cull_rect(), clip_cull_rect); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); + EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); - EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(clip_rrect)})); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); - EXPECT_FALSE(mock_layer->needs_painting(paint_context())); - ASSERT_FALSE(layer->needs_painting(paint_context())); - // Top level layer not visible so calling layer->Paint() - // would trip an FML_DCHECK + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } TEST_F(ClipRRectLayerTest, FullyContainedChild) { @@ -149,13 +139,9 @@ TEST_F(ClipRRectLayerTest, FullyContainedChild) { auto layer = std::make_shared(layer_rrect, Clip::hardEdge); layer->Add(mock_layer); - preroll_context()->state_stack.set_initial_transform(initial_matrix); - layer->Preroll(preroll_context()); - - // Untouched - EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), kGiantRect); - EXPECT_TRUE(preroll_context()->state_stack.is_empty()); - + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); @@ -180,51 +166,41 @@ TEST_F(ClipRRectLayerTest, FullyContainedChild) { TEST_F(ClipRRectLayerTest, PartiallyContainedChild) { const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); - const SkRect local_cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 4.0, 5.5); - const SkRect device_cull_bounds = initial_matrix.mapRect(local_cull_bounds); + const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 4.0, 5.5); const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); - const SkRect clip_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); const SkPath child_path = SkPath().addRect(child_bounds); - const SkRRect clip_rrect = SkRRect::MakeRect(clip_bounds); + const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); const SkPaint child_paint = SkPaint(SkColors::kYellow); auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(clip_rrect, Clip::hardEdge); + auto layer = std::make_shared(layer_rrect, Clip::hardEdge); layer->Add(mock_layer); - SkRect clip_cull_rect = clip_bounds; - ASSERT_TRUE(clip_cull_rect.intersect(local_cull_bounds)); - SkRect clip_layer_bounds = child_bounds; - ASSERT_TRUE(clip_layer_bounds.intersect(clip_bounds)); - - preroll_context()->state_stack.set_initial_state(device_cull_bounds, - initial_matrix); - paint_context().state_stack.set_initial_state(device_cull_bounds, - initial_matrix); - - layer->Preroll(preroll_context()); - // Untouched - EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), - device_cull_bounds); - EXPECT_EQ(preroll_context()->state_stack.local_cull_rect(), - local_cull_bounds); - EXPECT_TRUE(preroll_context()->state_stack.is_empty()); + SkRect intersect_bounds = layer_bounds; + SkRect child_intersect_bounds = layer_bounds; + intersect_bounds.intersect(cull_bounds); + child_intersect_bounds.intersect(child_bounds); + preroll_context()->cull_rect = cull_bounds; // Cull child + layer->Preroll(preroll_context(), initial_matrix); + EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched + EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); - EXPECT_EQ(layer->paint_bounds(), clip_layer_bounds); + EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); - EXPECT_EQ(mock_layer->parent_cull_rect(), clip_cull_rect); - EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); - EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(clip_rrect)})); - EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); + EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); + layer->Paint(paint_context()); EXPECT_EQ( mock_canvas().draw_calls(), std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, MockCanvas::DrawCall{ - 1, MockCanvas::ClipRectData{clip_bounds, SkClipOp::kIntersect, + 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, MockCanvas::kHard_ClipEdgeStyle}}, MockCanvas::DrawCall{ 1, MockCanvas::DrawPathData{child_path, child_paint}}, @@ -235,6 +211,7 @@ static bool ReadbackResult(PrerollContext* context, Clip clip_behavior, const std::shared_ptr& child, bool before) { + const SkMatrix initial_matrix = SkMatrix(); const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); auto layer = std::make_shared(layer_rrect, clip_behavior); @@ -242,7 +219,7 @@ static bool ReadbackResult(PrerollContext* context, layer->Add(child); } context->surface_needs_readback = before; - layer->Preroll(context); + layer->Preroll(context, initial_matrix); return context->surface_needs_readback; } @@ -302,9 +279,9 @@ TEST_F(ClipRRectLayerTest, OpacityInheritance) { // ClipRectLayer will pass through compatibility from a compatible child PrerollContext* context = preroll_context(); - clip_rrect_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + clip_rrect_layer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); auto path2 = SkPath().addRect({40, 40, 50, 50}); auto mock2 = MockLayer::MakeOpacityCompatible(path2); @@ -312,9 +289,9 @@ TEST_F(ClipRRectLayerTest, OpacityInheritance) { // ClipRectLayer will pass through compatibility from multiple // non-overlapping compatible children - clip_rrect_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + clip_rrect_layer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); auto path3 = SkPath().addRect({20, 20, 40, 40}); auto mock3 = MockLayer::MakeOpacityCompatible(path3); @@ -322,8 +299,9 @@ TEST_F(ClipRRectLayerTest, OpacityInheritance) { // ClipRectLayer will not pass through compatibility from multiple // overlapping children even if they are individually compatible - clip_rrect_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, 0); + context->subtree_can_inherit_opacity = false; + clip_rrect_layer->Preroll(context, SkMatrix::I()); + EXPECT_FALSE(context->subtree_can_inherit_opacity); { // ClipRectLayer(aa with saveLayer) will always be compatible @@ -333,13 +311,15 @@ TEST_F(ClipRRectLayerTest, OpacityInheritance) { clip_rrect_savelayer->Add(mock2); // Double check first two children are compatible and non-overlapping - clip_rrect_savelayer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags); + context->subtree_can_inherit_opacity = false; + clip_rrect_savelayer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); // Now add the overlapping child and test again, should still be compatible clip_rrect_savelayer->Add(mock3); - clip_rrect_savelayer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags); + context->subtree_can_inherit_opacity = false; + clip_rrect_savelayer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); } // An incompatible, but non-overlapping child for the following tests @@ -354,16 +334,17 @@ TEST_F(ClipRRectLayerTest, OpacityInheritance) { clip_rrect_bad_child->Add(mock2); // Double check first two children are compatible and non-overlapping - clip_rrect_bad_child->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + clip_rrect_bad_child->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); clip_rrect_bad_child->Add(mock4); // The third child is non-overlapping, but not compatible so the // TransformLayer should end up incompatible - clip_rrect_bad_child->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, 0); + context->subtree_can_inherit_opacity = false; + clip_rrect_bad_child->Preroll(context, SkMatrix::I()); + EXPECT_FALSE(context->subtree_can_inherit_opacity); } { @@ -374,13 +355,15 @@ TEST_F(ClipRRectLayerTest, OpacityInheritance) { clip_rrect_savelayer_bad_child->Add(mock2); // Double check first two children are compatible and non-overlapping - clip_rrect_savelayer_bad_child->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags); + context->subtree_can_inherit_opacity = false; + clip_rrect_savelayer_bad_child->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); // Now add the incompatible child and test again, should still be compatible clip_rrect_savelayer_bad_child->Add(mock4); - clip_rrect_savelayer_bad_child->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags); + context->subtree_can_inherit_opacity = false; + clip_rrect_savelayer_bad_child->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); } } @@ -399,15 +382,16 @@ TEST_F(ClipRRectLayerTest, OpacityInheritancePainting) { // ClipRectLayer will pass through compatibility from multiple // non-overlapping compatible children PrerollContext* context = preroll_context(); - clip_rect_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + clip_rect_layer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); int opacity_alpha = 0x7F; SkPoint offset = SkPoint::Make(10, 10); auto opacity_layer = std::make_shared(opacity_alpha, offset); opacity_layer->Add(clip_rect_layer); - opacity_layer->Preroll(context); + context->subtree_can_inherit_opacity = false; + opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); DisplayListBuilder expected_builder; @@ -419,10 +403,22 @@ TEST_F(ClipRRectLayerTest, OpacityInheritancePainting) { expected_builder.save(); expected_builder.clipRRect(clip_rrect, SkClipOp::kIntersect, true); /* child layer1 paint */ { - expected_builder.drawPath(path1, DlPaint().setAlpha(opacity_alpha)); + expected_builder.setColor(opacity_alpha << 24); + expected_builder.saveLayer(&path1.getBounds(), true); + { + expected_builder.setColor(0xFF000000); + expected_builder.drawPath(path1); + } + expected_builder.restore(); } /* child layer2 paint */ { - expected_builder.drawPath(path2, DlPaint().setAlpha(opacity_alpha)); + expected_builder.setColor(opacity_alpha << 24); + expected_builder.saveLayer(&path2.getBounds(), true); + { + expected_builder.setColor(0xFF000000); + expected_builder.drawPath(path2); + } + expected_builder.restore(); } expected_builder.restore(); } @@ -451,14 +447,16 @@ TEST_F(ClipRRectLayerTest, OpacityInheritanceSaveLayerPainting) { // ClipRectLayer will pass through compatibility from multiple // non-overlapping compatible children PrerollContext* context = preroll_context(); - clip_rrect_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags); + context->subtree_can_inherit_opacity = false; + clip_rrect_layer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); int opacity_alpha = 0x7F; SkPoint offset = SkPoint::Make(10, 10); auto opacity_layer = std::make_shared(opacity_alpha, offset); opacity_layer->Add(clip_rrect_layer); - opacity_layer->Preroll(context); + context->subtree_can_inherit_opacity = false; + opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); DisplayListBuilder expected_builder; @@ -504,22 +502,21 @@ TEST_F(ClipRRectLayerTest, LayerCached) { cache_canvas.setMatrix(cache_ctm); use_mock_raster_cache(); - preroll_context()->state_stack.set_initial_transform(initial_transform); const auto* clip_cache_item = layer->raster_cache_item(); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); EXPECT_EQ(clip_cache_item->cache_state(), @@ -543,22 +540,21 @@ TEST_F(ClipRRectLayerTest, NoSaveLayerShouldNotCache) { cache_canvas.setMatrix(cache_ctm); use_mock_raster_cache(); - preroll_context()->state_stack.set_initial_transform(initial_transform); const auto* clip_cache_item = layer->raster_cache_item(); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); diff --git a/flow/layers/clip_shape_layer.h b/flow/layers/clip_shape_layer.h index 39b7acc6b642a..85f6b4393dada 100644 --- a/flow/layers/clip_shape_layer.h +++ b/flow/layers/clip_shape_layer.h @@ -42,64 +42,68 @@ class ClipShapeLayer : public CacheableContainerLayer { context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); } - void Preroll(PrerollContext* context) override { + void Preroll(PrerollContext* context, const SkMatrix& matrix) override { + SkRect previous_cull_rect = context->cull_rect; bool uses_save_layer = UsesSaveLayer(); + if (!context->cull_rect.intersect(clip_shape_bounds())) { + context->cull_rect.setEmpty(); + } + SkMatrix child_matrix = matrix; // We can use the raster_cache for children only when the use_save_layer is // true so if use_save_layer is false we pass the layer_raster_item is // nullptr which mean we don't do raster cache logic. AutoCache cache = AutoCache(uses_save_layer ? layer_raster_cache_item_.get() : nullptr, - context, context->state_stack.transform_3x3()); + context, child_matrix); Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); + OnMutatorsStackPushClipShape(context->mutators_stack); - auto mutator = context->state_stack.save(); - ApplyClip(mutator); + // Collect inheritance information on our children in Preroll so that + // we can pass it along by default. + context->subtree_can_inherit_opacity = true; SkRect child_paint_bounds = SkRect::MakeEmpty(); - PrerollChildren(context, &child_paint_bounds); + PrerollChildren(context, matrix, &child_paint_bounds); if (child_paint_bounds.intersect(clip_shape_bounds())) { set_paint_bounds(child_paint_bounds); - } else { - set_paint_bounds(SkRect::MakeEmpty()); } // If we use a SaveLayer then we can accept opacity on behalf // of our children and apply it in the saveLayer. if (uses_save_layer) { - context->renderable_state_flags = kSaveLayerRenderFlags; + context->subtree_can_inherit_opacity = true; } + + context->mutators_stack.Pop(); + context->cull_rect = previous_cull_rect; } void Paint(PaintContext& context) const override { FML_DCHECK(needs_painting(context)); - auto mutator = context.state_stack.save(); - ApplyClip(mutator); - if (context.state_stack.content_culled(child_paint_bounds())) { - return; - } + SkAutoCanvasRestore save(context.internal_nodes_canvas, true); + OnCanvasClipShape(context.internal_nodes_canvas); if (!UsesSaveLayer()) { PaintChildren(context); return; } + AutoCachePaint cache_paint(context); if (context.raster_cache) { - mutator.integralTransform(); - auto restore_apply = context.state_stack.applyState( - paint_bounds(), LayerStateStack::kCallerCanApplyOpacity); - - SkPaint paint; - if (layer_raster_cache_item_->Draw(context, - context.state_stack.fill(paint))) { + context.internal_nodes_canvas->setMatrix( + RasterCacheUtil::GetIntegralTransCTM( + context.leaf_nodes_canvas->getTotalMatrix())); + if (layer_raster_cache_item_->Draw(context, cache_paint.sk_paint())) { return; } } - mutator.saveLayer(paint_bounds()); + Layer::AutoSaveLayer save_layer = Layer::AutoSaveLayer::Create( + context, paint_bounds(), cache_paint.sk_paint()); PaintChildren(context); } @@ -109,7 +113,8 @@ class ClipShapeLayer : public CacheableContainerLayer { protected: virtual const SkRect& clip_shape_bounds() const = 0; - virtual void ApplyClip(LayerStateStack::MutatorContext& mutator) const = 0; + virtual void OnMutatorsStackPushClipShape(MutatorsStack& mutators_stack) = 0; + virtual void OnCanvasClipShape(SkCanvas* canvas) const = 0; virtual ~ClipShapeLayer() = default; const ClipShape& clip_shape() const { return clip_shape_; } diff --git a/flow/layers/color_filter_layer.cc b/flow/layers/color_filter_layer.cc index a5966e4542c36..d3da4763fce77 100644 --- a/flow/layers/color_filter_layer.cc +++ b/flow/layers/color_filter_layer.cc @@ -37,65 +37,49 @@ void ColorFilterLayer::Diff(DiffContext* context, const Layer* old_layer) { context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); } -void ColorFilterLayer::Preroll(PrerollContext* context) { +void ColorFilterLayer::Preroll(PrerollContext* context, + const SkMatrix& matrix) { Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context); - AutoCache cache = AutoCache(layer_raster_cache_item_.get(), context, - context->state_stack.transform_3x3()); - - ContainerLayer::Preroll(context); - - // Our saveLayer would apply any outstanding opacity or any outstanding - // image filter before it applies our color filter, but that is in the - // wrong order compared to how these attributes were applied to the tree - // (they would have come from one of our ancestors). So we cannot apply - // those attributes with our saveLayer normally. - // However, some color filters can commute themselves with an opacity - // modulation so in that case we can apply the opacity on behalf of our - // ancestors - otherwise we can apply no attributes. - if (filter_) { - context->renderable_state_flags = - filter_->can_commute_with_opacity() - ? LayerStateStack::kCallerCanApplyOpacity - : 0; - } - // else - we can apply whatever our children can apply. + SkMatrix child_matrix = matrix; + AutoCache cache = + AutoCache(layer_raster_cache_item_.get(), context, child_matrix); + + ContainerLayer::Preroll(context, child_matrix); + // We always use a saveLayer (or a cached rendering), so we + // can always apply opacity in those cases. + context->subtree_can_inherit_opacity = true; } void ColorFilterLayer::Paint(PaintContext& context) const { FML_DCHECK(needs_painting(context)); - auto mutator = context.state_stack.save(); - if (context.raster_cache) { - // Always apply the integral transform in the presence of a raster cache - // whether or not we will draw from the cache - mutator.integralTransform(); - - // Try drawing the layer cache item from the cache before applying the - // color filter if it was cached with the filter applied. - if (!layer_raster_cache_item_->IsCacheChildren()) { - SkPaint sk_paint; - if (layer_raster_cache_item_->Draw(context, - context.state_stack.fill(sk_paint))) { - return; - } + context.internal_nodes_canvas->setMatrix( + RasterCacheUtil::GetIntegralTransCTM( + context.leaf_nodes_canvas->getTotalMatrix())); + AutoCachePaint cache_paint(context); + if (layer_raster_cache_item_->IsCacheChildren()) { + cache_paint.setColorFilter(filter_.get()); } - } - - // Now apply the color filter and then try rendering children either from - // cache or directly. - mutator.applyColorFilter(paint_bounds(), filter_); - - if (context.raster_cache && layer_raster_cache_item_->IsCacheChildren()) { - SkPaint sk_paint; - if (layer_raster_cache_item_->Draw(context, - context.state_stack.fill(sk_paint))) { + if (layer_raster_cache_item_->Draw(context, cache_paint.sk_paint())) { return; } } - PaintChildren(context); + AutoCachePaint cache_paint(context); + cache_paint.setColorFilter(filter_.get()); + if (context.leaf_nodes_builder) { + FML_DCHECK(context.builder_multiplexer); + context.builder_multiplexer->saveLayer(&paint_bounds(), + cache_paint.dl_paint()); + PaintChildren(context); + context.builder_multiplexer->restore(); + } else { + Layer::AutoSaveLayer save = Layer::AutoSaveLayer::Create( + context, paint_bounds(), cache_paint.sk_paint()); + PaintChildren(context); + } } } // namespace flutter diff --git a/flow/layers/color_filter_layer.h b/flow/layers/color_filter_layer.h index e105e12550029..62f702b33e01a 100644 --- a/flow/layers/color_filter_layer.h +++ b/flow/layers/color_filter_layer.h @@ -17,7 +17,7 @@ class ColorFilterLayer : public CacheableContainerLayer { void Diff(DiffContext* context, const Layer* old_layer) override; - void Preroll(PrerollContext* context) override; + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/color_filter_layer_unittests.cc b/flow/layers/color_filter_layer_unittests.cc index 433606428f6bb..e45604a45b939 100644 --- a/flow/layers/color_filter_layer_unittests.cc +++ b/flow/layers/color_filter_layer_unittests.cc @@ -31,7 +31,7 @@ TEST_F(ColorFilterLayerTest, PaintingEmptyLayerDies) { auto layer = std::make_shared( std::make_shared(sk_sp())); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); EXPECT_FALSE(layer->needs_painting(paint_context())); @@ -65,8 +65,7 @@ TEST_F(ColorFilterLayerTest, EmptyFilter) { auto layer = std::make_shared(nullptr); layer->Add(mock_layer); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); @@ -75,11 +74,14 @@ TEST_F(ColorFilterLayerTest, EmptyFilter) { SkPaint filter_paint; filter_paint.setColorFilter(nullptr); layer->Paint(paint_context()); - EXPECT_EQ(mock_canvas().draw_calls(), - std::vector({ - MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{child_path, child_paint}}, - })); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, + nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } TEST_F(ColorFilterLayerTest, SimpleFilter) { @@ -93,8 +95,7 @@ TEST_F(ColorFilterLayerTest, SimpleFilter) { auto layer = std::make_shared(dl_color_filter); layer->Add(mock_layer); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); @@ -136,8 +137,7 @@ TEST_F(ColorFilterLayerTest, MultipleChildren) { SkRect children_bounds = child_path1.getBounds(); children_bounds.join(child_path2.getBounds()); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer->paint_bounds(), children_bounds); @@ -191,8 +191,7 @@ TEST_F(ColorFilterLayerTest, Nested) { SkRect children_bounds = child_path1.getBounds(); children_bounds.join(child_path2.getBounds()); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer1->Preroll(preroll_context()); + layer1->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer1->paint_bounds(), children_bounds); @@ -245,8 +244,7 @@ TEST_F(ColorFilterLayerTest, Readback) { auto layer = std::make_shared( DlLinearToSrgbGammaColorFilter::instance); preroll_context()->surface_needs_readback = false; - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_FALSE(preroll_context()->surface_needs_readback); // ColorFilterLayer blocks child with readback @@ -254,7 +252,7 @@ TEST_F(ColorFilterLayerTest, Readback) { mock_layer->set_fake_reads_surface(true); layer->Add(mock_layer); preroll_context()->surface_needs_readback = false; - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_FALSE(preroll_context()->surface_needs_readback); } @@ -282,8 +280,7 @@ TEST_F(ColorFilterLayerTest, CacheChild) { RasterCacheItem::CacheState::kNone); EXPECT_FALSE(cacheable_color_filter_item->GetId().has_value()); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); @@ -328,8 +325,7 @@ TEST_F(ColorFilterLayerTest, CacheChildren) { RasterCacheItem::CacheState::kNone); EXPECT_FALSE(cacheable_color_filter_item->GetId().has_value()); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); @@ -365,14 +361,13 @@ TEST_F(ColorFilterLayerTest, CacheColorFilterLayerSelf) { other_canvas.setMatrix(other_transform); use_mock_raster_cache(); - preroll_context()->state_stack.set_initial_transform(initial_transform); const auto* cacheable_color_filter_item = layer->raster_cache_item(); // frame 1. - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); layer->Paint(paint_context()); // frame 2. - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); // ColorFilterLayer default cache children. EXPECT_EQ(cacheable_color_filter_item->cache_state(), @@ -384,7 +379,7 @@ TEST_F(ColorFilterLayerTest, CacheColorFilterLayerSelf) { layer->Paint(paint_context()); // frame 3. - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); layer->Paint(paint_context()); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); @@ -421,19 +416,18 @@ TEST_F(ColorFilterLayerTest, OpacityInheritance) { color_filter_layer->Add(mock_layer); PrerollContext* context = preroll_context(); - context->state_stack.set_initial_transform(initial_transform); - color_filter_layer->Preroll(preroll_context()); + context->subtree_can_inherit_opacity = false; + color_filter_layer->Preroll(preroll_context(), initial_transform); // ColorFilterLayer can always inherit opacity whether or not their // children are compatible. - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + EXPECT_TRUE(context->subtree_can_inherit_opacity); int opacity_alpha = 0x7F; SkPoint offset = SkPoint::Make(10, 10); auto opacity_layer = std::make_shared(opacity_alpha, offset); opacity_layer->Add(color_filter_layer); - preroll_context()->state_stack.set_initial_transform(SkMatrix::I()); - opacity_layer->Preroll(context); + context->subtree_can_inherit_opacity = false; + opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); DisplayListBuilder expected_builder; diff --git a/flow/layers/container_layer.cc b/flow/layers/container_layer.cc index 74829417f2432..cd1157565ddb9 100644 --- a/flow/layers/container_layer.cc +++ b/flow/layers/container_layer.cc @@ -107,9 +107,9 @@ void ContainerLayer::Add(std::shared_ptr layer) { layers_.emplace_back(std::move(layer)); } -void ContainerLayer::Preroll(PrerollContext* context) { +void ContainerLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkRect child_paint_bounds = SkRect::MakeEmpty(); - PrerollChildren(context, &child_paint_bounds); + PrerollChildren(context, matrix, &child_paint_bounds); set_paint_bounds(child_paint_bounds); } @@ -127,6 +127,7 @@ static bool safe_intersection_test(const SkRect* rect1, const SkRect& rect2) { } void ContainerLayer::PrerollChildren(PrerollContext* context, + const SkMatrix& child_matrix, SkRect* child_paint_bounds) { // Platform views have no children, so context->has_platform_view should // always be false. @@ -135,7 +136,7 @@ void ContainerLayer::PrerollChildren(PrerollContext* context, bool child_has_platform_view = false; bool child_has_texture_layer = false; - bool all_renderable_state_flags = LayerStateStack::kCallerCanApplyAnything; + bool subtree_can_inherit_opacity = context->subtree_can_inherit_opacity; for (auto& layer : layers_) { // Reset context->has_platform_view and context->has_texture_layer to false @@ -144,18 +145,20 @@ void ContainerLayer::PrerollChildren(PrerollContext* context, context->has_platform_view = false; context->has_texture_layer = false; - // Initialize the renderable state flags to false to force the layer to - // opt-in to applying state attributes during its |Preroll| - context->renderable_state_flags = 0; + // Initialize the "inherit opacity" flag to false and allow the layer to + // override the answer during its |Preroll| + context->subtree_can_inherit_opacity = false; - layer->Preroll(context); + layer->Preroll(context, child_matrix); - all_renderable_state_flags &= context->renderable_state_flags; - if (safe_intersection_test(child_paint_bounds, layer->paint_bounds())) { + subtree_can_inherit_opacity = + subtree_can_inherit_opacity && context->subtree_can_inherit_opacity; + if (subtree_can_inherit_opacity && + safe_intersection_test(child_paint_bounds, layer->paint_bounds())) { // This will allow inheritance by a linear sequence of non-overlapping // children, but will fail with a grid or other arbitrary 2D layout. // See https://github.com/flutter/flutter/issues/93899 - all_renderable_state_flags = 0; + subtree_can_inherit_opacity = false; } child_paint_bounds->join(layer->paint_bounds()); @@ -167,10 +170,9 @@ void ContainerLayer::PrerollChildren(PrerollContext* context, context->has_platform_view = child_has_platform_view; context->has_texture_layer = child_has_texture_layer; - context->renderable_state_flags = all_renderable_state_flags; + context->subtree_can_inherit_opacity = subtree_can_inherit_opacity; set_subtree_has_platform_view(child_has_platform_view); - set_children_renderable_state_flags(all_renderable_state_flags); - set_child_paint_bounds(*child_paint_bounds); + child_paint_bounds_ = *child_paint_bounds; } void ContainerLayer::PaintChildren(PaintContext& context) const { @@ -180,11 +182,6 @@ void ContainerLayer::PaintChildren(PaintContext& context) const { // layer calls PaintChildren(), though, it may have modified the // PaintContext so the test doesn't work in this "context". - // Apply any outstanding state that the children cannot individually - // and collectively handle. - auto restore = context.state_stack.applyState( - child_paint_bounds(), children_renderable_state_flags()); - // Intentionally not tracing here as there should be no self-time // and the trace event on this common function has a small overhead. for (auto& layer : layers_) { diff --git a/flow/layers/container_layer.h b/flow/layers/container_layer.h index 638d951e1568f..cb3d497aa642a 100644 --- a/flow/layers/container_layer.h +++ b/flow/layers/container_layer.h @@ -20,7 +20,7 @@ class ContainerLayer : public Layer { virtual void Add(std::shared_ptr layer); - void Preroll(PrerollContext* context) override; + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; const std::vector>& layers() const { return layers_; } @@ -33,24 +33,15 @@ class ContainerLayer : public Layer { const ContainerLayer* as_container_layer() const override { return this; } const SkRect& child_paint_bounds() const { return child_paint_bounds_; } - void set_child_paint_bounds(const SkRect& bounds) { - child_paint_bounds_ = bounds; - } - - int children_renderable_state_flags() const { - return children_renderable_state_flags_; - } - void set_children_renderable_state_flags(int flags) { - children_renderable_state_flags_ = flags; - } protected: - void PrerollChildren(PrerollContext* context, SkRect* child_paint_bounds); + void PrerollChildren(PrerollContext* context, + const SkMatrix& child_matrix, + SkRect* child_paint_bounds); private: std::vector> layers_; SkRect child_paint_bounds_; - int children_renderable_state_flags_ = 0; FML_DISALLOW_COPY_AND_ASSIGN(ContainerLayer); }; diff --git a/flow/layers/container_layer_unittests.cc b/flow/layers/container_layer_unittests.cc index fea3a66f58e63..b2b4adc936ccd 100644 --- a/flow/layers/container_layer_unittests.cc +++ b/flow/layers/container_layer_unittests.cc @@ -25,7 +25,7 @@ TEST_F(ContainerLayerTest, LayerWithParentHasPlatformView) { auto layer = std::make_shared(); preroll_context()->has_platform_view = true; - EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context()), + EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), "!context->has_platform_view"); } @@ -33,14 +33,14 @@ TEST_F(ContainerLayerTest, LayerWithParentHasTextureLayer) { auto layer = std::make_shared(); preroll_context()->has_texture_layer = true; - EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context()), + EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), "!context->has_texture_layer"); } TEST_F(ContainerLayerTest, PaintingEmptyLayerDies) { auto layer = std::make_shared(); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); EXPECT_EQ(layer->child_paint_bounds(), SkRect::MakeEmpty()); EXPECT_FALSE(layer->needs_painting(paint_context())); @@ -84,7 +84,7 @@ TEST_F(ContainerLayerTest, LayerWithParentHasTextureLayerNeedsResetFlag) { container_layer2->Add(mock_layer2); EXPECT_EQ(preroll_context()->has_texture_layer, false); - root->Preroll(preroll_context()); + root->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(preroll_context()->has_texture_layer, true); // The flag for holding texture layer from parent needs to be clear EXPECT_EQ(mock_layer2->parent_has_texture_layer(), false); @@ -100,8 +100,7 @@ TEST_F(ContainerLayerTest, Simple) { auto layer = std::make_shared(); layer->Add(mock_layer); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_FALSE(preroll_context()->has_platform_view); EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer->paint_bounds(), child_path.getBounds()); @@ -135,8 +134,7 @@ TEST_F(ContainerLayerTest, Multiple) { SkRect expected_total_bounds = child_path1.getBounds(); expected_total_bounds.join(child_path2.getBounds()); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_TRUE(preroll_context()->has_platform_view); EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); @@ -173,8 +171,7 @@ TEST_F(ContainerLayerTest, MultipleWithEmpty) { layer->Add(mock_layer1); layer->Add(mock_layer2); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_FALSE(preroll_context()->has_platform_view); EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), SkPath().getBounds()); @@ -212,8 +209,7 @@ TEST_F(ContainerLayerTest, NeedsSystemComposite) { SkRect expected_total_bounds = child_path1.getBounds(); expected_total_bounds.join(child_path2.getBounds()); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_FALSE(preroll_context()->has_platform_view); EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); @@ -290,7 +286,7 @@ TEST_F(ContainerLayerTest, RasterCacheTest) { cache_canvas.setMatrix(SkMatrix::I()); // Initial Preroll for check the layer paint bounds - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix::I()); EXPECT_EQ(mock_layer1->paint_bounds(), SkRect::MakeLTRB(5.f, 6.f, 20.5f, 21.5f)); @@ -308,7 +304,7 @@ TEST_F(ContainerLayerTest, RasterCacheTest) { // frame1 use_mock_raster_cache(); preroll_context()->raster_cache->BeginFrame(); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix::I()); preroll_context()->raster_cache->EvictUnusedCacheEntries(); // Cache the cacheable entries LayerTree::TryToRasterCache(*(preroll_context()->raster_cached_entries), @@ -348,7 +344,7 @@ TEST_F(ContainerLayerTest, RasterCacheTest) { // clear the cached_entries preroll_context()->raster_cached_entries->clear(); preroll_context()->raster_cache->BeginFrame(); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix::I()); preroll_context()->raster_cache->EvictUnusedCacheEntries(); // Cache the cacheable entries @@ -392,7 +388,7 @@ TEST_F(ContainerLayerTest, RasterCacheTest) { // clear the cached_entries preroll_context()->raster_cache->BeginFrame(); preroll_context()->raster_cached_entries->clear(); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix::I()); preroll_context()->raster_cache->EvictUnusedCacheEntries(); // Cache the cacheable entries LayerTree::TryToRasterCache(*(preroll_context()->raster_cached_entries), @@ -433,7 +429,7 @@ TEST_F(ContainerLayerTest, RasterCacheTest) { preroll_context()->raster_cache->BeginFrame(); // frame4 preroll_context()->raster_cached_entries->clear(); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix::I()); preroll_context()->raster_cache->EvictUnusedCacheEntries(); LayerTree::TryToRasterCache(*(preroll_context()->raster_cached_entries), &paint_context()); @@ -442,7 +438,7 @@ TEST_F(ContainerLayerTest, RasterCacheTest) { // frame5 preroll_context()->raster_cache->BeginFrame(); preroll_context()->raster_cached_entries->clear(); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix::I()); LayerTree::TryToRasterCache(*(preroll_context()->raster_cached_entries), &paint_context()); preroll_context()->raster_cache->EndFrame(); @@ -450,7 +446,7 @@ TEST_F(ContainerLayerTest, RasterCacheTest) { // frame6 preroll_context()->raster_cache->BeginFrame(); preroll_context()->raster_cached_entries->clear(); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix::I()); LayerTree::TryToRasterCache(*(preroll_context()->raster_cached_entries), &paint_context()); preroll_context()->raster_cache->EndFrame(); @@ -463,21 +459,22 @@ TEST_F(ContainerLayerTest, OpacityInheritance) { auto container1 = std::make_shared(); container1->Add(mock1); - // ContainerLayer will pass through compatibility + // ContainerLayer will not pass through compatibility on its own + // Subclasses must explicitly enable this in their own Preroll PrerollContext* context = preroll_context(); - container1->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + container1->Preroll(context, SkMatrix::I()); + EXPECT_FALSE(context->subtree_can_inherit_opacity); auto path2 = SkPath().addRect({40, 40, 50, 50}); auto mock2 = MockLayer::MakeOpacityCompatible(path2); container1->Add(mock2); // ContainerLayer will pass through compatibility from multiple - // non-overlapping compatible children - container1->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + // non-overlapping compatible children if the caller enables it + context->subtree_can_inherit_opacity = true; + container1->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); auto path3 = SkPath().addRect({20, 20, 40, 40}); auto mock3 = MockLayer::MakeOpacityCompatible(path3); @@ -485,28 +482,31 @@ TEST_F(ContainerLayerTest, OpacityInheritance) { // ContainerLayer will not pass through compatibility from multiple // overlapping children even if they are individually compatible - container1->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, 0); + // and the caller requests it + context->subtree_can_inherit_opacity = true; + container1->Preroll(context, SkMatrix::I()); + EXPECT_FALSE(context->subtree_can_inherit_opacity); auto container2 = std::make_shared(); container2->Add(mock1); container2->Add(mock2); // Double check first two children are compatible and non-overlapping - container2->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + // if the caller requests it + context->subtree_can_inherit_opacity = true; + container2->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); auto path4 = SkPath().addRect({60, 60, 70, 70}); auto mock4 = MockLayer::Make(path4); container2->Add(mock4); // The third child is non-overlapping, but not compatible so the - // ContainerLayer should end up incompatible - container2->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, 0); + // TransformLayer should end up incompatible + context->subtree_can_inherit_opacity = true; + container2->Preroll(context, SkMatrix::I()); + EXPECT_FALSE(context->subtree_can_inherit_opacity); } - TEST_F(ContainerLayerTest, CollectionCacheableLayer) { SkPath child_path; child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); @@ -529,15 +529,14 @@ TEST_F(ContainerLayerTest, CollectionCacheableLayer) { layer->Add(mock_cacheable_container_layer1); layer->Add(mock_layer1); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); // raster cache is null, so no entry ASSERT_EQ(preroll_context()->raster_cached_entries->size(), static_cast(0)); use_mock_raster_cache(); // preroll_context()->raster_cache = raster_cache(); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); ASSERT_EQ(preroll_context()->raster_cached_entries->size(), static_cast(2)); } diff --git a/flow/layers/display_list_layer.cc b/flow/layers/display_list_layer.cc index cf2b314d8e359..0d75bd88ff457 100644 --- a/flow/layers/display_list_layer.cc +++ b/flow/layers/display_list_layer.cc @@ -95,13 +95,15 @@ bool DisplayListLayer::Compare(DiffContext::Statistics& statistics, return res; } -void DisplayListLayer::Preroll(PrerollContext* context) { +void DisplayListLayer::Preroll(PrerollContext* context, + const SkMatrix& matrix) { DisplayList* disp_list = display_list(); + SkMatrix child_matrix = matrix; - AutoCache cache = AutoCache(display_list_raster_cache_item_.get(), context, - context->state_stack.transform_3x3()); + AutoCache cache = + AutoCache(display_list_raster_cache_item_.get(), context, child_matrix); if (disp_list->can_apply_group_opacity()) { - context->renderable_state_flags = LayerStateStack::kCallerCanApplyOpacity; + context->subtree_can_inherit_opacity = true; } set_paint_bounds(bounds_); } @@ -110,32 +112,28 @@ void DisplayListLayer::Paint(PaintContext& context) const { FML_DCHECK(display_list_.skia_object()); FML_DCHECK(needs_painting(context)); - auto mutator = context.state_stack.save(); - mutator.translate(offset_.x(), offset_.y()); - + SkAutoCanvasRestore save(context.leaf_nodes_canvas, true); + context.leaf_nodes_canvas->translate(offset_.x(), offset_.y()); if (context.raster_cache) { - // Always apply the integral transform in the presence of a raster cache - // whether or not we successfully draw from the cache - mutator.integralTransform(); - - if (display_list_raster_cache_item_) { - SkPaint paint; - if (display_list_raster_cache_item_->Draw( - context, context.state_stack.fill(paint))) { - TRACE_EVENT_INSTANT0("flutter", "raster cache hit"); - return; - } - } + context.leaf_nodes_canvas->setMatrix(RasterCacheUtil::GetIntegralTransCTM( + context.leaf_nodes_canvas->getTotalMatrix())); } - SkScalar opacity = context.state_stack.outstanding_opacity(); + if (context.raster_cache && display_list_raster_cache_item_) { + AutoCachePaint cache_paint(context); + if (display_list_raster_cache_item_->Draw(context, + cache_paint.sk_paint())) { + TRACE_EVENT_INSTANT0("flutter", "raster cache hit"); + return; + } + } if (context.enable_leaf_layer_tracing) { - const auto canvas_size = context.canvas->getBaseLayerSize(); + const auto canvas_size = context.leaf_nodes_canvas->getBaseLayerSize(); auto offscreen_surface = std::make_unique(context.gr_context, canvas_size); - const auto& ctm = context.canvas->getTotalMatrix(); + const auto& ctm = context.leaf_nodes_canvas->getTotalMatrix(); const auto start_time = fml::TimePoint::Now(); { @@ -144,7 +142,7 @@ void DisplayListLayer::Paint(PaintContext& context) const { SkAutoCanvasRestore save(canvas, true); canvas->clear(SK_ColorTRANSPARENT); canvas->setMatrix(ctm); - display_list()->RenderTo(canvas, opacity); + display_list()->RenderTo(canvas, context.inherited_opacity); canvas->flush(); } const fml::TimeDelta offscreen_render_time = @@ -158,12 +156,18 @@ void DisplayListLayer::Paint(PaintContext& context) const { context.layer_snapshot_store->Add(snapshot_data); } - if (context.builder) { - auto display_list = display_list_.skia_object(); - auto restore = context.state_stack.applyState(display_list->bounds(), 0); - context.builder->drawDisplayList(display_list); + if (context.leaf_nodes_builder) { + AutoCachePaint save_paint(context); + int restore_count = context.leaf_nodes_builder->getSaveCount(); + if (save_paint.dl_paint() != nullptr) { + context.leaf_nodes_builder->saveLayer(&paint_bounds(), + save_paint.dl_paint()); + } + context.leaf_nodes_builder->drawDisplayList(display_list_.skia_object()); + context.leaf_nodes_builder->restoreToCount(restore_count); } else { - display_list()->RenderTo(context.canvas, opacity); + display_list()->RenderTo(context.leaf_nodes_canvas, + context.inherited_opacity); } } diff --git a/flow/layers/display_list_layer.h b/flow/layers/display_list_layer.h index fd981e167a556..c8066d0d43baf 100644 --- a/flow/layers/display_list_layer.h +++ b/flow/layers/display_list_layer.h @@ -36,7 +36,7 @@ class DisplayListLayer : public Layer { return this; } - void Preroll(PrerollContext* frame) override; + void Preroll(PrerollContext* frame, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/display_list_layer_unittests.cc b/flow/layers/display_list_layer_unittests.cc index 300d9d6eaf08a..4db87af8d0451 100644 --- a/flow/layers/display_list_layer_unittests.cc +++ b/flow/layers/display_list_layer_unittests.cc @@ -52,7 +52,7 @@ TEST_F(DisplayListLayerTest, PaintingEmptyLayerDies) { layer_offset, SkiaGPUObject(display_list, unref_queue()), false, false); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); EXPECT_FALSE(layer->needs_painting(paint_context())); @@ -66,7 +66,7 @@ TEST_F(DisplayListLayerTest, InvalidDisplayListDies) { layer_offset, SkiaGPUObject(), false, false); // Crashes reading a nullptr. - EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context()), ""); + EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), ""); } #endif @@ -81,7 +81,7 @@ TEST_F(DisplayListLayerTest, SimpleDisplayList) { auto layer = std::make_shared( layer_offset, SkiaGPUObject(display_list, unref_queue()), false, false); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), picture_bounds.makeOffset(layer_offset.fX, layer_offset.fY)); EXPECT_EQ(layer->display_list(), display_list.get()); @@ -109,22 +109,25 @@ TEST_F(DisplayListLayerTest, SimpleDisplayListOpacityInheritance) { EXPECT_TRUE(display_list->can_apply_group_opacity()); auto context = preroll_context(); - display_list_layer->Preroll(preroll_context()); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + display_list_layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); int opacity_alpha = 0x7F; SkPoint opacity_offset = SkPoint::Make(10, 10); auto opacity_layer = std::make_shared(opacity_alpha, opacity_offset); opacity_layer->Add(display_list_layer); - opacity_layer->Preroll(context); + context->subtree_can_inherit_opacity = false; + opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); DisplayListBuilder child_builder; child_builder.drawRect(picture_bounds); auto child_display_list = child_builder.Build(); + auto save_layer_bounds = + picture_bounds.makeOffset(layer_offset.fX, layer_offset.fY); DisplayListBuilder expected_builder; /* opacity_layer::Paint() */ { expected_builder.save(); @@ -135,7 +138,7 @@ TEST_F(DisplayListLayerTest, SimpleDisplayListOpacityInheritance) { { expected_builder.translate(layer_offset.fX, layer_offset.fY); expected_builder.setColor(opacity_alpha << 24); - expected_builder.saveLayer(&picture_bounds, true); + expected_builder.saveLayer(&save_layer_bounds, true); /* display_list contents */ { // expected_builder.drawDisplayList(child_display_list); } @@ -149,7 +152,7 @@ TEST_F(DisplayListLayerTest, SimpleDisplayListOpacityInheritance) { opacity_layer->Paint(display_list_paint_context()); EXPECT_TRUE( - DisplayListsEQ_Verbose(this->display_list(), expected_builder.Build())); + DisplayListsEQ_Verbose(expected_builder.Build(), this->display_list())); } TEST_F(DisplayListLayerTest, IncompatibleDisplayListOpacityInheritance) { @@ -165,15 +168,17 @@ TEST_F(DisplayListLayerTest, IncompatibleDisplayListOpacityInheritance) { EXPECT_FALSE(display_list->can_apply_group_opacity()); auto context = preroll_context(); - display_list_layer->Preroll(preroll_context()); - EXPECT_EQ(context->renderable_state_flags, 0); + context->subtree_can_inherit_opacity = false; + display_list_layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_FALSE(context->subtree_can_inherit_opacity); int opacity_alpha = 0x7F; SkPoint opacity_offset = SkPoint::Make(10, 10); auto opacity_layer = std::make_shared(opacity_alpha, opacity_offset); opacity_layer->Add(display_list_layer); - opacity_layer->Preroll(context); + context->subtree_can_inherit_opacity = false; + opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_FALSE(opacity_layer->children_can_accept_opacity()); DisplayListBuilder child_builder; @@ -185,6 +190,7 @@ TEST_F(DisplayListLayerTest, IncompatibleDisplayListOpacityInheritance) { display_list_bounds.join(picture2_bounds); auto save_layer_bounds = display_list_bounds.makeOffset(layer_offset.fX, layer_offset.fY); + save_layer_bounds.roundOut(&save_layer_bounds); DisplayListBuilder expected_builder; /* opacity_layer::Paint() */ { expected_builder.save(); @@ -209,7 +215,7 @@ TEST_F(DisplayListLayerTest, IncompatibleDisplayListOpacityInheritance) { opacity_layer->Paint(display_list_paint_context()); EXPECT_TRUE( - DisplayListsEQ_Verbose(this->display_list(), expected_builder.Build())); + DisplayListsEQ_Verbose(expected_builder.Build(), this->display_list())); } TEST_F(DisplayListLayerTest, CachedIncompatibleDisplayListOpacityInheritance) { @@ -227,20 +233,22 @@ TEST_F(DisplayListLayerTest, CachedIncompatibleDisplayListOpacityInheritance) { use_skia_raster_cache(); auto context = preroll_context(); - display_list_layer->Preroll(preroll_context()); - EXPECT_EQ(context->renderable_state_flags, 0); + context->subtree_can_inherit_opacity = false; + display_list_layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_FALSE(context->subtree_can_inherit_opacity); // Pump the DisplayListLayer until it is ready to cache its DL - display_list_layer->Preroll(preroll_context()); - display_list_layer->Preroll(preroll_context()); - display_list_layer->Preroll(preroll_context()); + display_list_layer->Preroll(preroll_context(), SkMatrix()); + display_list_layer->Preroll(preroll_context(), SkMatrix()); + display_list_layer->Preroll(preroll_context(), SkMatrix()); int opacity_alpha = 0x7F; SkPoint opacity_offset = SkPoint::Make(10, 10); auto opacity_layer = std::make_shared(opacity_alpha, opacity_offset); opacity_layer->Add(display_list_layer); - opacity_layer->Preroll(context); + context->subtree_can_inherit_opacity = false; + opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); // The following would be a great test of the painting of the above @@ -379,7 +387,7 @@ TEST_F(DisplayListLayerTest, LayerTreeSnapshotsWhenEnabled) { auto layer = std::make_shared( layer_offset, SkiaGPUObject(display_list, unref_queue()), false, false); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); enable_leaf_layer_tracing(); layer->Paint(paint_context()); @@ -398,7 +406,7 @@ TEST_F(DisplayListLayerTest, NoLayerTreeSnapshotsWhenDisabledByDefault) { auto layer = std::make_shared( layer_offset, SkiaGPUObject(display_list, unref_queue()), false, false); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); layer->Paint(paint_context()); auto& snapshot_store = layer_snapshot_store(); @@ -422,10 +430,10 @@ TEST_F(DisplayListLayerTest, DisplayListAccessCountDependsOnVisibility) { // First Preroll the DisplayListLayer a few times where it does not intersect // the cull rect. No caching progress should occur during this time, the // access_count should remain 0 because the DisplayList was never "visible". - preroll_context()->state_stack.set_initial_cull_rect(missed_cull_rect); + preroll_context()->cull_rect = missed_cull_rect; for (int i = 0; i < 10; i++) { preroll_context()->raster_cached_entries->clear(); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix::I()); ASSERT_EQ(raster_cache_item->cache_state(), RasterCacheItem::kNone); ASSERT_TRUE(raster_cache_item->GetId().has_value()); ASSERT_EQ(preroll_context()->raster_cache->GetAccessCount( @@ -442,9 +450,9 @@ TEST_F(DisplayListLayerTest, DisplayListAccessCountDependsOnVisibility) { // the cull rect. No caching progress should occur during this time // since this is the first frame in which it was visible, but the // count should start incrementing. - preroll_context()->state_stack.set_initial_cull_rect(hit_cull_rect); + preroll_context()->cull_rect = hit_cull_rect; preroll_context()->raster_cached_entries->clear(); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); ASSERT_EQ(raster_cache_item->cache_state(), RasterCacheItem::kNone); ASSERT_TRUE(raster_cache_item->GetId().has_value()); ASSERT_EQ(preroll_context()->raster_cache->GetAccessCount( @@ -460,10 +468,10 @@ TEST_F(DisplayListLayerTest, DisplayListAccessCountDependsOnVisibility) { // it does not intersect and it should continue to count these operations // even though it is not visible. No actual caching should occur yet, // even though we will surpass its threshold. - preroll_context()->state_stack.set_initial_cull_rect(missed_cull_rect); + preroll_context()->cull_rect = missed_cull_rect; for (int i = 0; i < 10; i++) { preroll_context()->raster_cached_entries->clear(); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); ASSERT_EQ(raster_cache_item->cache_state(), RasterCacheItem::kNone); ASSERT_TRUE(raster_cache_item->GetId().has_value()); ASSERT_EQ(preroll_context()->raster_cache->GetAccessCount( @@ -480,9 +488,9 @@ TEST_F(DisplayListLayerTest, DisplayListAccessCountDependsOnVisibility) { // the cull rect. Since we should have exhausted our access count // threshold in the loop above, these operations should result in the // DisplayList being cached. - preroll_context()->state_stack.set_initial_cull_rect(hit_cull_rect); + preroll_context()->cull_rect = hit_cull_rect; preroll_context()->raster_cached_entries->clear(); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); ASSERT_EQ(raster_cache_item->cache_state(), RasterCacheItem::kCurrent); ASSERT_TRUE(raster_cache_item->GetId().has_value()); ASSERT_EQ(preroll_context()->raster_cache->GetAccessCount( diff --git a/flow/layers/display_list_raster_cache_item.cc b/flow/layers/display_list_raster_cache_item.cc index cb774e4498dd8..10a64cff3a45e 100644 --- a/flow/layers/display_list_raster_cache_item.cc +++ b/flow/layers/display_list_raster_cache_item.cc @@ -108,12 +108,12 @@ void DisplayListRasterCacheItem::PrerollFinalize(PrerollContext* context, // if the rect is intersect we will get the entry access_count to confirm if // it great than the threshold. Otherwise we only increase the entry // access_count. - bool visible = !context->state_stack.content_culled(bounds); + bool visible = context->cull_rect.intersect(bounds); int accesses = raster_cache->MarkSeen(key_id_, matrix, visible); if (!visible || accesses <= raster_cache->access_threshold()) { cache_state_ = kNone; } else { - context->renderable_state_flags |= LayerStateStack::kCallerCanApplyOpacity; + context->subtree_can_inherit_opacity = true; cache_state_ = kCurrent; } return; @@ -121,7 +121,7 @@ void DisplayListRasterCacheItem::PrerollFinalize(PrerollContext* context, bool DisplayListRasterCacheItem::Draw(const PaintContext& context, const SkPaint* paint) const { - return Draw(context, context.canvas, paint); + return Draw(context, context.leaf_nodes_canvas, paint); } bool DisplayListRasterCacheItem::Draw(const PaintContext& context, diff --git a/flow/layers/image_filter_layer.cc b/flow/layers/image_filter_layer.cc index 79a3c4c27329f..b08baad68f85e 100644 --- a/flow/layers/image_filter_layer.cc +++ b/flow/layers/image_filter_layer.cc @@ -46,29 +46,28 @@ void ImageFilterLayer::Diff(DiffContext* context, const Layer* old_layer) { context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); } -void ImageFilterLayer::Preroll(PrerollContext* context) { +void ImageFilterLayer::Preroll(PrerollContext* context, + const SkMatrix& matrix) { Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context); + SkMatrix child_matrix = matrix; - AutoCache cache = AutoCache(layer_raster_cache_item_.get(), context, - context->state_stack.transform_3x3()); + AutoCache cache = + AutoCache(layer_raster_cache_item_.get(), context, child_matrix); SkRect child_bounds = SkRect::MakeEmpty(); - PrerollChildren(context, &child_bounds); + PrerollChildren(context, child_matrix, &child_bounds); + + // We always paint with a saveLayer (or a cached rendering), + // so we can always apply opacity in any of those cases. + context->subtree_can_inherit_opacity = true; if (!filter_) { set_paint_bounds(child_bounds); return; } - // Our saveLayer would apply any outstanding opacity or any outstanding - // color filter after it applies our image filter. So we can apply either - // of those attributes with our saveLayer. - context->renderable_state_flags = - (LayerStateStack::kCallerCanApplyOpacity | - LayerStateStack::kCallerCanApplyColorFilter); - const SkIRect filter_in_bounds = child_bounds.roundOut(); SkIRect filter_out_bounds; filter_->map_device_bounds(filter_in_bounds, SkMatrix::I(), @@ -81,8 +80,7 @@ void ImageFilterLayer::Preroll(PrerollContext* context) { // So in here we reset the LayerRasterCacheItem cache state. layer_raster_cache_item_->MarkNotCacheChildren(); - transformed_filter_ = - filter_->makeWithLocalMatrix(context->state_stack.transform_3x3()); + transformed_filter_ = filter_->makeWithLocalMatrix(child_matrix); if (transformed_filter_) { layer_raster_cache_item_->MarkCacheChildren(); } @@ -91,41 +89,35 @@ void ImageFilterLayer::Preroll(PrerollContext* context) { void ImageFilterLayer::Paint(PaintContext& context) const { FML_DCHECK(needs_painting(context)); - auto mutator = context.state_stack.save(); - + AutoCachePaint cache_paint(context); if (context.raster_cache) { - // Always apply the integral transform in the presence of a raster cache - // whether or not we will draw from the cache - mutator.integralTransform(); - - // Try drawing the layer cache item from the cache before applying the - // image filter if it was cached with the filter applied. - if (!layer_raster_cache_item_->IsCacheChildren()) { - SkPaint sk_paint; - if (layer_raster_cache_item_->Draw(context, - context.state_stack.fill(sk_paint))) { - return; - } + context.internal_nodes_canvas->setMatrix( + RasterCacheUtil::GetIntegralTransCTM( + context.leaf_nodes_canvas->getTotalMatrix())); + if (layer_raster_cache_item_->IsCacheChildren()) { + cache_paint.setImageFilter(transformed_filter_.get()); } - } - - if (context.raster_cache && layer_raster_cache_item_->IsCacheChildren()) { - // If we render the children from cache then we need the special - // transformed version of the filter so we must process it into the - // cache paint object manually. - FML_DCHECK(transformed_filter_ != nullptr); - SkPaint sk_paint; - context.state_stack.fill(sk_paint); - sk_paint.setImageFilter(transformed_filter_->skia_object()); - if (layer_raster_cache_item_->Draw(context, &sk_paint)) { + if (layer_raster_cache_item_->Draw(context, cache_paint.sk_paint())) { return; } } - // Now apply the image filter and then try rendering the children. - mutator.applyImageFilter(child_paint_bounds(), filter_); - - PaintChildren(context); + cache_paint.setImageFilter(filter_.get()); + if (context.leaf_nodes_builder) { + FML_DCHECK(context.builder_multiplexer); + context.builder_multiplexer->saveLayer(&child_paint_bounds(), + cache_paint.dl_paint()); + PaintChildren(context); + context.builder_multiplexer->restore(); + } else { + // Normally a save_layer is sized to the current layer bounds, but in this + // case the bounds of the child may not be the same as the filtered version + // so we use the bounds of the child container which do not include any + // modifications that the filter might apply. + Layer::AutoSaveLayer save_layer = Layer::AutoSaveLayer::Create( + context, child_paint_bounds(), cache_paint.sk_paint()); + PaintChildren(context); + } } } // namespace flutter diff --git a/flow/layers/image_filter_layer.h b/flow/layers/image_filter_layer.h index 894b5d2e98187..e4987bd9fb75b 100644 --- a/flow/layers/image_filter_layer.h +++ b/flow/layers/image_filter_layer.h @@ -19,7 +19,7 @@ class ImageFilterLayer : public CacheableContainerLayer { void Diff(DiffContext* context, const Layer* old_layer) override; - void Preroll(PrerollContext* context) override; + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/image_filter_layer_unittests.cc b/flow/layers/image_filter_layer_unittests.cc index 82e5fd93b70b7..dc855affc15fe 100644 --- a/flow/layers/image_filter_layer_unittests.cc +++ b/flow/layers/image_filter_layer_unittests.cc @@ -26,7 +26,7 @@ using ImageFilterLayerTest = LayerTest; TEST_F(ImageFilterLayerTest, PaintingEmptyLayerDies) { auto layer = std::make_shared(nullptr); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); EXPECT_FALSE(layer->needs_painting(paint_context())); @@ -57,8 +57,7 @@ TEST_F(ImageFilterLayerTest, EmptyFilter) { auto layer = std::make_shared(nullptr); layer->Add(mock_layer); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); @@ -70,7 +69,11 @@ TEST_F(ImageFilterLayerTest, EmptyFilter) { EXPECT_EQ(mock_canvas().draw_calls(), std::vector({ MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{child_path, child_paint}}, + 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, + nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}, })); } @@ -88,8 +91,7 @@ TEST_F(ImageFilterLayerTest, SimpleFilter) { const SkRect child_rounded_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 21.0f, 22.0f); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_rounded_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); @@ -129,8 +131,7 @@ TEST_F(ImageFilterLayerTest, SimpleFilterBounds) { const SkRect filter_bounds = SkRect::MakeLTRB(10.0f, 12.0f, 42.0f, 44.0f); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), filter_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); @@ -175,8 +176,7 @@ TEST_F(ImageFilterLayerTest, MultipleChildren) { children_bounds.join(child_path2.getBounds()); SkRect children_rounded_bounds = SkRect::Make(children_bounds.roundOut()); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer->paint_bounds(), children_rounded_bounds); @@ -237,8 +237,7 @@ TEST_F(ImageFilterLayerTest, Nested) { const SkRect mock_layer2_rounded_bounds = SkRect::Make(child_path2.getBounds().roundOut()); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer1->Preroll(preroll_context()); + layer1->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer1->paint_bounds(), children_rounded_bounds); @@ -285,10 +284,12 @@ TEST_F(ImageFilterLayerTest, Readback) { auto dl_image_filter = std::make_shared( SkMatrix(), DlImageSampling::kLinear); + auto initial_transform = SkMatrix(); + // ImageFilterLayer does not read from surface auto layer = std::make_shared(dl_image_filter); preroll_context()->surface_needs_readback = false; - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_FALSE(preroll_context()->surface_needs_readback); // ImageFilterLayer blocks child with readback @@ -296,7 +297,7 @@ TEST_F(ImageFilterLayerTest, Readback) { mock_layer->set_fake_reads_surface(true); layer->Add(mock_layer); preroll_context()->surface_needs_readback = false; - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_FALSE(preroll_context()->surface_needs_readback); } @@ -326,8 +327,7 @@ TEST_F(ImageFilterLayerTest, CacheChild) { RasterCacheItem::CacheState::kNone); EXPECT_FALSE(cacheable_image_filter_item->Draw(paint_context(), &paint)); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); @@ -371,8 +371,7 @@ TEST_F(ImageFilterLayerTest, CacheChildren) { RasterCacheItem::CacheState::kNone); EXPECT_FALSE(cacheable_image_filter_item->Draw(paint_context(), &paint)); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); @@ -406,16 +405,15 @@ TEST_F(ImageFilterLayerTest, CacheImageFilterLayerSelf) { SkPaint paint = SkPaint(); use_mock_raster_cache(); - preroll_context()->state_stack.set_initial_transform(initial_transform); const auto* cacheable_image_filter_item = layer->raster_cache_item(); // frame 1. - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); layer->Paint(paint_context()); // frame 2. - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); layer->Paint(paint_context()); // frame 3. - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); layer->Paint(paint_context()); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); @@ -448,20 +446,18 @@ TEST_F(ImageFilterLayerTest, OpacityInheritance) { image_filter_layer->Add(mock_layer); PrerollContext* context = preroll_context(); - context->state_stack.set_initial_transform(initial_transform); - image_filter_layer->Preroll(preroll_context()); + context->subtree_can_inherit_opacity = false; + image_filter_layer->Preroll(preroll_context(), initial_transform); // ImageFilterLayers can always inherit opacity whether or not their // children are compatible. - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity | - LayerStateStack::kCallerCanApplyColorFilter); + EXPECT_TRUE(context->subtree_can_inherit_opacity); int opacity_alpha = 0x7F; SkPoint offset = SkPoint::Make(10, 10); auto opacity_layer = std::make_shared(opacity_alpha, offset); opacity_layer->Add(image_filter_layer); - context->state_stack.set_initial_transform(SkMatrix::I()); - opacity_layer->Preroll(context); + context->subtree_can_inherit_opacity = false; + opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); DisplayListBuilder expected_builder; diff --git a/flow/layers/layer.cc b/flow/layers/layer.cc index 2e8beec7e6289..e96033776ff03 100644 --- a/flow/layers/layer.cc +++ b/flow/layers/layer.cc @@ -26,6 +26,8 @@ uint64_t Layer::NextUniqueID() { return id; } +void Layer::Preroll(PrerollContext* context, const SkMatrix& matrix) {} + Layer::AutoPrerollSaveLayerState::AutoPrerollSaveLayerState( PrerollContext* preroll_context, bool save_layer_is_active, @@ -54,4 +56,51 @@ Layer::AutoPrerollSaveLayerState::~AutoPrerollSaveLayerState() { } } +Layer::AutoSaveLayer::AutoSaveLayer(const PaintContext& paint_context, + const SkRect& bounds, + const SkPaint* paint, + SaveMode save_mode) + : paint_context_(paint_context), + bounds_(bounds), + canvas_(save_mode == SaveMode::kInternalNodesCanvas + ? *(paint_context.internal_nodes_canvas) + : *(paint_context.leaf_nodes_canvas)) { + TRACE_EVENT0("flutter", "Canvas::saveLayer"); + canvas_.saveLayer(bounds_, paint); +} + +Layer::AutoSaveLayer::AutoSaveLayer(const PaintContext& paint_context, + const SkCanvas::SaveLayerRec& layer_rec, + SaveMode save_mode) + : paint_context_(paint_context), + bounds_(*layer_rec.fBounds), + canvas_(save_mode == SaveMode::kInternalNodesCanvas + ? *(paint_context.internal_nodes_canvas) + : *(paint_context.leaf_nodes_canvas)) { + TRACE_EVENT0("flutter", "Canvas::saveLayer"); + canvas_.saveLayer(layer_rec); +} + +Layer::AutoSaveLayer Layer::AutoSaveLayer::Create( + const PaintContext& paint_context, + const SkRect& bounds, + const SkPaint* paint, + SaveMode save_mode) { + return Layer::AutoSaveLayer(paint_context, bounds, paint, save_mode); +} + +Layer::AutoSaveLayer Layer::AutoSaveLayer::Create( + const PaintContext& paint_context, + const SkCanvas::SaveLayerRec& layer_rec, + SaveMode save_mode) { + return Layer::AutoSaveLayer(paint_context, layer_rec, save_mode); +} + +Layer::AutoSaveLayer::~AutoSaveLayer() { + if (paint_context_.checkerboard_offscreen_layers) { + DrawCheckerboard(&canvas_, bounds_); + } + canvas_.restore(); +} + } // namespace flutter diff --git a/flow/layers/layer.h b/flow/layers/layer.h index ca568a3916e37..09f47b5de4d50 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -16,7 +16,6 @@ #include "flutter/flow/embedded_views.h" #include "flutter/flow/instrumentation.h" #include "flutter/flow/layer_snapshot_store.h" -#include "flutter/flow/layers/layer_state_stack.h" #include "flutter/flow/raster_cache.h" #include "flutter/fml/build_config.h" #include "flutter/fml/compiler_specific.h" @@ -53,14 +52,16 @@ struct PrerollContext { RasterCache* raster_cache; GrDirectContext* gr_context; ExternalViewEmbedder* view_embedder; - LayerStateStack& state_stack; + MutatorsStack& mutators_stack; SkColorSpace* dst_color_space; + SkRect cull_rect; bool surface_needs_readback; // These allow us to paint in the end of subtree Preroll. const Stopwatch& raster_time; const Stopwatch& ui_time; std::shared_ptr texture_registry; + const bool checkerboard_offscreen_layers; const float frame_device_pixel_ratio = 1.0f; // These allow us to track properties like elevation, opacity, and the @@ -70,15 +71,42 @@ struct PrerollContext { // presence of a texture layer during Preroll. bool has_texture_layer = false; - // The list of flags that describe which rendering state attributes - // (such as opacity, ColorFilter, ImageFilter) a given layer can - // render itself without requiring the parent to perform a protective - // saveLayer with those attributes. - // For containers, the flags will be set to the intersection (logical - // and) of all of the state bits that all of the children can render - // or to 0 if some of the children overlap and, as such, cannot apply - // those attributes individually and separately. - int renderable_state_flags = 0; + // This field indicates whether the subtree rooted at this layer can + // inherit an opacity value and modulate its visibility accordingly. + // + // Any layer is free to ignore this flag. Its value will be false upon + // entry into its Preroll method, it will remain false if it calls + // PrerollChildren on any children it might have, and it will remain + // false upon exit from the Preroll method unless it takes specific + // action compute if it should be true. Thus, this property is "opt-in". + // + // If the value is false when the Preroll method exits, then the + // |PaintContext::inherited_opacity| value should always be set to + // 1.0 when its |Paint| method is called. + // + // Leaf layers need only be concerned with their own rendering and + // can set the value according to whether they can apply the opacity. + // + // For containers, there are 3 ways to interact with this field: + // + // 1. If you need to know whether your children are compatible, then + // set the field to true before you call PrerollChildren. That + // method will then reset the field to false if it detects any + // incompatible children. + // + // 2. If your decision on whether to inherit the opacity depends on + // the answer from the children, then remember the value of the + // field when PrerollChildren returns. (eg. OpacityLayer remembers + // this value to control whether to set the opacity value into the + // |PaintContext::inherited_opacity| field in |Paint| before + // recursing to its children in Paint) + // + // 3. If you want to indicate to your parents that you can accept + // inherited opacity regardless of whether your children were + // compatible then set this field to true before returning + // from your Preroll method. (eg. layers that always apply a + // saveLayer when rendering anyway can apply the opacity there) + bool subtree_can_inherit_opacity = false; std::vector* raster_cached_entries; @@ -91,21 +119,17 @@ struct PrerollContext { struct PaintContext { // When splitting the scene into multiple canvases (e.g when embedding - // a platform view on iOS) during the paint traversal we apply any state - // changes which affect children (i.e. saveLayer attributes) to the - // state_stack and any local rendering state changes for leaf layers to - // the canvas or builder. - // When we switch a canvas or builder (when painting a PlatformViewLayer) - // the new canvas receives all of the stateful changes from the state_stack - // to put it into the exact same state that the outgoing canvas had at the - // time it was swapped out. - // The state stack lazily applies saveLayer calls to its current canvas, - // allowing leaf layers to report that they can handle rendering some of - // its state attributes themselves via the |applyState| method. - LayerStateStack& state_stack; - SkCanvas* canvas; - DisplayListBuilder* builder = nullptr; - + // a platform view on iOS) during the paint traversal we apply the non leaf + // flow layers to all canvases, and leaf layers just to the "current" + // canvas. Applying the non leaf layers to all canvases ensures that when + // we switch a canvas (when painting a PlatformViewLayer) the next canvas + // has the exact same state as the current canvas. + // The internal_nodes_canvas is a SkNWayCanvas which is used by non leaf + // and applies the operations to all canvases. + // The leaf_nodes_canvas is the "current" canvas and is used by leaf + // layers. + SkCanvas* internal_nodes_canvas; + SkCanvas* leaf_nodes_canvas; GrDirectContext* gr_context; SkColorSpace* dst_color_space; ExternalViewEmbedder* view_embedder; @@ -113,32 +137,29 @@ struct PaintContext { const Stopwatch& ui_time; std::shared_ptr texture_registry; const RasterCache* raster_cache; + const bool checkerboard_offscreen_layers; const float frame_device_pixel_ratio = 1.0f; // Snapshot store to collect leaf layer snapshots. The store is non-null // only when leaf layer tracing is enabled. LayerSnapshotStore* layer_snapshot_store = nullptr; bool enable_leaf_layer_tracing = false; + + // The following value should be used to modulate the opacity of the + // layer during |Paint|. If the layer does not set the corresponding + // |layer_can_inherit_opacity()| flag, then this value should always + // be |SK_Scalar1|. The value is to be applied as if by using a + // |saveLayer| with an |SkPaint| initialized to this alphaf value and + // a |kSrcOver| blend mode. + SkScalar inherited_opacity = SK_Scalar1; + DisplayListBuilder* leaf_nodes_builder = nullptr; + DisplayListBuilderMultiplexer* builder_multiplexer = nullptr; }; // Represents a single composited layer. Created on the UI thread but then // subsequently used on the Rasterizer thread. class Layer { public: - // The state attribute flags that represent which attributes a - // layer can render if it plans to use a saveLayer call in its - // |Paint| method. - static constexpr int kSaveLayerRenderFlags = - LayerStateStack::kCallerCanApplyOpacity | - LayerStateStack::kCallerCanApplyColorFilter | - LayerStateStack::kCallerCanApplyImageFilter; - - // The state attribute flags that represent which attributes a - // layer can render if it will be rendering its content/children - // from a cached representation. - static constexpr int kRasterCacheRenderFlags = - LayerStateStack::kCallerCanApplyOpacity; - Layer(); virtual ~Layer(); @@ -165,7 +186,7 @@ class Layer { context->SetLayerPaintRegion(this, context->GetOldLayerPaintRegion(this)); } - virtual void Preroll(PrerollContext* context) = 0; + virtual void Preroll(PrerollContext* context, const SkMatrix& matrix); // Used during Preroll by layers that employ a saveLayer to manage the // PrerollContext settings with values affected by the saveLayer mechanism. @@ -193,6 +214,114 @@ class Layer { bool prev_surface_needs_readback_; }; + class AutoCachePaint { + public: + explicit AutoCachePaint(PaintContext& context) : context_(context) { + needs_paint_ = context.inherited_opacity < SK_Scalar1; + if (needs_paint_) { + sk_paint_.setAlphaf(context.inherited_opacity); + dl_paint_.setAlpha(SkScalarRoundToInt(context.inherited_opacity * 255)); + context.inherited_opacity = SK_Scalar1; + } + } + + ~AutoCachePaint() { context_.inherited_opacity = sk_paint_.getAlphaf(); } + + void setImageFilter(const DlImageFilter* filter) { + sk_paint_.setImageFilter(!filter ? nullptr : filter->skia_object()); + dl_paint_.setImageFilter(filter); + update_needs_paint(); + } + + void setColorFilter(const DlColorFilter* filter) { + sk_paint_.setColorFilter(!filter ? nullptr : filter->skia_object()); + dl_paint_.setColorFilter(filter); + update_needs_paint(); + } + + void setBlendMode(DlBlendMode mode) { + sk_paint_.setBlendMode(ToSk(mode)); + dl_paint_.setBlendMode(mode); + update_needs_paint(); + } + + const SkPaint* sk_paint() { return needs_paint_ ? &sk_paint_ : nullptr; } + const DlPaint* dl_paint() { return needs_paint_ ? &dl_paint_ : nullptr; } + + private: + PaintContext& context_; + SkPaint sk_paint_; + DlPaint dl_paint_; + bool needs_paint_; + + void update_needs_paint() { + needs_paint_ = sk_paint_.getImageFilter() != nullptr || + sk_paint_.getColorFilter() != nullptr || + !sk_paint_.isSrcOver() || + sk_paint_.getAlphaf() < SK_Scalar1; + } + }; + + // Calls SkCanvas::saveLayer and restores the layer upon destruction. Also + // draws a checkerboard over the layer if that is enabled in the PaintContext. + class AutoSaveLayer { + public: + // Indicates which canvas the layer should be saved on. + // + // Usually layers are saved on the internal_nodes_canvas, so that all + // the canvas keep track of the current state of the layer tree. + // In some special cases, layers should only save on the leaf_nodes_canvas, + // See https:://flutter.dev/go/backdrop-filter-with-overlay-canvas for why + // it is the case for Backdrop filter layer. + enum SaveMode { + // The layer is saved on the internal_nodes_canvas. + kInternalNodesCanvas, + // The layer is saved on the leaf_nodes_canvas. + kLeafNodesCanvas + }; + + // Create a layer and save it on the canvas. + // + // The layer is restored from the canvas in destructor. + // + // By default, the layer is saved on and restored from + // `internal_nodes_canvas`. The `save_mode` parameter can be modified to + // save the layer on other canvases. + [[nodiscard]] static AutoSaveLayer Create( + const PaintContext& paint_context, + const SkRect& bounds, + const SkPaint* paint, + SaveMode save_mode = SaveMode::kInternalNodesCanvas); + // Create a layer and save it on the canvas. + // + // The layer is restored from the canvas in destructor. + // + // By default, the layer is saved on and restored from + // `internal_nodes_canvas`. The `save_mode` parameter can be modified to + // save the layer on other canvases. + [[nodiscard]] static AutoSaveLayer Create( + const PaintContext& paint_context, + const SkCanvas::SaveLayerRec& layer_rec, + SaveMode save_mode = SaveMode::kInternalNodesCanvas); + + ~AutoSaveLayer(); + + private: + AutoSaveLayer(const PaintContext& paint_context, + const SkRect& bounds, + const SkPaint* paint, + SaveMode save_mode = SaveMode::kInternalNodesCanvas); + + AutoSaveLayer(const PaintContext& paint_context, + const SkCanvas::SaveLayerRec& layer_rec, + SaveMode save_mode = SaveMode::kInternalNodesCanvas); + + const PaintContext& paint_context_; + const SkRect bounds_; + // The canvas that this layer is saved on and popped from. + SkCanvas& canvas_; + }; + virtual void Paint(PaintContext& context) const = 0; virtual void PaintChildren(PaintContext& context) const { FML_DCHECK(false); } @@ -239,8 +368,15 @@ class Layer { // See https://github.com/flutter/flutter/issues/81419 return true; } - return !context.state_stack.painting_is_nop() && - !context.state_stack.content_culled(paint_bounds_); + if (context.inherited_opacity == 0) { + return false; + } + // Workaround for Skia bug (quickReject does not reject empty bounds). + // https://bugs.chromium.org/p/skia/issues/detail?id=10951 + if (paint_bounds_.isEmpty()) { + return false; + } + return !context.leaf_nodes_canvas->quickReject(paint_bounds_); } // Propagated unique_id of the first layer in "chain" of replacement layers diff --git a/flow/layers/layer_raster_cache_item.cc b/flow/layers/layer_raster_cache_item.cc index 9794fad46a970..63c2ef70be774 100644 --- a/flow/layers/layer_raster_cache_item.cc +++ b/flow/layers/layer_raster_cache_item.cc @@ -47,7 +47,7 @@ void LayerRasterCacheItem::PrerollFinalize(PrerollContext* context, // alive, but if the following conditions apply then we need to set our // state back to kDoNotCache so that we don't populate the entry later. if (context->has_platform_view || context->has_texture_layer || - context->state_stack.content_culled(layer_->paint_bounds())) { + !SkRect::Intersects(context->cull_rect, layer_->paint_bounds())) { return; } child_items_ = context->raster_cached_entries->size() - child_items_; @@ -103,24 +103,23 @@ bool Rasterize(RasterCacheItem::CacheState cache_state, const PaintContext& paint_context, SkCanvas* canvas) { FML_DCHECK(cache_state != RasterCacheItem::CacheState::kNone); - LayerStateStack state_stack; - state_stack.set_delegate(canvas); - state_stack.set_checkerboard_func( - paint_context.state_stack.checkerboard_func()); - auto mutator = state_stack.save(); - mutator.transform(canvas->getTotalMatrix()); + SkISize canvas_size = canvas->getBaseLayerSize(); + SkNWayCanvas internal_nodes_canvas(canvas_size.width(), canvas_size.height()); + internal_nodes_canvas.setMatrix(canvas->getTotalMatrix()); + internal_nodes_canvas.addCanvas(canvas); PaintContext context = { // clang-format off - .state_stack = state_stack, - .canvas = canvas, - .gr_context = paint_context.gr_context, - .dst_color_space = paint_context.dst_color_space, - .view_embedder = paint_context.view_embedder, - .raster_time = paint_context.raster_time, - .ui_time = paint_context.ui_time, - .texture_registry = paint_context.texture_registry, - .raster_cache = paint_context.raster_cache, - .frame_device_pixel_ratio = paint_context.frame_device_pixel_ratio, + .internal_nodes_canvas = static_cast(&internal_nodes_canvas), + .leaf_nodes_canvas = canvas, + .gr_context = paint_context.gr_context, + .dst_color_space = paint_context.dst_color_space, + .view_embedder = paint_context.view_embedder, + .raster_time = paint_context.raster_time, + .ui_time = paint_context.ui_time, + .texture_registry = paint_context.texture_registry, + .raster_cache = paint_context.raster_cache, + .checkerboard_offscreen_layers = paint_context.checkerboard_offscreen_layers, + .frame_device_pixel_ratio = paint_context.frame_device_pixel_ratio, // clang-format on }; @@ -170,7 +169,7 @@ bool LayerRasterCacheItem::TryToPrepareRasterCache(const PaintContext& context, bool LayerRasterCacheItem::Draw(const PaintContext& context, const SkPaint* paint) const { - return Draw(context, context.canvas, paint); + return Draw(context, context.leaf_nodes_canvas, paint); } bool LayerRasterCacheItem::Draw(const PaintContext& context, diff --git a/flow/layers/layer_state_stack.cc b/flow/layers/layer_state_stack.cc deleted file mode 100644 index 9ea44644590dc..0000000000000 --- a/flow/layers/layer_state_stack.cc +++ /dev/null @@ -1,796 +0,0 @@ -// 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/flow/layers/layer_state_stack.h" -#include "flutter/flow/layers/layer.h" -#include "flutter/flow/paint_utils.h" -#include "flutter/flow/raster_cache_util.h" - -namespace flutter { - -using AutoRestore = LayerStateStack::AutoRestore; -using MutatorContext = LayerStateStack::MutatorContext; - -static inline bool has_perspective(const SkM44& matrix) { - return (matrix.rc(3, 0) != 0 || // - matrix.rc(3, 1) != 0 || // - matrix.rc(3, 2) != 0 || // - matrix.rc(3, 3) != 1); -} - -LayerStateStack::LayerStateStack(const SkRect* cull_rect) { - if (cull_rect) { - initial_cull_rect_ = cull_rect_ = *cull_rect; - } else { - initial_cull_rect_ = cull_rect_ = kGiantRect; - } -} - -void LayerStateStack::clear_delegate() { - if (canvas_) { - canvas_->restoreToCount(restore_count_); - canvas_ = nullptr; - } - if (builder_) { - builder_->restoreToCount(restore_count_); - builder_ = nullptr; - } - if (mutators_) { - mutators_->PopTo(restore_count_); - mutators_ = nullptr; - } -} - -void LayerStateStack::set_delegate(SkCanvas* canvas) { - if (canvas == canvas_) { - return; - } - clear_delegate(); - if (canvas) { - restore_count_ = canvas->getSaveCount(); - canvas_ = canvas; - reapply_all(); - } -} - -void LayerStateStack::set_delegate(DisplayListBuilder* builder) { - if (builder == builder_) { - return; - } - clear_delegate(); - if (builder) { - restore_count_ = builder->getSaveCount(); - builder_ = builder; - reapply_all(); - } -} - -void LayerStateStack::set_delegate(MutatorsStack* stack) { - if (stack == mutators_) { - return; - } - clear_delegate(); - if (stack) { - restore_count_ = stack->stack_count(); - mutators_ = stack; - reapply_all(); - } -} - -void LayerStateStack::set_initial_cull_rect(const SkRect& cull_rect) { - FML_DCHECK(is_empty()) << "set_initial_cull_rect() must be called before any " - "state is pushed onto the state stack"; - initial_cull_rect_ = cull_rect_ = cull_rect; -} - -void LayerStateStack::set_initial_transform(const SkMatrix& matrix) { - FML_DCHECK(is_empty()) << "set_initial_transform() must be called before any " - "state is pushed onto the state stack"; - initial_matrix_ = matrix_ = SkM44(matrix); -} - -void LayerStateStack::set_initial_transform(const SkM44& matrix) { - FML_DCHECK(is_empty()) << "set_initial_transform() must be called before any " - "state is pushed onto the state stack"; - initial_matrix_ = matrix_ = matrix; -} - -void LayerStateStack::set_initial_state(const SkRect& cull_rect, - const SkMatrix& matrix) { - FML_DCHECK(is_empty()) << "set_initial_state() must be called before any " - "state is pushed onto the state stack"; - initial_cull_rect_ = cull_rect_ = cull_rect; - initial_matrix_ = matrix_ = SkM44(matrix); -} - -void LayerStateStack::set_initial_state(const SkRect& cull_rect, - const SkM44& matrix) { - FML_DCHECK(is_empty()) << "set_initial_state() must be called before any " - "state is pushed onto the state stack"; - initial_cull_rect_ = cull_rect_ = cull_rect; - initial_matrix_ = matrix_ = matrix; -} - -void LayerStateStack::reapply_all() { - // We use a local RenderingAttributes instance so that it can track the - // necessary state changes independently as they occur in the stack. - // Reusing |outstanding_| would wreak havoc on the current state of - // the stack. When we are finished, though, the local attributes - // contents should match the current outstanding_ values; - RenderingAttributes attributes = outstanding_; - SkM44 matrix = matrix_; - SkRect cull_rect = cull_rect_; - outstanding_ = {}; - matrix_ = initial_matrix_; - cull_rect_ = initial_cull_rect_; - for (auto& state : state_stack_) { - state->reapply(this); - } - FML_DCHECK(attributes == outstanding_); - FML_DCHECK(matrix == matrix_); - FML_DCHECK(cull_rect == cull_rect_); -} - -AutoRestore::AutoRestore(LayerStateStack* stack) - : layer_state_stack_(stack), stack_restore_count_(stack->stack_count()) {} - -AutoRestore::~AutoRestore() { - layer_state_stack_->restore_to_count(stack_restore_count_); -} - -AutoRestore LayerStateStack::applyState(const SkRect& bounds, - int can_apply_flags) { - auto ret = AutoRestore(this); - if (needs_save_layer(can_apply_flags)) { - save_layer(bounds); - } - return ret; -} - -SkPaint* LayerStateStack::RenderingAttributes::fill(SkPaint& paint, - DlBlendMode mode) const { - SkPaint* ret = nullptr; - if (opacity < SK_Scalar1) { - paint.setAlphaf(std::max(opacity, 0.0f)); - ret = &paint; - } else { - paint.setAlphaf(SK_Scalar1); - } - if (color_filter) { - paint.setColorFilter(color_filter->skia_object()); - ret = &paint; - } else { - paint.setColorFilter(nullptr); - } - if (image_filter) { - paint.setImageFilter(image_filter->skia_object()); - ret = &paint; - } else { - paint.setImageFilter(nullptr); - } - paint.setBlendMode(ToSk(mode)); - if (mode != DlBlendMode::kSrcOver) { - ret = &paint; - } - return ret; -} - -DlPaint* LayerStateStack::RenderingAttributes::fill(DlPaint& paint, - DlBlendMode mode) const { - DlPaint* ret = nullptr; - if (opacity < SK_Scalar1) { - paint.setOpacity(std::max(opacity, 0.0f)); - ret = &paint; - } else { - paint.setOpacity(SK_Scalar1); - } - paint.setColorFilter(color_filter); - if (color_filter) { - ret = &paint; - } - paint.setImageFilter(image_filter); - if (image_filter) { - ret = &paint; - } - paint.setBlendMode(mode); - if (mode != DlBlendMode::kSrcOver) { - ret = &paint; - } - return ret; -} - -SkRect LayerStateStack::local_cull_rect() const { - SkM44 inverse; - if (cull_rect_.isEmpty() || !matrix_.invert(&inverse)) { - // Either rendering is clipped out or transformed into emptiness - return SkRect::MakeEmpty(); - } - if (has_perspective(inverse)) { - // We could do a 4-point long-form conversion, but since this is - // only used for culling, let's just return a non-constricting - // cull rect. - return kGiantRect; - } - return inverse.asM33().mapRect(cull_rect_); -} - -bool LayerStateStack::content_culled(const SkRect& content_bounds) const { - if (cull_rect_.isEmpty() || content_bounds.isEmpty()) { - return true; - } - if (has_perspective(matrix_)) { - return false; - } - return !matrix_.asM33().mapRect(content_bounds).intersects(cull_rect_); -} - -MutatorContext LayerStateStack::save() { - auto ret = MutatorContext(this); - state_stack_.emplace_back(std::make_unique()); - state_stack_.back()->apply(this); - return ret; -} - -void MutatorContext::saveLayer(const SkRect& bounds) { - layer_state_stack_->save_layer(bounds); -} - -void MutatorContext::applyOpacity(const SkRect& bounds, SkScalar opacity) { - if (opacity < SK_Scalar1) { - layer_state_stack_->push_attributes(); - layer_state_stack_->maybe_save_layer(opacity); - layer_state_stack_->push_opacity(bounds, opacity); - } -} - -void MutatorContext::applyImageFilter( - const SkRect& bounds, - const std::shared_ptr& filter) { - if (filter) { - layer_state_stack_->push_attributes(); - layer_state_stack_->maybe_save_layer(filter); - layer_state_stack_->push_image_filter(bounds, filter); - } -} - -void MutatorContext::applyColorFilter( - const SkRect& bounds, - const std::shared_ptr& filter) { - if (filter) { - layer_state_stack_->push_attributes(); - layer_state_stack_->maybe_save_layer(filter); - layer_state_stack_->push_color_filter(bounds, filter); - } -} - -void MutatorContext::applyBackdropFilter( - const SkRect& bounds, - const std::shared_ptr& filter, - DlBlendMode blend_mode) { - layer_state_stack_->push_backdrop(bounds, filter, blend_mode); -} - -void MutatorContext::translate(SkScalar tx, SkScalar ty) { - if (!(tx == 0 && ty == 0)) { - layer_state_stack_->maybe_save_layer_for_transform(); - layer_state_stack_->push_translate(tx, ty); - } -} - -void MutatorContext::transform(const SkMatrix& matrix) { - if (matrix.isTranslate()) { - translate(matrix.getTranslateX(), matrix.getTranslateY()); - } else if (!matrix.isIdentity()) { - layer_state_stack_->maybe_save_layer_for_transform(); - layer_state_stack_->push_transform(matrix); - } -} - -void MutatorContext::transform(const SkM44& m44) { - layer_state_stack_->maybe_save_layer_for_transform(); - layer_state_stack_->push_transform(m44); -} - -void MutatorContext::integralTransform() { - layer_state_stack_->maybe_save_layer_for_transform(); - layer_state_stack_->push_integral_transform(); -} - -void MutatorContext::clipRect(const SkRect& rect, bool is_aa) { - layer_state_stack_->maybe_save_layer_for_clip(); - layer_state_stack_->push_clip_rect(rect, is_aa); -} - -void MutatorContext::clipRRect(const SkRRect& rrect, bool is_aa) { - layer_state_stack_->maybe_save_layer_for_clip(); - layer_state_stack_->push_clip_rrect(rrect, is_aa); -} - -void MutatorContext::clipPath(const SkPath& path, bool is_aa) { - layer_state_stack_->maybe_save_layer_for_clip(); - layer_state_stack_->push_clip_path(path, is_aa); -} - -void LayerStateStack::restore_to_count(size_t restore_count) { - while (state_stack_.size() > restore_count) { - state_stack_.back()->restore(this); - state_stack_.pop_back(); - } -} - -void LayerStateStack::push_attributes() { - state_stack_.emplace_back(std::make_unique(outstanding_)); -} - -void LayerStateStack::push_opacity(const SkRect& bounds, SkScalar opacity) { - state_stack_.emplace_back(std::make_unique(bounds, opacity)); - apply_last_entry(); -} - -void LayerStateStack::push_color_filter( - const SkRect& bounds, - const std::shared_ptr& filter) { - state_stack_.emplace_back(std::make_unique(bounds, filter)); - apply_last_entry(); -} - -void LayerStateStack::push_image_filter( - const SkRect& bounds, - const std::shared_ptr& filter) { - state_stack_.emplace_back(std::make_unique(bounds, filter)); - apply_last_entry(); -} - -void LayerStateStack::push_backdrop( - const SkRect& bounds, - const std::shared_ptr& filter, - DlBlendMode blend_mode) { - state_stack_.emplace_back( - std::make_unique(bounds, filter, blend_mode)); - apply_last_entry(); -} - -void LayerStateStack::push_translate(SkScalar tx, SkScalar ty) { - state_stack_.emplace_back(std::make_unique(matrix_, tx, ty)); - apply_last_entry(); -} - -void LayerStateStack::push_transform(const SkM44& m44) { - state_stack_.emplace_back(std::make_unique(matrix_, m44)); - apply_last_entry(); -} - -void LayerStateStack::push_transform(const SkMatrix& matrix) { - state_stack_.emplace_back( - std::make_unique(matrix_, matrix)); - apply_last_entry(); -} - -void LayerStateStack::push_integral_transform() { - state_stack_.emplace_back(std::make_unique(matrix_)); - apply_last_entry(); -} - -void LayerStateStack::push_clip_rect(const SkRect& rect, bool is_aa) { - state_stack_.emplace_back( - std::make_unique(cull_rect_, rect, is_aa)); - apply_last_entry(); -} - -void LayerStateStack::push_clip_rrect(const SkRRect& rrect, bool is_aa) { - state_stack_.emplace_back( - std::make_unique(cull_rect_, rrect, is_aa)); - apply_last_entry(); -} - -void LayerStateStack::push_clip_path(const SkPath& path, bool is_aa) { - state_stack_.emplace_back( - std::make_unique(cull_rect_, path, is_aa)); - apply_last_entry(); -} - -bool LayerStateStack::needs_save_layer(int flags) const { - if (outstanding_.opacity < SK_Scalar1 && - (flags & LayerStateStack::kCallerCanApplyOpacity) == 0) { - return true; - } - if (outstanding_.image_filter && - (flags & LayerStateStack::kCallerCanApplyImageFilter) == 0) { - return true; - } - if (outstanding_.color_filter && - (flags & LayerStateStack::kCallerCanApplyColorFilter) == 0) { - return true; - } - return false; -} - -void LayerStateStack::save_layer(const SkRect& bounds) { - push_attributes(); - state_stack_.emplace_back( - std::make_unique(bounds, DlBlendMode::kSrcOver)); - apply_last_entry(); -} - -void LayerStateStack::maybe_save_layer_for_transform() { - // Alpha and ColorFilter don't care about transform - if (outstanding_.image_filter) { - save_layer(outstanding_.save_layer_bounds); - } -} - -void LayerStateStack::maybe_save_layer_for_clip() { - // Alpha and ColorFilter don't care about clipping - // - Alpha of clipped content == clip of alpha content - // - Color-filtering of clipped content == clip of color-filtered content - if (outstanding_.image_filter) { - save_layer(outstanding_.save_layer_bounds); - } -} - -void LayerStateStack::maybe_save_layer(int apply_flags) { - if (needs_save_layer(apply_flags)) { - save_layer(outstanding_.save_layer_bounds); - } -} - -void LayerStateStack::maybe_save_layer(SkScalar opacity) { - if (outstanding_.image_filter) { - save_layer(outstanding_.save_layer_bounds); - } -} - -void LayerStateStack::maybe_save_layer( - const std::shared_ptr& filter) { - if (outstanding_.color_filter || outstanding_.image_filter || - (outstanding_.opacity < SK_Scalar1 && - !filter->can_commute_with_opacity())) { - // TBD: compose the 2 color filters together. - save_layer(outstanding_.save_layer_bounds); - } -} - -void LayerStateStack::maybe_save_layer( - const std::shared_ptr& filter) { - if (outstanding_.image_filter) { - // TBD: compose the 2 image filters together. - save_layer(outstanding_.save_layer_bounds); - } -} - -void LayerStateStack::intersect_cull_rect(const SkRRect& clip, - SkClipOp op, - bool is_aa) { - switch (op) { - case SkClipOp::kIntersect: - break; - case SkClipOp::kDifference: - if (!clip.isRect()) { - return; - } - break; - } - intersect_cull_rect(clip.getBounds(), op, is_aa); -} - -void LayerStateStack::intersect_cull_rect(const SkPath& clip, - SkClipOp op, - bool is_aa) { - SkRect bounds; - switch (op) { - case SkClipOp::kIntersect: - bounds = clip.getBounds(); - break; - case SkClipOp::kDifference: - if (!clip.isRect(&bounds)) { - return; - } - break; - } - intersect_cull_rect(bounds, op, is_aa); -} - -void LayerStateStack::intersect_cull_rect(const SkRect& clip, - SkClipOp op, - bool is_aa) { - if (has_perspective(matrix_)) { - // We can conservatively ignore this clip. - return; - } - if (cull_rect_.isEmpty()) { - // No point in intersecting further. - return; - } - SkRect rect = clip; - switch (op) { - case SkClipOp::kIntersect: - if (rect.isEmpty()) { - cull_rect_.setEmpty(); - break; - } - rect = matrix_.asM33().mapRect(rect); - if (is_aa) { - rect.roundOut(&rect); - } - if (!cull_rect_.intersect(rect)) { - cull_rect_.setEmpty(); - } - break; - case SkClipOp::kDifference: - if (rect.isEmpty() || !rect.intersects(cull_rect_)) { - break; - } - if (matrix_.asM33().mapRect(&rect)) { - // This technique only works if it is rect -> rect - if (is_aa) { - SkIRect rounded; - rect.round(&rounded); - if (rounded.isEmpty()) { - break; - } - rect.set(rounded); - } - if (rect.fLeft <= cull_rect_.fLeft && - rect.fRight >= cull_rect_.fRight) { - // bounds spans entire width of cull_rect_ - // therefore we can slice off a top or bottom - // edge of the cull_rect_. - SkScalar top = std::max(rect.fBottom, cull_rect_.fTop); - SkScalar btm = std::min(rect.fTop, cull_rect_.fBottom); - if (top < btm) { - cull_rect_.fTop = top; - cull_rect_.fBottom = btm; - } else { - cull_rect_.setEmpty(); - } - } else if (rect.fTop <= cull_rect_.fTop && - rect.fBottom >= cull_rect_.fBottom) { - // bounds spans entire height of cull_rect_ - // therefore we can slice off a left or right - // edge of the cull_rect_. - SkScalar lft = std::max(rect.fRight, cull_rect_.fLeft); - SkScalar rgt = std::min(rect.fLeft, cull_rect_.fRight); - if (lft < rgt) { - cull_rect_.fLeft = lft; - cull_rect_.fRight = rgt; - } else { - cull_rect_.setEmpty(); - } - } - } - break; - } -} - -void LayerStateStack::AttributesEntry::restore(LayerStateStack* stack) const { - stack->outstanding_ = attributes_; -} - -void LayerStateStack::SaveEntry::apply(LayerStateStack* stack) const { - if (stack->canvas_) { - stack->canvas_->save(); - } - if (stack->builder_) { - stack->builder_->save(); - } -} - -void LayerStateStack::SaveEntry::restore(LayerStateStack* stack) const { - do_checkerboard(stack); - if (stack->canvas_) { - stack->canvas_->restore(); - } - if (stack->builder_) { - stack->builder_->restore(); - } -} - -void LayerStateStack::SaveLayerEntry::apply(LayerStateStack* stack) const { - if (stack->canvas_) { - TRACE_EVENT0("flutter", "Canvas::saveLayer"); - SkPaint paint; - stack->canvas_->saveLayer(bounds_, - stack->outstanding_.fill(paint, blend_mode_)); - } - if (stack->builder_) { - TRACE_EVENT0("flutter", "Canvas::saveLayer"); - DlPaint paint; - stack->builder_->saveLayer(&bounds_, - stack->outstanding_.fill(paint, blend_mode_)); - } - stack->outstanding_ = {}; -} - -void LayerStateStack::SaveLayerEntry::do_checkerboard( - LayerStateStack* stack) const { - if (stack->checkerboard_func_) { - (*stack->checkerboard_func_)(stack->canvas_, stack->builder_, bounds_); - } -} - -void LayerStateStack::OpacityEntry::apply(LayerStateStack* stack) const { - stack->outstanding_.save_layer_bounds = bounds_; - stack->outstanding_.opacity *= opacity_; - if (stack->mutators_) { - stack->mutators_->PushOpacity(DlColor::toAlpha(opacity_)); - } -} - -void LayerStateStack::OpacityEntry::restore(LayerStateStack* stack) const { - if (stack->mutators_) { - stack->mutators_->Pop(); - } -} - -void LayerStateStack::ImageFilterEntry::apply(LayerStateStack* stack) const { - stack->outstanding_.save_layer_bounds = bounds_; - stack->outstanding_.image_filter = filter_; - if (stack->mutators_) { - // MutatorsStack::PushImageFilter does not exist... - } -} - -void LayerStateStack::ColorFilterEntry::apply(LayerStateStack* stack) const { - stack->outstanding_.save_layer_bounds = bounds_; - stack->outstanding_.color_filter = filter_; - if (stack->mutators_) { - // MutatorsStack::PushColorFilter does not exist... - } -} - -void LayerStateStack::BackdropFilterEntry::apply(LayerStateStack* stack) const { - if (stack->canvas_) { - TRACE_EVENT0("flutter", "Canvas::saveLayer"); - sk_sp backdrop_filter = - filter_ ? filter_->skia_object() : nullptr; - SkPaint paint; - SkPaint* pPaint = stack->outstanding_.fill(paint, blend_mode_); - stack->canvas_->saveLayer( - SkCanvas::SaveLayerRec{&bounds_, pPaint, backdrop_filter.get(), 0}); - } - if (stack->builder_) { - TRACE_EVENT0("flutter", "Canvas::saveLayer"); - DlPaint paint; - DlPaint* pPaint = stack->outstanding_.fill(paint, blend_mode_); - stack->builder_->saveLayer(&bounds_, pPaint, filter_.get()); - } - if (stack->mutators_) { - stack->mutators_->PushBackdropFilter(filter_); - } - stack->outstanding_ = {}; -} - -void LayerStateStack::BackdropFilterEntry::restore( - LayerStateStack* stack) const { - if (stack->mutators_) { - stack->mutators_->Pop(); - } - LayerStateStack::SaveLayerEntry::restore(stack); -} - -void LayerStateStack::BackdropFilterEntry::reapply( - LayerStateStack* stack) const { - // On the reapply for subsequent overlay layers, we do not - // want to reapply the backdrop filter, but we do need to - // do a saveLayer to encapsulate the contents and match the - // restore that will be forthcoming. Note that this is not - // perfect if the BlendMode is not associative as we will be - // compositing multiple parts of the content in batches. - // Luckily the most common SrcOver is associative. - SaveLayerEntry::apply(stack); -} - -void LayerStateStack::TransformEntry::restore(LayerStateStack* stack) const { - stack->matrix_ = previous_matrix_; - if (stack->mutators_) { - stack->mutators_->Pop(); - } -} - -void LayerStateStack::TranslateEntry::apply(LayerStateStack* stack) const { - if (stack->canvas_) { - stack->canvas_->translate(tx_, ty_); - } - if (stack->builder_) { - stack->builder_->translate(tx_, ty_); - } - if (stack->mutators_) { - stack->mutators_->PushTransform(SkMatrix::Translate(tx_, ty_)); - } - stack->matrix_.preConcat(SkM44::Translate(tx_, ty_)); -} - -void LayerStateStack::TransformMatrixEntry::apply( - LayerStateStack* stack) const { - if (stack->canvas_) { - stack->canvas_->concat(matrix_); - } - if (stack->builder_) { - stack->builder_->transform(matrix_); - } - if (stack->mutators_) { - stack->mutators_->PushTransform(matrix_); - } - stack->matrix_.preConcat(matrix_); -} - -void LayerStateStack::TransformM44Entry::apply(LayerStateStack* stack) const { - if (stack->canvas_) { - stack->canvas_->concat(m44_); - } - if (stack->builder_) { - stack->builder_->transform(m44_); - } - if (stack->mutators_) { - stack->mutators_->PushTransform(m44_.asM33()); - } - stack->matrix_.preConcat(m44_); -} - -void LayerStateStack::IntegralTransformEntry::apply( - LayerStateStack* stack) const { - SkM44 matrix = RasterCacheUtil::GetIntegralTransCTM(stack->matrix_); - if (stack->canvas_) { - stack->canvas_->setMatrix(matrix); - } - if (stack->builder_) { - stack->builder_->transformReset(); - stack->builder_->transform(matrix); - } - if (stack->mutators_) { - // There is no "SetMatrix" on MutatorsStack, but we need to push - // something to match the corresponding pop on the transform - // restore. - stack->mutators_->PushTransform(SkMatrix::I()); - } - stack->matrix_ = matrix; -} - -void LayerStateStack::ClipEntry::restore(LayerStateStack* stack) const { - stack->cull_rect_ = previous_cull_rect_; - if (stack->mutators_) { - stack->mutators_->Pop(); - } -} - -void LayerStateStack::ClipRectEntry::apply(LayerStateStack* stack) const { - if (stack->canvas_) { - stack->canvas_->clipRect(clip_rect_, SkClipOp::kIntersect, is_aa_); - } - if (stack->builder_) { - stack->builder_->clipRect(clip_rect_, SkClipOp::kIntersect, is_aa_); - } - if (stack->mutators_) { - stack->mutators_->PushClipRect(clip_rect_); - } - stack->intersect_cull_rect(clip_rect_, SkClipOp::kIntersect, is_aa_); -} - -void LayerStateStack::ClipRRectEntry::apply(LayerStateStack* stack) const { - if (stack->canvas_) { - stack->canvas_->clipRRect(clip_rrect_, SkClipOp::kIntersect, is_aa_); - } - if (stack->builder_) { - stack->builder_->clipRRect(clip_rrect_, SkClipOp::kIntersect, is_aa_); - } - if (stack->mutators_) { - stack->mutators_->PushClipRRect(clip_rrect_); - } - stack->intersect_cull_rect(clip_rrect_, SkClipOp::kIntersect, is_aa_); -} - -void LayerStateStack::ClipPathEntry::apply(LayerStateStack* stack) const { - if (stack->canvas_) { - stack->canvas_->clipPath(clip_path_, SkClipOp::kIntersect, is_aa_); - } - if (stack->builder_) { - stack->builder_->clipPath(clip_path_, SkClipOp::kIntersect, is_aa_); - } - if (stack->mutators_) { - stack->mutators_->PushClipPath(clip_path_); - } - stack->intersect_cull_rect(clip_path_, SkClipOp::kIntersect, is_aa_); -} - -} // namespace flutter diff --git a/flow/layers/layer_state_stack.h b/flow/layers/layer_state_stack.h deleted file mode 100644 index 65c1076df1361..0000000000000 --- a/flow/layers/layer_state_stack.h +++ /dev/null @@ -1,608 +0,0 @@ -// 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_FLOW_LAYERS_LAYER_STATE_STACK_H_ -#define FLUTTER_FLOW_LAYERS_LAYER_STATE_STACK_H_ - -#include "flutter/display_list/display_list_builder.h" -#include "flutter/display_list/display_list_canvas_recorder.h" -#include "flutter/flow/embedded_views.h" -#include "flutter/flow/paint_utils.h" - -namespace flutter { - -/// The LayerStateStack manages the inherited state passed down between -/// |Layer| objects in a |LayerTree| during |Preroll| and |Paint|. -/// -/// More specifically, it manages the clip and transform state during -/// recursive rendering and will hold and lazily apply opacity, ImageFilter -/// and ColorFilter attributes to recursive content. This is not a truly -/// general state management mechnanism as it makes assumptions that code -/// will be applying the attributes to rendered content that happens in -/// recursive calls. The automatic save/restore mechanisms only work in -/// a context where C++ auto-destruct calls will engage the restore at -/// the end of a code block and that any applied attributes will only -/// be applied to the content rendered inside that block. These restrictions -/// match the organization of the |LayerTree| precisely. -/// -/// The stack can manage a single state delegate. The stack will both -/// record the state internally regardless of any delegate and will also -/// apply it to a delegate as needed. The delegate can be swapped out -/// on the fly (as is typically done by PlatformViewLayer when recording -/// the state for multiple inter-embedded-view sub-trees) and the old -/// delegate will be restored to its original state (before it became a -/// delegate) and the new delegate will have all of the state recorded -/// by the stack replayed into it to bring it up to speed with the -/// current rendering context. -/// -/// The delegate can be any one of: -/// - MutatorsStack: used during Preroll to remember the outstanding -/// state for embedded platform layers -/// - SkCanvas: used during Paint for the default output to a Skia -/// surface -/// - DisplayListBuilder: used during Paint to construct a DisplayList -/// for Impeller output -/// The stack will know which state needs to be conveyed to any of these -/// delegates and when is the best time to convey that state (i.e. lazy -/// saveLayer calls for example). -/// -/// The rendering state attributes will be automatically applied to the -/// nested content using a |saveLayer| call at the point at which we -/// encounter rendered content (i.e. various nested layers that exist only -/// to apply new state will not trigger the |saveLayer| and the attributes -/// can accumulate until we reach actual content that is rendered.) Some -/// rendered content can avoid the |saveLayer| if it reports to the object -/// that it is able to apply all of the attributes that happen to be -/// outstanding (accumulated from parent state-modifiers). A |ContainerLayer| -/// can also monitor the attribute rendering capabilities of a list of -/// children and can ask the object to apply a protective |saveLayer| or -/// not based on the negotiated capabilities of the entire group. -/// -/// Any code that is planning to modify the clip, transform, or rendering -/// attributes for its child content must start by calling the |save| method -/// which returns a MutatorContext object. The methods that modify such -/// state only exist on the MutatorContext object so it is difficult to get -/// that wrong, but the caller must make sure that the call happens within -/// a C++ code block that will define the "rendering scope" of those -/// state changes as they will be automatically restored on exit from that -/// block. Note that the layer might make similar state calls directly on -/// the canvas or builder during the Paint cycle (via saveLayer, transform, -/// or clip calls), but should avoid doing so if there is any nested content -/// that needs to track or react to those state calls. -/// -/// Code that needs to render content can simply inform the parent of their -/// abilities by setting the |PrerollContext::renderable_state_flags| during -/// |Preroll| and then render with those attributes during |Paint| by -/// requesting the outstanding values of those attributes from the state_stack -/// object. Individual leaf layers can ignore this feature as the default -/// behavior during |Preroll| will have their parent |ContainerLayer| assume -/// that they cannot render any outstanding state attributes and will apply -/// the protective saveLayer on their behalf if needed. As such, this object -/// only provides "opt-in" features for leaf layers and no responsibilities -/// otherwise. -/// See |LayerStateStack::fill| -/// See |LayerStateStack::outstanding_opacity| -/// See |LayerStateStack::outstanding_color_filter| -/// See |LayerStateStack::outstanding_image_filter| -/// -/// State-modifying layers should contain code similar to this pattern in both -/// their |Preroll| and |Paint| methods. -/// -/// void [LayerType]::[Preroll/Paint](context) { -/// auto mutator = context.state_stack.save(); -/// mutator.translate(origin.x, origin.y); -/// mutator.applyOpacity(content_bounds, opacity_value); -/// mutator.applyColorFilter(content_bounds, color_filter); -/// -/// // Children will react to the state applied above during their -/// // Preroll/Paint methods or ContainerLayer will protect them -/// // conservatively by default. -/// [Preroll/Paint]Children(context); -/// -/// // here the mutator will be auto-destructed and the state accumulated -/// // by it will be restored out of the state_stack and its associated -/// // delegates. -/// } -class LayerStateStack { - public: - explicit LayerStateStack(const SkRect* cull_rect = nullptr); - - CheckerboardFunc checkerboard_func() const { return checkerboard_func_; } - void set_checkerboard_func(CheckerboardFunc checkerboard_func) { - checkerboard_func_ = checkerboard_func; - } - - // Clears out any old delegate to make room for a new one. - void clear_delegate(); - - // Return the SkCanvas delegate if the state stack has such a delegate. - // The state stack will only have one of an SkCanvas, Builder, or Mutators - // delegate at any given time. - // See also |builder_delegate| and |mutators_delegate|. - SkCanvas* canvas_delegate() { return canvas_; } - - // Return the DisplayListBuilder delegate if the state stack has such a - // delegate. - // The state stack will only have one of an SkCanvas, Builder, or Mutators - // delegate at any given time. - // See also |builder_delegate| and |mutators_delegate|. - DisplayListBuilder* builder_delegate() { return builder_; } - - // Return the MutatorsStack delegate if the state stack has such a - // delegate. - // The state stack will only have one of an SkCanvas, Builder, or Mutators - // delegate at any given time. - // See also |builder_delegate| and |mutators_delegate|. - MutatorsStack* mutators_delegate() { return mutators_; } - - // Clears the old delegate and sets the canvas delegate to the indicated - // canvas (if not nullptr). This ensures that only one delegate - either - // a canvas, a builder, or mutator stack - is present at any one time. - void set_delegate(SkCanvas* canvas); - - // Clears the old delegate and sets the builder delegate to the indicated - // buider (if not nullptr). This ensures that only one delegate - either - // a canvas, a builder, or mutator stack - is present at any one time. - void set_delegate(DisplayListBuilder* builder); - void set_delegate(sk_sp builder) { - set_delegate(builder.get()); - } - void set_delegate(DisplayListCanvasRecorder& recorder) { - set_delegate(recorder.builder().get()); - } - - // Clears the old delegate and sets the mutators delegate to the indicated - // MutatorsStack (if not null). This ensures that only one delegate - either - // a canvas, a builder, or mutator stack - is present at any one time. - void set_delegate(MutatorsStack* stack); - - // Overrides the initial cull rect and/or transform when it is not known at - // the time that the LayerStateStack is constructed. Must be called before - // any state has been pushed on the stack. - void set_initial_cull_rect(const SkRect& cull_rect); - void set_initial_transform(const SkMatrix& matrix); - void set_initial_transform(const SkM44& matrix); - void set_initial_state(const SkRect& cull_rect, const SkMatrix& matrix); - void set_initial_state(const SkRect& cull_rect, const SkM44& matrix); - - class AutoRestore { - public: - ~AutoRestore(); - - protected: - LayerStateStack* layer_state_stack_; - - private: - AutoRestore(LayerStateStack* stack); - friend class LayerStateStack; - - const size_t stack_restore_count_; - }; - - static constexpr int kCallerCanApplyOpacity = 0x1; - static constexpr int kCallerCanApplyColorFilter = 0x2; - static constexpr int kCallerCanApplyImageFilter = 0x4; - static constexpr int kCallerCanApplyAnything = - (kCallerCanApplyOpacity | kCallerCanApplyColorFilter | - kCallerCanApplyImageFilter); - - class MutatorContext : public AutoRestore { - public: - // Immediately executes a saveLayer with all accumulated state - // onto the canvas or builder to be applied at the next matching - // restore. A saveLayer is always executed by this method even if - // there are no outstanding attributes. - void saveLayer(const SkRect& bounds); - - // Records the opacity for application at the next call to - // saveLayer or applyState. A saveLayer may be executed at - // this time if the opacity cannot be batched with other - // outstanding attributes. - void applyOpacity(const SkRect& bounds, SkScalar opacity); - - // Records the image filter for application at the next call to - // saveLayer or applyState. A saveLayer may be executed at - // this time if the image filter cannot be batched with other - // outstanding attributes. - // (Currently only opacity is recorded for batching) - void applyImageFilter(const SkRect& bounds, - const std::shared_ptr& filter); - - // Records the color filter for application at the next call to - // saveLayer or applyState. A saveLayer may be executed at - // this time if the color filter cannot be batched with other - // outstanding attributes. - // (Currently only opacity is recorded for batching) - void applyColorFilter(const SkRect& bounds, - const std::shared_ptr& filter); - - // Saves the state stack and immediately executes a saveLayer - // with the indicated backdrop filter and any outstanding - // state attributes. Since the backdrop filter only applies - // to the pixels alrady on the screen when this call is made, - // the backdrop filter will only be applied to the canvas or - // builder installed at the time that this call is made, and - // subsequent canvas or builder objects that are made delegates - // will only see a saveLayer with the indicated blend_mode. - void applyBackdropFilter(const SkRect& bounds, - const std::shared_ptr& filter, - DlBlendMode blend_mode); - - void translate(SkScalar tx, SkScalar ty); - void translate(SkPoint tp) { translate(tp.fX, tp.fY); } - void transform(const SkM44& m44); - void transform(const SkMatrix& matrix); - void integralTransform(); - - void clipRect(const SkRect& rect, bool is_aa); - void clipRRect(const SkRRect& rrect, bool is_aa); - void clipPath(const SkPath& path, bool is_aa); - - private: - MutatorContext(LayerStateStack* stack) : AutoRestore(stack) {} - friend class LayerStateStack; - }; - - // Apply the outstanding state via saveLayer if necessary, - // respecting the flags representing which potentially - // outstanding attributes the calling layer can apply - // themselves. - // - // A saveLayer may or may not be sent to the delegates depending - // on how the outstanding state intersects with the flags supplied - // by the caller. - // - // An AutoRestore instance will always be returned even if there - // was no saveLayer applied. - [[nodiscard]] AutoRestore applyState(const SkRect& bounds, - int can_apply_flags = 0); - - SkScalar outstanding_opacity() const { return outstanding_.opacity; } - - std::shared_ptr outstanding_color_filter() const { - return outstanding_.color_filter; - } - - std::shared_ptr outstanding_image_filter() const { - return outstanding_.image_filter; - } - - SkRect outstanding_bounds() const { return outstanding_.save_layer_bounds; } - - // Fill the provided paint object with any oustanding attributes and - // return a pointer to it, or return a nullptr if there were no - // outstanding attributes to paint with. - SkPaint* fill(SkPaint& paint) const { return outstanding_.fill(paint); } - - // Fill the provided paint object with any oustanding attributes and - // return a pointer to it, or return a nullptr if there were no - // outstanding attributes to paint with. - DlPaint* fill(DlPaint& paint) const { return outstanding_.fill(paint); } - - SkRect device_cull_rect() const { return cull_rect_; } - SkRect local_cull_rect() const; - SkM44 transform_4x4() const { return matrix_; } - SkMatrix transform_3x3() const { return matrix_.asM33(); } - - // Tests if painting content with the current outstanding attributes - // will produce any content. - bool painting_is_nop() const { return outstanding_.opacity <= 0; } - - // Tests if painting content with the given bounds will produce any output. - bool content_culled(const SkRect& content_bounds) const; - - // Saves the current state of the state stack and returns a - // MutatorContext which can be used to manipulate the state. - // The state stack will be restored to its current state - // when the MutatorContext object goes out of scope. - [[nodiscard]] MutatorContext save(); - - bool is_empty() const { return state_stack_.empty(); } - - private: - size_t stack_count() const { return state_stack_.size(); } - void restore_to_count(size_t restore_count); - void reapply_all(); - - void apply_last_entry() { state_stack_.back()->apply(this); } - - // The push methods simply push an associated StateEntry on the stack - // and then apply it to the current canvas and builder. - // --------------------- - void push_attributes(); - void push_opacity(const SkRect& rect, SkScalar opacity); - void push_color_filter(const SkRect& bounds, - const std::shared_ptr& filter); - void push_image_filter(const SkRect& bounds, - const std::shared_ptr& filter); - void push_backdrop(const SkRect& bounds, - const std::shared_ptr& filter, - DlBlendMode blend_mode); - - void push_translate(SkScalar tx, SkScalar ty); - void push_transform(const SkM44& matrix); - void push_transform(const SkMatrix& matrix); - void push_integral_transform(); - - void push_clip_rect(const SkRect& rect, bool is_aa); - void push_clip_rrect(const SkRRect& rrect, bool is_aa); - void push_clip_path(const SkPath& path, bool is_aa); - // --------------------- - - // The maybe/needs_save_layer methods will determine if the indicated - // attribute can be incorporated into the outstanding attributes as is, - // or if the apply_flags are compatible with the outstanding attributes. - // If the oustanding attributes are incompatible with the new attribute - // or the apply flags, then a protective saveLayer will be executed. - // --------------------- - bool needs_save_layer(int flags) const; - void save_layer(const SkRect& bounds); - void maybe_save_layer_for_transform(); - void maybe_save_layer_for_clip(); - void maybe_save_layer(int apply_flags); - void maybe_save_layer(SkScalar opacity); - void maybe_save_layer(const std::shared_ptr& filter); - void maybe_save_layer(const std::shared_ptr& filter); - // --------------------- - - void intersect_cull_rect(const SkRect& clip, SkClipOp op, bool is_aa); - void intersect_cull_rect(const SkRRect& clip, SkClipOp op, bool is_aa); - void intersect_cull_rect(const SkPath& clip, SkClipOp op, bool is_aa); - - struct RenderingAttributes { - // We need to record the last bounds we received for the last - // attribute that we recorded so that we can perform a saveLayer - // on the proper area. When an attribute is applied that cannot - // be merged with the existing attributes, it will be submitted - // with a bounds for its own source content, not the bounds for - // the content that will be included in the saveLayer that applies - // the existing outstanding attributes - thus we need to record - // the bounds that were supplied with the most recent previous - // attribute to be applied. - SkRect save_layer_bounds{0, 0, 0, 0}; - - SkScalar opacity = SK_Scalar1; - std::shared_ptr color_filter; - std::shared_ptr image_filter; - - SkPaint* fill(SkPaint& paint, - DlBlendMode mode = DlBlendMode::kSrcOver) const; - DlPaint* fill(DlPaint& paint, - DlBlendMode mode = DlBlendMode::kSrcOver) const; - - bool operator==(const RenderingAttributes& other) const { - return save_layer_bounds == other.save_layer_bounds && - opacity == other.opacity && - Equals(color_filter, other.color_filter) && - Equals(image_filter, other.image_filter); - } - }; - - class StateEntry { - public: - virtual ~StateEntry() = default; - - virtual void apply(LayerStateStack* stack) const = 0; - - virtual void reapply(LayerStateStack* stack) const { apply(stack); } - - virtual void restore(LayerStateStack* stack) const {} - }; - - class AttributesEntry : public StateEntry { - public: - AttributesEntry(RenderingAttributes attributes) : attributes_(attributes) {} - - virtual void apply(LayerStateStack* stack) const override {} - - void restore(LayerStateStack* stack) const override; - - private: - const RenderingAttributes attributes_; - }; - - class SaveEntry : public StateEntry { - public: - SaveEntry() = default; - - void apply(LayerStateStack* stack) const override; - void restore(LayerStateStack* stack) const override; - - protected: - virtual void do_checkerboard(LayerStateStack* stack) const {} - }; - - class SaveLayerEntry : public SaveEntry { - public: - SaveLayerEntry(const SkRect& bounds, DlBlendMode blend_mode) - : bounds_(bounds), blend_mode_(blend_mode) {} - - void apply(LayerStateStack* stack) const override; - - protected: - const SkRect bounds_; - const DlBlendMode blend_mode_; - - void do_checkerboard(LayerStateStack* stack) const override; - }; - - class OpacityEntry : public StateEntry { - public: - OpacityEntry(const SkRect& bounds, SkScalar opacity) - : bounds_(bounds), opacity_(opacity) {} - - void apply(LayerStateStack* stack) const override; - void restore(LayerStateStack* stack) const override; - - private: - const SkRect bounds_; - const SkScalar opacity_; - }; - - class ImageFilterEntry : public StateEntry { - public: - ImageFilterEntry(const SkRect& bounds, - const std::shared_ptr& filter) - : bounds_(bounds), filter_(filter) {} - ~ImageFilterEntry() override = default; - - void apply(LayerStateStack* stack) const override; - - private: - const SkRect bounds_; - const std::shared_ptr filter_; - }; - - class ColorFilterEntry : public StateEntry { - public: - ColorFilterEntry(const SkRect& bounds, - const std::shared_ptr& filter) - : bounds_(bounds), filter_(filter) {} - ~ColorFilterEntry() override = default; - - void apply(LayerStateStack* stack) const override; - - private: - const SkRect bounds_; - const std::shared_ptr filter_; - }; - - class BackdropFilterEntry : public SaveLayerEntry { - public: - BackdropFilterEntry(const SkRect& bounds, - const std::shared_ptr& filter, - DlBlendMode blend_mode) - : SaveLayerEntry(bounds, blend_mode), filter_(filter) {} - ~BackdropFilterEntry() override = default; - - void apply(LayerStateStack* stack) const override; - void restore(LayerStateStack* stack) const override; - - void reapply(LayerStateStack* stack) const override; - - private: - const std::shared_ptr filter_; - friend class LayerStateStack; - }; - - class TransformEntry : public StateEntry { - public: - TransformEntry(const SkM44& matrix) : previous_matrix_(matrix) {} - - void restore(LayerStateStack* stack) const override; - - private: - const SkM44 previous_matrix_; - }; - - class TranslateEntry : public TransformEntry { - public: - TranslateEntry(const SkM44& previous_matrix, SkScalar tx, SkScalar ty) - : TransformEntry(previous_matrix), tx_(tx), ty_(ty) {} - - void apply(LayerStateStack* stack) const override; - - private: - const SkScalar tx_; - const SkScalar ty_; - }; - - class TransformMatrixEntry : public TransformEntry { - public: - TransformMatrixEntry(const SkM44 previous_matrix, const SkMatrix& matrix) - : TransformEntry(previous_matrix), matrix_(matrix) {} - - void apply(LayerStateStack* stack) const override; - - private: - const SkMatrix matrix_; - }; - - class TransformM44Entry : public TransformEntry { - public: - TransformM44Entry(const SkM44 previous_matrix, const SkM44& m44) - : TransformEntry(previous_matrix), m44_(m44) {} - - void apply(LayerStateStack* stack) const override; - - private: - const SkM44 m44_; - }; - - class IntegralTransformEntry : public TransformEntry { - public: - IntegralTransformEntry(const SkM44 previous_matrix) - : TransformEntry(previous_matrix) {} - - void apply(LayerStateStack* stack) const override; - }; - - class ClipEntry : public StateEntry { - protected: - ClipEntry(const SkRect& cull_rect, bool is_aa) - : previous_cull_rect_(cull_rect), is_aa_(is_aa) {} - - void restore(LayerStateStack* stack) const override; - - const SkRect previous_cull_rect_; - const bool is_aa_; - }; - - class ClipRectEntry : public ClipEntry { - public: - ClipRectEntry(const SkRect& cull_rect, const SkRect& clip_rect, bool is_aa) - : ClipEntry(cull_rect, is_aa), clip_rect_(clip_rect) {} - - void apply(LayerStateStack* stack) const override; - - private: - const SkRect clip_rect_; - }; - - class ClipRRectEntry : public ClipEntry { - public: - ClipRRectEntry(const SkRect& cull_rect, - const SkRRect& clip_rrect, - bool is_aa) - : ClipEntry(cull_rect, is_aa), clip_rrect_(clip_rrect) {} - - void apply(LayerStateStack* stack) const override; - - private: - const SkRRect clip_rrect_; - }; - - class ClipPathEntry : public ClipEntry { - public: - ClipPathEntry(const SkRect& cull_rect, const SkPath& clip_path, bool is_aa) - : ClipEntry(cull_rect, is_aa), clip_path_(clip_path) {} - ~ClipPathEntry() override = default; - - void apply(LayerStateStack* stack) const override; - - private: - const SkPath clip_path_; - }; - - std::vector> state_stack_; - friend class MutatorContext; - - SkM44 initial_matrix_; - SkM44 matrix_; - SkRect initial_cull_rect_; - SkRect cull_rect_; - - SkCanvas* canvas_ = nullptr; - DisplayListBuilder* builder_ = nullptr; - MutatorsStack* mutators_ = nullptr; - int restore_count_ = 0; - RenderingAttributes outstanding_; - CheckerboardFunc checkerboard_func_ = nullptr; - - friend class SaveLayerEntry; -}; - -} // namespace flutter - -#endif // FLUTTER_FLOW_LAYERS_LAYER_STATE_STACK_H_ diff --git a/flow/layers/layer_state_stack_unittests.cc b/flow/layers/layer_state_stack_unittests.cc deleted file mode 100644 index 65e9ddd78e034..0000000000000 --- a/flow/layers/layer_state_stack_unittests.cc +++ /dev/null @@ -1,491 +0,0 @@ -// 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 "gtest/gtest.h" - -#include "flutter/display_list/display_list_color_filter.h" -#include "flutter/display_list/display_list_image_filter.h" -#include "flutter/flow/layers/layer.h" -#include "flutter/flow/layers/layer_state_stack.h" -#include "flutter/testing/display_list_testing.h" -#include "flutter/testing/mock_canvas.h" - -namespace flutter { -namespace testing { - -TEST(LayerStateStack, Defaults) { - LayerStateStack state_stack; - - ASSERT_EQ(state_stack.canvas_delegate(), nullptr); - ASSERT_EQ(state_stack.builder_delegate(), nullptr); - ASSERT_EQ(state_stack.checkerboard_func(), nullptr); - ASSERT_EQ(state_stack.outstanding_opacity(), SK_Scalar1); - ASSERT_EQ(state_stack.outstanding_color_filter(), nullptr); - ASSERT_EQ(state_stack.outstanding_image_filter(), nullptr); - ASSERT_EQ(state_stack.outstanding_bounds(), SkRect()); - ASSERT_EQ(state_stack.device_cull_rect(), kGiantRect); - ASSERT_EQ(state_stack.local_cull_rect(), kGiantRect); - ASSERT_EQ(state_stack.transform_3x3(), SkMatrix::I()); - ASSERT_EQ(state_stack.transform_4x4(), SkM44()); - - SkPaint sk_paint; - state_stack.fill(sk_paint); - ASSERT_EQ(sk_paint, SkPaint()); - - DlPaint dl_paint; - state_stack.fill(dl_paint); - ASSERT_EQ(dl_paint, DlPaint()); -} - -TEST(LayerStateStack, SingularDelegate) { - LayerStateStack state_stack; - ASSERT_EQ(state_stack.canvas_delegate(), nullptr); - ASSERT_EQ(state_stack.builder_delegate(), nullptr); - - DisplayListBuilder builder; - MockCanvas canvas; - - // no delegate -> builder delegate - state_stack.set_delegate(&builder); - ASSERT_EQ(state_stack.canvas_delegate(), nullptr); - ASSERT_EQ(state_stack.builder_delegate(), &builder); - - // builder delegate -> canvas delegate - state_stack.set_delegate(&canvas); - ASSERT_EQ(state_stack.canvas_delegate(), &canvas); - ASSERT_EQ(state_stack.builder_delegate(), nullptr); - - // canvas delegate -> builder delegate - state_stack.set_delegate(&builder); - ASSERT_EQ(state_stack.canvas_delegate(), nullptr); - ASSERT_EQ(state_stack.builder_delegate(), &builder); - - // builder delegate -> no delegate - state_stack.clear_delegate(); - ASSERT_EQ(state_stack.canvas_delegate(), nullptr); - ASSERT_EQ(state_stack.builder_delegate(), nullptr); - - // canvas delegate -> no delegate - state_stack.set_delegate(&canvas); - state_stack.clear_delegate(); - ASSERT_EQ(state_stack.canvas_delegate(), nullptr); - ASSERT_EQ(state_stack.builder_delegate(), nullptr); -} - -TEST(LayerStateStack, Opacity) { - SkRect rect = {10, 10, 20, 20}; - - LayerStateStack state_stack; - { - auto mutator = state_stack.save(); - mutator.applyOpacity(rect, 0.5f); - - ASSERT_EQ(state_stack.outstanding_opacity(), 0.5f); - ASSERT_EQ(state_stack.outstanding_bounds(), rect); - - // Check nested opacities multiply with each other - { - auto mutator2 = state_stack.save(); - mutator.applyOpacity(rect, 0.5f); - - ASSERT_EQ(state_stack.outstanding_opacity(), 0.25f); - ASSERT_EQ(state_stack.outstanding_bounds(), rect); - - // Verify output with applyState that does not accept opacity - { - DisplayListBuilder builder; - state_stack.set_delegate(&builder); - { - auto restore = state_stack.applyState(rect, 0); - ASSERT_EQ(state_stack.outstanding_opacity(), SK_Scalar1); - ASSERT_EQ(state_stack.outstanding_bounds(), SkRect()); - - DlPaint paint; - state_stack.fill(paint); - builder.drawRect(rect, paint); - } - state_stack.clear_delegate(); - - DisplayListBuilder expected; - DlPaint save_paint = - DlPaint().setOpacity(state_stack.outstanding_opacity()); - expected.saveLayer(&rect, &save_paint); - expected.drawRect(rect, DlPaint()); - expected.restore(); - ASSERT_TRUE(DisplayListsEQ_Verbose(builder.Build(), expected.Build())); - } - - // Verify output with applyState that accepts opacity - { - DisplayListBuilder builder; - state_stack.set_delegate(&builder); - { - auto restore = state_stack.applyState( - rect, LayerStateStack::kCallerCanApplyOpacity); - ASSERT_EQ(state_stack.outstanding_opacity(), 0.25f); - ASSERT_EQ(state_stack.outstanding_bounds(), rect); - - DlPaint paint; - state_stack.fill(paint); - builder.drawRect(rect, paint); - } - state_stack.clear_delegate(); - - DisplayListBuilder expected; - expected.drawRect(rect, DlPaint().setOpacity(0.25f)); - ASSERT_TRUE(DisplayListsEQ_Verbose(builder.Build(), expected.Build())); - } - } - - ASSERT_EQ(state_stack.outstanding_opacity(), 0.5f); - ASSERT_EQ(state_stack.outstanding_bounds(), rect); - } - - ASSERT_EQ(state_stack.outstanding_opacity(), SK_Scalar1); - ASSERT_EQ(state_stack.outstanding_bounds(), SkRect()); -} - -TEST(LayerStateStack, ColorFilter) { - SkRect rect = {10, 10, 20, 20}; - std::shared_ptr outer_filter = - std::make_shared(DlColor::kYellow(), - DlBlendMode::kColorBurn); - std::shared_ptr inner_filter = - std::make_shared(DlColor::kRed(), - DlBlendMode::kColorBurn); - - LayerStateStack state_stack; - { - auto mutator = state_stack.save(); - mutator.applyColorFilter(rect, outer_filter); - - ASSERT_EQ(state_stack.outstanding_color_filter(), outer_filter); - - // Check nested color filters result in nested saveLayers - { - auto mutator2 = state_stack.save(); - mutator.applyColorFilter(rect, inner_filter); - - ASSERT_EQ(state_stack.outstanding_color_filter(), inner_filter); - - // Verify output with applyState that does not accept color filters - { - DisplayListBuilder builder; - state_stack.set_delegate(&builder); - { - auto restore = state_stack.applyState(rect, 0); - ASSERT_EQ(state_stack.outstanding_color_filter(), nullptr); - - DlPaint paint; - state_stack.fill(paint); - builder.drawRect(rect, paint); - } - state_stack.clear_delegate(); - - DisplayListBuilder expected; - DlPaint outer_save_paint = DlPaint().setColorFilter(outer_filter); - DlPaint inner_save_paint = DlPaint().setColorFilter(inner_filter); - expected.saveLayer(&rect, &outer_save_paint); - expected.saveLayer(&rect, &inner_save_paint); - expected.drawRect(rect, DlPaint()); - expected.restore(); - expected.restore(); - ASSERT_TRUE(DisplayListsEQ_Verbose(builder.Build(), expected.Build())); - } - - // Verify output with applyState that accepts color filters - { - SkRect rect = {10, 10, 20, 20}; - DisplayListBuilder builder; - state_stack.set_delegate(&builder); - { - auto restore = state_stack.applyState( - rect, LayerStateStack::kCallerCanApplyColorFilter); - ASSERT_EQ(state_stack.outstanding_color_filter(), inner_filter); - - DlPaint paint; - state_stack.fill(paint); - builder.drawRect(rect, paint); - } - state_stack.clear_delegate(); - - DisplayListBuilder expected; - DlPaint save_paint = DlPaint().setColorFilter(outer_filter); - DlPaint draw_paint = DlPaint().setColorFilter(inner_filter); - expected.saveLayer(&rect, &save_paint); - expected.drawRect(rect, draw_paint); - ASSERT_TRUE(DisplayListsEQ_Verbose(builder.Build(), expected.Build())); - } - } - - ASSERT_EQ(state_stack.outstanding_color_filter(), outer_filter); - } - - ASSERT_EQ(state_stack.outstanding_color_filter(), nullptr); -} - -TEST(LayerStateStack, ImageFilter) { - SkRect rect = {10, 10, 20, 20}; - std::shared_ptr outer_filter = - std::make_shared(2.0f, 2.0f, DlTileMode::kClamp); - std::shared_ptr inner_filter = - std::make_shared(3.0f, 3.0f, DlTileMode::kClamp); - SkRect inner_src_rect = rect; - SkRect outer_src_rect; - ASSERT_EQ(inner_filter->map_local_bounds(rect, outer_src_rect), - &outer_src_rect); - - LayerStateStack state_stack; - { - auto mutator = state_stack.save(); - mutator.applyImageFilter(outer_src_rect, outer_filter); - - ASSERT_EQ(state_stack.outstanding_image_filter(), outer_filter); - - // Check nested color filters result in nested saveLayers - { - auto mutator2 = state_stack.save(); - mutator.applyImageFilter(rect, inner_filter); - - ASSERT_EQ(state_stack.outstanding_image_filter(), inner_filter); - - // Verify output with applyState that does not accept color filters - { - DisplayListBuilder builder; - state_stack.set_delegate(&builder); - { - auto restore = state_stack.applyState(rect, 0); - ASSERT_EQ(state_stack.outstanding_image_filter(), nullptr); - - DlPaint paint; - state_stack.fill(paint); - builder.drawRect(rect, paint); - } - state_stack.clear_delegate(); - - DisplayListBuilder expected; - DlPaint outer_save_paint = DlPaint().setImageFilter(outer_filter); - DlPaint inner_save_paint = DlPaint().setImageFilter(inner_filter); - expected.saveLayer(&outer_src_rect, &outer_save_paint); - expected.saveLayer(&inner_src_rect, &inner_save_paint); - expected.drawRect(rect, DlPaint()); - expected.restore(); - expected.restore(); - ASSERT_TRUE(DisplayListsEQ_Verbose(builder.Build(), expected.Build())); - } - - // Verify output with applyState that accepts color filters - { - SkRect rect = {10, 10, 20, 20}; - DisplayListBuilder builder; - state_stack.set_delegate(&builder); - { - auto restore = state_stack.applyState( - rect, LayerStateStack::kCallerCanApplyImageFilter); - ASSERT_EQ(state_stack.outstanding_image_filter(), inner_filter); - - DlPaint paint; - state_stack.fill(paint); - builder.drawRect(rect, paint); - } - state_stack.clear_delegate(); - - DisplayListBuilder expected; - DlPaint save_paint = DlPaint().setImageFilter(outer_filter); - DlPaint draw_paint = DlPaint().setImageFilter(inner_filter); - expected.saveLayer(&outer_src_rect, &save_paint); - expected.drawRect(rect, draw_paint); - ASSERT_TRUE(DisplayListsEQ_Verbose(builder.Build(), expected.Build())); - } - } - - ASSERT_EQ(state_stack.outstanding_image_filter(), outer_filter); - } - - ASSERT_EQ(state_stack.outstanding_image_filter(), nullptr); -} - -TEST(LayerStateStack, OpacityAndColorFilterInteraction) { - SkRect rect = {10, 10, 20, 20}; - std::shared_ptr color_filter = - std::make_shared(DlColor::kYellow(), - DlBlendMode::kColorBurn); - - LayerStateStack state_stack; - DisplayListBuilder builder; - state_stack.set_delegate(&builder); - ASSERT_EQ(builder.getSaveCount(), 1); - - { - auto mutator1 = state_stack.save(); - ASSERT_EQ(builder.getSaveCount(), 2); - mutator1.applyOpacity(rect, 0.5f); - ASSERT_EQ(builder.getSaveCount(), 2); - - { - auto mutator2 = state_stack.save(); - ASSERT_EQ(builder.getSaveCount(), 3); - mutator2.applyColorFilter(rect, color_filter); - - // The opacity will have been resolved by a saveLayer - ASSERT_EQ(builder.getSaveCount(), 4); - ASSERT_EQ(state_stack.outstanding_color_filter(), color_filter); - ASSERT_EQ(state_stack.outstanding_opacity(), SK_Scalar1); - } - ASSERT_EQ(builder.getSaveCount(), 2); - ASSERT_EQ(state_stack.outstanding_color_filter(), nullptr); - ASSERT_EQ(state_stack.outstanding_opacity(), 0.5f); - } - ASSERT_EQ(builder.getSaveCount(), 1); - ASSERT_EQ(state_stack.outstanding_color_filter(), nullptr); - ASSERT_EQ(state_stack.outstanding_opacity(), SK_Scalar1); - - { - auto mutator1 = state_stack.save(); - ASSERT_EQ(builder.getSaveCount(), 2); - mutator1.applyColorFilter(rect, color_filter); - ASSERT_EQ(builder.getSaveCount(), 2); - - { - auto mutator2 = state_stack.save(); - ASSERT_EQ(builder.getSaveCount(), 3); - mutator2.applyOpacity(rect, 0.5f); - - // color filter applied to opacity can be applied together - ASSERT_EQ(builder.getSaveCount(), 3); - ASSERT_EQ(state_stack.outstanding_color_filter(), color_filter); - ASSERT_EQ(state_stack.outstanding_opacity(), 0.5f); - } - ASSERT_EQ(builder.getSaveCount(), 2); - ASSERT_EQ(state_stack.outstanding_color_filter(), color_filter); - ASSERT_EQ(state_stack.outstanding_opacity(), SK_Scalar1); - } - ASSERT_EQ(builder.getSaveCount(), 1); - ASSERT_EQ(state_stack.outstanding_color_filter(), nullptr); - ASSERT_EQ(state_stack.outstanding_opacity(), SK_Scalar1); -} - -TEST(LayerStateStack, OpacityAndImageFilterInteraction) { - SkRect rect = {10, 10, 20, 20}; - std::shared_ptr image_filter = - std::make_shared(2.0f, 2.0f, DlTileMode::kClamp); - - LayerStateStack state_stack; - DisplayListBuilder builder; - state_stack.set_delegate(&builder); - ASSERT_EQ(builder.getSaveCount(), 1); - - { - auto mutator1 = state_stack.save(); - ASSERT_EQ(builder.getSaveCount(), 2); - mutator1.applyOpacity(rect, 0.5f); - ASSERT_EQ(builder.getSaveCount(), 2); - - { - auto mutator2 = state_stack.save(); - ASSERT_EQ(builder.getSaveCount(), 3); - mutator2.applyImageFilter(rect, image_filter); - - // opacity applied to image filter can be applied together - ASSERT_EQ(builder.getSaveCount(), 3); - ASSERT_EQ(state_stack.outstanding_image_filter(), image_filter); - ASSERT_EQ(state_stack.outstanding_opacity(), 0.5f); - } - ASSERT_EQ(builder.getSaveCount(), 2); - ASSERT_EQ(state_stack.outstanding_image_filter(), nullptr); - ASSERT_EQ(state_stack.outstanding_opacity(), 0.5f); - } - ASSERT_EQ(builder.getSaveCount(), 1); - ASSERT_EQ(state_stack.outstanding_image_filter(), nullptr); - ASSERT_EQ(state_stack.outstanding_opacity(), SK_Scalar1); - - { - auto mutator1 = state_stack.save(); - ASSERT_EQ(builder.getSaveCount(), 2); - mutator1.applyImageFilter(rect, image_filter); - ASSERT_EQ(builder.getSaveCount(), 2); - - { - auto mutator2 = state_stack.save(); - ASSERT_EQ(builder.getSaveCount(), 3); - mutator2.applyOpacity(rect, 0.5f); - - // The image filter will have been resolved by a saveLayer - ASSERT_EQ(builder.getSaveCount(), 4); - ASSERT_EQ(state_stack.outstanding_image_filter(), nullptr); - ASSERT_EQ(state_stack.outstanding_opacity(), 0.5f); - } - ASSERT_EQ(builder.getSaveCount(), 2); - ASSERT_EQ(state_stack.outstanding_image_filter(), image_filter); - ASSERT_EQ(state_stack.outstanding_opacity(), SK_Scalar1); - } - ASSERT_EQ(builder.getSaveCount(), 1); - ASSERT_EQ(state_stack.outstanding_image_filter(), nullptr); - ASSERT_EQ(state_stack.outstanding_opacity(), SK_Scalar1); -} - -TEST(LayerStateStack, ColorFilterAndImageFilterInteraction) { - SkRect rect = {10, 10, 20, 20}; - std::shared_ptr color_filter = - std::make_shared(DlColor::kYellow(), - DlBlendMode::kColorBurn); - std::shared_ptr image_filter = - std::make_shared(2.0f, 2.0f, DlTileMode::kClamp); - - LayerStateStack state_stack; - DisplayListBuilder builder; - state_stack.set_delegate(&builder); - ASSERT_EQ(builder.getSaveCount(), 1); - - { - auto mutator1 = state_stack.save(); - ASSERT_EQ(builder.getSaveCount(), 2); - mutator1.applyColorFilter(rect, color_filter); - ASSERT_EQ(builder.getSaveCount(), 2); - - { - auto mutator2 = state_stack.save(); - ASSERT_EQ(builder.getSaveCount(), 3); - mutator2.applyImageFilter(rect, image_filter); - - // color filter applied to image filter can be applied together - ASSERT_EQ(builder.getSaveCount(), 3); - ASSERT_EQ(state_stack.outstanding_image_filter(), image_filter); - ASSERT_EQ(state_stack.outstanding_color_filter(), color_filter); - } - ASSERT_EQ(builder.getSaveCount(), 2); - ASSERT_EQ(state_stack.outstanding_image_filter(), nullptr); - ASSERT_EQ(state_stack.outstanding_color_filter(), color_filter); - } - ASSERT_EQ(builder.getSaveCount(), 1); - ASSERT_EQ(state_stack.outstanding_image_filter(), nullptr); - ASSERT_EQ(state_stack.outstanding_color_filter(), nullptr); - - { - auto mutator1 = state_stack.save(); - ASSERT_EQ(builder.getSaveCount(), 2); - mutator1.applyImageFilter(rect, image_filter); - ASSERT_EQ(builder.getSaveCount(), 2); - - { - auto mutator2 = state_stack.save(); - ASSERT_EQ(builder.getSaveCount(), 3); - mutator2.applyColorFilter(rect, color_filter); - - // The image filter will have been resolved by a saveLayer - ASSERT_EQ(builder.getSaveCount(), 4); - ASSERT_EQ(state_stack.outstanding_image_filter(), nullptr); - ASSERT_EQ(state_stack.outstanding_color_filter(), color_filter); - } - ASSERT_EQ(builder.getSaveCount(), 2); - ASSERT_EQ(state_stack.outstanding_image_filter(), image_filter); - ASSERT_EQ(state_stack.outstanding_color_filter(), nullptr); - } - ASSERT_EQ(builder.getSaveCount(), 1); - ASSERT_EQ(state_stack.outstanding_image_filter(), nullptr); - ASSERT_EQ(state_stack.outstanding_color_filter(), nullptr); -} - -} // namespace testing -} // namespace flutter diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index ddb80e24f225a..0e5aacc98fe92 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -9,7 +9,6 @@ #include "flutter/flow/layer_snapshot_store.h" #include "flutter/flow/layers/cacheable_layer.h" #include "flutter/flow/layers/layer.h" -#include "flutter/flow/paint_utils.h" #include "flutter/flow/raster_cache.h" #include "flutter/fml/time/time_point.h" #include "flutter/fml/trace_event.h" @@ -45,10 +44,7 @@ bool LayerTree::Preroll(CompositorContext::ScopedFrame& frame, SkColorSpace* color_space = GetColorSpace(frame.canvas()); frame.context().raster_cache().SetCheckboardCacheImages( checkerboard_raster_cache_images_); - LayerStateStack state_stack; - state_stack.set_initial_state(cull_rect, frame.root_surface_transformation()); MutatorsStack stack; - state_stack.set_delegate(&stack); RasterCache* cache = ignore_raster_cache ? nullptr : &frame.context().raster_cache(); raster_cache_items_.clear(); @@ -58,19 +54,21 @@ bool LayerTree::Preroll(CompositorContext::ScopedFrame& frame, .raster_cache = cache, .gr_context = frame.gr_context(), .view_embedder = frame.view_embedder(), - .state_stack = state_stack, + .mutators_stack = stack, .dst_color_space = color_space, + .cull_rect = cull_rect, .surface_needs_readback = false, .raster_time = frame.context().raster_time(), .ui_time = frame.context().ui_time(), .texture_registry = frame.context().texture_registry(), + .checkerboard_offscreen_layers = checkerboard_offscreen_layers_, .frame_device_pixel_ratio = device_pixel_ratio_, .raster_cached_entries = &raster_cache_items_, .display_list_enabled = frame.display_list_builder() != nullptr, // clang-format on }; - root_layer_->Preroll(&context); + root_layer_->Preroll(&context, frame.root_surface_transformation()); return context.surface_needs_readback; } @@ -112,18 +110,24 @@ void LayerTree::Paint(CompositorContext::ScopedFrame& frame, return; } - SkRect cull_rect = SkRect::Make(frame.canvas()->getDeviceClipBounds()); - LayerStateStack state_stack; - state_stack.set_initial_state(cull_rect, frame.root_surface_transformation()); - if (checkerboard_offscreen_layers_) { - state_stack.set_checkerboard_func(DrawCheckerboard); + SkISize canvas_size = frame.canvas()->getBaseLayerSize(); + SkNWayCanvas internal_nodes_canvas(canvas_size.width(), canvas_size.height()); + internal_nodes_canvas.addCanvas(frame.canvas()); + if (frame.view_embedder() != nullptr) { + auto overlay_canvases = frame.view_embedder()->GetCurrentCanvases(); + for (size_t i = 0; i < overlay_canvases.size(); i++) { + internal_nodes_canvas.addCanvas(overlay_canvases[i]); + } } - DisplayListBuilder* builder = frame.display_list_builder(); + DisplayListBuilderMultiplexer builder_multiplexer; if (builder) { - state_stack.set_delegate(builder); - } else { - state_stack.set_delegate(frame.canvas()); + builder_multiplexer.addBuilder(builder); + if (frame.view_embedder()) { + for (auto* view_builder : frame.view_embedder()->GetCurrentBuilders()) { + builder_multiplexer.addBuilder(view_builder); + } + } } // clear the previous snapshots. @@ -138,9 +142,8 @@ void LayerTree::Paint(CompositorContext::ScopedFrame& frame, ignore_raster_cache ? nullptr : &frame.context().raster_cache(); PaintContext context = { // clang-format off - .state_stack = state_stack, - .canvas = frame.canvas(), - .builder = builder, + .internal_nodes_canvas = &internal_nodes_canvas, + .leaf_nodes_canvas = frame.canvas(), .gr_context = frame.gr_context(), .dst_color_space = color_space, .view_embedder = frame.view_embedder(), @@ -148,9 +151,13 @@ void LayerTree::Paint(CompositorContext::ScopedFrame& frame, .ui_time = frame.context().ui_time(), .texture_registry = frame.context().texture_registry(), .raster_cache = cache, + .checkerboard_offscreen_layers = checkerboard_offscreen_layers_, .frame_device_pixel_ratio = device_pixel_ratio_, .layer_snapshot_store = snapshot_store, .enable_leaf_layer_tracing = enable_leaf_layer_tracing_, + .inherited_opacity = SK_Scalar1, + .leaf_nodes_builder = builder, + .builder_multiplexer = builder ? &builder_multiplexer : nullptr, // clang-format on }; @@ -170,36 +177,42 @@ sk_sp LayerTree::Flatten( GrDirectContext* gr_context) { TRACE_EVENT0("flutter", "LayerTree::Flatten"); - DisplayListCanvasRecorder recorder(bounds); - - LayerStateStack state_stack; - state_stack.set_checkerboard_func(nullptr); - // No root surface transformation. So assume identity. - state_stack.set_initial_state(kGiantRect, SkMatrix::I()); + DisplayListCanvasRecorder builder(bounds); MutatorsStack unused_stack; const FixedRefreshRateStopwatch unused_stopwatch; + SkMatrix root_surface_transformation; + + // No root surface transformation. So assume identity. + root_surface_transformation.reset(); PrerollContext preroll_context{ // clang-format off .raster_cache = nullptr, .gr_context = gr_context, .view_embedder = nullptr, - .state_stack = state_stack, + .mutators_stack = unused_stack, .dst_color_space = nullptr, + .cull_rect = kGiantRect, .surface_needs_readback = false, .raster_time = unused_stopwatch, .ui_time = unused_stopwatch, .texture_registry = texture_registry, + .checkerboard_offscreen_layers = false, .frame_device_pixel_ratio = device_pixel_ratio_ // clang-format on }; + SkISize canvas_size = builder.getBaseLayerSize(); + SkNWayCanvas internal_nodes_canvas(canvas_size.width(), canvas_size.height()); + internal_nodes_canvas.addCanvas(&builder); + DisplayListBuilderMultiplexer multiplexer; + multiplexer.addBuilder(builder.builder().get()); + PaintContext paint_context = { // clang-format off - .state_stack = state_stack, - .canvas = &recorder, - .builder = recorder.builder().get(), + .internal_nodes_canvas = &internal_nodes_canvas, + .leaf_nodes_canvas = &builder, .gr_context = gr_context, .dst_color_space = nullptr, .view_embedder = nullptr, @@ -207,29 +220,26 @@ sk_sp LayerTree::Flatten( .ui_time = unused_stopwatch, .texture_registry = texture_registry, .raster_cache = nullptr, + .checkerboard_offscreen_layers = false, .frame_device_pixel_ratio = device_pixel_ratio_, .layer_snapshot_store = nullptr, .enable_leaf_layer_tracing = false, + .leaf_nodes_builder = builder.builder().get(), + .builder_multiplexer = &multiplexer, // clang-format on }; // Even if we don't have a root layer, we still need to create an empty // picture. if (root_layer_) { - state_stack.set_delegate(&unused_stack); - root_layer_->Preroll(&preroll_context); - FML_DCHECK(state_stack.is_empty()); - FML_DCHECK(state_stack.device_cull_rect() == kGiantRect); - FML_DCHECK(state_stack.transform_4x4() == SkM44()); - + root_layer_->Preroll(&preroll_context, root_surface_transformation); // The needs painting flag may be set after the preroll. So check it after. if (root_layer_->needs_painting(paint_context)) { - state_stack.set_delegate(recorder.builder()); root_layer_->Paint(paint_context); } } - return recorder.Build(); + return builder.Build(); } } // namespace flutter diff --git a/flow/layers/layer_tree_unittests.cc b/flow/layers/layer_tree_unittests.cc index 09115f0c35ae7..950e542c3a685 100644 --- a/flow/layers/layer_tree_unittests.cc +++ b/flow/layers/layer_tree_unittests.cc @@ -194,38 +194,37 @@ TEST_F(LayerTreeTest, NeedsSystemComposite) { } TEST_F(LayerTreeTest, PrerollContextInitialization) { - LayerStateStack state_stack; + MutatorsStack mock_mutators; FixedRefreshRateStopwatch mock_raster_time; FixedRefreshRateStopwatch mock_ui_time; std::shared_ptr mock_registry; - auto expect_defaults = [&state_stack, &mock_raster_time, &mock_ui_time, + auto expect_defaults = [&mock_mutators, &mock_raster_time, &mock_ui_time, &mock_registry](const PrerollContext& context) { EXPECT_EQ(context.raster_cache, nullptr); EXPECT_EQ(context.gr_context, nullptr); EXPECT_EQ(context.view_embedder, nullptr); - EXPECT_EQ(&context.state_stack, &state_stack); + EXPECT_EQ(&context.mutators_stack, &mock_mutators); EXPECT_EQ(context.dst_color_space, nullptr); - EXPECT_EQ(context.state_stack.device_cull_rect(), kGiantRect); - EXPECT_EQ(context.state_stack.transform_3x3(), SkMatrix::I()); - EXPECT_EQ(context.state_stack.transform_4x4(), SkM44()); + EXPECT_EQ(context.cull_rect, SkRect::MakeEmpty()); EXPECT_EQ(context.surface_needs_readback, false); EXPECT_EQ(&context.raster_time, &mock_raster_time); EXPECT_EQ(&context.ui_time, &mock_ui_time); EXPECT_EQ(context.texture_registry.get(), mock_registry.get()); + EXPECT_EQ(context.checkerboard_offscreen_layers, false); EXPECT_EQ(context.frame_device_pixel_ratio, 1.0f); EXPECT_EQ(context.has_platform_view, false); EXPECT_EQ(context.has_texture_layer, false); - EXPECT_EQ(context.renderable_state_flags, 0); + EXPECT_EQ(context.subtree_can_inherit_opacity, false); EXPECT_EQ(context.raster_cached_entries, nullptr); }; // These 4 initializers are required because they are handled by reference PrerollContext context{ - .state_stack = state_stack, + .mutators_stack = mock_mutators, .raster_time = mock_raster_time, .ui_time = mock_ui_time, .texture_registry = mock_registry, @@ -234,32 +233,33 @@ TEST_F(LayerTreeTest, PrerollContextInitialization) { } TEST_F(LayerTreeTest, PaintContextInitialization) { - LayerStateStack state_stack; FixedRefreshRateStopwatch mock_raster_time; FixedRefreshRateStopwatch mock_ui_time; std::shared_ptr mock_registry; - auto expect_defaults = [&state_stack, &mock_raster_time, &mock_ui_time, + auto expect_defaults = [&mock_raster_time, &mock_ui_time, &mock_registry](const PaintContext& context) { - EXPECT_EQ(&context.state_stack, &state_stack); - EXPECT_EQ(context.canvas, nullptr); - EXPECT_EQ(context.builder, nullptr); + EXPECT_EQ(context.internal_nodes_canvas, nullptr); + EXPECT_EQ(context.leaf_nodes_canvas, nullptr); EXPECT_EQ(context.gr_context, nullptr); EXPECT_EQ(context.view_embedder, nullptr); EXPECT_EQ(&context.raster_time, &mock_raster_time); EXPECT_EQ(&context.ui_time, &mock_ui_time); EXPECT_EQ(context.texture_registry.get(), mock_registry.get()); EXPECT_EQ(context.raster_cache, nullptr); - EXPECT_EQ(context.state_stack.checkerboard_func(), nullptr); + EXPECT_EQ(context.checkerboard_offscreen_layers, false); EXPECT_EQ(context.frame_device_pixel_ratio, 1.0f); EXPECT_EQ(context.enable_leaf_layer_tracing, false); EXPECT_EQ(context.layer_snapshot_store, nullptr); + + EXPECT_EQ(context.inherited_opacity, SK_Scalar1); + EXPECT_EQ(context.leaf_nodes_builder, nullptr); + EXPECT_EQ(context.builder_multiplexer, nullptr); }; // These 4 initializers are required because they are handled by reference PaintContext context{ - .state_stack = state_stack, .raster_time = mock_raster_time, .ui_time = mock_ui_time, .texture_registry = mock_registry, diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index eba507dcef0d6..111fbf798ea5b 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -36,27 +36,39 @@ void OpacityLayer::Diff(DiffContext* context, const Layer* old_layer) { context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); } -void OpacityLayer::Preroll(PrerollContext* context) { +void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { FML_DCHECK(!layers().empty()); // We can't be a leaf. - auto mutator = context->state_stack.save(); - mutator.translate(offset_); - mutator.applyOpacity(SkRect(), DlColor::toOpacity(alpha_)); + SkMatrix child_matrix = matrix; + child_matrix.preTranslate(offset_.fX, offset_.fY); - AutoCache auto_cache = AutoCache(layer_raster_cache_item_.get(), context, - context->state_stack.transform_3x3()); + // Similar to what's done in TransformLayer::Preroll, we have to apply the + // reverse transformation to the cull rect to properly cull child layers. + context->cull_rect = context->cull_rect.makeOffset(-offset_.fX, -offset_.fY); + + context->mutators_stack.PushTransform( + SkMatrix::Translate(offset_.fX, offset_.fY)); + context->mutators_stack.PushOpacity(alpha_); + + AutoCache auto_cache = + AutoCache(layer_raster_cache_item_.get(), context, child_matrix); Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context); - ContainerLayer::Preroll(context); + // Collect inheritance information on our children in Preroll so that + // we can decide whether or not to use a saveLayer in Paint. + context->subtree_can_inherit_opacity = true; + // ContainerLayer will turn the flag off if any children are + // incompatible or if they overlap + ContainerLayer::Preroll(context, child_matrix); // We store the inheritance ability of our children for |Paint| - set_children_can_accept_opacity((context->renderable_state_flags & - LayerStateStack::kCallerCanApplyOpacity) != - 0); + set_children_can_accept_opacity(context->subtree_can_inherit_opacity); // Now we let our parent layers know that we, too, can inherit opacity // regardless of what our children are capable of - context->renderable_state_flags |= LayerStateStack::kCallerCanApplyOpacity; + context->subtree_can_inherit_opacity = true; + context->mutators_stack.Pop(); + context->mutators_stack.Pop(); set_paint_bounds(paint_bounds().makeOffset(offset_.fX, offset_.fY)); @@ -66,22 +78,57 @@ void OpacityLayer::Preroll(PrerollContext* context) { // should tell the AutoCache object don't do raster_cache. auto_cache.ShouldNotBeCached(); } + + // Restore cull_rect + context->cull_rect = context->cull_rect.makeOffset(offset_.fX, offset_.fY); } void OpacityLayer::Paint(PaintContext& context) const { FML_DCHECK(needs_painting(context)); - auto mutator = context.state_stack.save(); - mutator.translate(offset_.fX, offset_.fY); + SkAutoCanvasRestore save(context.internal_nodes_canvas, true); + context.internal_nodes_canvas->translate(offset_.fX, offset_.fY); if (context.raster_cache) { - mutator.integralTransform(); + context.internal_nodes_canvas->setMatrix( + RasterCacheUtil::GetIntegralTransCTM( + context.leaf_nodes_canvas->getTotalMatrix())); } - mutator.applyOpacity(child_paint_bounds(), opacity()); + SkScalar inherited_opacity = context.inherited_opacity; + SkScalar subtree_opacity = opacity() * inherited_opacity; - if (!context.state_stack.painting_is_nop()) { + if (children_can_accept_opacity()) { + context.inherited_opacity = subtree_opacity; PaintChildren(context); + context.inherited_opacity = inherited_opacity; + return; } + + SkPaint paint; + paint.setAlphaf(subtree_opacity); + + if (layer_raster_cache_item_->Draw(context, &paint)) { + return; + } + + // Skia may clip the content with save_layer_bounds (although it's not a + // guaranteed clip). So we have to provide a big enough save_layer_bounds. To + // do so, we first remove the offset from paint bounds since it's already in + // the matrix. Then we round out the bounds. + // + // Note that the following lines are only accessible when the raster cache is + // not available (e.g., when we're using the software backend in golden + // tests). + SkRect save_layer_bounds; + paint_bounds() + .makeOffset(-offset_.fX, -offset_.fY) + .roundOut(&save_layer_bounds); + + Layer::AutoSaveLayer save_layer = + Layer::AutoSaveLayer::Create(context, save_layer_bounds, &paint); + context.inherited_opacity = SK_Scalar1; + PaintChildren(context); + context.inherited_opacity = inherited_opacity; } } // namespace flutter diff --git a/flow/layers/opacity_layer.h b/flow/layers/opacity_layer.h index 041efcef41938..aed2e7c7b0fd6 100644 --- a/flow/layers/opacity_layer.h +++ b/flow/layers/opacity_layer.h @@ -31,7 +31,7 @@ class OpacityLayer : public CacheableContainerLayer { void Diff(DiffContext* context, const Layer* old_layer) override; - void Preroll(PrerollContext* context) override; + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/opacity_layer_unittests.cc b/flow/layers/opacity_layer_unittests.cc index d760944641200..84a8ef29cd5a5 100644 --- a/flow/layers/opacity_layer_unittests.cc +++ b/flow/layers/opacity_layer_unittests.cc @@ -28,7 +28,7 @@ TEST_F(OpacityLayerTest, LeafLayer) { auto layer = std::make_shared(SK_AlphaOPAQUE, SkPoint::Make(0.0f, 0.0f)); - EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context()), + EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), "\\!layers\\(\\)\\.empty\\(\\)"); } @@ -38,7 +38,7 @@ TEST_F(OpacityLayerTest, PaintingEmptyLayerDies) { std::make_shared(SK_AlphaOPAQUE, SkPoint::Make(0.0f, 0.0f)); layer->Add(mock_layer); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(mock_layer->paint_bounds(), SkPath().getBounds()); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); EXPECT_EQ(layer->child_paint_bounds(), mock_layer->paint_bounds()); @@ -71,8 +71,7 @@ TEST_F(OpacityLayerTest, TranslateChildren) { layer->Add(mock_layer1); auto initial_transform = SkMatrix::Scale(2.0, 2.0); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); SkRect layer_bounds = mock_layer1->paint_bounds(); mock_layer1->parent_matrix().mapRect(&layer_bounds); @@ -108,8 +107,7 @@ TEST_F(OpacityLayerTest, CacheChild) { RasterCacheItem::CacheState::kNone); EXPECT_FALSE(cacheable_opacity_item->GetId().has_value()); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); @@ -157,8 +155,7 @@ TEST_F(OpacityLayerTest, CacheChildren) { RasterCacheItem::CacheState::kNone); EXPECT_FALSE(cacheable_opacity_item->GetId().has_value()); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); @@ -183,6 +180,7 @@ TEST_F(OpacityLayerTest, ShouldNotCacheChildren) { opacity_layer->Add(mock_layer); PrerollContext* context = preroll_context(); + context->subtree_can_inherit_opacity = false; use_mock_raster_cache(); @@ -195,10 +193,9 @@ TEST_F(OpacityLayerTest, ShouldNotCacheChildren) { RasterCacheItem::CacheState::kNone); EXPECT_FALSE(cacheable_opacity_item->GetId().has_value()); - opacity_layer->Preroll(preroll_context()); + opacity_layer->Preroll(preroll_context(), SkMatrix::I()); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + EXPECT_TRUE(context->subtree_can_inherit_opacity); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); @@ -220,8 +217,7 @@ TEST_F(OpacityLayerTest, FullyOpaque) { auto layer = std::make_shared(SK_AlphaOPAQUE, layer_offset); layer->Add(mock_layer); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_path.getBounds()); @@ -230,7 +226,7 @@ TEST_F(OpacityLayerTest, FullyOpaque) { EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix::Concat(initial_transform, layer_transform)); EXPECT_EQ(mock_layer->parent_mutators(), - std::vector({Mutator(layer_transform)})); + std::vector({Mutator(layer_transform), Mutator(SK_AlphaOPAQUE)})); const SkPaint opacity_paint = SkPaint(SkColors::kBlack); // A = 1.0f SkRect opacity_bounds; @@ -241,7 +237,11 @@ TEST_F(OpacityLayerTest, FullyOpaque) { MockCanvas::DrawCall{ 1, MockCanvas::ConcatMatrixData{SkM44(layer_transform)}}, MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, + 1, MockCanvas::SaveLayerData{opacity_bounds, + opacity_paint, nullptr, 2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); layer->Paint(paint_context()); EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); @@ -261,8 +261,7 @@ TEST_F(OpacityLayerTest, FullyTransparent) { std::make_shared(SK_AlphaTRANSPARENT, layer_offset); layer->Add(mock_layer); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_path.getBounds()); @@ -274,11 +273,16 @@ TEST_F(OpacityLayerTest, FullyTransparent) { mock_layer->parent_mutators(), std::vector({Mutator(layer_transform), Mutator(SK_AlphaTRANSPARENT)})); - auto expected_draw_calls = - std::vector({MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ConcatMatrixData{SkM44(layer_transform)}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); + auto expected_draw_calls = std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{SkM44(layer_transform)}}, + MockCanvas::DrawCall{1, MockCanvas::SaveData{2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::ClipRectData{kEmptyRect, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); layer->Paint(paint_context()); EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); } @@ -297,8 +301,7 @@ TEST_F(OpacityLayerTest, HalfTransparent) { auto layer = std::make_shared(alpha_half, layer_offset); layer->Add(mock_layer); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_path.getBounds()); @@ -360,8 +363,7 @@ TEST_F(OpacityLayerTest, Nested) { layer1_child_bounds.join(child1_path.getBounds()); layer1_child_bounds.join(child3_path.getBounds()); SkRect expected_layer1_bounds = layer1_transform.mapRect(layer1_child_bounds); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer1->Preroll(preroll_context()); + layer1->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer1->paint_bounds(), child1_path.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child2_path.getBounds()); EXPECT_EQ(mock_layer3->paint_bounds(), child3_path.getBounds()); @@ -394,10 +396,11 @@ TEST_F(OpacityLayerTest, Nested) { opacity1_paint.setAlphaf(alpha1 * (1.0 / SK_AlphaOPAQUE)); SkPaint opacity2_paint; opacity2_paint.setAlphaf(alpha2 * (1.0 / SK_AlphaOPAQUE)); - SkRect opacity1_bounds = - expected_layer1_bounds.makeOffset(-layer1_offset.fX, -layer1_offset.fY); - SkRect opacity2_bounds = - expected_layer2_bounds.makeOffset(-layer2_offset.fX, -layer2_offset.fY); + SkRect opacity1_bounds, opacity2_bounds; + expected_layer1_bounds.makeOffset(-layer1_offset.fX, -layer1_offset.fY) + .roundOut(&opacity1_bounds); + expected_layer2_bounds.makeOffset(-layer2_offset.fX, -layer2_offset.fY) + .roundOut(&opacity2_bounds); auto expected_draw_calls = std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, MockCanvas::DrawCall{ @@ -426,12 +429,13 @@ TEST_F(OpacityLayerTest, Nested) { } TEST_F(OpacityLayerTest, Readback) { + auto initial_transform = SkMatrix(); auto layer = std::make_shared(kOpaque_SkAlphaType, SkPoint()); layer->Add(std::make_shared(SkPath())); // OpacityLayer does not read from surface preroll_context()->surface_needs_readback = false; - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_FALSE(preroll_context()->surface_needs_readback); // OpacityLayer blocks child with readback @@ -439,7 +443,7 @@ TEST_F(OpacityLayerTest, Readback) { mock_layer->set_fake_reads_surface(true); layer->Add(mock_layer); preroll_context()->surface_needs_readback = false; - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_FALSE(preroll_context()->surface_needs_readback); } @@ -451,7 +455,7 @@ TEST_F(OpacityLayerTest, CullRectIsTransformed) { auto mock_layer = std::make_shared(SkPath()); clip_rect_layer->Add(opacity_layer); opacity_layer->Add(mock_layer); - clip_rect_layer->Preroll(preroll_context()); + clip_rect_layer->Preroll(preroll_context(), SkMatrix::I()); EXPECT_EQ(mock_layer->parent_cull_rect().fLeft, -20); EXPECT_EQ(mock_layer->parent_cull_rect().fTop, -20); } @@ -463,9 +467,9 @@ TEST_F(OpacityLayerTest, OpacityInheritanceCompatibleChild) { opacity_layer->Add(mock_layer); PrerollContext* context = preroll_context(); - opacity_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + opacity_layer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); } @@ -476,9 +480,9 @@ TEST_F(OpacityLayerTest, OpacityInheritanceIncompatibleChild) { opacity_layer->Add(mock_layer); PrerollContext* context = preroll_context(); - opacity_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + opacity_layer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); EXPECT_FALSE(opacity_layer->children_can_accept_opacity()); } @@ -491,10 +495,14 @@ TEST_F(OpacityLayerTest, OpacityInheritanceThroughContainer) { opacity_layer->Add(container_layer); PrerollContext* context = preroll_context(); - opacity_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); - EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); + context->subtree_can_inherit_opacity = false; + opacity_layer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); + // By default a container layer will not pass opacity through to + // its children - specific subclasses will have to enable this + // pass through by setting the flag to true themselves before + // calling their super method ContainerLayer::Preroll(). + EXPECT_FALSE(opacity_layer->children_can_accept_opacity()); } TEST_F(OpacityLayerTest, OpacityInheritanceThroughTransform) { @@ -506,9 +514,9 @@ TEST_F(OpacityLayerTest, OpacityInheritanceThroughTransform) { opacity_layer->Add(transformLayer); PrerollContext* context = preroll_context(); - opacity_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + opacity_layer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); } @@ -522,9 +530,9 @@ TEST_F(OpacityLayerTest, OpacityInheritanceThroughImageFilter) { opacity_layer->Add(filter_layer); PrerollContext* context = preroll_context(); - opacity_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + opacity_layer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); } @@ -539,9 +547,9 @@ TEST_F(OpacityLayerTest, OpacityInheritanceNestedWithCompatibleChild) { opacity_layer_1->Add(opacity_layer_2); PrerollContext* context = preroll_context(); - opacity_layer_1->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + opacity_layer_1->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); EXPECT_TRUE(opacity_layer_1->children_can_accept_opacity()); EXPECT_TRUE(opacity_layer_2->children_can_accept_opacity()); @@ -560,8 +568,13 @@ TEST_F(OpacityLayerTest, OpacityInheritanceNestedWithCompatibleChild) { { expected_builder.translate(offset2.fX, offset2.fY); /* mock_layer::Paint */ { - expected_builder.drawPath(mock_path, - DlPaint().setOpacity(inherited_opacity)); + expected_builder.setColor(savelayer_paint.getAlpha() << 24); + expected_builder.saveLayer(&mock_path.getBounds(), true); + { + expected_builder.setColor(0xFF000000); + expected_builder.drawPath(mock_path); + } + expected_builder.restore(); } } expected_builder.restore(); @@ -585,9 +598,9 @@ TEST_F(OpacityLayerTest, OpacityInheritanceNestedWithIncompatibleChild) { opacity_layer_1->Add(opacity_layer_2); PrerollContext* context = preroll_context(); - opacity_layer_1->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + opacity_layer_1->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); EXPECT_TRUE(opacity_layer_1->children_can_accept_opacity()); EXPECT_FALSE(opacity_layer_2->children_can_accept_opacity()); @@ -664,9 +677,7 @@ TEST_F(OpacityLayerTest, FullyOpaqueWithFractionalValues) { auto mock_layer = std::make_shared(child_path, child_paint); auto layer = std::make_shared(SK_AlphaOPAQUE, layer_offset); layer->Add(mock_layer); - - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); const SkPaint opacity_paint = SkPaint(SkColors::kBlack); // A = 1.0f SkRect opacity_bounds; @@ -679,8 +690,12 @@ TEST_F(OpacityLayerTest, FullyOpaqueWithFractionalValues) { MockCanvas::DrawCall{ 1, MockCanvas::SetMatrixData{SkM44( RasterCacheUtil::GetIntegralTransCTM(layer_transform))}}, - MockCanvas::DrawCall{1, + MockCanvas::DrawCall{ + 1, MockCanvas::SaveLayerData{opacity_bounds, opacity_paint, nullptr, + 2}}, + MockCanvas::DrawCall{2, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); layer->Paint(paint_context()); EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); diff --git a/flow/layers/performance_overlay_layer.cc b/flow/layers/performance_overlay_layer.cc index 680873c5cab3f..fe8b76288a90c 100644 --- a/flow/layers/performance_overlay_layer.cc +++ b/flow/layers/performance_overlay_layer.cc @@ -97,15 +97,16 @@ void PerformanceOverlayLayer::Paint(PaintContext& context) const { SkScalar y = paint_bounds().y() + padding; SkScalar width = paint_bounds().width() - (padding * 2); SkScalar height = paint_bounds().height() / 2; - auto mutator = context.state_stack.save(); + SkAutoCanvasRestore save(context.leaf_nodes_canvas, true); VisualizeStopWatch( - context.canvas, context.raster_time, x, y, width, height - padding, - options_ & kVisualizeRasterizerStatistics, + context.leaf_nodes_canvas, context.raster_time, x, y, width, + height - padding, options_ & kVisualizeRasterizerStatistics, options_ & kDisplayRasterizerStatistics, "Raster", font_path_); - VisualizeStopWatch(context.canvas, context.ui_time, x, y + height, width, - height - padding, options_ & kVisualizeEngineStatistics, + VisualizeStopWatch(context.leaf_nodes_canvas, context.ui_time, x, y + height, + width, height - padding, + options_ & kVisualizeEngineStatistics, options_ & kDisplayEngineStatistics, "UI", font_path_); } diff --git a/flow/layers/performance_overlay_layer.h b/flow/layers/performance_overlay_layer.h index 62f26754574e0..2645a6a508452 100644 --- a/flow/layers/performance_overlay_layer.h +++ b/flow/layers/performance_overlay_layer.h @@ -39,7 +39,6 @@ class PerformanceOverlayLayer : public Layer { explicit PerformanceOverlayLayer(uint64_t options, const char* font_path = nullptr); - void Preroll(PrerollContext* context) override {} void Paint(PaintContext& context) const override; private: diff --git a/flow/layers/performance_overlay_layer_unittests.cc b/flow/layers/performance_overlay_layer_unittests.cc index 7c2f1edbe8265..23e92937ea328 100644 --- a/flow/layers/performance_overlay_layer_unittests.cc +++ b/flow/layers/performance_overlay_layer_unittests.cc @@ -58,17 +58,17 @@ static void TestPerformanceOverlayLayerGold(int refresh_rate) { ASSERT_TRUE(surface != nullptr); - LayerStateStack state_stack; flutter::PaintContext paint_context = { // clang-format off - .state_stack = state_stack, - .canvas = surface->getCanvas(), + .internal_nodes_canvas = nullptr, + .leaf_nodes_canvas = surface->getCanvas(), .gr_context = nullptr, .view_embedder = nullptr, .raster_time = mock_stopwatch, .ui_time = mock_stopwatch, .texture_registry = nullptr, .raster_cache = nullptr, + .checkerboard_offscreen_layers = false, .frame_device_pixel_ratio = 1.0f, // clang-format on }; @@ -131,7 +131,7 @@ TEST_F(PerformanceOverlayLayerTest, PaintingEmptyLayerDies) { const uint64_t overlay_opts = kVisualizeRasterizerStatistics; auto layer = std::make_shared(overlay_opts); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); EXPECT_FALSE(layer->needs_painting(paint_context())); @@ -148,7 +148,7 @@ TEST_F(PerformanceOverlayLayerTest, InvalidOptions) { // this a constructor parameter and move the set_paint_bounds into Preroll layer->set_paint_bounds(layer_bounds); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), layer_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); @@ -166,7 +166,7 @@ TEST_F(PerformanceOverlayLayerTest, SimpleRasterizerStatistics) { // this a constructor parameter and move the set_paint_bounds into Preroll layer->set_paint_bounds(layer_bounds); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), layer_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); @@ -197,7 +197,7 @@ TEST_F(PerformanceOverlayLayerTest, MarkAsDirtyWhenResized) { const uint64_t overlay_opts = kVisualizeRasterizerStatistics; auto layer = std::make_shared(overlay_opts); layer->set_paint_bounds(SkRect::MakeLTRB(0.0f, 0.0f, 48.0f, 48.0f)); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); layer->Paint(paint_context()); auto data = mock_canvas().draw_calls().front().data; auto image_data = std::get(data); @@ -206,7 +206,7 @@ TEST_F(PerformanceOverlayLayerTest, MarkAsDirtyWhenResized) { // Create a second PerformanceOverlayLayer with different bounds. layer = std::make_shared(overlay_opts); layer->set_paint_bounds(SkRect::MakeLTRB(0.0f, 0.0f, 64.0f, 64.0f)); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); layer->Paint(paint_context()); data = mock_canvas().draw_calls().back().data; image_data = std::get(data); diff --git a/flow/layers/physical_shape_layer.cc b/flow/layers/physical_shape_layer.cc index 77f347271e28e..d84320228b5ea 100644 --- a/flow/layers/physical_shape_layer.cc +++ b/flow/layers/physical_shape_layer.cc @@ -50,14 +50,13 @@ void PhysicalShapeLayer::Diff(DiffContext* context, const Layer* old_layer) { context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); } -void PhysicalShapeLayer::Preroll(PrerollContext* context) { +void PhysicalShapeLayer::Preroll(PrerollContext* context, + const SkMatrix& matrix) { Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); SkRect child_paint_bounds = SkRect::MakeEmpty(); - PrerollChildren(context, &child_paint_bounds); - context->renderable_state_flags = - UsesSaveLayer() ? Layer::kSaveLayerRenderFlags : 0; + PrerollChildren(context, matrix, &child_paint_bounds); SkRect paint_bounds; if (elevation_ == 0) { @@ -66,8 +65,7 @@ void PhysicalShapeLayer::Preroll(PrerollContext* context) { // We will draw the shadow in Paint(), so add some margin to the paint // bounds to leave space for the shadow. paint_bounds = DisplayListCanvasDispatcher::ComputeShadowBounds( - path_, elevation_, context->frame_device_pixel_ratio, - context->state_stack.transform_3x3()); + path_, elevation_, context->frame_device_pixel_ratio, matrix); } if (clip_behavior_ == Clip::none) { @@ -82,7 +80,7 @@ void PhysicalShapeLayer::Paint(PaintContext& context) const { if (elevation_ != 0) { DisplayListCanvasDispatcher::DrawShadow( - context.canvas, path_, shadow_color_, elevation_, + context.leaf_nodes_canvas, path_, shadow_color_, elevation_, SkColorGetA(color_) != 0xff, context.frame_device_pixel_ratio); } @@ -91,21 +89,21 @@ void PhysicalShapeLayer::Paint(PaintContext& context) const { paint.setColor(color_); paint.setAntiAlias(true); if (clip_behavior_ != Clip::antiAliasWithSaveLayer) { - context.canvas->drawPath(path_, paint); + context.leaf_nodes_canvas->drawPath(path_, paint); } - auto mutator = context.state_stack.save(); + int save_count = context.internal_nodes_canvas->save(); switch (clip_behavior_) { case Clip::hardEdge: - mutator.clipPath(path_, false); + context.internal_nodes_canvas->clipPath(path_, false); break; case Clip::antiAlias: - mutator.clipPath(path_, true); + context.internal_nodes_canvas->clipPath(path_, true); break; case Clip::antiAliasWithSaveLayer: { TRACE_EVENT0("flutter", "Canvas::saveLayer"); - mutator.clipPath(path_, true); - mutator.saveLayer(paint_bounds()); + context.internal_nodes_canvas->clipPath(path_, true); + context.internal_nodes_canvas->saveLayer(paint_bounds(), nullptr); } break; case Clip::none: break; @@ -116,10 +114,18 @@ void PhysicalShapeLayer::Paint(PaintContext& context) const { // (https://github.com/flutter/flutter/issues/18057#issue-328003931) // using saveLayer, we have to call drawPaint instead of drawPath as // anti-aliased drawPath will always have such artifacts. - context.canvas->drawPaint(paint); + context.leaf_nodes_canvas->drawPaint(paint); } PaintChildren(context); + + context.internal_nodes_canvas->restoreToCount(save_count); + + if (UsesSaveLayer()) { + if (context.checkerboard_offscreen_layers) { + DrawCheckerboard(context.internal_nodes_canvas, paint_bounds()); + } + } } } // namespace flutter diff --git a/flow/layers/physical_shape_layer.h b/flow/layers/physical_shape_layer.h index b4a90785f6585..0f2b24b87f754 100644 --- a/flow/layers/physical_shape_layer.h +++ b/flow/layers/physical_shape_layer.h @@ -19,7 +19,7 @@ class PhysicalShapeLayer : public ContainerLayer { void Diff(DiffContext* context, const Layer* old_layer) override; - void Preroll(PrerollContext* context) override; + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/physical_shape_layer_unittests.cc b/flow/layers/physical_shape_layer_unittests.cc index b6c8e664254f1..97937a9de8c97 100644 --- a/flow/layers/physical_shape_layer_unittests.cc +++ b/flow/layers/physical_shape_layer_unittests.cc @@ -23,7 +23,7 @@ TEST_F(PhysicalShapeLayerTest, PaintingEmptyLayerDies) { 0.0f, // elevation SkPath(), Clip::none); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); EXPECT_EQ(layer->child_paint_bounds(), SkRect::MakeEmpty()); EXPECT_FALSE(layer->needs_painting(paint_context())); @@ -54,7 +54,7 @@ TEST_F(PhysicalShapeLayerTest, NonEmptyLayer) { std::make_shared(SK_ColorGREEN, SK_ColorBLACK, 0.0f, // elevation layer_path, Clip::none); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), layer_path.getBounds()); EXPECT_EQ(layer->child_paint_bounds(), SkRect::MakeEmpty()); EXPECT_TRUE(layer->needs_painting(paint_context())); @@ -90,7 +90,7 @@ TEST_F(PhysicalShapeLayerTest, ChildrenLargerThanPathClip) { layer->Add(child2); SkRect child_paint_bounds = SkRect::MakeEmpty(); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); child_paint_bounds.join(child1->paint_bounds()); child_paint_bounds.join(child2->paint_bounds()); EXPECT_EQ(layer->paint_bounds(), layer_path.getBounds()); @@ -118,6 +118,8 @@ TEST_F(PhysicalShapeLayerTest, ChildrenLargerThanPathClip) { SkClipOp::kIntersect}}, MockCanvas::DrawCall{ 1, MockCanvas::DrawPathData{child1_path, child1_paint}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child2_path, child2_paint}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}, })); } @@ -143,7 +145,7 @@ TEST_F(PhysicalShapeLayerTest, ChildrenLargerThanPathNoClip) { layer->Add(child1); layer->Add(child2); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); SkRect child_bounds = child1->paint_bounds(); child_bounds.join(child2->paint_bounds()); SkRect total_bounds = child_bounds; @@ -180,7 +182,7 @@ TEST_F(PhysicalShapeLayerTest, ElevationSimple) { auto layer = std::make_shared( SK_ColorGREEN, SK_ColorBLACK, initial_elevation, layer_path, Clip::none); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and // their shadows , so we do not do any painting there. EXPECT_EQ(layer->paint_bounds(), @@ -227,7 +229,7 @@ TEST_F(PhysicalShapeLayerTest, ElevationComplex) { layers[0]->Add(layers[2]); layers[2]->Add(layers[3]); - layers[0]->Preroll(preroll_context()); + layers[0]->Preroll(preroll_context(), SkMatrix()); for (int i = 0; i < 4; i += 1) { // On Fuchsia, the system compositor handles all elevated // PhysicalShapeLayers and their shadows , so we do not do any painting @@ -355,6 +357,7 @@ static bool ReadbackResult(PrerollContext* context, Clip clip_behavior, const std::shared_ptr& child, bool before) { + const SkMatrix initial_matrix = SkMatrix(); const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); const SkPath layer_path = SkPath().addRect(layer_bounds); auto layer = @@ -365,7 +368,7 @@ static bool ReadbackResult(PrerollContext* context, layer->Add(child); } context->surface_needs_readback = before; - layer->Preroll(context); + layer->Preroll(context, initial_matrix); return context->surface_needs_readback; } @@ -423,9 +426,9 @@ TEST_F(PhysicalShapeLayerTest, OpacityInheritance) { layer_path, Clip::none); PrerollContext* context = preroll_context(); - context->renderable_state_flags = 0; - layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, 0); + context->subtree_can_inherit_opacity = false; + layer->Preroll(context, SkMatrix()); + EXPECT_FALSE(context->subtree_can_inherit_opacity); } using PhysicalShapeLayerDiffTest = DiffContextTest; diff --git a/flow/layers/platform_view_layer.cc b/flow/layers/platform_view_layer.cc index d5b581ce05c84..a86886fb1b04f 100644 --- a/flow/layers/platform_view_layer.cc +++ b/flow/layers/platform_view_layer.cc @@ -11,7 +11,8 @@ PlatformViewLayer::PlatformViewLayer(const SkPoint& offset, int64_t view_id) : offset_(offset), size_(size), view_id_(view_id) {} -void PlatformViewLayer::Preroll(PrerollContext* context) { +void PlatformViewLayer::Preroll(PrerollContext* context, + const SkMatrix& matrix) { set_paint_bounds(SkRect::MakeXYWH(offset_.x(), offset_.y(), size_.width(), size_.height())); @@ -22,10 +23,9 @@ void PlatformViewLayer::Preroll(PrerollContext* context) { } context->has_platform_view = true; set_subtree_has_platform_view(true); - auto mutators = context->state_stack.mutators_delegate(); std::unique_ptr params = - std::make_unique(context->state_stack.transform_3x3(), - size_, *mutators, + std::make_unique(matrix, size_, + context->mutators_stack, context->display_list_enabled); context->view_embedder->PrerollCompositeEmbeddedView(view_id_, std::move(params)); @@ -40,13 +40,8 @@ void PlatformViewLayer::Paint(PaintContext& context) const { } EmbedderPaintContext embedder_context = context.view_embedder->CompositeEmbeddedView(view_id_); - context.canvas = embedder_context.canvas; - context.builder = embedder_context.builder; - if (context.builder) { - context.state_stack.set_delegate(context.builder); - } else { - context.state_stack.set_delegate(context.canvas); - } + context.leaf_nodes_canvas = embedder_context.canvas; + context.leaf_nodes_builder = embedder_context.builder; } } // namespace flutter diff --git a/flow/layers/platform_view_layer.h b/flow/layers/platform_view_layer.h index f812e52901ca0..242b3734dd3b1 100644 --- a/flow/layers/platform_view_layer.h +++ b/flow/layers/platform_view_layer.h @@ -15,7 +15,7 @@ class PlatformViewLayer : public Layer { public: PlatformViewLayer(const SkPoint& offset, const SkSize& size, int64_t view_id); - void Preroll(PrerollContext* context) override; + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; private: diff --git a/flow/layers/platform_view_layer_unittests.cc b/flow/layers/platform_view_layer_unittests.cc index c30661824c22a..ecbbe3f39f036 100644 --- a/flow/layers/platform_view_layer_unittests.cc +++ b/flow/layers/platform_view_layer_unittests.cc @@ -4,7 +4,6 @@ #include "flutter/flow/layers/clip_rect_layer.h" #include "flutter/flow/layers/platform_view_layer.h" -#include "flutter/flow/layers/transform_layer.h" #include "flutter/flow/testing/layer_test.h" #include "flutter/flow/testing/mock_embedder.h" @@ -24,7 +23,7 @@ TEST_F(PlatformViewLayerTest, NullViewEmbedderDoesntPrerollCompositeOrPaint) { auto layer = std::make_shared(layer_offset, layer_size, view_id); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_FALSE(preroll_context()->has_platform_view); EXPECT_EQ(layer->paint_bounds(), SkRect::MakeSize(layer_size) @@ -33,7 +32,7 @@ TEST_F(PlatformViewLayerTest, NullViewEmbedderDoesntPrerollCompositeOrPaint) { EXPECT_FALSE(layer->subtree_has_platform_view()); layer->Paint(paint_context()); - EXPECT_EQ(paint_context().canvas, &mock_canvas()); + EXPECT_EQ(paint_context().leaf_nodes_canvas, &mock_canvas()); EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); } @@ -55,7 +54,7 @@ TEST_F(PlatformViewLayerTest, ClippedPlatformViewPrerollsAndPaintsNothing) { auto embedder = MockViewEmbedder(); preroll_context()->view_embedder = &embedder; - parent_clip_layer->Preroll(preroll_context()); + parent_clip_layer->Preroll(preroll_context(), SkMatrix()); EXPECT_TRUE(preroll_context()->has_platform_view); EXPECT_EQ(layer->paint_bounds(), SkRect::MakeSize(layer_size) @@ -68,7 +67,7 @@ TEST_F(PlatformViewLayerTest, ClippedPlatformViewPrerollsAndPaintsNothing) { EXPECT_TRUE(parent_clip_layer->subtree_has_platform_view()); parent_clip_layer->Paint(paint_context()); - EXPECT_EQ(paint_context().canvas, &mock_canvas()); + EXPECT_EQ(paint_context().leaf_nodes_canvas, &mock_canvas()); EXPECT_EQ( mock_canvas().draw_calls(), std::vector( @@ -76,6 +75,11 @@ TEST_F(PlatformViewLayerTest, ClippedPlatformViewPrerollsAndPaintsNothing) { MockCanvas::DrawCall{ 1, MockCanvas::ClipRectData{parent_clip, SkClipOp::kIntersect, MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{1, MockCanvas::SaveData{2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::ClipRectData{child_clip, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } @@ -87,49 +91,9 @@ TEST_F(PlatformViewLayerTest, OpacityInheritance) { std::make_shared(layer_offset, layer_size, view_id); PrerollContext* context = preroll_context(); - layer->Preroll(preroll_context()); - EXPECT_EQ(context->renderable_state_flags, 0); -} - -TEST_F(PlatformViewLayerTest, StateTransfer) { - const SkMatrix transform1 = SkMatrix::Translate(5, 5); - const SkMatrix transform2 = SkMatrix::Translate(15, 15); - const SkMatrix combined_transform = SkMatrix::Translate(20, 20); - const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); - const SkSize layer_size = SkSize::Make(8.0f, 8.0f); - const int64_t view_id = 0; - const SkPath path1 = SkPath().addOval({10, 10, 20, 20}); - const SkPath path2 = SkPath().addOval({15, 15, 30, 30}); - - // transform_layer1 - // |- child1 - // |- platform_layer - // |- transform_layer2 - // |- child2 - auto transform_layer1 = std::make_shared(transform1); - auto transform_layer2 = std::make_shared(transform2); - auto platform_layer = - std::make_shared(layer_offset, layer_size, view_id); - auto child1 = std::make_shared(path1); - child1->set_expected_paint_matrix(transform1); - auto child2 = std::make_shared(path2); - child2->set_expected_paint_matrix(combined_transform); - transform_layer1->Add(child1); - transform_layer1->Add(platform_layer); - transform_layer1->Add(transform_layer2); - transform_layer2->Add(child2); - - auto embedder = MockViewEmbedder(); - DisplayListCanvasRecorder recorder({0, 0, 500, 500}); - embedder.AddRecorder(&recorder); - - PrerollContext* preroll_ctx = preroll_context(); - preroll_ctx->view_embedder = &embedder; - transform_layer1->Preroll(preroll_ctx); - - PaintContext& paint_ctx = paint_context(); - paint_ctx.view_embedder = &embedder; - transform_layer1->Paint(paint_ctx); + context->subtree_can_inherit_opacity = false; + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_FALSE(context->subtree_can_inherit_opacity); } } // namespace testing diff --git a/flow/layers/shader_mask_layer.cc b/flow/layers/shader_mask_layer.cc index d22c41b1b27f6..1b88bc88dfd61 100644 --- a/flow/layers/shader_mask_layer.cc +++ b/flow/layers/shader_mask_layer.cc @@ -35,36 +35,40 @@ void ShaderMaskLayer::Diff(DiffContext* context, const Layer* old_layer) { context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); } -void ShaderMaskLayer::Preroll(PrerollContext* context) { +void ShaderMaskLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context); - AutoCache cache = AutoCache(layer_raster_cache_item_.get(), context, - context->state_stack.transform_3x3()); + SkMatrix child_matrix = matrix; + AutoCache cache = + AutoCache(layer_raster_cache_item_.get(), context, child_matrix); - ContainerLayer::Preroll(context); + ContainerLayer::Preroll(context, child_matrix); // We always paint with a saveLayer (or a cached rendering), // so we can always apply opacity in any of those cases. - context->renderable_state_flags = kSaveLayerRenderFlags; + context->subtree_can_inherit_opacity = true; } void ShaderMaskLayer::Paint(PaintContext& context) const { FML_DCHECK(needs_painting(context)); - auto mutator = context.state_stack.save(); + AutoCachePaint cache_paint(context); if (context.raster_cache) { - mutator.integralTransform(); + context.internal_nodes_canvas->setMatrix( + RasterCacheUtil::GetIntegralTransCTM( + context.leaf_nodes_canvas->getTotalMatrix())); + } - SkPaint sk_paint; - if (layer_raster_cache_item_->Draw(context, - context.state_stack.fill(sk_paint))) { + if (context.raster_cache) { + if (layer_raster_cache_item_->Draw(context, cache_paint.sk_paint())) { return; } } auto shader_rect = SkRect::MakeWH(mask_rect_.width(), mask_rect_.height()); - mutator.saveLayer(paint_bounds()); - if (context.builder) { + if (context.leaf_nodes_builder) { + context.builder_multiplexer->saveLayer(&paint_bounds(), + cache_paint.dl_paint()); PaintChildren(context); DlPaint dl_paint; @@ -72,17 +76,20 @@ void ShaderMaskLayer::Paint(PaintContext& context) const { if (color_source_) { dl_paint.setColorSource(color_source_.get()); } - context.builder->translate(mask_rect_.left(), mask_rect_.top()); - context.builder->drawRect(shader_rect, dl_paint); + context.leaf_nodes_builder->translate(mask_rect_.left(), mask_rect_.top()); + context.leaf_nodes_builder->drawRect(shader_rect, dl_paint); + context.builder_multiplexer->restore(); } else { + Layer::AutoSaveLayer save = Layer::AutoSaveLayer::Create( + context, paint_bounds(), cache_paint.sk_paint()); PaintChildren(context); SkPaint paint; paint.setBlendMode(ToSk(blend_mode_)); if (color_source_) { paint.setShader(color_source_->skia_object()); } - context.canvas->translate(mask_rect_.left(), mask_rect_.top()); - context.canvas->drawRect(shader_rect, paint); + context.leaf_nodes_canvas->translate(mask_rect_.left(), mask_rect_.top()); + context.leaf_nodes_canvas->drawRect(shader_rect, paint); } } diff --git a/flow/layers/shader_mask_layer.h b/flow/layers/shader_mask_layer.h index ae3b1d6accea5..491348ae66347 100644 --- a/flow/layers/shader_mask_layer.h +++ b/flow/layers/shader_mask_layer.h @@ -18,7 +18,7 @@ class ShaderMaskLayer : public CacheableContainerLayer { void Diff(DiffContext* context, const Layer* old_layer) override; - void Preroll(PrerollContext* context) override; + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/shader_mask_layer_unittests.cc b/flow/layers/shader_mask_layer_unittests.cc index 73e23f7880abc..f19d72e8bd742 100644 --- a/flow/layers/shader_mask_layer_unittests.cc +++ b/flow/layers/shader_mask_layer_unittests.cc @@ -26,7 +26,7 @@ TEST_F(ShaderMaskLayerTest, PaintingEmptyLayerDies) { auto layer = std::make_shared(nullptr, kEmptyRect, DlBlendMode::kSrc); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); EXPECT_FALSE(layer->needs_painting(paint_context())); @@ -61,8 +61,7 @@ TEST_F(ShaderMaskLayerTest, EmptyFilter) { DlBlendMode::kSrc); layer->Add(mock_layer); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); @@ -106,8 +105,7 @@ TEST_F(ShaderMaskLayerTest, SimpleFilter) { DlBlendMode::kSrc); layer->Add(mock_layer); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); @@ -156,8 +154,7 @@ TEST_F(ShaderMaskLayerTest, MultipleChildren) { SkRect children_bounds = child_path1.getBounds(); children_bounds.join(child_path2.getBounds()); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer->paint_bounds(), children_bounds); @@ -219,8 +216,7 @@ TEST_F(ShaderMaskLayerTest, Nested) { SkRect children_bounds = child_path1.getBounds(); children_bounds.join(child_path2.getBounds()); - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer1->Preroll(preroll_context()); + layer1->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer1->paint_bounds(), children_bounds); @@ -274,6 +270,7 @@ TEST_F(ShaderMaskLayerTest, Nested) { } TEST_F(ShaderMaskLayerTest, Readback) { + auto initial_transform = SkMatrix(); const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 20.5f, 20.5f); auto layer_filter = SkPerlinNoiseShader::MakeFractalNoise(1.0f, 1.0f, 1, 1.0f); @@ -283,7 +280,7 @@ TEST_F(ShaderMaskLayerTest, Readback) { // ShaderMaskLayer does not read from surface preroll_context()->surface_needs_readback = false; - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_FALSE(preroll_context()->surface_needs_readback); // ShaderMaskLayer blocks child with readback @@ -291,7 +288,7 @@ TEST_F(ShaderMaskLayerTest, Readback) { mock_layer->set_fake_reads_surface(true); layer->Add(mock_layer); preroll_context()->surface_needs_readback = false; - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); EXPECT_FALSE(preroll_context()->surface_needs_readback); } @@ -313,7 +310,6 @@ TEST_F(ShaderMaskLayerTest, LayerCached) { cache_canvas.setMatrix(cache_ctm); use_mock_raster_cache(); - preroll_context()->state_stack.set_initial_transform(initial_transform); const auto* cacheable_shader_masker_item = layer->raster_cache_item(); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); @@ -322,7 +318,7 @@ TEST_F(ShaderMaskLayerTest, LayerCached) { EXPECT_FALSE(cacheable_shader_masker_item->GetId().has_value()); // frame 1. - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); @@ -331,7 +327,7 @@ TEST_F(ShaderMaskLayerTest, LayerCached) { EXPECT_FALSE(cacheable_shader_masker_item->GetId().has_value()); // frame 2. - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); EXPECT_EQ(cacheable_shader_masker_item->cache_state(), @@ -339,7 +335,7 @@ TEST_F(ShaderMaskLayerTest, LayerCached) { EXPECT_FALSE(cacheable_shader_masker_item->GetId().has_value()); // frame 3. - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); EXPECT_EQ(cacheable_shader_masker_item->cache_state(), @@ -360,14 +356,16 @@ TEST_F(ShaderMaskLayerTest, OpacityInheritance) { // ShaderMaskLayers can always support opacity despite incompatible children PrerollContext* context = preroll_context(); - shader_mask_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags); + context->subtree_can_inherit_opacity = false; + shader_mask_layer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); int opacity_alpha = 0x7F; SkPoint offset = SkPoint::Make(10, 10); auto opacity_layer = std::make_shared(opacity_alpha, offset); opacity_layer->Add(shader_mask_layer); - opacity_layer->Preroll(context); + context->subtree_can_inherit_opacity = false; + opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); DisplayListBuilder expected_builder; @@ -412,9 +410,7 @@ TEST_F(ShaderMaskLayerTest, SimpleFilterWithRasterCache) { auto layer = std::make_shared(dl_filter, layer_bounds, DlBlendMode::kSrc); layer->Add(mock_layer); - - preroll_context()->state_stack.set_initial_transform(initial_transform); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), initial_transform); SkPaint filter_paint; filter_paint.setBlendMode(SkBlendMode::kSrc); @@ -422,23 +418,21 @@ TEST_F(ShaderMaskLayerTest, SimpleFilterWithRasterCache) { layer->Paint(paint_context()); EXPECT_EQ( mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkM44( + std::vector({MockCanvas::DrawCall{0, MockCanvas::SetMatrixData{SkM44( SkMatrix::Translate(0.0, 0.0))}}, MockCanvas::DrawCall{ - 1, MockCanvas::SaveLayerData{child_bounds, SkPaint(), - nullptr, 2}}, + 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(), + nullptr, 1}}, MockCanvas::DrawCall{ - 2, MockCanvas::DrawPathData{child_path, child_paint}}, + 1, MockCanvas::DrawPathData{child_path, child_paint}}, MockCanvas::DrawCall{ - 2, MockCanvas::ConcatMatrixData{SkM44::Translate( + 1, MockCanvas::ConcatMatrixData{SkM44::Translate( layer_bounds.fLeft, layer_bounds.fTop)}}, MockCanvas::DrawCall{ - 2, MockCanvas::DrawRectData{SkRect::MakeWH( + 1, MockCanvas::DrawRectData{SkRect::MakeWH( layer_bounds.width(), layer_bounds.height()), filter_paint}}, - MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } diff --git a/flow/layers/texture_layer.cc b/flow/layers/texture_layer.cc index 5e0b3a7b8dd92..00361d1606132 100644 --- a/flow/layers/texture_layer.cc +++ b/flow/layers/texture_layer.cc @@ -40,11 +40,11 @@ void TextureLayer::Diff(DiffContext* context, const Layer* old_layer) { context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); } -void TextureLayer::Preroll(PrerollContext* context) { +void TextureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { set_paint_bounds(SkRect::MakeXYWH(offset_.x(), offset_.y(), size_.width(), size_.height())); context->has_texture_layer = true; - context->renderable_state_flags = LayerStateStack::kCallerCanApplyOpacity; + context->subtree_can_inherit_opacity = true; } void TextureLayer::Paint(PaintContext& context) const { @@ -58,14 +58,13 @@ void TextureLayer::Paint(PaintContext& context) const { TRACE_EVENT_INSTANT0("flutter", "null texture"); return; } - SkPaint sk_paint; - DlPaint dl_paint; + AutoCachePaint cache_paint(context); Texture::PaintContext ctx{ - .canvas = context.canvas, - .builder = context.builder, + .canvas = context.leaf_nodes_canvas, + .builder = context.leaf_nodes_builder, .gr_context = context.gr_context, - .sk_paint = context.state_stack.fill(sk_paint), - .dl_paint = context.state_stack.fill(dl_paint), + .sk_paint = cache_paint.sk_paint(), + .dl_paint = cache_paint.dl_paint(), }; texture->Paint(ctx, paint_bounds(), freeze_, ToSk(sampling_)); } diff --git a/flow/layers/texture_layer.h b/flow/layers/texture_layer.h index 460e6a7fec12f..434d74a81de73 100644 --- a/flow/layers/texture_layer.h +++ b/flow/layers/texture_layer.h @@ -27,7 +27,7 @@ class TextureLayer : public Layer { const TextureLayer* as_texture_layer() const override { return this; } - void Preroll(PrerollContext* context) override; + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; private: diff --git a/flow/layers/texture_layer_unittests.cc b/flow/layers/texture_layer_unittests.cc index 0862a81da9ab3..7d45d1d54e825 100644 --- a/flow/layers/texture_layer_unittests.cc +++ b/flow/layers/texture_layer_unittests.cc @@ -22,7 +22,7 @@ TEST_F(TextureLayerTest, InvalidTexture) { auto layer = std::make_shared( layer_offset, layer_size, 0, false, DlImageSampling::kNearestNeighbor); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), (SkRect::MakeSize(layer_size) .makeOffset(layer_offset.fX, layer_offset.fY))); @@ -45,7 +45,7 @@ TEST_F(TextureLayerTest, PaintingEmptyLayerDies) { // Ensure the texture is located by the Layer. preroll_context()->texture_registry->RegisterTexture(mock_texture); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); EXPECT_FALSE(layer->needs_painting(paint_context())); @@ -80,7 +80,7 @@ TEST_F(TextureLayerTest, PaintingWithLinearSampling) { // Ensure the texture is located by the Layer. preroll_context()->texture_registry->RegisterTexture(mock_texture); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), (SkRect::MakeSize(layer_size) .makeOffset(layer_offset.fX, layer_offset.fY))); @@ -128,10 +128,10 @@ TEST_F(TextureLayerTest, OpacityInheritance) { // The texture layer always reports opacity compatibility. PrerollContext* context = preroll_context(); + context->subtree_can_inherit_opacity = false; context->texture_registry->RegisterTexture(mock_texture); - layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + layer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); // MockTexture has no actual textur to render into the // PaintContext canvas so we have no way to verify its diff --git a/flow/layers/transform_layer.cc b/flow/layers/transform_layer.cc index 0c72924fd65cf..335c80d070739 100644 --- a/flow/layers/transform_layer.cc +++ b/flow/layers/transform_layer.cc @@ -40,22 +40,39 @@ void TransformLayer::Diff(DiffContext* context, const Layer* old_layer) { context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); } -void TransformLayer::Preroll(PrerollContext* context) { - auto mutator = context->state_stack.save(); - mutator.transform(transform_); +void TransformLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { + SkMatrix child_matrix; + child_matrix.setConcat(matrix, transform_); + context->mutators_stack.PushTransform(transform_); + SkRect previous_cull_rect = context->cull_rect; + SkMatrix inverse_transform; + // Perspective projections don't produce rectangles that are useful for + // culling for some reason. + if (!transform_.hasPerspective() && transform_.invert(&inverse_transform)) { + inverse_transform.mapRect(&context->cull_rect); + } else { + context->cull_rect = kGiantRect; + } + + // Collect inheritance information on our children in Preroll so that + // we can pass it along by default. + context->subtree_can_inherit_opacity = true; SkRect child_paint_bounds = SkRect::MakeEmpty(); - PrerollChildren(context, &child_paint_bounds); + PrerollChildren(context, child_matrix, &child_paint_bounds); transform_.mapRect(&child_paint_bounds); set_paint_bounds(child_paint_bounds); + + context->cull_rect = previous_cull_rect; + context->mutators_stack.Pop(); } void TransformLayer::Paint(PaintContext& context) const { FML_DCHECK(needs_painting(context)); - auto mutator = context.state_stack.save(); - mutator.transform(transform_); + SkAutoCanvasRestore save(context.internal_nodes_canvas, true); + context.internal_nodes_canvas->concat(transform_); PaintChildren(context); } diff --git a/flow/layers/transform_layer.h b/flow/layers/transform_layer.h index cb4c712d7bfd7..72ad561acd46e 100644 --- a/flow/layers/transform_layer.h +++ b/flow/layers/transform_layer.h @@ -17,7 +17,7 @@ class TransformLayer : public ContainerLayer { void Diff(DiffContext* context, const Layer* old_layer) override; - void Preroll(PrerollContext* context) override; + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/transform_layer_unittests.cc b/flow/layers/transform_layer_unittests.cc index 30744f39fffc1..e05334c2337eb 100644 --- a/flow/layers/transform_layer_unittests.cc +++ b/flow/layers/transform_layer_unittests.cc @@ -19,7 +19,7 @@ using TransformLayerTest = LayerTest; TEST_F(TransformLayerTest, PaintingEmptyLayerDies) { auto layer = std::make_shared(SkMatrix()); // identity - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); EXPECT_EQ(layer->child_paint_bounds(), SkRect::MakeEmpty()); EXPECT_FALSE(layer->needs_painting(paint_context())); @@ -48,8 +48,8 @@ TEST_F(TransformLayerTest, Identity) { auto layer = std::make_shared(SkMatrix()); // identity layer->Add(mock_layer); - preroll_context()->state_stack.set_initial_cull_rect(cull_rect); - layer->Preroll(preroll_context()); + preroll_context()->cull_rect = cull_rect; + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); EXPECT_EQ(layer->child_paint_bounds(), mock_layer->paint_bounds()); @@ -57,7 +57,7 @@ TEST_F(TransformLayerTest, Identity) { EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); // identity EXPECT_EQ(mock_layer->parent_cull_rect(), cull_rect); - EXPECT_EQ(mock_layer->parent_mutators(), MutatorsStack()); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(SkMatrix())})); layer->Paint(paint_context()); EXPECT_EQ(mock_canvas().draw_calls(), @@ -68,9 +68,8 @@ TEST_F(TransformLayerTest, Identity) { TEST_F(TransformLayerTest, Simple) { SkPath child_path; child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkRect cull_rect = SkRect::MakeXYWH(2.0f, 2.0f, 14.0f, 14.0f); SkMatrix initial_transform = SkMatrix::Translate(-0.5f, -0.5f); - SkRect local_cull_rect = SkRect::MakeXYWH(2.0f, 2.0f, 14.0f, 14.0f); - SkRect device_cull_rect = initial_transform.mapRect(local_cull_rect); SkMatrix layer_transform = SkMatrix::Translate(2.5f, 2.5f); SkMatrix inverse_layer_transform; EXPECT_TRUE(layer_transform.invert(&inverse_layer_transform)); @@ -79,9 +78,8 @@ TEST_F(TransformLayerTest, Simple) { auto layer = std::make_shared(layer_transform); layer->Add(mock_layer); - preroll_context()->state_stack.set_initial_state(device_cull_rect, - initial_transform); - layer->Preroll(preroll_context()); + preroll_context()->cull_rect = cull_rect; + layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer->paint_bounds(), layer_transform.mapRect(mock_layer->paint_bounds())); @@ -91,7 +89,7 @@ TEST_F(TransformLayerTest, Simple) { EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix::Concat(initial_transform, layer_transform)); EXPECT_EQ(mock_layer->parent_cull_rect(), - inverse_layer_transform.mapRect(local_cull_rect)); + inverse_layer_transform.mapRect(cull_rect)); EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_transform)})); @@ -109,9 +107,8 @@ TEST_F(TransformLayerTest, Simple) { TEST_F(TransformLayerTest, Nested) { SkPath child_path; child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkRect cull_rect = SkRect::MakeXYWH(2.0f, 2.0f, 14.0f, 14.0f); SkMatrix initial_transform = SkMatrix::Translate(-0.5f, -0.5f); - SkRect local_cull_rect = SkRect::MakeXYWH(2.0f, 2.0f, 14.0f, 14.0f); - SkRect device_cull_rect = initial_transform.mapRect(local_cull_rect); SkMatrix layer1_transform = SkMatrix::Translate(2.5f, 2.5f); SkMatrix layer2_transform = SkMatrix::Translate(2.5f, 2.5f); SkMatrix inverse_layer1_transform, inverse_layer2_transform; @@ -124,9 +121,8 @@ TEST_F(TransformLayerTest, Nested) { layer1->Add(layer2); layer2->Add(mock_layer); - preroll_context()->state_stack.set_initial_state(device_cull_rect, - initial_transform); - layer1->Preroll(preroll_context()); + preroll_context()->cull_rect = cull_rect; + layer1->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer2->paint_bounds(), layer2_transform.mapRect(mock_layer->paint_bounds())); @@ -143,7 +139,7 @@ TEST_F(TransformLayerTest, Nested) { layer2_transform)); EXPECT_EQ(mock_layer->parent_cull_rect(), inverse_layer2_transform.mapRect( - inverse_layer1_transform.mapRect(local_cull_rect))); + inverse_layer1_transform.mapRect(cull_rect))); EXPECT_EQ( mock_layer->parent_mutators(), std::vector({Mutator(layer2_transform), Mutator(layer1_transform)})); @@ -166,9 +162,8 @@ TEST_F(TransformLayerTest, Nested) { TEST_F(TransformLayerTest, NestedSeparated) { SkPath child_path; child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkRect cull_rect = SkRect::MakeXYWH(2.0f, 2.0f, 14.0f, 14.0f); SkMatrix initial_transform = SkMatrix::Translate(-0.5f, -0.5f); - SkRect local_cull_rect = SkRect::MakeXYWH(2.0f, 2.0f, 14.0f, 14.0f); - SkRect device_cull_rect = initial_transform.mapRect(local_cull_rect); SkMatrix layer1_transform = SkMatrix::Translate(2.5f, 2.5f); SkMatrix layer2_transform = SkMatrix::Translate(2.5f, 2.5f); SkMatrix inverse_layer1_transform, inverse_layer2_transform; @@ -185,9 +180,8 @@ TEST_F(TransformLayerTest, NestedSeparated) { layer1->Add(layer2); layer2->Add(mock_layer2); - preroll_context()->state_stack.set_initial_state(device_cull_rect, - initial_transform); - layer1->Preroll(preroll_context()); + preroll_context()->cull_rect = cull_rect; + layer1->Preroll(preroll_context(), initial_transform); SkRect layer1_child_bounds = layer2->paint_bounds(); layer1_child_bounds.join(mock_layer1->paint_bounds()); SkRect expected_layer1_bounds = layer1_child_bounds; @@ -211,10 +205,10 @@ TEST_F(TransformLayerTest, NestedSeparated) { SkMatrix::Concat(SkMatrix::Concat(initial_transform, layer1_transform), layer2_transform)); EXPECT_EQ(mock_layer1->parent_cull_rect(), - inverse_layer1_transform.mapRect(local_cull_rect)); + inverse_layer1_transform.mapRect(cull_rect)); EXPECT_EQ(mock_layer2->parent_cull_rect(), inverse_layer2_transform.mapRect( - inverse_layer1_transform.mapRect(local_cull_rect))); + inverse_layer1_transform.mapRect(cull_rect))); EXPECT_EQ(mock_layer1->parent_mutators(), std::vector({Mutator(layer1_transform)})); EXPECT_EQ( @@ -248,9 +242,9 @@ TEST_F(TransformLayerTest, OpacityInheritance) { // TransformLayer will pass through compatibility from a compatible child PrerollContext* context = preroll_context(); - transform1->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + transform1->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); auto path2 = SkPath().addRect({40, 40, 50, 50}); auto mock2 = MockLayer::MakeOpacityCompatible(path2); @@ -258,9 +252,9 @@ TEST_F(TransformLayerTest, OpacityInheritance) { // TransformLayer will pass through compatibility from multiple // non-overlapping compatible children - transform1->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + transform1->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); auto path3 = SkPath().addRect({20, 20, 40, 40}); auto mock3 = MockLayer::MakeOpacityCompatible(path3); @@ -268,17 +262,18 @@ TEST_F(TransformLayerTest, OpacityInheritance) { // TransformLayer will not pass through compatibility from multiple // overlapping children even if they are individually compatible - transform1->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, 0); + context->subtree_can_inherit_opacity = false; + transform1->Preroll(context, SkMatrix::I()); + EXPECT_FALSE(context->subtree_can_inherit_opacity); auto transform2 = std::make_shared(SkMatrix::Scale(2, 2)); transform2->Add(mock1); transform2->Add(mock2); // Double check first two children are compatible and non-overlapping - transform2->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + transform2->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); auto path4 = SkPath().addRect({60, 60, 70, 70}); auto mock4 = MockLayer::Make(path4); @@ -286,8 +281,9 @@ TEST_F(TransformLayerTest, OpacityInheritance) { // The third child is non-overlapping, but not compatible so the // TransformLayer should end up incompatible - transform2->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, 0); + context->subtree_can_inherit_opacity = false; + transform2->Preroll(context, SkMatrix::I()); + EXPECT_FALSE(context->subtree_can_inherit_opacity); } TEST_F(TransformLayerTest, OpacityInheritancePainting) { @@ -303,15 +299,16 @@ TEST_F(TransformLayerTest, OpacityInheritancePainting) { // TransformLayer will pass through compatibility from multiple // non-overlapping compatible children PrerollContext* context = preroll_context(); - transform_layer->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + transform_layer->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); int opacity_alpha = 0x7F; SkPoint offset = SkPoint::Make(10, 10); auto opacity_layer = std::make_shared(opacity_alpha, offset); opacity_layer->Add(transform_layer); - opacity_layer->Preroll(context); + context->subtree_can_inherit_opacity = false; + opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); DisplayListBuilder expected_builder; @@ -323,10 +320,22 @@ TEST_F(TransformLayerTest, OpacityInheritancePainting) { expected_builder.save(); expected_builder.transform(transform); /* child layer1 paint */ { - expected_builder.drawPath(path1, DlPaint().setAlpha(opacity_alpha)); + expected_builder.setColor(opacity_alpha << 24); + expected_builder.saveLayer(&path1.getBounds(), true); + { + expected_builder.setColor(0xFF000000); + expected_builder.drawPath(path1); + } + expected_builder.restore(); } /* child layer2 paint */ { - expected_builder.drawPath(path2, DlPaint().setAlpha(opacity_alpha)); + expected_builder.setColor(opacity_alpha << 24); + expected_builder.saveLayer(&path2.getBounds(), true); + { + expected_builder.setColor(0xFF000000); + expected_builder.drawPath(path2); + } + expected_builder.restore(); } expected_builder.restore(); } @@ -335,7 +344,7 @@ TEST_F(TransformLayerTest, OpacityInheritancePainting) { } opacity_layer->Paint(display_list_paint_context()); - EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); + EXPECT_TRUE(DisplayListsEQ_Verbose(expected_builder.Build(), display_list())); } using TransformLayerLayerDiffTest = DiffContextTest; diff --git a/flow/paint_utils.cc b/flow/paint_utils.cc index 2d1d6f65029ef..0e61e16200b76 100644 --- a/flow/paint_utils.cc +++ b/flow/paint_utils.cc @@ -24,19 +24,14 @@ sk_sp CreateCheckerboardShader(SkColor c1, SkColor c2, int size) { SkSamplingOptions()); } -} // anonymous namespace - -void DrawCheckerboard(SkCanvas* canvas, - DisplayListBuilder* builder, - const SkRect& rect) { - if (canvas) { - DrawCheckerboard(canvas, rect); - } - if (builder) { - DrawCheckerboard(builder, rect); - } +void DrawCheckerboard(SkCanvas* canvas, SkColor c1, SkColor c2, int size) { + SkPaint paint; + paint.setShader(CreateCheckerboardShader(c1, c2, size)); + canvas->drawPaint(paint); } +} // anonymous namespace + void DrawCheckerboard(SkCanvas* canvas, const SkRect& rect) { // Draw a checkerboard canvas->save(); @@ -48,9 +43,7 @@ void DrawCheckerboard(SkCanvas* canvas, const SkRect& rect) { SkColorSetARGB(64, rand() % 256, rand() % 256, rand() % 256); // NOLINTEND(clang-analyzer-security.insecureAPI.rand) - SkPaint paint; - paint.setShader(CreateCheckerboardShader(checkerboard_color, 0x00000000, 12)); - canvas->drawPaint(paint); + DrawCheckerboard(canvas, checkerboard_color, 0x00000000, 12); canvas->restore(); // Stroke the drawn area @@ -61,29 +54,4 @@ void DrawCheckerboard(SkCanvas* canvas, const SkRect& rect) { canvas->drawRect(rect, debug_paint); } -void DrawCheckerboard(DisplayListBuilder* builder, const SkRect& rect) { - // Draw a checkerboard - builder->save(); - builder->clipRect(rect, SkClipOp::kIntersect, false); - - // Secure random number generation isn't needed here. - // NOLINTBEGIN(clang-analyzer-security.insecureAPI.rand) - auto checkerboard_color = - SkColorSetARGB(64, rand() % 256, rand() % 256, rand() % 256); - // NOLINTEND(clang-analyzer-security.insecureAPI.rand) - - DlPaint paint; - paint.setColorSource(DlColorSource::From( - CreateCheckerboardShader(checkerboard_color, 0x00000000, 12))); - builder->drawPaint(paint); - builder->restore(); - - // Stroke the drawn area - DlPaint debug_paint; - debug_paint.setStrokeWidth(8); - debug_paint.setColor(SkColorSetA(checkerboard_color, 255)); - debug_paint.setDrawStyle(DlDrawStyle::kStroke); - builder->drawRect(rect, debug_paint); -} - } // namespace flutter diff --git a/flow/paint_utils.h b/flow/paint_utils.h index c01d7d1a8c5f4..432dd12811372 100644 --- a/flow/paint_utils.h +++ b/flow/paint_utils.h @@ -5,20 +5,13 @@ #ifndef FLUTTER_FLOW_PAINT_UTILS_H_ #define FLUTTER_FLOW_PAINT_UTILS_H_ -#include "flutter/display_list/display_list_builder.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkRect.h" namespace flutter { -typedef void (*CheckerboardFunc)(SkCanvas*, DisplayListBuilder*, const SkRect&); - -void DrawCheckerboard(SkCanvas* canvas, - DisplayListBuilder* builder, - const SkRect& rect); void DrawCheckerboard(SkCanvas* canvas, const SkRect& rect); -void DrawCheckerboard(DisplayListBuilder* builder, const SkRect& rect); } // namespace flutter diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index 31f73f5d330a6..c25f656e13c22 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -92,8 +92,8 @@ bool RasterCache::UpdateCacheEntry( RasterCacheKey key = RasterCacheKey(id, raster_cache_context.matrix); Entry& entry = cache_[key]; if (!entry.image) { - void (*func)(SkCanvas*, const SkRect& rect) = DrawCheckerboard; - entry.image = Rasterize(raster_cache_context, render_function, func); + entry.image = + Rasterize(raster_cache_context, render_function, DrawCheckerboard); if (entry.image != nullptr) { switch (id.type()) { case RasterCacheKeyType::kDisplayList: { diff --git a/flow/raster_cache_unittests.cc b/flow/raster_cache_unittests.cc index 0a209da0ebda1..d72b91abf3b1b 100644 --- a/flow/raster_cache_unittests.cc +++ b/flow/raster_cache_unittests.cc @@ -38,13 +38,13 @@ TEST(RasterCache, MetricsOmitUnpopulatedEntries) { SkCanvas dummy_canvas; SkPaint paint; - LayerStateStack state_stack; FixedRefreshRateStopwatch raster_time; FixedRefreshRateStopwatch ui_time; + MutatorsStack mutators_stack; PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( - state_stack, &cache, &raster_time, &ui_time); + &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; @@ -94,13 +94,13 @@ TEST(RasterCache, ThresholdIsRespectedForDisplayList) { SkCanvas dummy_canvas; SkPaint paint; - LayerStateStack state_stack; FixedRefreshRateStopwatch raster_time; FixedRefreshRateStopwatch ui_time; + MutatorsStack mutators_stack; PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( - state_stack, &cache, &raster_time, &ui_time); + &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; @@ -138,11 +138,10 @@ TEST(RasterCache, SetCheckboardCacheImages) { SkMatrix matrix = SkMatrix::I(); auto display_list = GetSampleDisplayList(); - LayerStateStack state_stack; FixedRefreshRateStopwatch raster_time; FixedRefreshRateStopwatch ui_time; PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); auto& paint_context = paint_context_holder.paint_context; auto dummy_draw_function = [](SkCanvas* canvas) {}; bool did_draw_checkerboard = false; @@ -179,13 +178,13 @@ TEST(RasterCache, AccessThresholdOfZeroDisablesCachingForSkPicture) { SkCanvas dummy_canvas; SkPaint paint; - LayerStateStack state_stack; FixedRefreshRateStopwatch raster_time; FixedRefreshRateStopwatch ui_time; + MutatorsStack mutators_stack; PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( - state_stack, &cache, &raster_time, &ui_time); + &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; @@ -208,13 +207,13 @@ TEST(RasterCache, AccessThresholdOfZeroDisablesCachingForDisplayList) { SkCanvas dummy_canvas; SkPaint paint; - LayerStateStack state_stack; FixedRefreshRateStopwatch raster_time; FixedRefreshRateStopwatch ui_time; + MutatorsStack mutators_stack; PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( - state_stack, &cache, &raster_time, &ui_time); + &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; @@ -239,13 +238,13 @@ TEST(RasterCache, PictureCacheLimitPerFrameIsRespectedWhenZeroForSkPicture) { SkCanvas dummy_canvas; SkPaint paint; - LayerStateStack state_stack; FixedRefreshRateStopwatch raster_time; FixedRefreshRateStopwatch ui_time; + MutatorsStack mutators_stack; PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( - state_stack, &cache, &raster_time, &ui_time); + &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; @@ -275,13 +274,13 @@ TEST(RasterCache, PictureCacheLimitPerFrameIsRespectedWhenZeroForDisplayList) { SkCanvas dummy_canvas; SkPaint paint; - LayerStateStack state_stack; FixedRefreshRateStopwatch raster_time; FixedRefreshRateStopwatch ui_time; + MutatorsStack mutators_stack; PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( - state_stack, &cache, &raster_time, &ui_time); + &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; @@ -315,13 +314,13 @@ TEST(RasterCache, EvitUnusedCacheEntries) { SkCanvas dummy_canvas; SkPaint paint; - LayerStateStack state_stack; FixedRefreshRateStopwatch raster_time; FixedRefreshRateStopwatch ui_time; + MutatorsStack mutators_stack; PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( - state_stack, &cache, &raster_time, &ui_time); + &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; @@ -425,13 +424,13 @@ TEST(RasterCache, DeviceRectRoundOutForDisplayList) { SkCanvas canvas(100, 100, nullptr); canvas.setMatrix(ctm); - LayerStateStack state_stack; FixedRefreshRateStopwatch raster_time; FixedRefreshRateStopwatch ui_time; + MutatorsStack mutators_stack; PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( - state_stack, &cache, &raster_time, &ui_time); + &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; @@ -468,13 +467,13 @@ TEST(RasterCache, NestedOpCountMetricUsedForDisplayList) { SkCanvas dummy_canvas; SkPaint paint; - LayerStateStack state_stack; FixedRefreshRateStopwatch raster_time; FixedRefreshRateStopwatch ui_time; + MutatorsStack mutators_stack; PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( - state_stack, &cache, &raster_time, &ui_time); + &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; @@ -515,13 +514,13 @@ TEST(RasterCache, NaiveComplexityScoringDisplayList) { SkCanvas dummy_canvas; SkPaint paint; - LayerStateStack state_stack; FixedRefreshRateStopwatch raster_time; FixedRefreshRateStopwatch ui_time; + MutatorsStack mutators_stack; PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( - state_stack, &cache, &raster_time, &ui_time); + &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; @@ -581,13 +580,13 @@ TEST(RasterCache, DisplayListWithSingularMatrixIsNotCached) { SkCanvas dummy_canvas; SkPaint paint; - LayerStateStack state_stack; FixedRefreshRateStopwatch raster_time; FixedRefreshRateStopwatch ui_time; + MutatorsStack mutators_stack; PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( - state_stack, &cache, &raster_time, &ui_time); + &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; diff --git a/flow/raster_cache_util.h b/flow/raster_cache_util.h index 18019fc83192f..71836fdaf502d 100644 --- a/flow/raster_cache_util.h +++ b/flow/raster_cache_util.h @@ -6,7 +6,6 @@ #define FLUTTER_FLOW_RASTER_CACHE_UTIL_H_ #include "flutter/fml/logging.h" -#include "include/core/SkM44.h" #include "include/core/SkMatrix.h" #include "include/core/SkRect.h" @@ -88,46 +87,6 @@ struct RasterCacheUtil { result[SkMatrix::kMTransY] = SkScalarRoundToScalar(ctm.getTranslateY()); return result; } - - /** - * @brief Snap the translation components of the matrix to integers. - * - * The snapping will only happen if the matrix only has scale and translation - * transformations. This is used, along with GetRoundedOutDeviceBounds, to - * ensure that the textures drawn by the raster cache are exactly aligned to - * physical pixels. Any layers that participate in raster caching must align - * themselves to physical pixels even when not cached to prevent a change in - * apparent location if caching is later applied. - * - * @param ctm the current transformation matrix. - * @return SkM44 the snapped transformation matrix. - */ - static SkM44 GetIntegralTransCTM(const SkM44& ctm) { - // Avoid integral snapping if the matrix has complex transformation to avoid - // the artifact observed in https://github.com/flutter/flutter/issues/41654. - if (ctm.rc(0, 1) != 0 || ctm.rc(0, 2) != 0) { - // X multiplied by either Y or Z - return ctm; - } - if (ctm.rc(1, 0) != 0 || ctm.rc(1, 2) != 0) { - // Y multiplied by either X or Z - return ctm; - } - // We do not need to worry about the Z row unless the W row - // has perspective entries... - if (ctm.rc(3, 0) != 0 || ctm.rc(3, 1) != 0 || ctm.rc(3, 2) != 0 || - ctm.rc(3, 3) != 1) { - // W not identity row, therefore perspective is applied - return ctm; - } - - SkM44 result = ctm; - result.setRC(0, 3, SkScalarRoundToScalar(ctm.rc(0, 3))); - result.setRC(1, 3, SkScalarRoundToScalar(ctm.rc(1, 3))); - // No need to worry about Z translation because it has no effect - // without perspective entries... - return result; - } }; } // namespace flutter diff --git a/flow/testing/auto_save_layer_unittests.cc b/flow/testing/auto_save_layer_unittests.cc new file mode 100644 index 0000000000000..a44ebdbead301 --- /dev/null +++ b/flow/testing/auto_save_layer_unittests.cc @@ -0,0 +1,117 @@ +// 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/flow/testing/layer_test.h" + +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +using AutoSaveLayerTests = LayerTest; + +TEST_F(AutoSaveLayerTests, SaveLayerOnInternalNodesCanvasByDefault) { + // For: + // static AutoSaveLayer Create(const PaintContext& paint_context, + // const SkRect& bounds, + // const SkPaint* paint, + // SaveMode save_mode); + { + int saved_count_before = + paint_context().internal_nodes_canvas->getSaveCount(); + { + const SkPaint paint; + const SkRect rect = SkRect::MakeEmpty(); + Layer::AutoSaveLayer save = + Layer::AutoSaveLayer::Create(paint_context(), rect, &paint); + EXPECT_EQ(paint_context().internal_nodes_canvas->getSaveCount(), + saved_count_before + 1); + EXPECT_EQ(paint_context().leaf_nodes_canvas->getSaveCount(), + saved_count_before + 1); + } + EXPECT_EQ(paint_context().internal_nodes_canvas->getSaveCount(), + saved_count_before); + EXPECT_EQ(paint_context().leaf_nodes_canvas->getSaveCount(), + saved_count_before); + } + // For: + // static AutoSaveLayer Create(const PaintContext& paint_context, + // const SkCanvas::SaveLayerRec& layer_rec, + // SaveMode save_mode); + { + int saved_count_before = + paint_context().internal_nodes_canvas->getSaveCount(); + { + const SkPaint paint; + const SkRect rect = SkRect::MakeEmpty(); + const SkCanvas::SaveLayerRec save_layer_rect = + SkCanvas::SaveLayerRec{&rect, &paint, nullptr, 0}; + Layer::AutoSaveLayer save = + Layer::AutoSaveLayer::Create(paint_context(), save_layer_rect); + EXPECT_EQ(paint_context().internal_nodes_canvas->getSaveCount(), + saved_count_before + 1); + EXPECT_EQ(paint_context().leaf_nodes_canvas->getSaveCount(), + saved_count_before + 1); + } + EXPECT_EQ(paint_context().internal_nodes_canvas->getSaveCount(), + saved_count_before); + EXPECT_EQ(paint_context().leaf_nodes_canvas->getSaveCount(), + saved_count_before); + } +} + +TEST_F(AutoSaveLayerTests, SaveLayerOnlyOnLeafNodesCanvas) { + // For: + // static AutoSaveLayer Create(const PaintContext& paint_context, + // const SkRect& bounds, + // const SkPaint* paint, + // SaveMode save_mode); + { + int saved_count_before = + paint_context().internal_nodes_canvas->getSaveCount(); + { + const SkPaint paint; + const SkRect rect = SkRect::MakeEmpty(); + Layer::AutoSaveLayer save = Layer::AutoSaveLayer::Create( + paint_context(), rect, &paint, + Layer::AutoSaveLayer::SaveMode::kLeafNodesCanvas); + EXPECT_EQ(paint_context().internal_nodes_canvas->getSaveCount(), + saved_count_before); + EXPECT_EQ(paint_context().leaf_nodes_canvas->getSaveCount(), + saved_count_before + 1); + } + EXPECT_EQ(paint_context().internal_nodes_canvas->getSaveCount(), + saved_count_before); + EXPECT_EQ(paint_context().leaf_nodes_canvas->getSaveCount(), + saved_count_before); + } + // For: + // static AutoSaveLayer Create(const PaintContext& paint_context, + // const SkCanvas::SaveLayerRec& layer_rec, + // SaveMode save_mode); + { + int saved_count_before = + paint_context().internal_nodes_canvas->getSaveCount(); + { + const SkPaint paint; + const SkRect rect = SkRect::MakeEmpty(); + const SkCanvas::SaveLayerRec save_layer_rect = + SkCanvas::SaveLayerRec{&rect, &paint, nullptr, 0}; + Layer::AutoSaveLayer save = Layer::AutoSaveLayer::Create( + paint_context(), save_layer_rect, + Layer::AutoSaveLayer::SaveMode::kLeafNodesCanvas); + EXPECT_EQ(paint_context().internal_nodes_canvas->getSaveCount(), + saved_count_before); + EXPECT_EQ(paint_context().leaf_nodes_canvas->getSaveCount(), + saved_count_before + 1); + } + EXPECT_EQ(paint_context().internal_nodes_canvas->getSaveCount(), + saved_count_before); + EXPECT_EQ(paint_context().leaf_nodes_canvas->getSaveCount(), + saved_count_before); + } +} + +} // namespace testing +} // namespace flutter diff --git a/flow/testing/layer_test.h b/flow/testing/layer_test.h index 8d0f1519f17c4..60b4536056ab6 100644 --- a/flow/testing/layer_test.h +++ b/flow/testing/layer_test.h @@ -51,12 +51,14 @@ class LayerTestBase : public CanvasTestBase { .raster_cache = nullptr, .gr_context = nullptr, .view_embedder = nullptr, - .state_stack = preroll_state_stack_, + .mutators_stack = mutators_stack_, .dst_color_space = TestT::mock_color_space(), + .cull_rect = kGiantRect, .surface_needs_readback = false, .raster_time = raster_time_, .ui_time = ui_time_, .texture_registry = texture_registry_, + .checkerboard_offscreen_layers = false, .frame_device_pixel_ratio = 1.0f, .has_platform_view = false, .raster_cached_entries = &cacheable_items_, @@ -64,52 +66,54 @@ class LayerTestBase : public CanvasTestBase { }, paint_context_{ // clang-format off - .state_stack = paint_state_stack_, - .canvas = &TestT::mock_canvas(), + .internal_nodes_canvas = TestT::mock_internal_canvas(), + .leaf_nodes_canvas = &TestT::mock_canvas(), .gr_context = nullptr, .view_embedder = nullptr, .raster_time = raster_time_, .ui_time = ui_time_, .texture_registry = texture_registry_, .raster_cache = nullptr, + .checkerboard_offscreen_layers = false, .frame_device_pixel_ratio = 1.0f, // clang-format on }, display_list_recorder_(kDlBounds), + internal_display_list_canvas_(kDlBounds.width(), kDlBounds.height()), display_list_paint_context_{ // clang-format off - .state_stack = display_list_state_stack_, - .canvas = &display_list_recorder_, - .builder = display_list_recorder_.builder().get(), + .internal_nodes_canvas = &internal_display_list_canvas_, + .leaf_nodes_canvas = &display_list_recorder_, .gr_context = nullptr, .view_embedder = nullptr, .raster_time = raster_time_, .ui_time = ui_time_, .texture_registry = texture_registry_, .raster_cache = nullptr, + .checkerboard_offscreen_layers = false, .frame_device_pixel_ratio = 1.0f, + .leaf_nodes_builder = display_list_recorder_.builder().get(), + .builder_multiplexer = &display_list_multiplexer_, // clang-format on }, - checkerboard_context_{ + check_board_context_{ // clang-format off - .state_stack = checkerboard_state_stack_, - .canvas = &TestT::mock_canvas(), + .internal_nodes_canvas = TestT::mock_internal_canvas(), + .leaf_nodes_canvas = &TestT::mock_canvas(), .gr_context = nullptr, .view_embedder = nullptr, .raster_time = raster_time_, .ui_time = ui_time_, .texture_registry = texture_registry_, .raster_cache = nullptr, + .checkerboard_offscreen_layers = true, .frame_device_pixel_ratio = 1.0f, // clang-format on } { + internal_display_list_canvas_.addCanvas(&display_list_recorder_); + display_list_multiplexer_.addBuilder( + display_list_recorder_.builder().get()); use_null_raster_cache(); - preroll_state_stack_.set_delegate(&mutators_stack_); - paint_state_stack_.set_delegate(&TestT::mock_canvas()); - display_list_state_stack_.set_delegate(display_list_recorder_); - checkerboard_state_stack_.set_delegate(&TestT::mock_canvas()); - checkerboard_state_stack_.set_checkerboard_func(draw_checkerboard); - checkerboard_paint_.setColor(checkerboard_color_); } /** @@ -172,8 +176,7 @@ class LayerTestBase : public CanvasTestBase { PaintContext& display_list_paint_context() { return display_list_paint_context_; } - const SkPaint& checkerboard_paint() { return checkerboard_paint_; } - PaintContext& checkerboard_context() { return checkerboard_context_; } + PaintContext& check_board_context() { return check_board_context_; } LayerSnapshotStore& layer_snapshot_store() { return snapshot_store_; } sk_sp display_list() { @@ -181,8 +184,10 @@ class LayerTestBase : public CanvasTestBase { display_list_ = display_list_recorder_.Build(); // null out the canvas and recorder fields of the PaintContext // to prevent future use. - display_list_paint_context_.canvas = nullptr; - display_list_paint_context_.builder = nullptr; + display_list_paint_context_.leaf_nodes_canvas = nullptr; + display_list_paint_context_.internal_nodes_canvas = nullptr; + display_list_paint_context_.leaf_nodes_builder = nullptr; + display_list_paint_context_.builder_multiplexer = nullptr; } return display_list_; } @@ -205,26 +210,6 @@ class LayerTestBase : public CanvasTestBase { display_list_paint_context_.raster_cache = raster_cache_.get(); } - static constexpr SkColor checkerboard_color_ = 0x42424242; - - static void draw_checkerboard(SkCanvas* canvas, - DisplayListBuilder* builder, - const SkRect& rect) { - if (canvas) { - SkPaint paint; - paint.setColor(checkerboard_color_); - canvas->drawRect(rect, paint); - } - if (builder) { - DlPaint paint; - paint.setColor(checkerboard_color_); - builder->drawRect(rect, paint); - } - } - - LayerStateStack preroll_state_stack_; - LayerStateStack paint_state_stack_; - LayerStateStack checkerboard_state_stack_; FixedRefreshRateStopwatch raster_time_; FixedRefreshRateStopwatch ui_time_; MutatorsStack mutators_stack_; @@ -234,11 +219,11 @@ class LayerTestBase : public CanvasTestBase { PrerollContext preroll_context_; PaintContext paint_context_; DisplayListCanvasRecorder display_list_recorder_; - LayerStateStack display_list_state_stack_; + DisplayListBuilderMultiplexer display_list_multiplexer_; sk_sp display_list_; + SkNWayCanvas internal_display_list_canvas_; PaintContext display_list_paint_context_; - SkPaint checkerboard_paint_; - PaintContext checkerboard_context_; + PaintContext check_board_context_; LayerSnapshotStore snapshot_store_; std::vector cacheable_items_; diff --git a/flow/testing/mock_embedder.cc b/flow/testing/mock_embedder.cc index 4767481ea6f5a..8004308d796c2 100644 --- a/flow/testing/mock_embedder.cc +++ b/flow/testing/mock_embedder.cc @@ -11,15 +11,6 @@ MockViewEmbedder::MockViewEmbedder() = default; MockViewEmbedder::~MockViewEmbedder() = default; -void MockViewEmbedder::AddCanvas(SkCanvas* canvas) { - contexts_.emplace_back(EmbedderPaintContext{canvas, nullptr}); -} - -void MockViewEmbedder::AddRecorder(DisplayListCanvasRecorder* recorder) { - contexts_.emplace_back( - EmbedderPaintContext{recorder, recorder->builder().get()}); -} - // |ExternalViewEmbedder| SkCanvas* MockViewEmbedder::GetRootCanvas() { return nullptr; @@ -52,9 +43,7 @@ std::vector MockViewEmbedder::GetCurrentBuilders() { // |ExternalViewEmbedder| EmbedderPaintContext MockViewEmbedder::CompositeEmbeddedView(int view_id) { - EmbedderPaintContext context = contexts_.front(); - contexts_.pop_front(); - return context; + return {nullptr, nullptr}; } } // namespace testing diff --git a/flow/testing/mock_embedder.h b/flow/testing/mock_embedder.h index 70e8d8ce1cc29..abc57d3d28530 100644 --- a/flow/testing/mock_embedder.h +++ b/flow/testing/mock_embedder.h @@ -16,9 +16,6 @@ class MockViewEmbedder : public ExternalViewEmbedder { ~MockViewEmbedder(); - void AddCanvas(SkCanvas* canvas); - void AddRecorder(DisplayListCanvasRecorder* recorder); - // |ExternalViewEmbedder| SkCanvas* GetRootCanvas() override; @@ -45,9 +42,6 @@ class MockViewEmbedder : public ExternalViewEmbedder { // |ExternalViewEmbedder| EmbedderPaintContext CompositeEmbeddedView(int view_id) override; - - private: - std::deque contexts_; }; } // namespace testing diff --git a/flow/testing/mock_layer.cc b/flow/testing/mock_layer.cc index 77dd3f216c328..838095ad49389 100644 --- a/flow/testing/mock_layer.cc +++ b/flow/testing/mock_layer.cc @@ -30,10 +30,10 @@ void MockLayer::Diff(DiffContext* context, const Layer* old_layer) { context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); } -void MockLayer::Preroll(PrerollContext* context) { - parent_mutators_ = *context->state_stack.mutators_delegate(); - parent_matrix_ = context->state_stack.transform_3x3(); - parent_cull_rect_ = context->state_stack.local_cull_rect(); +void MockLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { + parent_mutators_ = context->mutators_stack; + parent_matrix_ = matrix; + parent_cull_rect_ = context->cull_rect; set_parent_has_platform_view(context->has_platform_view); set_parent_has_texture_layer(context->has_texture_layer); @@ -45,41 +45,42 @@ void MockLayer::Preroll(PrerollContext* context) { context->surface_needs_readback = true; } if (fake_opacity_compatible()) { - context->renderable_state_flags = LayerStateStack::kCallerCanApplyOpacity; + context->subtree_can_inherit_opacity = true; } } void MockLayer::Paint(PaintContext& context) const { FML_DCHECK(needs_painting(context)); - if (expected_paint_matrix_.has_value()) { - SkMatrix matrix = context.builder ? context.builder->getTransform() - : context.canvas->getTotalMatrix(); - - ASSERT_EQ(matrix, expected_paint_matrix_.value()); + if (context.inherited_opacity < SK_Scalar1) { + SkPaint p; + p.setAlphaf(context.inherited_opacity); + context.leaf_nodes_canvas->saveLayer(fake_paint_path_.getBounds(), &p); + } + context.leaf_nodes_canvas->drawPath(fake_paint_path_, fake_paint_); + if (context.inherited_opacity < SK_Scalar1) { + context.leaf_nodes_canvas->restore(); } - - SkPaint sk_paint = fake_paint_; - context.state_stack.fill(sk_paint); - context.canvas->drawPath(fake_paint_path_, sk_paint); } -void MockCacheableContainerLayer::Preroll(PrerollContext* context) { +void MockCacheableContainerLayer::Preroll(PrerollContext* context, + const SkMatrix& matrix) { Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context); - auto cache = AutoCache(layer_raster_cache_item_.get(), context, - context->state_stack.transform_3x3()); + SkMatrix child_matrix = matrix; + auto cache = AutoCache(layer_raster_cache_item_.get(), context, child_matrix); - ContainerLayer::Preroll(context); + ContainerLayer::Preroll(context, child_matrix); } -void MockCacheableLayer::Preroll(PrerollContext* context) { +void MockCacheableLayer::Preroll(PrerollContext* context, + const SkMatrix& matrix) { Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context); - auto cache = AutoCache(raster_cache_item_.get(), context, - context->state_stack.transform_3x3()); + SkMatrix child_matrix = matrix; + auto cache = AutoCache(raster_cache_item_.get(), context, child_matrix); - MockLayer::Preroll(context); + MockLayer::Preroll(context, child_matrix); } } // namespace testing diff --git a/flow/testing/mock_layer.h b/flow/testing/mock_layer.h index 804e837942218..bc351f3fc3270 100644 --- a/flow/testing/mock_layer.h +++ b/flow/testing/mock_layer.h @@ -37,7 +37,7 @@ class MockLayer : public Layer { return mock_layer; } - void Preroll(PrerollContext* context) override; + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; const MutatorsStack& parent_mutators() { return parent_mutators_; } @@ -102,17 +102,12 @@ class MockLayer : public Layer { return *this; } - void set_expected_paint_matrix(const SkMatrix& matrix) { - expected_paint_matrix_ = matrix; - } - private: MutatorsStack parent_mutators_; SkMatrix parent_matrix_; SkRect parent_cull_rect_ = SkRect::MakeEmpty(); SkPath fake_paint_path_; SkPaint fake_paint_; - std::optional expected_paint_matrix_; static constexpr int kParentHasPlatformView = 1 << 0; static constexpr int kParentHasTextureLayer = 1 << 1; @@ -140,7 +135,7 @@ class MockCacheableContainerLayer : public CacheableContainerLayer { return std::make_shared(); } - void Preroll(PrerollContext* context) override; + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; explicit MockCacheableContainerLayer(bool cache_children = false) : CacheableContainerLayer(3, cache_children) {} @@ -164,7 +159,7 @@ class MockCacheableLayer : public MockLayer { return raster_cache_item_.get(); } - void Preroll(PrerollContext* context) override; + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; private: std::unique_ptr raster_cache_item_; diff --git a/flow/testing/mock_layer_unittests.cc b/flow/testing/mock_layer_unittests.cc index 4e62dd6aeb1ee..d7f007eed25db 100644 --- a/flow/testing/mock_layer_unittests.cc +++ b/flow/testing/mock_layer_unittests.cc @@ -25,7 +25,7 @@ TEST_F(MockLayerTest, PaintBeforePrerollDies) { TEST_F(MockLayerTest, PaintingEmptyLayerDies) { auto layer = std::make_shared(SkPath(), SkPaint()); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), SkPath().getBounds()); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), @@ -38,24 +38,20 @@ TEST_F(MockLayerTest, SimpleParams) { const SkPaint paint = SkPaint(SkColors::kBlue); const SkMatrix start_matrix = SkMatrix::Translate(1.0f, 2.0f); const SkMatrix scale_matrix = SkMatrix::Scale(0.5f, 0.5f); - const SkMatrix combined_matrix = SkMatrix::Concat(start_matrix, scale_matrix); - const SkRect local_cull_rect = SkRect::MakeWH(5.0f, 5.0f); - const SkRect device_cull_rect = combined_matrix.mapRect(local_cull_rect); + const SkRect cull_rect = SkRect::MakeWH(5.0f, 5.0f); const bool parent_has_platform_view = true; auto layer = std::make_shared(path, paint); - preroll_context()->state_stack.set_initial_state(device_cull_rect, - start_matrix); - auto mutator = preroll_context()->state_stack.save(); - mutator.transform(scale_matrix); + preroll_context()->mutators_stack.PushTransform(scale_matrix); + preroll_context()->cull_rect = cull_rect; preroll_context()->has_platform_view = parent_has_platform_view; - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), start_matrix); EXPECT_EQ(preroll_context()->has_platform_view, false); EXPECT_EQ(layer->paint_bounds(), path.getBounds()); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(layer->parent_mutators(), std::vector{Mutator(scale_matrix)}); - EXPECT_EQ(layer->parent_matrix(), combined_matrix); - EXPECT_EQ(layer->parent_cull_rect(), local_cull_rect); + EXPECT_EQ(layer->parent_matrix(), start_matrix); + EXPECT_EQ(layer->parent_cull_rect(), cull_rect); EXPECT_EQ(layer->parent_has_platform_view(), parent_has_platform_view); layer->Paint(paint_context()); @@ -69,7 +65,7 @@ TEST_F(MockLayerTest, FakePlatformView) { layer->set_fake_has_platform_view(true); EXPECT_EQ(preroll_context()->has_platform_view, false); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(preroll_context()->has_platform_view, true); } @@ -78,7 +74,7 @@ TEST_F(MockLayerTest, SaveLayerOnLeafNodesCanvas) { layer->set_fake_has_platform_view(true); EXPECT_EQ(preroll_context()->has_platform_view, false); - layer->Preroll(preroll_context()); + layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(preroll_context()->has_platform_view, true); } @@ -87,13 +83,14 @@ TEST_F(MockLayerTest, OpacityInheritance) { PrerollContext* context = preroll_context(); auto mock1 = std::make_shared(path1); - mock1->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, 0); + context->subtree_can_inherit_opacity = false; + mock1->Preroll(context, SkMatrix::I()); + EXPECT_FALSE(context->subtree_can_inherit_opacity); auto mock2 = MockLayer::MakeOpacityCompatible(path1); - mock2->Preroll(context); - EXPECT_EQ(context->renderable_state_flags, - LayerStateStack::kCallerCanApplyOpacity); + context->subtree_can_inherit_opacity = false; + mock2->Preroll(context, SkMatrix::I()); + EXPECT_TRUE(context->subtree_can_inherit_opacity); } TEST_F(MockLayerTest, FlagGetSet) { diff --git a/flow/testing/mock_raster_cache.cc b/flow/testing/mock_raster_cache.cc index b08b0f6d829f4..33b81c15030b3 100644 --- a/flow/testing/mock_raster_cache.cc +++ b/flow/testing/mock_raster_cache.cc @@ -27,7 +27,7 @@ void MockRasterCache::AddMockLayer(int width, int height) { int layer_cached_threshold = 1; MockCacheableLayer layer = MockCacheableLayer(path, SkPaint(), layer_cached_threshold); - layer.Preroll(&preroll_context_); + layer.Preroll(&preroll_context_, ctm); layer.raster_cache_item()->TryToPrepareRasterCache(paint_context_); RasterCache::Context r_context = { // clang-format off @@ -58,9 +58,8 @@ void MockRasterCache::AddMockPicture(int width, int height) { FixedRefreshRateStopwatch raster_time; FixedRefreshRateStopwatch ui_time; - LayerStateStack state_stack; PaintContextHolder holder = - GetSamplePaintContextHolder(state_stack, this, &raster_time, &ui_time); + GetSamplePaintContextHolder(this, &raster_time, &ui_time); holder.paint_context.dst_color_space = color_space_; DisplayListRasterCacheItem display_list_item(display_list.get(), SkPoint(), @@ -86,10 +85,10 @@ void MockRasterCache::AddMockPicture(int width, int height) { } PrerollContextHolder GetSamplePrerollContextHolder( - LayerStateStack& state_stack, RasterCache* raster_cache, FixedRefreshRateStopwatch* raster_time, - FixedRefreshRateStopwatch* ui_time) { + FixedRefreshRateStopwatch* ui_time, + MutatorsStack* mutators_stack) { sk_sp srgb = SkColorSpace::MakeSRGB(); PrerollContextHolder holder = { @@ -98,12 +97,14 @@ PrerollContextHolder GetSamplePrerollContextHolder( .raster_cache = raster_cache, .gr_context = nullptr, .view_embedder = nullptr, - .state_stack = state_stack, + .mutators_stack = *mutators_stack, .dst_color_space = srgb.get(), + .cull_rect = kGiantRect, .surface_needs_readback = false, .raster_time = *raster_time, .ui_time = *ui_time, .texture_registry = nullptr, + .checkerboard_offscreen_layers = false, .frame_device_pixel_ratio = 1.0f, .has_platform_view = false, .has_texture_layer = false, @@ -116,23 +117,24 @@ PrerollContextHolder GetSamplePrerollContextHolder( } PaintContextHolder GetSamplePaintContextHolder( - LayerStateStack& state_stack, RasterCache* raster_cache, FixedRefreshRateStopwatch* raster_time, FixedRefreshRateStopwatch* ui_time) { sk_sp srgb = SkColorSpace::MakeSRGB(); PaintContextHolder holder = {// clang-format off { - .state_stack = state_stack, - .canvas = nullptr, - .gr_context = nullptr, - .dst_color_space = srgb.get(), - .view_embedder = nullptr, - .raster_time = *raster_time, - .ui_time = *ui_time, - .texture_registry = nullptr, - .raster_cache = raster_cache, - .frame_device_pixel_ratio = 1.0f, + .internal_nodes_canvas = nullptr, + .leaf_nodes_canvas = nullptr, + .gr_context = nullptr, + .dst_color_space = srgb.get(), + .view_embedder = nullptr, + .raster_time = *raster_time, + .ui_time = *ui_time, + .texture_registry = nullptr, + .raster_cache = raster_cache, + .checkerboard_offscreen_layers = false, + .frame_device_pixel_ratio = 1.0f, + .inherited_opacity = SK_Scalar1, }, // clang-format on srgb}; diff --git a/flow/testing/mock_raster_cache.h b/flow/testing/mock_raster_cache.h index ea223a34b4b46..37742486409bb 100644 --- a/flow/testing/mock_raster_cache.h +++ b/flow/testing/mock_raster_cache.h @@ -61,15 +61,12 @@ class MockRasterCache : public RasterCache { size_t picture_and_display_list_cache_limit_per_frame = RasterCacheUtil::kDefaultPictureAndDispLayListCacheLimitPerFrame) : RasterCache(access_threshold, - picture_and_display_list_cache_limit_per_frame) { - state_stack_.set_delegate(&mutators_stack_); - } + picture_and_display_list_cache_limit_per_frame) {} void AddMockLayer(int width, int height); void AddMockPicture(int width, int height); private: - LayerStateStack state_stack_; MockCanvas mock_canvas_; SkColorSpace* color_space_ = mock_canvas_.imageInfo().colorSpace(); MutatorsStack mutators_stack_; @@ -81,12 +78,14 @@ class MockRasterCache : public RasterCache { .raster_cache = this, .gr_context = nullptr, .view_embedder = nullptr, - .state_stack = state_stack_, + .mutators_stack = mutators_stack_, .dst_color_space = color_space_, + .cull_rect = kGiantRect, .surface_needs_readback = false, .raster_time = raster_time_, .ui_time = ui_time_, .texture_registry = texture_registry_, + .checkerboard_offscreen_layers = false, .frame_device_pixel_ratio = 1.0f, .has_platform_view = false, .has_texture_layer = false, @@ -96,16 +95,18 @@ class MockRasterCache : public RasterCache { PaintContext paint_context_ = { // clang-format off - .state_stack = state_stack_, - .canvas = nullptr, - .gr_context = nullptr, - .dst_color_space = color_space_, - .view_embedder = nullptr, - .raster_time = raster_time_, - .ui_time = ui_time_, - .texture_registry = texture_registry_, - .raster_cache = nullptr, - .frame_device_pixel_ratio = 1.0f, + .internal_nodes_canvas = nullptr, + .leaf_nodes_canvas = nullptr, + .gr_context = nullptr, + .dst_color_space = color_space_, + .view_embedder = nullptr, + .raster_time = raster_time_, + .ui_time = ui_time_, + .texture_registry = texture_registry_, + .raster_cache = nullptr, + .checkerboard_offscreen_layers = false, + .frame_device_pixel_ratio = 1.0f, + .inherited_opacity = SK_Scalar1, // clang-format on }; }; @@ -121,13 +122,12 @@ struct PaintContextHolder { }; PrerollContextHolder GetSamplePrerollContextHolder( - LayerStateStack& state_stack, RasterCache* raster_cache, FixedRefreshRateStopwatch* raster_time, - FixedRefreshRateStopwatch* ui_time); + FixedRefreshRateStopwatch* ui_time, + MutatorsStack* mutators_stack); PaintContextHolder GetSamplePaintContextHolder( - LayerStateStack& state_stack, RasterCache* raster_cache, FixedRefreshRateStopwatch* raster_time, FixedRefreshRateStopwatch* ui_time); diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 799aa814a841a..32deedfdfbc37 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -2434,14 +2434,13 @@ TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) { auto* compositor_context = shell->GetRasterizer()->compositor_context(); auto& raster_cache = compositor_context->raster_cache(); - LayerStateStack state_stack; FixedRefreshRateStopwatch raster_time; FixedRefreshRateStopwatch ui_time; MutatorsStack mutators_stack; PaintContext paint_context = { // clang-format off - .state_stack = state_stack, - .canvas = nullptr, + .internal_nodes_canvas = nullptr, + .leaf_nodes_canvas = nullptr, .gr_context = nullptr, .dst_color_space = nullptr, .view_embedder = nullptr, @@ -2449,7 +2448,9 @@ TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) { .ui_time = ui_time, .texture_registry = nullptr, .raster_cache = &raster_cache, + .checkerboard_offscreen_layers = false, .frame_device_pixel_ratio = 1.0f, + .inherited_opacity = SK_Scalar1, // clang-format on }; @@ -2458,12 +2459,14 @@ TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) { .raster_cache = &raster_cache, .gr_context = nullptr, .view_embedder = nullptr, - .state_stack = state_stack, + .mutators_stack = mutators_stack, .dst_color_space = nullptr, + .cull_rect = kGiantRect, .surface_needs_readback = false, .raster_time = raster_time, .ui_time = ui_time, .texture_registry = nullptr, + .checkerboard_offscreen_layers = false, .frame_device_pixel_ratio = 1.0f, .has_platform_view = false, .has_texture_layer = false, @@ -2480,13 +2483,11 @@ TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) { display_list.get(), SkPoint(), true, false); for (int i = 0; i < 4; i += 1) { SkMatrix matrix = SkMatrix::I(); - state_stack.set_delegate(&mutators_stack); display_list_raster_cache_item.PrerollSetup(&preroll_context, matrix); display_list_raster_cache_item.PrerollFinalize(&preroll_context, matrix); picture_cache_generated = display_list_raster_cache_item.need_caching(); - state_stack.set_delegate(&dummy_canvas); display_list_raster_cache_item.TryToPrepareRasterCache(paint_context); display_list_raster_cache_item.Draw(paint_context, &dummy_canvas, &paint); diff --git a/testing/canvas_test.h b/testing/canvas_test.h index 0d9eda9080c13..e01b0cd018542 100644 --- a/testing/canvas_test.h +++ b/testing/canvas_test.h @@ -20,6 +20,7 @@ class CanvasTestBase : public BaseT { MockCanvas& mock_canvas() { return canvas_; } SkColorSpace* mock_color_space() { return canvas_.imageInfo().colorSpace(); } + SkNWayCanvas* mock_internal_canvas() { return canvas_.internal_canvas(); } private: MockCanvas canvas_; diff --git a/testing/mock_canvas.h b/testing/mock_canvas.h index 345adbc0cde18..c09acf5d9e0a3 100644 --- a/testing/mock_canvas.h +++ b/testing/mock_canvas.h @@ -155,7 +155,6 @@ class MockCanvas : public SkCanvasVirtualEnforcer { SkNWayCanvas* internal_canvas() { return &internal_canvas_; } const std::vector& draw_calls() const { return draw_calls_; } - void reset_draw_calls() { draw_calls_.clear(); } protected: // Save/restore/set operations that we track.