diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 41a98a9b6568d..d6aced63f813f 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -171,6 +171,8 @@ 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_tree.cc FILE: ../../../flutter/flow/layers/layer_tree.h FILE: ../../../flutter/flow/layers/layer_tree_unittests.cc diff --git a/display_list/display_list_color_filter.h b/display_list/display_list_color_filter.h index 7f03fbf5cb1df..8ac6eba47ee35 100644 --- a/display_list/display_list_color_filter.h +++ b/display_list/display_list_color_filter.h @@ -57,6 +57,11 @@ 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_alpha() 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; } @@ -150,6 +155,12 @@ class DlMatrixColorFilter final : public DlColorFilter { sk_filter->filterColor(SK_ColorTRANSPARENT) != SK_ColorTRANSPARENT; } + bool can_commute_with_alpha() 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); } diff --git a/display_list/display_list_paint.h b/display_list/display_list_paint.h index 2892d13685633..9eb0ad0de2b2a 100644 --- a/display_list/display_list_paint.h +++ b/display_list/display_list_paint.h @@ -104,6 +104,10 @@ 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_); @@ -166,7 +170,7 @@ class DlPaint { return colorFilter_; } const DlColorFilter* getColorFilterPtr() const { return colorFilter_.get(); } - DlPaint& setColorFilter(std::shared_ptr filter) { + DlPaint& setColorFilter(const std::shared_ptr filter) { colorFilter_ = filter ? filter->shared() : nullptr; return *this; } @@ -179,7 +183,7 @@ class DlPaint { return imageFilter_; } const DlImageFilter* getImageFilterPtr() const { return imageFilter_.get(); } - DlPaint& setImageFilter(std::shared_ptr filter) { + DlPaint& setImageFilter(const std::shared_ptr filter) { imageFilter_ = filter; return *this; } diff --git a/display_list/display_list_utils.cc b/display_list/display_list_utils.cc index 58ddba1425c46..dc2da62df4020 100644 --- a/display_list/display_list_utils.cc +++ b/display_list/display_list_utils.cc @@ -19,15 +19,6 @@ namespace flutter { -// clang-format off -constexpr float kInvertColorMatrix[20] = { - -1.0, 0, 0, 1.0, 0, - 0, -1.0, 0, 1.0, 0, - 0, 0, -1.0, 1.0, 0, - 1.0, 1.0, 1.0, 1.0, 0 -}; -// clang-format on - void SkPaintDispatchHelper::save_opacity(SkScalar child_opacity) { save_stack_.emplace_back(opacity_); set_opacity(child_opacity); @@ -96,13 +87,19 @@ void SkPaintDispatchHelper::setMaskFilter(const DlMaskFilter* filter) { } sk_sp SkPaintDispatchHelper::makeColorFilter() const { - if (!invert_colors_) { - return color_filter_ ? color_filter_->skia_object() : nullptr; + return MakeColorFilter(invert_colors_, color_filter_.get()); +} + +sk_sp SkPaintDispatchHelper::MakeColorFilter( + bool isInvertColors, + const DlColorFilter* color_filter) { + if (!isInvertColors) { + return color_filter ? color_filter->skia_object() : nullptr; } sk_sp invert_filter = SkColorFilters::Matrix(kInvertColorMatrix); - if (color_filter_) { - invert_filter = invert_filter->makeComposed(color_filter_->skia_object()); + if (color_filter) { + invert_filter = invert_filter->makeComposed(color_filter->skia_object()); } return invert_filter; } diff --git a/display_list/display_list_utils.h b/display_list/display_list_utils.h index ff2bad1544fd7..af7db4c6c5fde 100644 --- a/display_list/display_list_utils.h +++ b/display_list/display_list_utils.h @@ -174,6 +174,18 @@ class SkPaintDispatchHelper : public virtual Dispatcher { } } + // clang-format off + static constexpr float kInvertColorMatrix[20] = { + -1.0, 0, 0, 1.0, 0, + 0, -1.0, 0, 1.0, 0, + 0, 0, -1.0, 1.0, 0, + 1.0, 1.0, 1.0, 1.0, 0 + }; + // clang-format on + static sk_sp MakeColorFilter( + bool isInvertColors, + const DlColorFilter* color_filter); + void setAntiAlias(bool aa) override; void setDither(bool dither) override; void setStyle(DlDrawStyle style) override; diff --git a/flow/BUILD.gn b/flow/BUILD.gn index c42fa8e7ab18c..b1f5f96ada066 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -45,6 +45,8 @@ 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", diff --git a/flow/layers/backdrop_filter_layer.cc b/flow/layers/backdrop_filter_layer.cc index d52ac787c44c0..036c14b66328e 100644 --- a/flow/layers/backdrop_filter_layer.cc +++ b/flow/layers/backdrop_filter_layer.cc @@ -47,38 +47,17 @@ void BackdropFilterLayer::Preroll(PrerollContext* context, PrerollChildren(context, matrix, &child_paint_bounds); child_paint_bounds.join(context->cull_rect); set_paint_bounds(child_paint_bounds); - context->subtree_can_inherit_opacity = true; + context->renderable_state_flags = SAVE_LAYER_RENDER_FLAGS; } void BackdropFilterLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "BackdropFilterLayer::Paint"); FML_DCHECK(needs_painting(context)); - 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()); + auto mutator = context.state_stack.save(); + mutator.applyBackdropFilter(paint_bounds(), filter_, blend_mode_); - 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); - } + PaintChildren(context); } } // namespace flutter diff --git a/flow/layers/backdrop_filter_layer_unittests.cc b/flow/layers/backdrop_filter_layer_unittests.cc index 16cf3bc242232..7d242b89e851f 100644 --- a/flow/layers/backdrop_filter_layer_unittests.cc +++ b/flow/layers/backdrop_filter_layer_unittests.cc @@ -292,7 +292,7 @@ TEST_F(BackdropFilterLayerTest, OpacityInheritance) { parent->Add(layer); clip->Add(parent); - clip->Preroll(preroll_context(), SkMatrix::I()); + clip->Preroll(display_list_preroll_context(), SkMatrix::I()); clip->Paint(display_list_paint_context()); diff --git a/flow/layers/checkerboard_layertree_unittests.cc b/flow/layers/checkerboard_layertree_unittests.cc index 1923b77278334..ec4f7afa8c08d 100644 --- a/flow/layers/checkerboard_layertree_unittests.cc +++ b/flow/layers/checkerboard_layertree_unittests.cc @@ -18,7 +18,7 @@ namespace testing { using CheckerBoardLayerTest = LayerTest; #ifndef NDEBUG -TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerNotCheckBoard) { +TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerNotCheckerBoard) { 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); @@ -68,7 +68,7 @@ TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerNotCheckBoard) { MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } -TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerCheckBoard) { +TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerCheckerBoard) { 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); @@ -100,7 +100,7 @@ TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerCheckBoard) { EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_bounds)})); - layer->Paint(check_board_context()); + layer->Paint(checkerboard_context()); EXPECT_NE( mock_canvas().draw_calls(), @@ -118,7 +118,7 @@ TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerCheckBoard) { MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } -TEST_F(CheckerBoardLayerTest, ClipPathSaveLayerNotCheckBoard) { +TEST_F(CheckerBoardLayerTest, ClipPathSaveLayerNotCheckerBoard) { 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); @@ -159,7 +159,7 @@ TEST_F(CheckerBoardLayerTest, ClipPathSaveLayerNotCheckBoard) { MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } -TEST_F(CheckerBoardLayerTest, ClipPathSaveLayerCheckBoard) { +TEST_F(CheckerBoardLayerTest, ClipPathSaveLayerCheckerBoard) { 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); @@ -183,7 +183,7 @@ TEST_F(CheckerBoardLayerTest, ClipPathSaveLayerCheckBoard) { EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); - layer->Paint(check_board_context()); + layer->Paint(checkerboard_context()); EXPECT_NE( mock_canvas().draw_calls(), std::vector( @@ -200,7 +200,7 @@ TEST_F(CheckerBoardLayerTest, ClipPathSaveLayerCheckBoard) { MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } -TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerNotCheckBoard) { +TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerNotCheckerBoard) { 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); @@ -241,7 +241,7 @@ TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerNotCheckBoard) { MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } -TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerCheckBoard) { +TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerCheckerBoard) { 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); @@ -265,7 +265,7 @@ TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerCheckBoard) { EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); - layer->Paint(check_board_context()); + layer->Paint(checkerboard_context()); EXPECT_NE( mock_canvas().draw_calls(), std::vector( @@ -282,7 +282,7 @@ TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerCheckBoard) { MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } -TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerNotCheckBoard) { +TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerNotCheckerBoard) { constexpr float initial_elevation = 20.0f; SkPath layer_path; layer_path.addRect(0, 0, 8, 8).close(); @@ -303,7 +303,6 @@ TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerNotCheckBoard) { const SkPaint clip_paint; SkPaint layer_paint; layer_paint.setColor(SK_ColorGREEN); - layer_paint.setAntiAlias(true); layer->Paint(paint_context()); EXPECT_EQ( mock_canvas().draw_calls(), @@ -321,7 +320,7 @@ TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerNotCheckBoard) { MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } -TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerCheckBoard) { +TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerCheckerBoard) { constexpr float initial_elevation = 20.0f; SkPath layer_path; layer_path.addRect(0, 0, 8, 8).close(); @@ -342,8 +341,7 @@ TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerCheckBoard) { const SkPaint clip_paint; SkPaint layer_paint; layer_paint.setColor(SK_ColorGREEN); - layer_paint.setAntiAlias(true); - layer->Paint(check_board_context()); + layer->Paint(checkerboard_context()); EXPECT_NE( mock_canvas().draw_calls(), std::vector( diff --git a/flow/layers/clip_path_layer.cc b/flow/layers/clip_path_layer.cc index 51426168e02ce..ffe22de7b5470 100644 --- a/flow/layers/clip_path_layer.cc +++ b/flow/layers/clip_path_layer.cc @@ -28,8 +28,9 @@ void ClipPathLayer::OnMutatorsStackPushClipShape( mutators_stack.PushClipPath(clip_shape()); } -void ClipPathLayer::OnCanvasClipShape(SkCanvas* canvas) const { - canvas->clipPath(clip_shape(), clip_behavior() != Clip::hardEdge); +void ClipPathLayer::OnStackClipShape( + LayerStateStack::MutatorContext& mutator) const { + mutator.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 c8ff1bad743a6..bc691331fe91d 100644 --- a/flow/layers/clip_path_layer.h +++ b/flow/layers/clip_path_layer.h @@ -23,7 +23,8 @@ class ClipPathLayer : public ClipShapeLayer { void OnMutatorsStackPushClipShape(MutatorsStack& mutators_stack) override; - void OnCanvasClipShape(SkCanvas* canvas) const override; + void OnStackClipShape( + LayerStateStack::MutatorContext& mutator) 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 f65af652ce5f5..da4e2baf97fff 100644 --- a/flow/layers/clip_path_layer_unittests.cc +++ b/flow/layers/clip_path_layer_unittests.cc @@ -77,7 +77,8 @@ TEST_F(ClipPathLayerTest, PaintingCulledLayerDies) { EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); - paint_context().internal_nodes_canvas->clipRect(distant_bounds, false); + auto mutator = paint_context().state_stack.save(); + mutator.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()), @@ -279,9 +280,9 @@ TEST_F(ClipPathLayerTest, OpacityInheritance) { // ClipRectLayer will pass through compatibility from a compatible child PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; clip_path_layer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); auto path2 = SkPath().addRect({40, 40, 50, 50}); auto mock2 = MockLayer::MakeOpacityCompatible(path2); @@ -289,9 +290,9 @@ TEST_F(ClipPathLayerTest, OpacityInheritance) { // ClipRectLayer will pass through compatibility from multiple // non-overlapping compatible children - context->subtree_can_inherit_opacity = false; clip_path_layer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); auto path3 = SkPath().addRect({20, 20, 40, 40}); auto mock3 = MockLayer::MakeOpacityCompatible(path3); @@ -299,9 +300,8 @@ TEST_F(ClipPathLayerTest, OpacityInheritance) { // ClipRectLayer will not pass through compatibility from multiple // overlapping children even if they are individually compatible - context->subtree_can_inherit_opacity = false; clip_path_layer->Preroll(context, SkMatrix::I()); - EXPECT_FALSE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, 0); { // ClipRectLayer(aa with saveLayer) will always be compatible @@ -311,15 +311,13 @@ TEST_F(ClipPathLayerTest, OpacityInheritance) { clip_path_saveLayer->Add(mock2); // Double check first two children are compatible and non-overlapping - context->subtree_can_inherit_opacity = false; clip_path_saveLayer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, Layer::SAVE_LAYER_RENDER_FLAGS); // Now add the overlapping child and test again, should still be compatible clip_path_saveLayer->Add(mock3); - context->subtree_can_inherit_opacity = false; clip_path_saveLayer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, Layer::SAVE_LAYER_RENDER_FLAGS); } // An incompatible, but non-overlapping child for the following tests @@ -334,17 +332,16 @@ TEST_F(ClipPathLayerTest, OpacityInheritance) { clip_path_bad_child->Add(mock2); // Double check first two children are compatible and non-overlapping - context->subtree_can_inherit_opacity = false; clip_path_bad_child->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); clip_path_bad_child->Add(mock4); // The third child is non-overlapping, but not compatible so the // TransformLayer should end up incompatible - context->subtree_can_inherit_opacity = false; clip_path_bad_child->Preroll(context, SkMatrix::I()); - EXPECT_FALSE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, 0); } { @@ -355,15 +352,13 @@ TEST_F(ClipPathLayerTest, OpacityInheritance) { clip_path_saveLayer_bad_child->Add(mock2); // Double check first two children are compatible and non-overlapping - context->subtree_can_inherit_opacity = false; clip_path_saveLayer_bad_child->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, Layer::SAVE_LAYER_RENDER_FLAGS); // Now add the incompatible child and test again, should still be compatible clip_path_saveLayer_bad_child->Add(mock4); - context->subtree_can_inherit_opacity = false; clip_path_saveLayer_bad_child->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, Layer::SAVE_LAYER_RENDER_FLAGS); } } @@ -382,16 +377,16 @@ TEST_F(ClipPathLayerTest, OpacityInheritancePainting) { // ClipRectLayer will pass through compatibility from multiple // non-overlapping compatible children - PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; + PrerollContext* context = display_list_preroll_context(); clip_path_layer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); int opacity_alpha = 0x7F; + DlPaint mock_paint = DlPaint().setAlpha(opacity_alpha); SkPoint offset = SkPoint::Make(10, 10); auto opacity_layer = std::make_shared(opacity_alpha, offset); opacity_layer->Add(clip_path_layer); - context->subtree_can_inherit_opacity = false; opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); @@ -404,22 +399,10 @@ TEST_F(ClipPathLayerTest, OpacityInheritancePainting) { expected_builder.save(); expected_builder.clipPath(layer_clip, SkClipOp::kIntersect, true); /* child layer1 paint */ { - expected_builder.setColor(opacity_alpha << 24); - expected_builder.saveLayer(&path1.getBounds(), true); - { - expected_builder.setColor(0xFF000000); - expected_builder.drawPath(path1); - } - expected_builder.restore(); + expected_builder.drawPath(path1, mock_paint); } /* child layer2 paint */ { - 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.drawPath(path2, mock_paint); } expected_builder.restore(); } @@ -428,7 +411,7 @@ TEST_F(ClipPathLayerTest, OpacityInheritancePainting) { } opacity_layer->Paint(display_list_paint_context()); - EXPECT_TRUE(DisplayListsEQ_Verbose(expected_builder.Build(), display_list())); + EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); } TEST_F(ClipPathLayerTest, OpacityInheritanceSaveLayerPainting) { @@ -446,18 +429,18 @@ TEST_F(ClipPathLayerTest, OpacityInheritanceSaveLayerPainting) { clip_path_layer->Add(mock1); clip_path_layer->Add(mock2); - // ClipRectLayer will pass through compatibility from multiple - // non-overlapping compatible children - PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; + // ClipRectLayer will implement compatibility itself for multiple + // overlapping compatible children + PrerollContext* context = display_list_preroll_context(); clip_path_layer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, Layer::SAVE_LAYER_RENDER_FLAGS); int opacity_alpha = 0x7F; + DlPaint save_layer_paint = DlPaint().setAlpha(opacity_alpha); + DlPaint mock_paint = DlPaint(); SkPoint offset = SkPoint::Make(10, 10); auto opacity_layer = std::make_shared(opacity_alpha, offset); opacity_layer->Add(clip_path_layer); - context->subtree_can_inherit_opacity = false; opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); @@ -469,14 +452,12 @@ TEST_F(ClipPathLayerTest, OpacityInheritanceSaveLayerPainting) { /* ClipRectLayer::Paint() */ { expected_builder.save(); expected_builder.clipPath(layer_clip, SkClipOp::kIntersect, true); - expected_builder.setColor(opacity_alpha << 24); - expected_builder.saveLayer(&children_bounds, true); + expected_builder.saveLayer(&children_bounds, &save_layer_paint); /* child layer1 paint */ { - expected_builder.setColor(0xFF000000); - expected_builder.drawPath(path1); + expected_builder.drawPath(path1, mock_paint); } /* child layer2 paint */ { // - expected_builder.drawPath(path2); + expected_builder.drawPath(path2, mock_paint); } expected_builder.restore(); } @@ -485,7 +466,7 @@ TEST_F(ClipPathLayerTest, OpacityInheritanceSaveLayerPainting) { } opacity_layer->Paint(display_list_paint_context()); - EXPECT_TRUE(DisplayListsEQ_Verbose(expected_builder.Build(), display_list())); + EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); } TEST_F(ClipPathLayerTest, LayerCached) { diff --git a/flow/layers/clip_rect_layer.cc b/flow/layers/clip_rect_layer.cc index e2db2aeac03fc..5d29cc403f72d 100644 --- a/flow/layers/clip_rect_layer.cc +++ b/flow/layers/clip_rect_layer.cc @@ -28,8 +28,9 @@ void ClipRectLayer::OnMutatorsStackPushClipShape( mutators_stack.PushClipRect(clip_shape()); } -void ClipRectLayer::OnCanvasClipShape(SkCanvas* canvas) const { - canvas->clipRect(clip_shape(), clip_behavior() != Clip::hardEdge); +void ClipRectLayer::OnStackClipShape( + LayerStateStack::MutatorContext& mutator) const { + mutator.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 967bd9b349cd7..57d76ae328e19 100644 --- a/flow/layers/clip_rect_layer.h +++ b/flow/layers/clip_rect_layer.h @@ -22,7 +22,8 @@ class ClipRectLayer : public ClipShapeLayer { void OnMutatorsStackPushClipShape(MutatorsStack& mutators_stack) override; - void OnCanvasClipShape(SkCanvas* canvas) const override; + void OnStackClipShape( + LayerStateStack::MutatorContext& mutator) 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 35ac1fb46f469..ba79e7a381464 100644 --- a/flow/layers/clip_rect_layer_unittests.cc +++ b/flow/layers/clip_rect_layer_unittests.cc @@ -73,7 +73,8 @@ TEST_F(ClipRectLayerTest, PaintingCulledLayerDies) { EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_bounds)})); - paint_context().internal_nodes_canvas->clipRect(distant_bounds, false); + auto mutator = paint_context().state_stack.save(); + mutator.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()), @@ -272,9 +273,9 @@ TEST_F(ClipRectLayerTest, OpacityInheritance) { // ClipRectLayer will pass through compatibility from a compatible child PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; clip_rect_layer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); auto path2 = SkPath().addRect({40, 40, 50, 50}); auto mock2 = MockLayer::MakeOpacityCompatible(path2); @@ -282,9 +283,9 @@ TEST_F(ClipRectLayerTest, OpacityInheritance) { // ClipRectLayer will pass through compatibility from multiple // non-overlapping compatible children - context->subtree_can_inherit_opacity = false; clip_rect_layer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); auto path3 = SkPath().addRect({20, 20, 40, 40}); auto mock3 = MockLayer::MakeOpacityCompatible(path3); @@ -292,9 +293,8 @@ TEST_F(ClipRectLayerTest, OpacityInheritance) { // ClipRectLayer will not pass through compatibility from multiple // overlapping children even if they are individually compatible - context->subtree_can_inherit_opacity = false; clip_rect_layer->Preroll(context, SkMatrix::I()); - EXPECT_FALSE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, 0); { // ClipRectLayer(aa with saveLayer) will always be compatible @@ -304,15 +304,13 @@ TEST_F(ClipRectLayerTest, OpacityInheritance) { clip_rect_saveLayer->Add(mock2); // Double check first two children are compatible and non-overlapping - context->subtree_can_inherit_opacity = false; clip_rect_saveLayer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, Layer::SAVE_LAYER_RENDER_FLAGS); // Now add the overlapping child and test again, should still be compatible clip_rect_saveLayer->Add(mock3); - context->subtree_can_inherit_opacity = false; clip_rect_saveLayer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, Layer::SAVE_LAYER_RENDER_FLAGS); } // An incompatible, but non-overlapping child for the following tests @@ -327,17 +325,16 @@ TEST_F(ClipRectLayerTest, OpacityInheritance) { clip_rect_bad_child->Add(mock2); // Double check first two children are compatible and non-overlapping - context->subtree_can_inherit_opacity = false; clip_rect_bad_child->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); clip_rect_bad_child->Add(mock4); // The third child is non-overlapping, but not compatible so the // TransformLayer should end up incompatible - context->subtree_can_inherit_opacity = false; clip_rect_bad_child->Preroll(context, SkMatrix::I()); - EXPECT_FALSE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, 0); } { @@ -348,15 +345,13 @@ TEST_F(ClipRectLayerTest, OpacityInheritance) { clip_rect_saveLayer_bad_child->Add(mock2); // Double check first two children are compatible and non-overlapping - context->subtree_can_inherit_opacity = false; clip_rect_saveLayer_bad_child->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, Layer::SAVE_LAYER_RENDER_FLAGS); // Now add the incompatible child and test again, should still be compatible clip_rect_saveLayer_bad_child->Add(mock4); - context->subtree_can_inherit_opacity = false; clip_rect_saveLayer_bad_child->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, Layer::SAVE_LAYER_RENDER_FLAGS); } } @@ -373,16 +368,16 @@ TEST_F(ClipRectLayerTest, OpacityInheritancePainting) { // ClipRectLayer will pass through compatibility from multiple // non-overlapping compatible children - PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; + PrerollContext* context = display_list_preroll_context(); clip_rect_layer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); int opacity_alpha = 0x7F; + DlPaint mock_paint = DlPaint().setAlpha(opacity_alpha); SkPoint offset = SkPoint::Make(10, 10); auto opacity_layer = std::make_shared(opacity_alpha, offset); opacity_layer->Add(clip_rect_layer); - context->subtree_can_inherit_opacity = false; opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); @@ -395,22 +390,10 @@ TEST_F(ClipRectLayerTest, OpacityInheritancePainting) { expected_builder.save(); expected_builder.clipRect(clip_rect, SkClipOp::kIntersect, true); /* child layer1 paint */ { - expected_builder.setColor(opacity_alpha << 24); - expected_builder.saveLayer(&path1.getBounds(), true); - { - expected_builder.setColor(0xFF000000); - expected_builder.drawPath(path1); - } - expected_builder.restore(); + expected_builder.drawPath(path1, mock_paint); } /* child layer2 paint */ { - 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.drawPath(path2, mock_paint); } expected_builder.restore(); } @@ -419,7 +402,7 @@ TEST_F(ClipRectLayerTest, OpacityInheritancePainting) { } opacity_layer->Paint(display_list_paint_context()); - EXPECT_TRUE(DisplayListsEQ_Verbose(expected_builder.Build(), display_list())); + EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); } TEST_F(ClipRectLayerTest, OpacityInheritanceSaveLayerPainting) { @@ -437,16 +420,16 @@ TEST_F(ClipRectLayerTest, OpacityInheritanceSaveLayerPainting) { // ClipRectLayer will pass through compatibility from multiple // non-overlapping compatible children - PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; + PrerollContext* context = display_list_preroll_context(); clip_rect_layer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, Layer::SAVE_LAYER_RENDER_FLAGS); int opacity_alpha = 0x7F; + DlPaint save_layer_paint = DlPaint().setAlpha(opacity_alpha); + DlPaint mock_paint; SkPoint offset = SkPoint::Make(10, 10); auto opacity_layer = std::make_shared(opacity_alpha, offset); opacity_layer->Add(clip_rect_layer); - context->subtree_can_inherit_opacity = false; opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); @@ -458,14 +441,12 @@ TEST_F(ClipRectLayerTest, OpacityInheritanceSaveLayerPainting) { /* ClipRectLayer::Paint() */ { expected_builder.save(); expected_builder.clipRect(clip_rect, SkClipOp::kIntersect, true); - expected_builder.setColor(opacity_alpha << 24); - expected_builder.saveLayer(&children_bounds, true); + expected_builder.saveLayer(&children_bounds, &save_layer_paint); /* child layer1 paint */ { - expected_builder.setColor(0xFF000000); - expected_builder.drawPath(path1); + expected_builder.drawPath(path1, mock_paint); } /* child layer2 paint */ { // - expected_builder.drawPath(path2); + expected_builder.drawPath(path2, mock_paint); } expected_builder.restore(); } @@ -474,7 +455,7 @@ TEST_F(ClipRectLayerTest, OpacityInheritanceSaveLayerPainting) { } opacity_layer->Paint(display_list_paint_context()); - EXPECT_TRUE(DisplayListsEQ_Verbose(expected_builder.Build(), display_list())); + EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); } TEST_F(ClipRectLayerTest, LayerCached) { diff --git a/flow/layers/clip_rrect_layer.cc b/flow/layers/clip_rrect_layer.cc index 6c1a5cca6cb86..9dbbe71ba90a2 100644 --- a/flow/layers/clip_rrect_layer.cc +++ b/flow/layers/clip_rrect_layer.cc @@ -28,8 +28,9 @@ void ClipRRectLayer::OnMutatorsStackPushClipShape( mutators_stack.PushClipRRect(clip_shape()); } -void ClipRRectLayer::OnCanvasClipShape(SkCanvas* canvas) const { - canvas->clipRRect(clip_shape(), clip_behavior() != Clip::hardEdge); +void ClipRRectLayer::OnStackClipShape( + LayerStateStack::MutatorContext& mutator) const { + mutator.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 15c71a732e467..17d4f29e0950c 100644 --- a/flow/layers/clip_rrect_layer.h +++ b/flow/layers/clip_rrect_layer.h @@ -22,7 +22,8 @@ class ClipRRectLayer : public ClipShapeLayer { void OnMutatorsStackPushClipShape(MutatorsStack& mutators_stack) override; - void OnCanvasClipShape(SkCanvas* canvas) const override; + void OnStackClipShape( + LayerStateStack::MutatorContext& mutator) 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 3f47f130ac500..eb50e9b982b85 100644 --- a/flow/layers/clip_rrect_layer_unittests.cc +++ b/flow/layers/clip_rrect_layer_unittests.cc @@ -77,7 +77,8 @@ TEST_F(ClipRRectLayerTest, PaintingCulledLayerDies) { EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); - paint_context().internal_nodes_canvas->clipRect(distant_bounds, false); + auto mutator = paint_context().state_stack.save(); + mutator.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()), @@ -278,9 +279,9 @@ TEST_F(ClipRRectLayerTest, OpacityInheritance) { // ClipRectLayer will pass through compatibility from a compatible child PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; clip_r_rect_layer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); auto path2 = SkPath().addRect({40, 40, 50, 50}); auto mock2 = MockLayer::MakeOpacityCompatible(path2); @@ -288,9 +289,9 @@ TEST_F(ClipRRectLayerTest, OpacityInheritance) { // ClipRectLayer will pass through compatibility from multiple // non-overlapping compatible children - context->subtree_can_inherit_opacity = false; clip_r_rect_layer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); auto path3 = SkPath().addRect({20, 20, 40, 40}); auto mock3 = MockLayer::MakeOpacityCompatible(path3); @@ -298,9 +299,8 @@ TEST_F(ClipRRectLayerTest, OpacityInheritance) { // ClipRectLayer will not pass through compatibility from multiple // overlapping children even if they are individually compatible - context->subtree_can_inherit_opacity = false; clip_r_rect_layer->Preroll(context, SkMatrix::I()); - EXPECT_FALSE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, 0); { // ClipRectLayer(aa with saveLayer) will always be compatible @@ -310,15 +310,13 @@ TEST_F(ClipRRectLayerTest, OpacityInheritance) { clip_r_rect_saveLayer->Add(mock2); // Double check first two children are compatible and non-overlapping - context->subtree_can_inherit_opacity = false; clip_r_rect_saveLayer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, Layer::SAVE_LAYER_RENDER_FLAGS); // Now add the overlapping child and test again, should still be compatible clip_r_rect_saveLayer->Add(mock3); - context->subtree_can_inherit_opacity = false; clip_r_rect_saveLayer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, Layer::SAVE_LAYER_RENDER_FLAGS); } // An incompatible, but non-overlapping child for the following tests @@ -333,17 +331,16 @@ TEST_F(ClipRRectLayerTest, OpacityInheritance) { clip_r_rect_bad_child->Add(mock2); // Double check first two children are compatible and non-overlapping - context->subtree_can_inherit_opacity = false; clip_r_rect_bad_child->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); clip_r_rect_bad_child->Add(mock4); // The third child is non-overlapping, but not compatible so the // TransformLayer should end up incompatible - context->subtree_can_inherit_opacity = false; clip_r_rect_bad_child->Preroll(context, SkMatrix::I()); - EXPECT_FALSE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, 0); } { @@ -354,15 +351,13 @@ TEST_F(ClipRRectLayerTest, OpacityInheritance) { clip_r_rect_saveLayer_bad_child->Add(mock2); // Double check first two children are compatible and non-overlapping - context->subtree_can_inherit_opacity = false; clip_r_rect_saveLayer_bad_child->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, Layer::SAVE_LAYER_RENDER_FLAGS); // Now add the incompatible child and test again, should still be compatible clip_r_rect_saveLayer_bad_child->Add(mock4); - context->subtree_can_inherit_opacity = false; clip_r_rect_saveLayer_bad_child->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, Layer::SAVE_LAYER_RENDER_FLAGS); } } @@ -380,16 +375,15 @@ TEST_F(ClipRRectLayerTest, OpacityInheritancePainting) { // ClipRectLayer will pass through compatibility from multiple // non-overlapping compatible children - PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; + PrerollContext* context = display_list_preroll_context(); clip_rect_layer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_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); - context->subtree_can_inherit_opacity = false; opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); @@ -403,21 +397,11 @@ TEST_F(ClipRRectLayerTest, OpacityInheritancePainting) { expected_builder.clipRRect(clip_r_rect, SkClipOp::kIntersect, true); /* child layer1 paint */ { expected_builder.setColor(opacity_alpha << 24); - expected_builder.saveLayer(&path1.getBounds(), true); - { - expected_builder.setColor(0xFF000000); - expected_builder.drawPath(path1); - } - expected_builder.restore(); + expected_builder.drawPath(path1); } /* child layer2 paint */ { 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.drawPath(path2); } expected_builder.restore(); } @@ -426,7 +410,7 @@ TEST_F(ClipRRectLayerTest, OpacityInheritancePainting) { } opacity_layer->Paint(display_list_paint_context()); - EXPECT_TRUE(DisplayListsEQ_Verbose(expected_builder.Build(), display_list())); + EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); } TEST_F(ClipRRectLayerTest, OpacityInheritanceSaveLayerPainting) { @@ -445,16 +429,14 @@ TEST_F(ClipRRectLayerTest, OpacityInheritanceSaveLayerPainting) { // ClipRectLayer will pass through compatibility from multiple // non-overlapping compatible children - PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; + PrerollContext* context = display_list_preroll_context(); clip_r_rect_layer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, Layer::SAVE_LAYER_RENDER_FLAGS); int opacity_alpha = 0x7F; SkPoint offset = SkPoint::Make(10, 10); auto opacity_layer = std::make_shared(opacity_alpha, offset); opacity_layer->Add(clip_r_rect_layer); - context->subtree_can_inherit_opacity = false; opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); diff --git a/flow/layers/clip_shape_layer.h b/flow/layers/clip_shape_layer.h index faf4c26d31b6c..a880cb6604068 100644 --- a/flow/layers/clip_shape_layer.h +++ b/flow/layers/clip_shape_layer.h @@ -56,10 +56,6 @@ class ClipShapeLayer : public CacheableContainerLayer { Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); OnMutatorsStackPushClipShape(context->mutators_stack); - // 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, matrix, &child_paint_bounds); if (child_paint_bounds.intersect(clip_shape_bounds())) { @@ -69,7 +65,7 @@ class ClipShapeLayer : public CacheableContainerLayer { // 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->subtree_can_inherit_opacity = true; + context->renderable_state_flags = SAVE_LAYER_RENDER_FLAGS; } context->mutators_stack.Pop(); @@ -79,23 +75,28 @@ class ClipShapeLayer : public CacheableContainerLayer { void Paint(PaintContext& context) const override { FML_DCHECK(needs_painting(context)); - SkAutoCanvasRestore save(context.internal_nodes_canvas, true); - OnCanvasClipShape(context.internal_nodes_canvas); + auto mutator = context.state_stack.save(); + OnStackClipShape(mutator); if (!UsesSaveLayer()) { PaintChildren(context); return; } - AutoCachePaint cache_paint(context); if (context.raster_cache) { - if (layer_raster_cache_item_->Draw(context, cache_paint.sk_paint())) { + auto restore_apply = context.state_stack.applyState( + paint_bounds(), LayerStateStack::CALLER_CAN_APPLY_OPACITY); + + SkPaint paint; + if (layer_raster_cache_item_->Draw(context, + context.state_stack.fill(paint))) { return; } } - Layer::AutoSaveLayer save_layer = Layer::AutoSaveLayer::Create( - context, paint_bounds(), cache_paint.sk_paint()); + // saveWithOpacity optimizes the case where opacity >= 1.0 + // to a simple saveLayer + mutator.saveLayer(paint_bounds()); PaintChildren(context); } @@ -106,7 +107,8 @@ class ClipShapeLayer : public CacheableContainerLayer { protected: virtual const SkRect& clip_shape_bounds() const = 0; virtual void OnMutatorsStackPushClipShape(MutatorsStack& mutators_stack) = 0; - virtual void OnCanvasClipShape(SkCanvas* canvas) const = 0; + virtual void OnStackClipShape( + LayerStateStack::MutatorContext& mutator) 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 02822bf68d612..fad9d6ff484de 100644 --- a/flow/layers/color_filter_layer.cc +++ b/flow/layers/color_filter_layer.cc @@ -39,38 +39,32 @@ void ColorFilterLayer::Preroll(PrerollContext* context, AutoCache cache = AutoCache(layer_raster_cache_item_.get(), context, matrix); ContainerLayer::Preroll(context, 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; + context->renderable_state_flags = LayerStateStack::CALLER_CAN_APPLY_OPACITY; } void ColorFilterLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ColorFilterLayer::Paint"); FML_DCHECK(needs_painting(context)); - if (context.raster_cache) { - AutoCachePaint cache_paint(context); - if (layer_raster_cache_item_->IsCacheChildren()) { - cache_paint.setColorFilter(filter_.get()); - } - if (layer_raster_cache_item_->Draw(context, cache_paint.sk_paint())) { - return; - } - } + // if (context.raster_cache) { + // AutoCachePaint cache_paint(context); + // if (layer_raster_cache_item_->IsCacheChildren()) { + // cache_paint.setColorFilter(filter_.get()); + // } + // if (layer_raster_cache_item_->Draw(context, cache_paint.sk_paint())) { + // return; + // } + // } - 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); - } + // AutoCachePaint cache_paint(context); + // cache_paint.setColorFilter(filter_.get()); + auto mutator = context.state_stack.save(); + mutator.applyColorFilter(paint_bounds(), filter_); + + PaintChildren(context); } } // namespace flutter diff --git a/flow/layers/color_filter_layer_unittests.cc b/flow/layers/color_filter_layer_unittests.cc index 63ac582c5b527..893afc03081fc 100644 --- a/flow/layers/color_filter_layer_unittests.cc +++ b/flow/layers/color_filter_layer_unittests.cc @@ -74,14 +74,9 @@ 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::SaveLayerData{child_bounds, filter_paint, - nullptr, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::DrawPathData{child_path, child_paint}}})); } TEST_F(ColorFilterLayerTest, SimpleFilter) { @@ -95,7 +90,7 @@ TEST_F(ColorFilterLayerTest, SimpleFilter) { auto layer = std::make_shared(dl_color_filter); layer->Add(mock_layer); - layer->Preroll(preroll_context(), initial_transform); + layer->Preroll(display_list_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())); @@ -137,7 +132,7 @@ TEST_F(ColorFilterLayerTest, MultipleChildren) { SkRect children_bounds = child_path1.getBounds(); children_bounds.join(child_path2.getBounds()); - layer->Preroll(preroll_context(), initial_transform); + layer->Preroll(display_list_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,7 +186,7 @@ TEST_F(ColorFilterLayerTest, Nested) { SkRect children_bounds = child_path1.getBounds(); children_bounds.join(child_path2.getBounds()); - layer1->Preroll(preroll_context(), initial_transform); + layer1->Preroll(display_list_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); @@ -415,18 +410,17 @@ TEST_F(ColorFilterLayerTest, OpacityInheritance) { std::make_shared(matrix)); color_filter_layer->Add(mock_layer); - PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; - color_filter_layer->Preroll(preroll_context(), initial_transform); + PrerollContext* context = display_list_preroll_context(); + color_filter_layer->Preroll(context, initial_transform); // ColorFilterLayer can always inherit opacity whether or not their // children are compatible. - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_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); - context->subtree_can_inherit_opacity = false; opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); diff --git a/flow/layers/container_layer.cc b/flow/layers/container_layer.cc index a37f7d92f4759..5c48a6716aef8 100644 --- a/flow/layers/container_layer.cc +++ b/flow/layers/container_layer.cc @@ -8,7 +8,9 @@ namespace flutter { -ContainerLayer::ContainerLayer() : child_paint_bounds_(SkRect::MakeEmpty()) {} +ContainerLayer::ContainerLayer() + : child_paint_bounds_(SkRect::MakeEmpty()), + children_rendering_state_flags_(0) {} void ContainerLayer::Diff(DiffContext* context, const Layer* old_layer) { auto old_container = static_cast(old_layer); @@ -138,7 +140,8 @@ void ContainerLayer::PrerollChildren(PrerollContext* context, bool child_has_platform_view = false; bool child_has_texture_layer = false; - bool subtree_can_inherit_opacity = context->subtree_can_inherit_opacity; + int children_renderable_state_flags = + LayerStateStack::CALLER_CAN_APPLY_ANYTHING; for (auto& layer : layers_) { // Reset context->has_platform_view and context->has_texture_layer to false @@ -149,18 +152,16 @@ void ContainerLayer::PrerollChildren(PrerollContext* context, // Initialize the "inherit opacity" flag to false and allow the layer to // override the answer during its |Preroll| - context->subtree_can_inherit_opacity = false; + context->renderable_state_flags = 0; layer->Preroll(context, child_matrix); - 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())) { + children_renderable_state_flags &= context->renderable_state_flags; + if (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 - subtree_can_inherit_opacity = false; + children_renderable_state_flags = 0; } child_paint_bounds->join(layer->paint_bounds()); @@ -172,8 +173,9 @@ void ContainerLayer::PrerollChildren(PrerollContext* context, context->has_platform_view = child_has_platform_view; context->has_texture_layer = child_has_texture_layer; - context->subtree_can_inherit_opacity = subtree_can_inherit_opacity; + context->renderable_state_flags = children_renderable_state_flags; set_subtree_has_platform_view(child_has_platform_view); + set_children_rendering_state_flags(children_renderable_state_flags); child_paint_bounds_ = *child_paint_bounds; } @@ -184,6 +186,11 @@ 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_rendering_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 cb3d497aa642a..a47307f73e6ed 100644 --- a/flow/layers/container_layer.h +++ b/flow/layers/container_layer.h @@ -33,6 +33,16 @@ 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_rendering_state_flags() const { + return children_rendering_state_flags_; + } + void set_children_rendering_state_flags(int flags) { + children_rendering_state_flags_ = flags; + } protected: void PrerollChildren(PrerollContext* context, @@ -42,6 +52,7 @@ class ContainerLayer : public Layer { private: std::vector> layers_; SkRect child_paint_bounds_; + int children_rendering_state_flags_; FML_DISALLOW_COPY_AND_ASSIGN(ContainerLayer); }; diff --git a/flow/layers/container_layer_unittests.cc b/flow/layers/container_layer_unittests.cc index f1c6df8cdf203..3480a62da2cda 100644 --- a/flow/layers/container_layer_unittests.cc +++ b/flow/layers/container_layer_unittests.cc @@ -460,53 +460,48 @@ TEST_F(ContainerLayerTest, OpacityInheritance) { auto container1 = std::make_shared(); container1->Add(mock1); - // ContainerLayer will not pass through compatibility on its own - // Subclasses must explicitly enable this in their own Preroll + // Single opacity compatible child makes container compatible PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; container1->Preroll(context, SkMatrix::I()); - EXPECT_FALSE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_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 if the caller enables it - context->subtree_can_inherit_opacity = true; + // Multiple non-overlapping opacity compatible children are compatible container1->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); auto path3 = SkPath().addRect({20, 20, 40, 40}); auto mock3 = MockLayer::MakeOpacityCompatible(path3); container1->Add(mock3); - // ContainerLayer will not pass through compatibility from multiple - // overlapping children even if they are individually compatible - // and the caller requests it - context->subtree_can_inherit_opacity = true; + // Multiple overlapping individually opacity compatible children result + // in a non-compatible layer container1->Preroll(context, SkMatrix::I()); - EXPECT_FALSE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, 0); auto container2 = std::make_shared(); container2->Add(mock1); container2->Add(mock2); - // Double check first two children are compatible and non-overlapping - // if the caller requests it - context->subtree_can_inherit_opacity = true; + // Start again with 2 non-overlapping individually opacity compatible + // children. container2->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_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 - // TransformLayer should end up incompatible - context->subtree_can_inherit_opacity = true; + // Add a third child which is not opacity compatible even though it is + // also non-overlapping which will result in an incompatible container. container2->Preroll(context, SkMatrix::I()); - EXPECT_FALSE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, 0); } TEST_F(ContainerLayerTest, CollectionCacheableLayer) { SkPath child_path; diff --git a/flow/layers/display_list_layer.cc b/flow/layers/display_list_layer.cc index 5039feae31c3b..cc754204a9686 100644 --- a/flow/layers/display_list_layer.cc +++ b/flow/layers/display_list_layer.cc @@ -99,7 +99,7 @@ void DisplayListLayer::Preroll(PrerollContext* context, AutoCache cache = AutoCache(display_list_raster_cache_item_.get(), context, matrix); if (disp_list->can_apply_group_opacity()) { - context->subtree_can_inherit_opacity = true; + context->renderable_state_flags = LayerStateStack::CALLER_CAN_APPLY_OPACITY; } set_paint_bounds(bounds_); } @@ -109,24 +109,26 @@ void DisplayListLayer::Paint(PaintContext& context) const { FML_DCHECK(display_list_.skia_object()); FML_DCHECK(needs_painting(context)); - SkAutoCanvasRestore save(context.leaf_nodes_canvas, true); - context.leaf_nodes_canvas->translate(offset_.x(), offset_.y()); + auto mutator = context.state_stack.save(); + mutator.translate(offset_.x(), offset_.y()); 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())) { + SkPaint paint; + if (display_list_raster_cache_item_->Draw( + context, context.state_stack.fill(paint))) { TRACE_EVENT_INSTANT0("flutter", "raster cache hit"); return; } } + SkScalar opacity = context.state_stack.outstanding_opacity(); + if (context.enable_leaf_layer_tracing) { - const auto canvas_size = context.leaf_nodes_canvas->getBaseLayerSize(); + const auto canvas_size = context.canvas->getBaseLayerSize(); auto offscreen_surface = std::make_unique(context.gr_context, canvas_size); - const auto& ctm = context.leaf_nodes_canvas->getTotalMatrix(); + const auto& ctm = context.canvas->getTotalMatrix(); const auto start_time = fml::TimePoint::Now(); { @@ -135,7 +137,7 @@ void DisplayListLayer::Paint(PaintContext& context) const { SkAutoCanvasRestore save(canvas, true); canvas->clear(SK_ColorTRANSPARENT); canvas->setMatrix(ctm); - display_list()->RenderTo(canvas, context.inherited_opacity); + display_list()->RenderTo(canvas, opacity); canvas->flush(); } const fml::TimeDelta offscreen_render_time = @@ -149,18 +151,12 @@ void DisplayListLayer::Paint(PaintContext& context) const { context.layer_snapshot_store->Add(snapshot_data); } - 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); + 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); } else { - display_list()->RenderTo(context.leaf_nodes_canvas, - context.inherited_opacity); + display_list()->RenderTo(context.canvas, opacity); } } diff --git a/flow/layers/display_list_layer_unittests.cc b/flow/layers/display_list_layer_unittests.cc index ca88583539144..7ab3af42efaca 100644 --- a/flow/layers/display_list_layer_unittests.cc +++ b/flow/layers/display_list_layer_unittests.cc @@ -108,17 +108,16 @@ TEST_F(DisplayListLayerTest, SimpleDisplayListOpacityInheritance) { layer_offset, SkiaGPUObject(display_list, unref_queue()), false, false); EXPECT_TRUE(display_list->can_apply_group_opacity()); - auto context = preroll_context(); - context->subtree_can_inherit_opacity = false; - display_list_layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + auto context = display_list_preroll_context(); + display_list_layer->Preroll(context, SkMatrix()); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_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); - context->subtree_can_inherit_opacity = false; opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); @@ -126,8 +125,6 @@ TEST_F(DisplayListLayerTest, SimpleDisplayListOpacityInheritance) { 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(); @@ -138,7 +135,7 @@ TEST_F(DisplayListLayerTest, SimpleDisplayListOpacityInheritance) { { expected_builder.translate(layer_offset.fX, layer_offset.fY); expected_builder.setColor(opacity_alpha << 24); - expected_builder.saveLayer(&save_layer_bounds, true); + expected_builder.saveLayer(&picture_bounds, true); /* display_list contents */ { // expected_builder.drawDisplayList(child_display_list); } @@ -152,7 +149,7 @@ TEST_F(DisplayListLayerTest, SimpleDisplayListOpacityInheritance) { opacity_layer->Paint(display_list_paint_context()); EXPECT_TRUE( - DisplayListsEQ_Verbose(expected_builder.Build(), this->display_list())); + DisplayListsEQ_Verbose(this->display_list(), expected_builder.Build())); } TEST_F(DisplayListLayerTest, IncompatibleDisplayListOpacityInheritance) { @@ -167,20 +164,20 @@ TEST_F(DisplayListLayerTest, IncompatibleDisplayListOpacityInheritance) { layer_offset, SkiaGPUObject(display_list, unref_queue()), false, false); EXPECT_FALSE(display_list->can_apply_group_opacity()); - auto context = preroll_context(); - context->subtree_can_inherit_opacity = false; - display_list_layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_FALSE(context->subtree_can_inherit_opacity); + auto context = display_list_preroll_context(); + display_list_layer->Preroll(context, SkMatrix()); + EXPECT_EQ(context->renderable_state_flags, 0); 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); - context->subtree_can_inherit_opacity = false; opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_FALSE(opacity_layer->children_can_accept_opacity()); + // We duplicate the display_list from above to verify that the output + // will still match based on the content of the 2 DisplayLists. DisplayListBuilder child_builder; child_builder.drawRect(picture1_bounds); child_builder.drawRect(picture2_bounds); @@ -190,7 +187,6 @@ 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(); @@ -215,7 +211,7 @@ TEST_F(DisplayListLayerTest, IncompatibleDisplayListOpacityInheritance) { opacity_layer->Paint(display_list_paint_context()); EXPECT_TRUE( - DisplayListsEQ_Verbose(expected_builder.Build(), this->display_list())); + DisplayListsEQ_Verbose(this->display_list(), expected_builder.Build())); } TEST_F(DisplayListLayerTest, CachedIncompatibleDisplayListOpacityInheritance) { @@ -233,9 +229,8 @@ TEST_F(DisplayListLayerTest, CachedIncompatibleDisplayListOpacityInheritance) { use_skia_raster_cache(); auto context = preroll_context(); - context->subtree_can_inherit_opacity = false; display_list_layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_FALSE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, 0); // Pump the DisplayListLayer until it is ready to cache its DL display_list_layer->Preroll(preroll_context(), SkMatrix()); @@ -247,7 +242,6 @@ TEST_F(DisplayListLayerTest, CachedIncompatibleDisplayListOpacityInheritance) { auto opacity_layer = std::make_shared(opacity_alpha, opacity_offset); opacity_layer->Add(display_list_layer); - context->subtree_can_inherit_opacity = false; opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); diff --git a/flow/layers/display_list_raster_cache_item.cc b/flow/layers/display_list_raster_cache_item.cc index 10a64cff3a45e..fcfc7a755ad21 100644 --- a/flow/layers/display_list_raster_cache_item.cc +++ b/flow/layers/display_list_raster_cache_item.cc @@ -113,7 +113,7 @@ void DisplayListRasterCacheItem::PrerollFinalize(PrerollContext* context, if (!visible || accesses <= raster_cache->access_threshold()) { cache_state_ = kNone; } else { - context->subtree_can_inherit_opacity = true; + context->renderable_state_flags = Layer::RASTER_CACHE_RENDER_FLAGS; 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.leaf_nodes_canvas, paint); + return Draw(context, context.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 addccef60d533..334c2f41f9cd1 100644 --- a/flow/layers/image_filter_layer.cc +++ b/flow/layers/image_filter_layer.cc @@ -53,15 +53,15 @@ void ImageFilterLayer::Preroll(PrerollContext* context, SkRect child_bounds = SkRect::MakeEmpty(); PrerollChildren(context, 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; } + // 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 = LayerStateStack::CALLER_CAN_APPLY_OPACITY; + const SkIRect filter_in_bounds = child_bounds.roundOut(); SkIRect filter_out_bounds; filter_->map_device_bounds(filter_in_bounds, SkMatrix::I(), @@ -84,32 +84,20 @@ void ImageFilterLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ImageFilterLayer::Paint"); FML_DCHECK(needs_painting(context)); - AutoCachePaint cache_paint(context); - if (context.raster_cache) { - if (layer_raster_cache_item_->IsCacheChildren()) { - cache_paint.setImageFilter(transformed_filter_.get()); - } - if (layer_raster_cache_item_->Draw(context, cache_paint.sk_paint())) { - return; - } - } + // AutoCachePaint cache_paint(context); + // if (context.raster_cache) { + // if (layer_raster_cache_item_->IsCacheChildren()) { + // cache_paint.setImageFilter(transformed_filter_.get()); + // } + // if (layer_raster_cache_item_->Draw(context, cache_paint.sk_paint())) { + // return; + // } + // } - 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); - } + auto mutator = context.state_stack.save(); + mutator.applyImageFilter(child_paint_bounds(), filter_); + + PaintChildren(context); } } // namespace flutter diff --git a/flow/layers/image_filter_layer_unittests.cc b/flow/layers/image_filter_layer_unittests.cc index 2ad4a7e3e86d6..e3a4e86b8300b 100644 --- a/flow/layers/image_filter_layer_unittests.cc +++ b/flow/layers/image_filter_layer_unittests.cc @@ -69,11 +69,7 @@ TEST_F(ImageFilterLayerTest, EmptyFilter) { 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}}, + 0, MockCanvas::DrawPathData{child_path, child_paint}}, })); } @@ -91,7 +87,7 @@ TEST_F(ImageFilterLayerTest, SimpleFilter) { const SkRect child_rounded_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 21.0f, 22.0f); - layer->Preroll(preroll_context(), initial_transform); + layer->Preroll(display_list_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())); @@ -131,7 +127,7 @@ TEST_F(ImageFilterLayerTest, SimpleFilterBounds) { const SkRect filter_bounds = SkRect::MakeLTRB(10.0f, 12.0f, 42.0f, 44.0f); - layer->Preroll(preroll_context(), initial_transform); + layer->Preroll(display_list_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())); @@ -176,7 +172,7 @@ TEST_F(ImageFilterLayerTest, MultipleChildren) { children_bounds.join(child_path2.getBounds()); SkRect children_rounded_bounds = SkRect::Make(children_bounds.roundOut()); - layer->Preroll(preroll_context(), initial_transform); + layer->Preroll(display_list_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,7 +233,7 @@ TEST_F(ImageFilterLayerTest, Nested) { const SkRect mock_layer2_rounded_bounds = SkRect::Make(child_path2.getBounds().roundOut()); - layer1->Preroll(preroll_context(), initial_transform); + layer1->Preroll(display_list_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); @@ -445,18 +441,17 @@ TEST_F(ImageFilterLayerTest, OpacityInheritance) { auto image_filter_layer = std::make_shared(dl_image_filter); image_filter_layer->Add(mock_layer); - PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; - image_filter_layer->Preroll(preroll_context(), initial_transform); + PrerollContext* context = display_list_preroll_context(); + image_filter_layer->Preroll(context, initial_transform); // ImageFilterLayers can always inherit opacity whether or not their // children are compatible. - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_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->subtree_can_inherit_opacity = false; opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); @@ -482,7 +477,7 @@ TEST_F(ImageFilterLayerTest, OpacityInheritance) { } opacity_layer->Paint(display_list_paint_context()); - EXPECT_TRUE(DisplayListsEQ_Verbose(expected_builder.Build(), display_list())); + EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); } using ImageFilterLayerDiffTest = DiffContextTest; diff --git a/flow/layers/layer.cc b/flow/layers/layer.cc index 8a34b775ef766..e19af5a13a5b2 100644 --- a/flow/layers/layer.cc +++ b/flow/layers/layer.cc @@ -56,51 +56,4 @@ 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 bb57b430fa3b0..e36567500798b 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -16,6 +16,7 @@ #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" @@ -61,7 +62,6 @@ struct PrerollContext { 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 @@ -71,42 +71,11 @@ struct PrerollContext { // prescence of a texture layer during Preroll. bool has_texture_layer = false; - // 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; + // 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. + int renderable_state_flags = 0; std::vector* raster_cached_entries; @@ -128,8 +97,8 @@ struct PaintContext { // 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; + LayerStateStack& state_stack; + SkCanvas* canvas; GrDirectContext* gr_context; SkColorSpace* dst_color_space; ExternalViewEmbedder* view_embedder; @@ -137,7 +106,6 @@ 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 @@ -145,21 +113,27 @@ struct PaintContext { 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; + DisplayListBuilder* builder = nullptr; }; // Represents a single composited layer. Created on the UI thread but then // subquently 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 SAVE_LAYER_RENDER_FLAGS = + LayerStateStack::CALLER_CAN_APPLY_OPACITY | + LayerStateStack::CALLER_CAN_APPLY_COLOR_FILTER | + LayerStateStack::CALLER_CAN_APPLY_IMAGE_FILTER; + + // 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 RASTER_CACHE_RENDER_FLAGS = + LayerStateStack::CALLER_CAN_APPLY_OPACITY; + Layer(); virtual ~Layer(); @@ -214,114 +188,6 @@ 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); } @@ -368,7 +234,7 @@ class Layer { // See https://github.com/flutter/flutter/issues/81419 return true; } - if (context.inherited_opacity == 0) { + if (!context.state_stack.needs_painting()) { return false; } // Workaround for Skia bug (quickReject does not reject empty bounds). @@ -376,7 +242,7 @@ class Layer { if (paint_bounds_.isEmpty()) { return false; } - return !context.leaf_nodes_canvas->quickReject(paint_bounds_); + return !context.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 d5ddc17224b5a..9f99501c453df 100644 --- a/flow/layers/layer_raster_cache_item.cc +++ b/flow/layers/layer_raster_cache_item.cc @@ -102,14 +102,11 @@ bool Rasterize(RasterCacheItem::CacheState cache_state, const PaintContext& paint_context, SkCanvas* canvas) { FML_DCHECK(cache_state != RasterCacheItem::CacheState::kNone); - 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); + LayerStateStack state_stack; PaintContext context = { // clang-format off - .internal_nodes_canvas = static_cast(&internal_nodes_canvas), - .leaf_nodes_canvas = canvas, + .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, @@ -117,7 +114,6 @@ bool Rasterize(RasterCacheItem::CacheState cache_state, .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 }; @@ -168,7 +164,7 @@ bool LayerRasterCacheItem::TryToPrepareRasterCache(const PaintContext& context, bool LayerRasterCacheItem::Draw(const PaintContext& context, const SkPaint* paint) const { - return Draw(context, context.leaf_nodes_canvas, paint); + return Draw(context, context.canvas, paint); } bool LayerRasterCacheItem::Draw(const PaintContext& context, diff --git a/flow/layers/layer_state_stack.cc b/flow/layers/layer_state_stack.cc new file mode 100644 index 0000000000000..eb8ea2735d822 --- /dev/null +++ b/flow/layers/layer_state_stack.cc @@ -0,0 +1,495 @@ +// 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/paint_utils.h" + +namespace flutter { + +using AutoRestore = LayerStateStack::AutoRestore; +using MutatorContext = LayerStateStack::MutatorContext; + +void LayerStateStack::set_canvas_delegate(SkCanvas* canvas) { + if (canvas_) { + canvas_->restoreToCount(canvas_restore_count_); + canvas_ = nullptr; + } + if (canvas) { + canvas_restore_count_ = canvas->getSaveCount(); + canvas_ = canvas; + reapply_all(canvas, nullptr); + } +} + +void LayerStateStack::set_builder_delegate(DisplayListBuilder* builder) { + if (builder_) { + builder_->restoreToCount(builder_restore_count_); + builder_ = nullptr; + } + if (builder) { + builder_restore_count_ = builder->getSaveCount(); + builder_ = builder; + reapply_all(nullptr, builder); + } +} + +void LayerStateStack::reapply_all(SkCanvas* canvas, + DisplayListBuilder* builder) { + // 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; + for (auto& state : state_stack_) { + state->reapply(&attributes, canvas, builder); + } + FML_DCHECK(attributes == outstanding_); +} + +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) { + 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) { + 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; +} + +MutatorContext LayerStateStack::save() { + auto ret = MutatorContext(this); + state_stack_.emplace_back(std::make_unique()); + state_stack_.back()->apply(&outstanding_, canvas_, builder_); + 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(bounds, opacity); + layer_state_stack_->push_opacity(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(bounds, filter); + layer_state_stack_->push_image_filter(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(bounds, filter); + layer_state_stack_->push_color_filter(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_->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_->push_transform(matrix); + } +} + +void MutatorContext::transform(const SkM44& m44) { + layer_state_stack_->push_transform(m44); +} + +void MutatorContext::clipRect(const SkRect& rect, bool is_aa) { + layer_state_stack_->push_clip_rect(rect, is_aa); +} + +void MutatorContext::clipRRect(const SkRRect& rrect, bool is_aa) { + layer_state_stack_->push_clip_rrect(rrect, is_aa); +} + +void MutatorContext::clipPath(const SkPath& path, bool is_aa) { + 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(&outstanding_, canvas_, builder_); + state_stack_.pop_back(); + } +} + +void LayerStateStack::push_attributes() { + state_stack_.emplace_back(std::make_unique(outstanding_)); +} + +void LayerStateStack::push_opacity(SkScalar opacity) { + state_stack_.emplace_back(std::make_unique(opacity)); + apply_last_entry(); +} + +void LayerStateStack::push_color_filter( + const std::shared_ptr filter) { + state_stack_.emplace_back(std::make_unique(filter)); + apply_last_entry(); +} + +void LayerStateStack::push_image_filter( + const std::shared_ptr filter) { + state_stack_.emplace_back(std::make_unique(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, do_checkerboard_)); + apply_last_entry(); +} + +void LayerStateStack::push_translate(SkScalar tx, SkScalar ty) { + state_stack_.emplace_back(std::make_unique(tx, ty)); + apply_last_entry(); +} + +void LayerStateStack::push_transform(const SkM44& m44) { + state_stack_.emplace_back(std::make_unique(m44)); + apply_last_entry(); +} + +void LayerStateStack::push_transform(const SkMatrix& matrix) { + 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(rect, is_aa)); + apply_last_entry(); +} + +void LayerStateStack::push_clip_rrect(const SkRRect& rrect, bool is_aa) { + state_stack_.emplace_back(std::make_unique(rrect, is_aa)); + apply_last_entry(); +} + +void LayerStateStack::push_clip_path(const SkPath& path, bool is_aa) { + state_stack_.emplace_back(std::make_unique(path, is_aa)); + apply_last_entry(); +} + +bool LayerStateStack::needs_save_layer(int flags) const { + if (outstanding_.opacity < SK_Scalar1 && + (flags & LayerStateStack::CALLER_CAN_APPLY_OPACITY) == 0) { + return true; + } + if (outstanding_.image_filter && + (flags & LayerStateStack::CALLER_CAN_APPLY_IMAGE_FILTER) == 0) { + return true; + } + if (outstanding_.color_filter && + (flags & LayerStateStack::CALLER_CAN_APPLY_COLOR_FILTER) == 0) { + return true; + } + return false; +} + +void LayerStateStack::save_layer(const SkRect& bounds) { + state_stack_.emplace_back(std::make_unique( + bounds, DlBlendMode::kSrcOver, do_checkerboard_)); + apply_last_entry(); +} + +void LayerStateStack::maybe_save_layer(const SkRect& bounds, int apply_flags) { + if (needs_save_layer(apply_flags)) { + save_layer(bounds); + } +} + +void LayerStateStack::maybe_save_layer(const SkRect& bounds, SkScalar opacity) { + if (outstanding_.image_filter) { + save_layer(bounds); + } +} + +void LayerStateStack::maybe_save_layer( + const SkRect& bounds, + const std::shared_ptr filter) { + if (outstanding_.color_filter || outstanding_.image_filter || + (outstanding_.opacity < SK_Scalar1 && + !filter->can_commute_with_alpha())) { + // TBD: compose the 2 color filters together. + save_layer(bounds); + } +} + +void LayerStateStack::maybe_save_layer( + const SkRect& bounds, + const std::shared_ptr filter) { + if (outstanding_.image_filter) { + // TBD: compose the 2 image filters together. + save_layer(bounds); + } +} + +void LayerStateStack::AttributesEntry::restore( + RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const { + *attributes = attributes_; +} + +void LayerStateStack::SaveEntry::apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const { + if (canvas) { + canvas->save(); + } + if (builder) { + builder->save(); + } +} + +void LayerStateStack::SaveEntry::restore(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const { + if (canvas) { + canvas->restore(); + } + if (builder) { + builder->restore(); + } + do_checkerboard(canvas, builder); +} + +void LayerStateStack::SaveLayerEntry::apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const { + if (canvas) { + SkPaint paint; + canvas->saveLayer(bounds_, attributes->fill(paint, blend_mode_)); + } + if (builder) { + DlPaint paint; + builder->saveLayer(&bounds_, attributes->fill(paint, blend_mode_)); + } + *attributes = {}; +} + +void LayerStateStack::SaveLayerEntry::do_checkerboard( + SkCanvas* canvas, + DisplayListBuilder* builder) const { + if (do_checkerboard_) { + if (canvas) { + SkDrawCheckerboard(canvas, bounds_); + } + if (builder) { + DlDrawCheckerboard(builder, bounds_); + } + } +} + +void LayerStateStack::OpacityEntry::apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const { + attributes->opacity *= opacity_; +} + +void LayerStateStack::ImageFilterEntry::apply( + RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const { + attributes->image_filter = filter_; +} + +void LayerStateStack::ColorFilterEntry::apply( + RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const { + attributes->color_filter = filter_; +} + +void LayerStateStack::BackdropFilterEntry::apply( + RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const { + if (canvas) { + sk_sp backdrop_filter = + filter_ ? filter_->skia_object() : nullptr; + SkPaint paint; + SkPaint* pPaint = attributes->fill(paint, blend_mode_); + canvas->saveLayer( + SkCanvas::SaveLayerRec{&bounds_, pPaint, backdrop_filter.get(), 0}); + } + if (builder) { + DlPaint paint; + DlPaint* pPaint = attributes->fill(paint, blend_mode_); + builder->saveLayer(&bounds_, pPaint, filter_.get()); + } + *attributes = {}; +} + +void LayerStateStack::BackdropFilterEntry::reapply( + RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) 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(attributes, canvas, builder); +} + +void LayerStateStack::TranslateEntry::apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const { + if (canvas != nullptr) { + canvas->translate(tx_, ty_); + } + if (builder != nullptr) { + builder->translate(tx_, ty_); + } +} + +void LayerStateStack::TransformMatrixEntry::apply( + RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const { + if (canvas != nullptr) { + canvas->concat(matrix_); + } + if (builder != nullptr) { + builder->transform(matrix_); + } +} + +void LayerStateStack::TransformM44Entry::apply( + RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const { + if (canvas != nullptr) { + canvas->concat(m44_); + } + if (builder != nullptr) { + builder->transform(m44_); + } +} + +void LayerStateStack::ClipRectEntry::apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const { + if (canvas != nullptr) { + canvas->clipRect(rect_, SkClipOp::kIntersect, is_aa_); + } + if (builder != nullptr) { + builder->clipRect(rect_, SkClipOp::kIntersect, is_aa_); + } +} + +void LayerStateStack::ClipRRectEntry::apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const { + if (canvas != nullptr) { + canvas->clipRRect(rrect_, SkClipOp::kIntersect, is_aa_); + } + if (builder != nullptr) { + builder->clipRRect(rrect_, SkClipOp::kIntersect, is_aa_); + } +} + +void LayerStateStack::ClipPathEntry::apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const { + if (canvas != nullptr) { + canvas->clipPath(path_, SkClipOp::kIntersect, is_aa_); + } + if (builder != nullptr) { + builder->clipPath(path_, SkClipOp::kIntersect, is_aa_); + } +} + +} // namespace flutter diff --git a/flow/layers/layer_state_stack.h b/flow/layers/layer_state_stack.h new file mode 100644 index 0000000000000..b9aa834dee2f7 --- /dev/null +++ b/flow/layers/layer_state_stack.h @@ -0,0 +1,453 @@ +// 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" + +namespace flutter { + +class LayerStateStack { + public: + LayerStateStack() = default; + + bool checkerboard_save_layers() { return do_checkerboard_; } + void set_checkerboard_save_layers(bool checkerboard) { + do_checkerboard_ = checkerboard; + } + + SkCanvas* canvas_delegate() { return canvas_; } + void set_canvas_delegate(SkCanvas* canvas); + void set_builder_delegate(sk_sp builder) { + set_builder_delegate(builder.get()); + } + + DisplayListBuilder* builder_delegate() { return builder_; } + void set_builder_delegate(DisplayListBuilder* builder); + void set_builder_delegate(DisplayListCanvasRecorder& recorder) { + set_builder_delegate(recorder.builder().get()); + } + + 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 CALLER_CAN_APPLY_OPACITY = 0x1; + static constexpr int CALLER_CAN_APPLY_COLOR_FILTER = 0x2; + static constexpr int CALLER_CAN_APPLY_IMAGE_FILTER = 0x4; + static constexpr int CALLER_CAN_APPLY_ANYTHING = 0x7; + + 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 transform(const SkM44& m44); + void transform(const SkMatrix& matrix); + + 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() { return outstanding_.opacity; } + + const std::shared_ptr outstanding_color_filter() { + return outstanding_.color_filter; + } + + const std::shared_ptr outstanding_image_filter() { + return outstanding_.image_filter; + } + + // 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) { 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) { return outstanding_.fill(paint); } + + bool needs_painting() { return outstanding_.opacity > 0; } + + // 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(); + + private: + size_t stack_count() { return state_stack_.size(); } + void restore_to_count(size_t restore_count); + void reapply_all(SkCanvas* canvas, DisplayListBuilder* builder); + + void apply_last_entry() { + state_stack_.back()->apply(&outstanding_, canvas_, builder_); + } + + // 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(SkScalar opacity); + void push_color_filter(const std::shared_ptr filter); + void push_image_filter(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_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(const SkRect& bounds, int apply_flags); + void maybe_save_layer(const SkRect& bounds, SkScalar opacity); + void maybe_save_layer(const SkRect& bounds, + const std::shared_ptr filter); + void maybe_save_layer(const SkRect& bounds, + const std::shared_ptr filter); + // --------------------- + + static std::optional OptionalBounds(const SkRect* bounds) { + return bounds ? std::make_optional(*bounds) : std::nullopt; + } + static const SkRect* BoundsPtr(const std::optional& bounds) { + return bounds.has_value() ? &bounds.value() : nullptr; + } + + struct RenderingAttributes { + SkScalar opacity = SK_Scalar1; + std::shared_ptr color_filter; + std::shared_ptr image_filter; + + SkPaint* fill(SkPaint& paint, DlBlendMode mode = DlBlendMode::kSrcOver); + DlPaint* fill(DlPaint& paint, DlBlendMode mode = DlBlendMode::kSrcOver); + + bool operator==(const RenderingAttributes& other) { + return opacity == other.opacity && + Equals(color_filter, other.color_filter) && + Equals(image_filter, other.image_filter); + } + }; + + class StateEntry { + public: + virtual ~StateEntry() = default; + + virtual void apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const = 0; + + virtual void reapply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const { + apply(attributes, canvas, builder); + } + + virtual void restore(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const {} + }; + + class AttributesEntry : public StateEntry { + public: + AttributesEntry(RenderingAttributes attributes) : attributes_(attributes) {} + + virtual void apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const override {} + + void restore(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const override; + + private: + const RenderingAttributes attributes_; + }; + + class SaveEntry : public StateEntry { + public: + SaveEntry() = default; + + void apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const override; + void restore(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const override; + + protected: + virtual void do_checkerboard(SkCanvas* canvas, + DisplayListBuilder* builder) const {} + }; + + class SaveLayerEntry : public SaveEntry { + public: + SaveLayerEntry(const SkRect& bounds, + DlBlendMode blend_mode, + bool checkerboard) + : bounds_(bounds), + blend_mode_(blend_mode), + do_checkerboard_(checkerboard) {} + + void apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const override; + + protected: + const SkRect bounds_; + const DlBlendMode blend_mode_; + const bool do_checkerboard_; + + void do_checkerboard(SkCanvas* canvas, + DisplayListBuilder* builder) const override; + }; + + class OpacityEntry : public StateEntry { + public: + OpacityEntry(SkScalar opacity) : opacity_(opacity) {} + + void apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const override; + + private: + const SkScalar opacity_; + }; + + class ImageFilterEntry : public StateEntry { + public: + ImageFilterEntry(const std::shared_ptr filter) + : filter_(filter) {} + ~ImageFilterEntry() override = default; + + void apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const override; + + private: + const std::shared_ptr filter_; + }; + + class ColorFilterEntry : public StateEntry { + public: + ColorFilterEntry(const std::shared_ptr filter) + : filter_(filter) {} + ~ColorFilterEntry() override = default; + + void apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const override; + + private: + const std::shared_ptr filter_; + }; + + class BackdropFilterEntry : public SaveLayerEntry { + public: + BackdropFilterEntry(const SkRect& bounds, + const std::shared_ptr filter, + DlBlendMode blend_mode, + bool checkerboard) + : SaveLayerEntry(bounds, blend_mode, checkerboard), filter_(filter) {} + ~BackdropFilterEntry() override = default; + + void apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const override; + + void reapply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const override; + + private: + const std::shared_ptr filter_; + friend class LayerStateStack; + }; + + class TransformEntry : public StateEntry {}; + + class TranslateEntry : public TransformEntry { + public: + TranslateEntry(SkScalar tx, SkScalar ty) : tx_(tx), ty_(ty) {} + + void apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const override; + + private: + const SkScalar tx_; + const SkScalar ty_; + }; + + class TransformMatrixEntry : public TransformEntry { + public: + TransformMatrixEntry(const SkMatrix& matrix) : matrix_(matrix) {} + + void apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const override; + + private: + const SkMatrix matrix_; + }; + + class TransformM44Entry : public TransformEntry { + public: + TransformM44Entry(const SkM44& m44) : m44_(m44) {} + + void apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const override; + + private: + const SkM44 m44_; + }; + + class ClipEntry : public StateEntry { + protected: + ClipEntry(bool is_aa) : is_aa_(is_aa) {} + + const bool is_aa_; + }; + + class ClipRectEntry : public ClipEntry { + public: + ClipRectEntry(const SkRect& rect, bool is_aa) + : ClipEntry(is_aa), rect_(rect) {} + + void apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const override; + + private: + const SkRect rect_; + }; + + class ClipRRectEntry : public ClipEntry { + public: + ClipRRectEntry(const SkRRect& rrect, bool is_aa) + : ClipEntry(is_aa), rrect_(rrect) {} + + void apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const override; + + private: + const SkRRect rrect_; + }; + + class ClipPathEntry : public ClipEntry { + public: + ClipPathEntry(const SkPath& path, bool is_aa) + : ClipEntry(is_aa), path_(path) {} + ~ClipPathEntry() override = default; + + void apply(RenderingAttributes* attributes, + SkCanvas* canvas, + DisplayListBuilder* builder) const override; + + private: + const SkPath path_; + }; + + std::vector> state_stack_; + friend class MutatorContext; + + SkCanvas* canvas_ = nullptr; + int canvas_restore_count_ = 0.0; + DisplayListBuilder* builder_ = nullptr; + int builder_restore_count_ = 0.0; + RenderingAttributes outstanding_; + + bool do_checkerboard_ = false; + friend class SaveLayerEntry; +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_LAYERS_LAYER_STATE_STACK_H_ diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index 3b4907b5336bf..02563a3369890 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -61,7 +61,6 @@ bool LayerTree::Preroll(CompositorContext::ScopedFrame& frame, .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, @@ -110,24 +109,13 @@ void LayerTree::Paint(CompositorContext::ScopedFrame& frame, return; } - 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; + LayerStateStack state_stack; + state_stack.set_checkerboard_save_layers(checkerboard_offscreen_layers_); if (builder) { - builder_multiplexer.addBuilder(builder); - if (frame.view_embedder()) { - for (auto* view_builder : frame.view_embedder()->GetCurrentBuilders()) { - builder_multiplexer.addBuilder(view_builder); - } - } + state_stack.set_builder_delegate(builder); + } else { + state_stack.set_canvas_delegate(frame.canvas()); } // clear the previous snapshots. @@ -142,8 +130,8 @@ void LayerTree::Paint(CompositorContext::ScopedFrame& frame, ignore_raster_cache ? nullptr : &frame.context().raster_cache(); PaintContext context = { // clang-format off - .internal_nodes_canvas = &internal_nodes_canvas, - .leaf_nodes_canvas = frame.canvas(), + .state_stack = state_stack, + .canvas = frame.canvas(), .gr_context = frame.gr_context(), .dst_color_space = color_space, .view_embedder = frame.view_embedder(), @@ -151,13 +139,10 @@ 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, + .builder = builder // clang-format on }; @@ -177,7 +162,7 @@ sk_sp LayerTree::Flatten( GrDirectContext* gr_context) { TRACE_EVENT0("flutter", "LayerTree::Flatten"); - DisplayListCanvasRecorder builder(bounds); + DisplayListCanvasRecorder recorder(bounds); MutatorsStack unused_stack; const FixedRefreshRateStopwatch unused_stopwatch; @@ -198,21 +183,18 @@ sk_sp LayerTree::Flatten( .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()); + LayerStateStack state_stack; + state_stack.set_checkerboard_save_layers(false); + state_stack.set_builder_delegate(recorder); PaintContext paint_context = { // clang-format off - .internal_nodes_canvas = &internal_nodes_canvas, - .leaf_nodes_canvas = &builder, + .state_stack = state_stack, + .canvas = &recorder, .gr_context = gr_context, .dst_color_space = nullptr, .view_embedder = nullptr, @@ -220,12 +202,10 @@ 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, + .builder = recorder.builder().get(), // clang-format on }; @@ -239,7 +219,7 @@ sk_sp LayerTree::Flatten( } } - return builder.Build(); + return recorder.Build(); } } // namespace flutter diff --git a/flow/layers/layer_tree_unittests.cc b/flow/layers/layer_tree_unittests.cc index 9eeac5a1f77cd..76e28882ac0db 100644 --- a/flow/layers/layer_tree_unittests.cc +++ b/flow/layers/layer_tree_unittests.cc @@ -213,14 +213,15 @@ TEST_F(LayerTreeTest, PrerollContextInitialization) { 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.subtree_can_inherit_opacity, false); + EXPECT_EQ(context.renderable_state_flags, 0); EXPECT_EQ(context.raster_cached_entries, nullptr); + + EXPECT_EQ(context.display_list_enabled, false); }; // These 4 initializers are required because they are handled by reference @@ -234,36 +235,37 @@ 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 = [&mock_raster_time, &mock_ui_time, + auto expect_defaults = [&state_stack, &mock_raster_time, &mock_ui_time, &mock_registry](const PaintContext& context) { - EXPECT_EQ(context.internal_nodes_canvas, nullptr); - EXPECT_EQ(context.leaf_nodes_canvas, nullptr); + EXPECT_EQ(&context.state_stack, &state_stack); + EXPECT_EQ(context.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.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); + EXPECT_EQ(context.builder, nullptr); }; // These 4 initializers are required because they are handled by reference PaintContext context{ - .raster_time = mock_raster_time, - .ui_time = mock_ui_time, - .texture_registry = mock_registry, + // clang-format off + .state_stack = state_stack, + .raster_time = mock_raster_time, + .ui_time = mock_ui_time, + .texture_registry = mock_registry, + // clang-format on }; expect_defaults(context); } diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index e9702629052ca..daa35693396d9 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -51,18 +51,15 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(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->subtree_can_inherit_opacity); + set_children_can_accept_opacity((context->renderable_state_flags & + LayerStateStack::CALLER_CAN_APPLY_OPACITY) != + 0); // Now we let our parent layers know that we, too, can inherit opacity // regardless of what our children are capable of - context->subtree_can_inherit_opacity = true; + context->renderable_state_flags = LayerStateStack::CALLER_CAN_APPLY_OPACITY; context->mutators_stack.Pop(); context->mutators_stack.Pop(); @@ -83,26 +80,6 @@ void OpacityLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "OpacityLayer::Paint"); FML_DCHECK(needs_painting(context)); - SkAutoCanvasRestore save(context.internal_nodes_canvas, true); - context.internal_nodes_canvas->translate(offset_.fX, offset_.fY); - - SkScalar inherited_opacity = context.inherited_opacity; - SkScalar subtree_opacity = opacity() * inherited_opacity; - - 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 saveLayerBounds (although it's not a // guaranteed clip). So we have to provide a big enough saveLayerBounds. To do // so, we first remove the offset from paint bounds since it's already in the @@ -116,11 +93,11 @@ void OpacityLayer::Paint(PaintContext& context) const { .makeOffset(-offset_.fX, -offset_.fY) .roundOut(&saveLayerBounds); - Layer::AutoSaveLayer save_layer = - Layer::AutoSaveLayer::Create(context, saveLayerBounds, &paint); - context.inherited_opacity = SK_Scalar1; + auto mutator = context.state_stack.save(); + mutator.translate(offset_.fX, offset_.fY); + mutator.applyOpacity(saveLayerBounds, opacity()); + PaintChildren(context); - context.inherited_opacity = inherited_opacity; } } // namespace flutter diff --git a/flow/layers/opacity_layer_unittests.cc b/flow/layers/opacity_layer_unittests.cc index 6844b9bc50682..fad38379e78fa 100644 --- a/flow/layers/opacity_layer_unittests.cc +++ b/flow/layers/opacity_layer_unittests.cc @@ -179,7 +179,6 @@ TEST_F(OpacityLayerTest, ShouldNotCacheChildren) { opacityLayer->Add(mockLayer); PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; use_mock_raster_cache(); @@ -194,7 +193,8 @@ TEST_F(OpacityLayerTest, ShouldNotCacheChildren) { opacityLayer->Preroll(preroll_context(), SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); EXPECT_TRUE(opacityLayer->children_can_accept_opacity()); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); @@ -236,11 +236,7 @@ TEST_F(OpacityLayerTest, FullyOpaque) { MockCanvas::DrawCall{ 1, MockCanvas::ConcatMatrixData{SkM44(layer_transform)}}, 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}}, + 1, MockCanvas::DrawPathData{child_path, child_paint}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); layer->Paint(paint_context()); EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); @@ -300,7 +296,7 @@ TEST_F(OpacityLayerTest, HalfTransparent) { auto layer = std::make_shared(alpha_half, layer_offset); layer->Add(mock_layer); - layer->Preroll(preroll_context(), initial_transform); + layer->Preroll(display_list_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()); @@ -395,11 +391,10 @@ 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, 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); + 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); auto expected_draw_calls = std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, MockCanvas::DrawCall{ @@ -466,9 +461,9 @@ TEST_F(OpacityLayerTest, OpacityInheritanceCompatibleChild) { opacityLayer->Add(mockLayer); PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; opacityLayer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); EXPECT_TRUE(opacityLayer->children_can_accept_opacity()); } @@ -479,9 +474,9 @@ TEST_F(OpacityLayerTest, OpacityInheritanceIncompatibleChild) { opacityLayer->Add(mockLayer); PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; opacityLayer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); EXPECT_FALSE(opacityLayer->children_can_accept_opacity()); } @@ -494,14 +489,10 @@ TEST_F(OpacityLayerTest, OpacityInheritanceThroughContainer) { opacityLayer->Add(containerLayer); PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; opacityLayer->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(opacityLayer->children_can_accept_opacity()); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); + EXPECT_TRUE(opacityLayer->children_can_accept_opacity()); } TEST_F(OpacityLayerTest, OpacityInheritanceThroughTransform) { @@ -513,9 +504,9 @@ TEST_F(OpacityLayerTest, OpacityInheritanceThroughTransform) { opacityLayer->Add(transformLayer); PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; opacityLayer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); EXPECT_TRUE(opacityLayer->children_can_accept_opacity()); } @@ -529,9 +520,9 @@ TEST_F(OpacityLayerTest, OpacityInheritanceThroughImageFilter) { opacityLayer->Add(filterLayer); PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; opacityLayer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); EXPECT_TRUE(opacityLayer->children_can_accept_opacity()); } @@ -545,10 +536,10 @@ TEST_F(OpacityLayerTest, OpacityInheritanceNestedWithCompatibleChild) { opacityLayer2->Add(mockLayer); opacityLayer1->Add(opacityLayer2); - PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; + PrerollContext* context = display_list_preroll_context(); opacityLayer1->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); EXPECT_TRUE(opacityLayer1->children_can_accept_opacity()); EXPECT_TRUE(opacityLayer2->children_can_accept_opacity()); @@ -557,6 +548,7 @@ TEST_F(OpacityLayerTest, OpacityInheritanceNestedWithCompatibleChild) { inheritedOpacity *= 64 * 1.0 / SK_AlphaOPAQUE; saveLayerPaint.setAlphaf(inheritedOpacity); + DlPaint mock_paint = DlPaint().setAlpha(saveLayerPaint.getAlpha()); DisplayListBuilder expected_builder; /* opacityLayer1::Paint */ { expected_builder.save(); @@ -567,13 +559,7 @@ TEST_F(OpacityLayerTest, OpacityInheritanceNestedWithCompatibleChild) { { expected_builder.translate(offset2.fX, offset2.fY); /* mockLayer::Paint */ { - expected_builder.setColor(saveLayerPaint.getAlpha() << 24); - expected_builder.saveLayer(&mockPath.getBounds(), true); - { - expected_builder.setColor(0xFF000000); - expected_builder.drawPath(mockPath); - } - expected_builder.restore(); + expected_builder.drawPath(mockPath, mock_paint); } } expected_builder.restore(); @@ -596,10 +582,10 @@ TEST_F(OpacityLayerTest, OpacityInheritanceNestedWithIncompatibleChild) { opacityLayer2->Add(mockLayer); opacityLayer1->Add(opacityLayer2); - PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; + PrerollContext* context = display_list_preroll_context(); opacityLayer1->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); EXPECT_TRUE(opacityLayer1->children_can_accept_opacity()); EXPECT_FALSE(opacityLayer2->children_can_accept_opacity()); diff --git a/flow/layers/performance_overlay_layer.cc b/flow/layers/performance_overlay_layer.cc index d211959a89c52..7d805d03ccda4 100644 --- a/flow/layers/performance_overlay_layer.cc +++ b/flow/layers/performance_overlay_layer.cc @@ -98,16 +98,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; - SkAutoCanvasRestore save(context.leaf_nodes_canvas, true); + + auto save = context.state_stack.save(); VisualizeStopWatch( - context.leaf_nodes_canvas, context.raster_time, x, y, width, - height - padding, options_ & kVisualizeRasterizerStatistics, + context.canvas, context.raster_time, x, y, width, height - padding, + options_ & kVisualizeRasterizerStatistics, options_ & kDisplayRasterizerStatistics, "Raster", font_path_); - VisualizeStopWatch(context.leaf_nodes_canvas, context.ui_time, x, y + height, - width, height - padding, - options_ & kVisualizeEngineStatistics, + VisualizeStopWatch(context.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_unittests.cc b/flow/layers/performance_overlay_layer_unittests.cc index f7c20d24d2900..a390dd637f7f4 100644 --- a/flow/layers/performance_overlay_layer_unittests.cc +++ b/flow/layers/performance_overlay_layer_unittests.cc @@ -58,17 +58,19 @@ static void TestPerformanceOverlayLayerGold(int refresh_rate) { ASSERT_TRUE(surface != nullptr); + LayerStateStack state_stack; + state_stack.set_canvas_delegate(surface->getCanvas()); + flutter::PaintContext paintContext = { // clang-format off - .internal_nodes_canvas = nullptr, - .leaf_nodes_canvas = surface->getCanvas(), + .state_stack = state_stack, + .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 }; diff --git a/flow/layers/physical_shape_layer.cc b/flow/layers/physical_shape_layer.cc index 8198d871e8330..9cd5f4d37c3ac 100644 --- a/flow/layers/physical_shape_layer.cc +++ b/flow/layers/physical_shape_layer.cc @@ -73,60 +73,42 @@ void PhysicalShapeLayer::Preroll(PrerollContext* context, paint_bounds.join(child_paint_bounds); } + context->renderable_state_flags = + UsesSaveLayer() ? SAVE_LAYER_RENDER_FLAGS : 0; set_paint_bounds(paint_bounds); } void PhysicalShapeLayer::Paint(PaintContext& context) const { + // + // Is this layer even used any more ???? + // + TRACE_EVENT0("flutter", "PhysicalShapeLayer::Paint"); FML_DCHECK(needs_painting(context)); if (elevation_ != 0) { DisplayListCanvasDispatcher::DrawShadow( - context.leaf_nodes_canvas, path_, shadow_color_, elevation_, + context.canvas, path_, shadow_color_, elevation_, SkColorGetA(color_) != 0xff, context.frame_device_pixel_ratio); } - // Call drawPath without clip if possible for better performance. - SkPaint paint; - paint.setColor(color_); - paint.setAntiAlias(true); - if (clip_behavior_ != Clip::antiAliasWithSaveLayer) { - context.leaf_nodes_canvas->drawPath(path_, paint); - } - - int saveCount = context.internal_nodes_canvas->save(); - switch (clip_behavior_) { - case Clip::hardEdge: - context.internal_nodes_canvas->clipPath(path_, false); - break; - case Clip::antiAlias: - context.internal_nodes_canvas->clipPath(path_, true); - break; - case Clip::antiAliasWithSaveLayer: { - TRACE_EVENT0("flutter", "Canvas::saveLayer"); - context.internal_nodes_canvas->clipPath(path_, true); - context.internal_nodes_canvas->saveLayer(paint_bounds(), nullptr); - } break; - case Clip::none: - break; - } - - if (UsesSaveLayer()) { - // If we want to avoid the bleeding edge artifact - // (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.leaf_nodes_canvas->drawPaint(paint); - } - - PaintChildren(context); - - context.internal_nodes_canvas->restoreToCount(saveCount); - - if (UsesSaveLayer()) { - if (context.checkerboard_offscreen_layers) { - DrawCheckerboard(context.internal_nodes_canvas, paint_bounds()); + auto mutator = context.state_stack.save(); + if (clip_behavior_ == Clip::antiAliasWithSaveLayer) { + mutator.clipPath(path_, true); + mutator.saveLayer(paint_bounds()); + context.canvas->drawColor(color_); + PaintChildren(context); + } else { + SkPaint paint; + paint.setColor(color_); + paint.setAntiAlias(true); + + // Call drawPath without clip if possible for better performance. + context.canvas->drawPath(path_, paint); + if (clip_behavior_ != Clip::none) { + mutator.clipPath(path_, clip_behavior_ == Clip::antiAlias); } + PaintChildren(context); } } diff --git a/flow/layers/physical_shape_layer_unittests.cc b/flow/layers/physical_shape_layer_unittests.cc index 3e41506ea4fad..e097e0c50f4a8 100644 --- a/flow/layers/physical_shape_layer_unittests.cc +++ b/flow/layers/physical_shape_layer_unittests.cc @@ -425,9 +425,8 @@ TEST_F(PhysicalShapeLayerTest, OpacityInheritance) { layer_path, Clip::none); PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; layer->Preroll(context, SkMatrix()); - EXPECT_FALSE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, 0); } using PhysicalShapeLayerDiffTest = DiffContextTest; diff --git a/flow/layers/platform_view_layer.cc b/flow/layers/platform_view_layer.cc index 8a46cbbce127c..efb5df6700631 100644 --- a/flow/layers/platform_view_layer.cc +++ b/flow/layers/platform_view_layer.cc @@ -39,8 +39,10 @@ void PlatformViewLayer::Paint(PaintContext& context) const { } EmbedderPaintContext embedder_context = context.view_embedder->CompositeEmbeddedView(view_id_); - context.leaf_nodes_canvas = embedder_context.canvas; - context.leaf_nodes_builder = embedder_context.builder; + context.state_stack.set_canvas_delegate(embedder_context.canvas); + context.state_stack.set_builder_delegate(embedder_context.builder); + context.canvas = embedder_context.canvas; + context.builder = embedder_context.builder; } } // namespace flutter diff --git a/flow/layers/platform_view_layer_unittests.cc b/flow/layers/platform_view_layer_unittests.cc index ecbbe3f39f036..f942ce62bd511 100644 --- a/flow/layers/platform_view_layer_unittests.cc +++ b/flow/layers/platform_view_layer_unittests.cc @@ -32,7 +32,7 @@ TEST_F(PlatformViewLayerTest, NullViewEmbedderDoesntPrerollCompositeOrPaint) { EXPECT_FALSE(layer->subtree_has_platform_view()); layer->Paint(paint_context()); - EXPECT_EQ(paint_context().leaf_nodes_canvas, &mock_canvas()); + EXPECT_EQ(paint_context().canvas, &mock_canvas()); EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); } @@ -67,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().leaf_nodes_canvas, &mock_canvas()); + EXPECT_EQ(paint_context().canvas, &mock_canvas()); EXPECT_EQ( mock_canvas().draw_calls(), std::vector( @@ -91,9 +91,8 @@ TEST_F(PlatformViewLayerTest, OpacityInheritance) { std::make_shared(layer_offset, layer_size, view_id); PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_FALSE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, 0); } } // namespace testing diff --git a/flow/layers/shader_mask_layer.cc b/flow/layers/shader_mask_layer.cc index d4a441417e966..f90bd4c0a6b7f 100644 --- a/flow/layers/shader_mask_layer.cc +++ b/flow/layers/shader_mask_layer.cc @@ -40,46 +40,46 @@ void ShaderMaskLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { ContainerLayer::Preroll(context, matrix); // 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; + context->renderable_state_flags = SAVE_LAYER_RENDER_FLAGS; } void ShaderMaskLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ShaderMaskLayer::Paint"); FML_DCHECK(needs_painting(context)); - AutoCachePaint cache_paint(context); - if (context.raster_cache) { - if (layer_raster_cache_item_->Draw(context, cache_paint.sk_paint())) { + auto restore = context.state_stack.applyState( + paint_bounds(), LayerStateStack::CALLER_CAN_APPLY_OPACITY); + + SkPaint paint; + if (layer_raster_cache_item_->Draw(context, + context.state_stack.fill(paint))) { return; } } auto shader_rect = SkRect::MakeWH(mask_rect_.width(), mask_rect_.height()); - if (context.leaf_nodes_builder) { - context.builder_multiplexer->saveLayer(&paint_bounds(), - cache_paint.dl_paint()); - PaintChildren(context); + auto mutator = context.state_stack.save(); + mutator.saveLayer(paint_bounds()); + + PaintChildren(context); + if (context.builder) { DlPaint dl_paint; dl_paint.setBlendMode(blend_mode_); if (color_source_) { dl_paint.setColorSource(color_source_.get()); } - context.leaf_nodes_builder->translate(mask_rect_.left(), mask_rect_.top()); - context.leaf_nodes_builder->drawRect(shader_rect, dl_paint); - context.builder_multiplexer->restore(); + context.builder->translate(mask_rect_.left(), mask_rect_.top()); + context.builder->drawRect(shader_rect, dl_paint); } 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.leaf_nodes_canvas->translate(mask_rect_.left(), mask_rect_.top()); - context.leaf_nodes_canvas->drawRect(shader_rect, paint); + context.canvas->translate(mask_rect_.left(), mask_rect_.top()); + context.canvas->drawRect(shader_rect, paint); } } diff --git a/flow/layers/shader_mask_layer_unittests.cc b/flow/layers/shader_mask_layer_unittests.cc index b6412f2497217..3af230e7d2433 100644 --- a/flow/layers/shader_mask_layer_unittests.cc +++ b/flow/layers/shader_mask_layer_unittests.cc @@ -354,16 +354,14 @@ TEST_F(ShaderMaskLayerTest, OpacityInheritance) { shader_mask_layer->Add(mock_layer); // ShaderMaskLayers can always support opacity despite incompatible children - PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; + PrerollContext* context = display_list_preroll_context(); shader_mask_layer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, Layer::SAVE_LAYER_RENDER_FLAGS); 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); - context->subtree_can_inherit_opacity = false; opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); diff --git a/flow/layers/texture_layer.cc b/flow/layers/texture_layer.cc index 7ece96ebd4364..543b06e7f19c4 100644 --- a/flow/layers/texture_layer.cc +++ b/flow/layers/texture_layer.cc @@ -46,7 +46,7 @@ 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->subtree_can_inherit_opacity = true; + context->renderable_state_flags = LayerStateStack::CALLER_CAN_APPLY_OPACITY; } void TextureLayer::Paint(PaintContext& context) const { @@ -61,9 +61,10 @@ void TextureLayer::Paint(PaintContext& context) const { TRACE_EVENT_INSTANT0("flutter", "null texture"); return; } - AutoCachePaint cache_paint(context); - texture->Paint(*context.leaf_nodes_canvas, paint_bounds(), freeze_, - context.gr_context, ToSk(sampling_), cache_paint.sk_paint()); + + SkPaint paint; + texture->Paint(*context.canvas, paint_bounds(), freeze_, context.gr_context, + ToSk(sampling_), context.state_stack.fill(paint)); } } // namespace flutter diff --git a/flow/layers/texture_layer_unittests.cc b/flow/layers/texture_layer_unittests.cc index 7d45d1d54e825..f71322f3d7299 100644 --- a/flow/layers/texture_layer_unittests.cc +++ b/flow/layers/texture_layer_unittests.cc @@ -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, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_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 f34566a915484..0c267544c0c3f 100644 --- a/flow/layers/transform_layer.cc +++ b/flow/layers/transform_layer.cc @@ -56,10 +56,6 @@ void TransformLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { 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_matrix, &child_paint_bounds); @@ -74,8 +70,8 @@ void TransformLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "TransformLayer::Paint"); FML_DCHECK(needs_painting(context)); - SkAutoCanvasRestore save(context.internal_nodes_canvas, true); - context.internal_nodes_canvas->concat(transform_); + auto mutator = context.state_stack.save(); + mutator.transform(transform_); PaintChildren(context); } diff --git a/flow/layers/transform_layer_unittests.cc b/flow/layers/transform_layer_unittests.cc index e05334c2337eb..fd13d520cec05 100644 --- a/flow/layers/transform_layer_unittests.cc +++ b/flow/layers/transform_layer_unittests.cc @@ -242,9 +242,9 @@ TEST_F(TransformLayerTest, OpacityInheritance) { // TransformLayer will pass through compatibility from a compatible child PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; transform1->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); auto path2 = SkPath().addRect({40, 40, 50, 50}); auto mock2 = MockLayer::MakeOpacityCompatible(path2); @@ -252,9 +252,9 @@ TEST_F(TransformLayerTest, OpacityInheritance) { // TransformLayer will pass through compatibility from multiple // non-overlapping compatible children - context->subtree_can_inherit_opacity = false; transform1->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); auto path3 = SkPath().addRect({20, 20, 40, 40}); auto mock3 = MockLayer::MakeOpacityCompatible(path3); @@ -262,18 +262,17 @@ TEST_F(TransformLayerTest, OpacityInheritance) { // TransformLayer will not pass through compatibility from multiple // overlapping children even if they are individually compatible - context->subtree_can_inherit_opacity = false; transform1->Preroll(context, SkMatrix::I()); - EXPECT_FALSE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, 0); 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 - context->subtree_can_inherit_opacity = false; transform2->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); auto path4 = SkPath().addRect({60, 60, 70, 70}); auto mock4 = MockLayer::Make(path4); @@ -281,9 +280,8 @@ TEST_F(TransformLayerTest, OpacityInheritance) { // The third child is non-overlapping, but not compatible so the // TransformLayer should end up incompatible - context->subtree_can_inherit_opacity = false; transform2->Preroll(context, SkMatrix::I()); - EXPECT_FALSE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, 0); } TEST_F(TransformLayerTest, OpacityInheritancePainting) { @@ -298,19 +296,19 @@ TEST_F(TransformLayerTest, OpacityInheritancePainting) { // TransformLayer will pass through compatibility from multiple // non-overlapping compatible children - PrerollContext* context = preroll_context(); - context->subtree_can_inherit_opacity = false; + PrerollContext* context = display_list_preroll_context(); transform_layer->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_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); - context->subtree_can_inherit_opacity = false; opacity_layer->Preroll(context, SkMatrix::I()); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); + DlPaint mock_paint = DlPaint().setAlpha(opacity_alpha); DisplayListBuilder expected_builder; /* opacity_layer paint */ { expected_builder.save(); @@ -318,26 +316,16 @@ TEST_F(TransformLayerTest, OpacityInheritancePainting) { expected_builder.translate(offset.fX, offset.fY); /* transform_layer paint */ { expected_builder.save(); - expected_builder.transform(transform); - /* child layer1 paint */ { - expected_builder.setColor(opacity_alpha << 24); - expected_builder.saveLayer(&path1.getBounds(), true); - { - expected_builder.setColor(0xFF000000); - expected_builder.drawPath(path1); + { + expected_builder.transform(transform); + /* child layer1 paint */ { + expected_builder.drawPath(path1, mock_paint); } - expected_builder.restore(); - } - /* child layer2 paint */ { - expected_builder.setColor(opacity_alpha << 24); - expected_builder.saveLayer(&path2.getBounds(), true); - { - expected_builder.setColor(0xFF000000); - expected_builder.drawPath(path2); + /* child layer2 paint */ { + expected_builder.drawPath(path2, mock_paint); } expected_builder.restore(); } - expected_builder.restore(); } } expected_builder.restore(); diff --git a/flow/paint_utils.cc b/flow/paint_utils.cc index 016b81f7a5dfe..5c16b6b0be62a 100644 --- a/flow/paint_utils.cc +++ b/flow/paint_utils.cc @@ -24,15 +24,9 @@ sk_sp CreateCheckerboardShader(SkColor c1, SkColor c2, int size) { SkSamplingOptions()); } -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) { +void SkDrawCheckerboard(SkCanvas* canvas, const SkRect& rect) { // Draw a checkerboard canvas->save(); canvas->clipRect(rect); @@ -43,7 +37,9 @@ void DrawCheckerboard(SkCanvas* canvas, const SkRect& rect) { SkColorSetARGB(64, rand() % 256, rand() % 256, rand() % 256); // NOLINTEND(clang-analyzer-security.insecureAPI.rand) - DrawCheckerboard(canvas, checkerboard_color, 0x00000000, 12); + SkPaint paint; + paint.setShader(CreateCheckerboardShader(checkerboard_color, 0x00000000, 12)); + canvas->drawPaint(paint); canvas->restore(); // Stroke the drawn area @@ -54,4 +50,29 @@ void DrawCheckerboard(SkCanvas* canvas, const SkRect& rect) { canvas->drawRect(rect, debugPaint); } +void DlDrawCheckerboard(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 debugPaint; + debugPaint.setStrokeWidth(8); + debugPaint.setColor(DlColor(checkerboard_color).withAlpha(255)); + debugPaint.setDrawStyle(DlDrawStyle::kStroke); + builder->drawRect(rect, debugPaint); +} + } // namespace flutter diff --git a/flow/paint_utils.h b/flow/paint_utils.h index 432dd12811372..cbfd38db0064a 100644 --- a/flow/paint_utils.h +++ b/flow/paint_utils.h @@ -5,13 +5,16 @@ #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 { -void DrawCheckerboard(SkCanvas* canvas, const SkRect& rect); +void SkDrawCheckerboard(SkCanvas* canvas, const SkRect& rect); + +void DlDrawCheckerboard(DisplayListBuilder* builder, const SkRect& rect); } // namespace flutter diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index 9b6e72d43c2c8..6087408abf989 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -76,7 +76,7 @@ RasterCache::RasterCache(size_t access_threshold, std::unique_ptr RasterCache::Rasterize( const RasterCache::Context& context, const std::function& draw_function, - const std::function& draw_checkerboard) + const std::function& draw_checkerboard) const { TRACE_EVENT0("flutter", "RasterCachePopulate"); @@ -120,7 +120,7 @@ bool RasterCache::UpdateCacheEntry( Entry& entry = cache_[key]; if (!entry.image) { entry.image = - Rasterize(raster_cache_context, render_function, DrawCheckerboard); + Rasterize(raster_cache_context, render_function, SkDrawCheckerboard); 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 9a72da8e698ff..b3faec986294c 100644 --- a/flow/raster_cache_unittests.cc +++ b/flow/raster_cache_unittests.cc @@ -29,6 +29,7 @@ TEST(RasterCache, SimpleInitialization) { TEST(RasterCache, MetricsOmitUnpopulatedEntries) { size_t threshold = 2; + LayerStateStack state_stack; flutter::RasterCache cache(threshold); SkMatrix matrix = SkMatrix::I(); @@ -44,7 +45,7 @@ TEST(RasterCache, MetricsOmitUnpopulatedEntries) { PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; @@ -85,6 +86,7 @@ TEST(RasterCache, MetricsOmitUnpopulatedEntries) { TEST(RasterCache, ThresholdIsRespectedForDisplayList) { size_t threshold = 2; + LayerStateStack state_stack; flutter::RasterCache cache(threshold); SkMatrix matrix = SkMatrix::I(); @@ -100,7 +102,7 @@ TEST(RasterCache, ThresholdIsRespectedForDisplayList) { PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; @@ -133,6 +135,7 @@ TEST(RasterCache, ThresholdIsRespectedForDisplayList) { TEST(RasterCache, SetCheckboardCacheImages) { size_t threshold = 1; + LayerStateStack state_stack; flutter::RasterCache cache(threshold); SkMatrix matrix = SkMatrix::I(); @@ -141,7 +144,7 @@ TEST(RasterCache, SetCheckboardCacheImages) { FixedRefreshRateStopwatch raster_time; FixedRefreshRateStopwatch ui_time; PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); auto& paint_context = paint_context_holder.paint_context; auto dummy_draw_function = [](SkCanvas* canvas) {}; bool did_draw_checkerboard = false; @@ -169,6 +172,7 @@ TEST(RasterCache, SetCheckboardCacheImages) { TEST(RasterCache, AccessThresholdOfZeroDisablesCachingForSkPicture) { size_t threshold = 0; + LayerStateStack state_stack; flutter::RasterCache cache(threshold); SkMatrix matrix = SkMatrix::I(); @@ -184,7 +188,7 @@ TEST(RasterCache, AccessThresholdOfZeroDisablesCachingForSkPicture) { PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; @@ -198,6 +202,7 @@ TEST(RasterCache, AccessThresholdOfZeroDisablesCachingForSkPicture) { TEST(RasterCache, AccessThresholdOfZeroDisablesCachingForDisplayList) { size_t threshold = 0; + LayerStateStack state_stack; flutter::RasterCache cache(threshold); SkMatrix matrix = SkMatrix::I(); @@ -213,7 +218,7 @@ TEST(RasterCache, AccessThresholdOfZeroDisablesCachingForDisplayList) { PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; @@ -228,12 +233,12 @@ TEST(RasterCache, AccessThresholdOfZeroDisablesCachingForDisplayList) { TEST(RasterCache, PictureCacheLimitPerFrameIsRespectedWhenZeroForSkPicture) { size_t picture_cache_limit_per_frame = 0; + LayerStateStack state_stack; flutter::RasterCache cache(3, picture_cache_limit_per_frame); SkMatrix matrix = SkMatrix::I(); auto display_list = GetSampleDisplayList(); - ; SkCanvas dummy_canvas; SkPaint paint; @@ -244,7 +249,7 @@ TEST(RasterCache, PictureCacheLimitPerFrameIsRespectedWhenZeroForSkPicture) { PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; @@ -265,6 +270,7 @@ TEST(RasterCache, PictureCacheLimitPerFrameIsRespectedWhenZeroForSkPicture) { TEST(RasterCache, PictureCacheLimitPerFrameIsRespectedWhenZeroForDisplayList) { size_t picture_cache_limit_per_frame = 0; + LayerStateStack state_stack; flutter::RasterCache cache(3, picture_cache_limit_per_frame); SkMatrix matrix = SkMatrix::I(); @@ -280,7 +286,7 @@ TEST(RasterCache, PictureCacheLimitPerFrameIsRespectedWhenZeroForDisplayList) { PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; @@ -304,6 +310,7 @@ TEST(RasterCache, PictureCacheLimitPerFrameIsRespectedWhenZeroForDisplayList) { TEST(RasterCache, EvitUnusedCacheEntries) { size_t threshold = 1; + LayerStateStack state_stack; flutter::RasterCache cache(threshold); SkMatrix matrix = SkMatrix::I(); @@ -320,7 +327,7 @@ TEST(RasterCache, EvitUnusedCacheEntries) { PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; @@ -410,6 +417,7 @@ TEST(RasterCache, ComputeDeviceRectBasedOnFractionalTranslation) { // triggering any assertions. TEST(RasterCache, DeviceRectRoundOutForDisplayList) { size_t threshold = 1; + LayerStateStack state_stack; flutter::RasterCache cache(threshold); SkRect logical_rect = SkRect::MakeLTRB(28, 0, 354.56731, 310.288); @@ -430,7 +438,7 @@ TEST(RasterCache, DeviceRectRoundOutForDisplayList) { PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; @@ -456,6 +464,7 @@ TEST(RasterCache, DeviceRectRoundOutForDisplayList) { TEST(RasterCache, NestedOpCountMetricUsedForDisplayList) { size_t threshold = 1; + LayerStateStack state_stack; flutter::RasterCache cache(threshold); SkMatrix matrix = SkMatrix::I(); @@ -473,7 +482,7 @@ TEST(RasterCache, NestedOpCountMetricUsedForDisplayList) { PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; @@ -499,6 +508,7 @@ TEST(RasterCache, NaiveComplexityScoringDisplayList) { DisplayListNaiveComplexityCalculator::GetInstance(); size_t threshold = 1; + LayerStateStack state_stack; flutter::RasterCache cache(threshold); SkMatrix matrix = SkMatrix::I(); @@ -520,7 +530,7 @@ TEST(RasterCache, NaiveComplexityScoringDisplayList) { PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(state_stack, &cache, &raster_time, &ui_time); auto& preroll_context = preroll_context_holder.preroll_context; auto& paint_context = paint_context_holder.paint_context; @@ -566,6 +576,7 @@ TEST(RasterCache, NaiveComplexityScoringDisplayList) { TEST(RasterCache, DisplayListWithSingularMatrixIsNotCached) { size_t threshold = 2; + LayerStateStack state_stack; flutter::RasterCache cache(threshold); SkMatrix matrices[] = { @@ -586,7 +597,7 @@ TEST(RasterCache, DisplayListWithSingularMatrixIsNotCached) { PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder( &cache, &raster_time, &ui_time, &mutators_stack); PaintContextHolder paint_context_holder = - GetSamplePaintContextHolder(&cache, &raster_time, &ui_time); + GetSamplePaintContextHolder(state_stack, &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/testing/auto_save_layer_unittests.cc b/flow/testing/auto_save_layer_unittests.cc index a44ebdbead301..69bee60f8e7a8 100644 --- a/flow/testing/auto_save_layer_unittests.cc +++ b/flow/testing/auto_save_layer_unittests.cc @@ -9,6 +9,8 @@ namespace flutter { namespace testing { +#if 0 + using AutoSaveLayerTests = LayerTest; TEST_F(AutoSaveLayerTests, SaveLayerOnInternalNodesCanvasByDefault) { @@ -113,5 +115,7 @@ TEST_F(AutoSaveLayerTests, SaveLayerOnlyOnLeafNodesCanvas) { } } +#endif + } // namespace testing } // namespace flutter diff --git a/flow/testing/layer_test.h b/flow/testing/layer_test.h index 60b4536056ab6..ee9b52f442987 100644 --- a/flow/testing/layer_test.h +++ b/flow/testing/layer_test.h @@ -58,61 +58,75 @@ class LayerTestBase : public CanvasTestBase { .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_, + .display_list_enabled = false, // clang-format on }, paint_context_{ // clang-format off - .internal_nodes_canvas = TestT::mock_internal_canvas(), - .leaf_nodes_canvas = &TestT::mock_canvas(), + .state_stack = canvas_state_stack_, + .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_preroll_context_{ + // clang-format off + .raster_cache = nullptr, + .gr_context = nullptr, + .view_embedder = nullptr, + .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_, + .frame_device_pixel_ratio = 1.0f, + .has_platform_view = false, + .raster_cached_entries = &cacheable_items_, + .display_list_enabled = true, + // clang-format on + }, display_list_paint_context_{ // clang-format off - .internal_nodes_canvas = &internal_display_list_canvas_, - .leaf_nodes_canvas = &display_list_recorder_, + .state_stack = display_list_state_stack_, + .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_, + .builder = display_list_recorder_.builder().get(), // clang-format on }, - check_board_context_{ + checkerboard_context_{ // clang-format off - .internal_nodes_canvas = TestT::mock_internal_canvas(), - .leaf_nodes_canvas = &TestT::mock_canvas(), + .state_stack = checkerboard_state_stack_, + .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()); + canvas_state_stack_.set_canvas_delegate(&TestT::mock_canvas()); + display_list_state_stack_.set_builder_delegate(display_list_recorder_); + checkerboard_state_stack_.set_canvas_delegate(&TestT::mock_canvas()); + checkerboard_state_stack_.set_checkerboard_save_layers(true); use_null_raster_cache(); } @@ -173,10 +187,13 @@ class LayerTestBase : public CanvasTestBase { RasterCache* raster_cache() { return raster_cache_.get(); } PrerollContext* preroll_context() { return &preroll_context_; } PaintContext& paint_context() { return paint_context_; } + PrerollContext* display_list_preroll_context() { + return &display_list_preroll_context_; + } PaintContext& display_list_paint_context() { return display_list_paint_context_; } - PaintContext& check_board_context() { return check_board_context_; } + PaintContext& checkerboard_context() { return checkerboard_context_; } LayerSnapshotStore& layer_snapshot_store() { return snapshot_store_; } sk_sp display_list() { @@ -184,10 +201,8 @@ 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_.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; + display_list_paint_context_.canvas = nullptr; + display_list_paint_context_.builder = nullptr; } return display_list_; } @@ -218,12 +233,14 @@ class LayerTestBase : public CanvasTestBase { std::unique_ptr raster_cache_; PrerollContext preroll_context_; PaintContext paint_context_; + LayerStateStack canvas_state_stack_; DisplayListCanvasRecorder display_list_recorder_; - DisplayListBuilderMultiplexer display_list_multiplexer_; sk_sp display_list_; - SkNWayCanvas internal_display_list_canvas_; + LayerStateStack display_list_state_stack_; + PrerollContext display_list_preroll_context_; PaintContext display_list_paint_context_; - PaintContext check_board_context_; + LayerStateStack checkerboard_state_stack_; + PaintContext checkerboard_context_; LayerSnapshotStore snapshot_store_; std::vector cacheable_items_; diff --git a/flow/testing/mock_layer.cc b/flow/testing/mock_layer.cc index 618a1d566798f..65949460c6c4d 100644 --- a/flow/testing/mock_layer.cc +++ b/flow/testing/mock_layer.cc @@ -52,22 +52,19 @@ void MockLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { context->surface_needs_readback = true; } if (fake_opacity_compatible_) { - context->subtree_can_inherit_opacity = true; + context->renderable_state_flags = LayerStateStack::CALLER_CAN_APPLY_OPACITY; } } void MockLayer::Paint(PaintContext& context) const { FML_DCHECK(needs_painting(context)); - 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 paint = fake_paint_; + if (fake_opacity_compatible_) { + paint.setAlphaf(paint.getAlphaf() * + context.state_stack.outstanding_opacity()); } + context.canvas->drawPath(fake_paint_path_, paint); } void MockCacheableContainerLayer::Preroll(PrerollContext* context, diff --git a/flow/testing/mock_layer_unittests.cc b/flow/testing/mock_layer_unittests.cc index 3323f76d07618..49b38c4630607 100644 --- a/flow/testing/mock_layer_unittests.cc +++ b/flow/testing/mock_layer_unittests.cc @@ -83,14 +83,13 @@ TEST_F(MockLayerTest, OpacityInheritance) { PrerollContext* context = preroll_context(); auto mock1 = std::make_shared(path1); - context->subtree_can_inherit_opacity = false; mock1->Preroll(context, SkMatrix::I()); - EXPECT_FALSE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, 0); auto mock2 = MockLayer::MakeOpacityCompatible(path1); - context->subtree_can_inherit_opacity = false; mock2->Preroll(context, SkMatrix::I()); - EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_EQ(context->renderable_state_flags, + LayerStateStack::CALLER_CAN_APPLY_OPACITY); } } // namespace testing diff --git a/flow/testing/mock_raster_cache.cc b/flow/testing/mock_raster_cache.cc index 33b81c15030b3..e6f97990d7252 100644 --- a/flow/testing/mock_raster_cache.cc +++ b/flow/testing/mock_raster_cache.cc @@ -56,10 +56,11 @@ void MockRasterCache::AddMockPicture(int width, int height) { recorder.drawPath(path, SkPaint()); sk_sp display_list = recorder.Build(); + LayerStateStack state_stack; FixedRefreshRateStopwatch raster_time; FixedRefreshRateStopwatch ui_time; PaintContextHolder holder = - GetSamplePaintContextHolder(this, &raster_time, &ui_time); + GetSamplePaintContextHolder(state_stack, this, &raster_time, &ui_time); holder.paint_context.dst_color_space = color_space_; DisplayListRasterCacheItem display_list_item(display_list.get(), SkPoint(), @@ -104,7 +105,6 @@ PrerollContextHolder GetSamplePrerollContextHolder( .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, @@ -117,14 +117,15 @@ 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 { - .internal_nodes_canvas = nullptr, - .leaf_nodes_canvas = nullptr, + .state_stack = state_stack, + .canvas = nullptr, .gr_context = nullptr, .dst_color_space = srgb.get(), .view_embedder = nullptr, @@ -132,9 +133,7 @@ PaintContextHolder GetSamplePaintContextHolder( .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 37742486409bb..4fc3d0c534e9d 100644 --- a/flow/testing/mock_raster_cache.h +++ b/flow/testing/mock_raster_cache.h @@ -67,6 +67,7 @@ class MockRasterCache : public RasterCache { void AddMockPicture(int width, int height); private: + LayerStateStack mock_state_stack_; MockCanvas mock_canvas_; SkColorSpace* color_space_ = mock_canvas_.imageInfo().colorSpace(); MutatorsStack mutators_stack_; @@ -85,7 +86,6 @@ class MockRasterCache : public RasterCache { .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, @@ -95,8 +95,8 @@ class MockRasterCache : public RasterCache { PaintContext paint_context_ = { // clang-format off - .internal_nodes_canvas = nullptr, - .leaf_nodes_canvas = nullptr, + .state_stack = mock_state_stack_, + .canvas = nullptr, .gr_context = nullptr, .dst_color_space = color_space_, .view_embedder = nullptr, @@ -104,9 +104,7 @@ class MockRasterCache : public RasterCache { .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 }; }; @@ -128,6 +126,7 @@ PrerollContextHolder GetSamplePrerollContextHolder( 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 e2193d9be7d24..217ba9051d489 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -2382,10 +2382,11 @@ TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) { FixedRefreshRateStopwatch raster_time; FixedRefreshRateStopwatch ui_time; MutatorsStack mutators_stack; + LayerStateStack state_stack; PaintContext paint_context = { // clang-format off - .internal_nodes_canvas = nullptr, - .leaf_nodes_canvas = nullptr, + .state_stack = state_stack, + .canvas = nullptr, .gr_context = nullptr, .dst_color_space = nullptr, .view_embedder = nullptr, @@ -2393,9 +2394,7 @@ 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 }; @@ -2411,7 +2410,6 @@ TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) { .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,