diff --git a/engine/src/flutter/flow/layers/backdrop_filter_layer_unittests.cc b/engine/src/flutter/flow/layers/backdrop_filter_layer_unittests.cc index 667b0dc713d..33792a7b3ce 100644 --- a/engine/src/flutter/flow/layers/backdrop_filter_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/backdrop_filter_layer_unittests.cc @@ -29,6 +29,7 @@ TEST_F(BackdropFilterLayerTest, PaintingEmptyLayerDies) { parent->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), @@ -44,6 +45,7 @@ TEST_F(BackdropFilterLayerTest, PaintBeforePrerollDies) { layer->Add(mock_layer); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), "needs_painting\\(context\\)"); } @@ -63,6 +65,7 @@ TEST_F(BackdropFilterLayerTest, EmptyFilter) { parent->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); @@ -92,6 +95,7 @@ TEST_F(BackdropFilterLayerTest, SimpleFilter) { parent->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); @@ -121,6 +125,7 @@ TEST_F(BackdropFilterLayerTest, NonSrcOverBlend) { parent->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); @@ -163,6 +168,7 @@ TEST_F(BackdropFilterLayerTest, MultipleChildren) { 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); + EXPECT_EQ(layer->child_paint_bounds(), children_bounds); EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); diff --git a/engine/src/flutter/flow/layers/clip_path_layer_unittests.cc b/engine/src/flutter/flow/layers/clip_path_layer_unittests.cc index 3cbbf0f0554..90ecbc3d698 100644 --- a/engine/src/flutter/flow/layers/clip_path_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/clip_path_layer_unittests.cc @@ -28,6 +28,7 @@ TEST_F(ClipPathLayerTest, PaintingEmptyLayerDies) { EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), @@ -39,6 +40,7 @@ TEST_F(ClipPathLayerTest, PaintBeforePrerollDies) { const SkPath layer_path = SkPath().addRect(layer_bounds); auto layer = std::make_shared(layer_path, Clip::hardEdge); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), @@ -63,6 +65,7 @@ TEST_F(ClipPathLayerTest, PaintingCulledLayerDies) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect); @@ -100,6 +103,7 @@ TEST_F(ClipPathLayerTest, ChildOutsideBounds) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); @@ -135,6 +139,7 @@ TEST_F(ClipPathLayerTest, FullyContainedChild) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); @@ -177,6 +182,7 @@ TEST_F(ClipPathLayerTest, PartiallyContainedChild) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); diff --git a/engine/src/flutter/flow/layers/clip_rect_layer_unittests.cc b/engine/src/flutter/flow/layers/clip_rect_layer_unittests.cc index 13c90d76e8f..098322f04a2 100644 --- a/engine/src/flutter/flow/layers/clip_rect_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/clip_rect_layer_unittests.cc @@ -28,6 +28,7 @@ TEST_F(ClipRectLayerTest, PaintingEmptyLayerDies) { EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), @@ -38,6 +39,7 @@ TEST_F(ClipRectLayerTest, PaintBeforePrerollDies) { const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); auto layer = std::make_shared(layer_bounds, Clip::hardEdge); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), @@ -61,6 +63,7 @@ TEST_F(ClipRectLayerTest, PaintingCulledLayerDies) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect); @@ -98,6 +101,7 @@ TEST_F(ClipRectLayerTest, ChildOutsideBounds) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); @@ -133,6 +137,7 @@ TEST_F(ClipRectLayerTest, FullyContainedChild) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); @@ -175,6 +180,7 @@ TEST_F(ClipRectLayerTest, PartiallyContainedChild) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); diff --git a/engine/src/flutter/flow/layers/clip_rrect_layer_unittests.cc b/engine/src/flutter/flow/layers/clip_rrect_layer_unittests.cc index b2adb225374..0391e0309b2 100644 --- a/engine/src/flutter/flow/layers/clip_rrect_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/clip_rrect_layer_unittests.cc @@ -30,6 +30,7 @@ TEST_F(ClipRRectLayerTest, PaintingEmptyLayerDies) { EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), @@ -41,6 +42,7 @@ TEST_F(ClipRRectLayerTest, PaintBeforePrerollDies) { const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); auto layer = std::make_shared(layer_rrect, Clip::hardEdge); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), @@ -66,6 +68,7 @@ TEST_F(ClipRRectLayerTest, PaintingCulledLayerDies) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect); @@ -103,6 +106,7 @@ TEST_F(ClipRRectLayerTest, ChildOutsideBounds) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); @@ -138,6 +142,7 @@ TEST_F(ClipRRectLayerTest, FullyContainedChild) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); @@ -180,6 +185,7 @@ TEST_F(ClipRRectLayerTest, PartiallyContainedChild) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); diff --git a/engine/src/flutter/flow/layers/color_filter_layer.cc b/engine/src/flutter/flow/layers/color_filter_layer.cc index 5624c063cc0..6f33b70981e 100644 --- a/engine/src/flutter/flow/layers/color_filter_layer.cc +++ b/engine/src/flutter/flow/layers/color_filter_layer.cc @@ -31,10 +31,12 @@ void ColorFilterLayer::Preroll(PrerollContext* context, ContainerLayer::Preroll(context, matrix); if (render_count_ >= kMinimumRendersBeforeCachingFilterLayer) { - TryToPrepareRasterCache(context, this, matrix); + TryToPrepareRasterCache(context, this, matrix, + RasterCacheLayerStrategy::kLayer); } else { render_count_++; - TryToPrepareRasterCache(context, GetCacheableChild(), matrix); + TryToPrepareRasterCache(context, this, matrix, + RasterCacheLayerStrategy::kLayerChildren); } } @@ -43,14 +45,16 @@ void ColorFilterLayer::Paint(PaintContext& context) const { FML_DCHECK(needs_painting(context)); if (context.raster_cache) { - if (context.raster_cache->Draw(this, *context.leaf_nodes_canvas)) { + if (context.raster_cache->Draw(this, *context.leaf_nodes_canvas, + RasterCacheLayerStrategy::kLayer)) { return; } SkPaint paint; paint.setColorFilter(filter_); - if (context.raster_cache->Draw(GetCacheableChild(), - *context.leaf_nodes_canvas, &paint)) { + if (context.raster_cache->Draw(this, *context.leaf_nodes_canvas, + RasterCacheLayerStrategy::kLayerChildren, + &paint)) { return; } } diff --git a/engine/src/flutter/flow/layers/color_filter_layer.h b/engine/src/flutter/flow/layers/color_filter_layer.h index 51432cea6a7..fc28295d457 100644 --- a/engine/src/flutter/flow/layers/color_filter_layer.h +++ b/engine/src/flutter/flow/layers/color_filter_layer.h @@ -10,7 +10,7 @@ namespace flutter { -class ColorFilterLayer : public MergedContainerLayer { +class ColorFilterLayer : public ContainerLayer { public: explicit ColorFilterLayer(sk_sp filter); diff --git a/engine/src/flutter/flow/layers/color_filter_layer_unittests.cc b/engine/src/flutter/flow/layers/color_filter_layer_unittests.cc index fe29ceac3a9..f721a5946a1 100644 --- a/engine/src/flutter/flow/layers/color_filter_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/color_filter_layer_unittests.cc @@ -22,6 +22,7 @@ TEST_F(ColorFilterLayerTest, PaintingEmptyLayerDies) { layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), @@ -36,6 +37,7 @@ TEST_F(ColorFilterLayerTest, PaintBeforePrerollDies) { layer->Add(mock_layer); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), "needs_painting\\(context\\)"); } @@ -52,6 +54,7 @@ TEST_F(ColorFilterLayerTest, EmptyFilter) { layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); @@ -81,6 +84,7 @@ TEST_F(ColorFilterLayerTest, SimpleFilter) { layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); @@ -119,6 +123,7 @@ TEST_F(ColorFilterLayerTest, MultipleChildren) { 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); + EXPECT_EQ(layer->child_paint_bounds(), children_bounds); EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); @@ -166,7 +171,9 @@ TEST_F(ColorFilterLayerTest, Nested) { 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); + EXPECT_EQ(layer1->child_paint_bounds(), children_bounds); EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds()); + EXPECT_EQ(layer2->child_paint_bounds(), mock_layer2->paint_bounds()); EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); EXPECT_TRUE(layer1->needs_painting(paint_context())); @@ -213,7 +220,7 @@ TEST_F(ColorFilterLayerTest, Readback) { EXPECT_FALSE(preroll_context()->surface_needs_readback); } -TEST_F(ColorFilterLayerTest, ChildIsCached) { +TEST_F(ColorFilterLayerTest, CacheChild) { auto layer_filter = SkColorMatrixFilter::MakeLightingFilter(SK_ColorGREEN, SK_ColorYELLOW); auto initial_transform = SkMatrix::Translate(50.0, 25.5); @@ -234,15 +241,31 @@ TEST_F(ColorFilterLayerTest, ChildIsCached) { EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas)); EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayerChildren)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayerChildren)); layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas)); - EXPECT_TRUE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); + EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayerChildren)); + EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayerChildren)); } -TEST_F(ColorFilterLayerTest, ChildrenNotCached) { +TEST_F(ColorFilterLayerTest, CacheChildren) { auto layer_filter = SkColorMatrixFilter::MakeLightingFilter(SK_ColorGREEN, SK_ColorYELLOW); auto initial_transform = SkMatrix::Translate(50.0, 25.5); @@ -268,6 +291,14 @@ TEST_F(ColorFilterLayerTest, ChildrenNotCached) { EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas)); EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas)); EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayerChildren)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayerChildren)); layer->Preroll(preroll_context(), initial_transform); @@ -276,6 +307,14 @@ TEST_F(ColorFilterLayerTest, ChildrenNotCached) { EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas)); EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas)); EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayerChildren)); + EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayerChildren)); } } // namespace testing diff --git a/engine/src/flutter/flow/layers/container_layer.cc b/engine/src/flutter/flow/layers/container_layer.cc index 6035ef56e83..36ea6ac883e 100644 --- a/engine/src/flutter/flow/layers/container_layer.cc +++ b/engine/src/flutter/flow/layers/container_layer.cc @@ -8,7 +8,7 @@ namespace flutter { -ContainerLayer::ContainerLayer() {} +ContainerLayer::ContainerLayer() : child_paint_bounds_(SkRect::MakeEmpty()) {} void ContainerLayer::Diff(DiffContext* context, const Layer* old_layer) { auto old_container = static_cast(old_layer); @@ -170,6 +170,7 @@ void ContainerLayer::PrerollChildren(PrerollContext* context, context->has_texture_layer = child_has_texture_layer; context->subtree_can_inherit_opacity = subtree_can_inherit_opacity; set_subtree_has_platform_view(child_has_platform_view); + child_paint_bounds_ = *child_paint_bounds; } void ContainerLayer::PaintChildren(PaintContext& context) const { @@ -188,71 +189,19 @@ void ContainerLayer::PaintChildren(PaintContext& context) const { } } -void ContainerLayer::TryToPrepareRasterCache(PrerollContext* context, - Layer* layer, - const SkMatrix& matrix) { +void ContainerLayer::TryToPrepareRasterCache( + PrerollContext* context, + Layer* layer, + const SkMatrix& matrix, + RasterCacheLayerStrategy strategy) { if (!context->has_platform_view && !context->has_texture_layer && context->raster_cache && SkRect::Intersects(context->cull_rect, layer->paint_bounds())) { - context->raster_cache->Prepare(context, layer, matrix); + context->raster_cache->Prepare(context, layer, matrix, strategy); } else if (context->raster_cache) { // Don't evict raster cache entry during partial repaint - context->raster_cache->Touch(layer, matrix); + context->raster_cache->Touch(layer, matrix, strategy); } } -MergedContainerLayer::MergedContainerLayer() { - // Ensure the layer has only one direct child. - // - // Any children will actually be added as children of this empty - // ContainerLayer which can be accessed via ::GetContainerLayer(). - // If only one child is ever added to this layer then that child - // will become the layer returned from ::GetCacheableChild(). - // If multiple child layers are added, then this implicit container - // child becomes the cacheable child, but at the potential cost of - // not being as stable in the raster cache from frame to frame. - ContainerLayer::Add(std::make_shared()); - - // The interposing Container only recurses to its children with no - // additional processing so, by default, it can pass an inherited - // opacity on to its children, subject only to the accumulation - // logic that happens during |PrerollChildren|. - GetChildContainer()->set_layer_can_inherit_opacity(true); -} - -void MergedContainerLayer::DiffChildren(DiffContext* context, - const ContainerLayer* old_layer) { - if (context->IsSubtreeDirty()) { - GetChildContainer()->Diff(context, nullptr); - return; - } - FML_DCHECK(old_layer); - - // For MergedContainerLayer we want to diff children of ChildContainer - // instead of the ChildContainer itself. This works around the fact - // that ChildContainerLayer is ephemeral and its original_layer_id_ is always - // different. - auto layer = static_cast(old_layer); - GetChildContainer()->DiffChildren(context, layer->GetChildContainer()); -} - -void MergedContainerLayer::Add(std::shared_ptr layer) { - GetChildContainer()->Add(std::move(layer)); -} - -ContainerLayer* MergedContainerLayer::GetChildContainer() const { - FML_DCHECK(layers().size() == 1); - - return static_cast(layers()[0].get()); -} - -Layer* MergedContainerLayer::GetCacheableChild() const { - ContainerLayer* child_container = GetChildContainer(); - if (child_container->layers().size() == 1) { - return child_container->layers()[0].get(); - } - - return child_container; -} - } // namespace flutter diff --git a/engine/src/flutter/flow/layers/container_layer.h b/engine/src/flutter/flow/layers/container_layer.h index dfea4011a77..937c4e586da 100644 --- a/engine/src/flutter/flow/layers/container_layer.h +++ b/engine/src/flutter/flow/layers/container_layer.h @@ -27,12 +27,15 @@ class ContainerLayer : public Layer { virtual void DiffChildren(DiffContext* context, const ContainerLayer* old_layer); + void PaintChildren(PaintContext& context) const; + const ContainerLayer* as_container_layer() const override { return this; } + + const SkRect& child_paint_bounds() const { return child_paint_bounds_; } protected: void PrerollChildren(PrerollContext* context, const SkMatrix& child_matrix, SkRect* child_paint_bounds); - void PaintChildren(PaintContext& context) const; // Try to prepare the raster cache for a given layer. // @@ -46,92 +49,16 @@ class ContainerLayer : public Layer { // cache a child layer and one can't access its child's protected method. static void TryToPrepareRasterCache(PrerollContext* context, Layer* layer, - const SkMatrix& matrix); + const SkMatrix& matrix, + RasterCacheLayerStrategy strategy); private: std::vector> layers_; + SkRect child_paint_bounds_; FML_DISALLOW_COPY_AND_ASSIGN(ContainerLayer); }; -//------------------------------------------------------------------------------ -/// Some ContainerLayer objects perform a rendering operation or filter on -/// the rendered output of their children. Often that operation is changed -/// slightly from frame to frame as part of an animation. During such an -/// animation, the children can be cached if they are stable to avoid having -/// to render them on every frame. Even if the children are not stable, -/// rendering them into the raster cache during a Preroll operation will save -/// an extra change of rendering surface during the Paint phase as compared -/// to using the SaveLayer that would otherwise be needed with no caching. -/// -/// Typically the Flutter Widget objects that lead to the creation of these -/// layers will try to enforce only a single child Widget by their design. -/// Unfortunately, the process of turning Widgets eventually into engine -/// layers is not a 1:1 process so this layer might end up with multiple -/// child layers even if the Widget only had a single child Widget. -/// -/// When such a layer goes to cache the output of its children, it will -/// need to supply a single layer to the cache mechanism since the raster -/// cache uses a layer unique_id() as part of the cache key. If this layer -/// ended up with multiple children, then it must first collect them into -/// one layer for the cache mechanism. In order to provide a single layer -/// for all of the children, this utility class will implicitly collect -/// the children into a secondary ContainerLayer called the child container. -/// -/// A by-product of creating a hidden child container, though, is that the -/// child container is created new every time this layer is created with -/// different properties, such as during an animation. In that scenario, -/// it would be best to cache the single real child of this layer if it -/// is unique and if it is stable from frame to frame. To facilitate this -/// optimal caching strategy, this class implements two accessor methods -/// to be used for different purposes: -/// -/// When the layer needs to recurse to perform some operation on its children, -/// it can call GetChildContainer() to return the hidden container containing -/// all of the real children. -/// -/// When the layer wants to cache the rendered contents of its children, it -/// should call GetCacheableChild() for best performance. This method may -/// end up returning the same layer as GetChildContainer(), but only if the -/// conditions for optimal caching of a single child are not met. -/// -class MergedContainerLayer : public ContainerLayer { - public: - MergedContainerLayer(); - - void Add(std::shared_ptr layer) override; - - void DiffChildren(DiffContext* context, - const ContainerLayer* old_layer) override; - - protected: - /** - * @brief Returns the ContainerLayer used to hold all of the children of the - * MergedContainerLayer. Note that this may not be the best layer to use - * for caching the children. - * - * @see GetCacheableChild() - * @return the ContainerLayer child used to hold the children - */ - ContainerLayer* GetChildContainer() const; - - /** - * @brief Returns the best choice for a Layer object that can be used - * in RasterCache operations to cache the children. - * - * The returned Layer must represent all children and try to remain stable - * if the MergedContainerLayer is reconstructed in subsequent frames of - * the scene. - * - * @see GetChildContainer() - * @return the best candidate Layer for caching the children - */ - Layer* GetCacheableChild() const; - - private: - FML_DISALLOW_COPY_AND_ASSIGN(MergedContainerLayer); -}; - } // namespace flutter #endif // FLUTTER_FLOW_LAYERS_CONTAINER_LAYER_H_ diff --git a/engine/src/flutter/flow/layers/container_layer_unittests.cc b/engine/src/flutter/flow/layers/container_layer_unittests.cc index aea84513a6f..7fd4b32bb6c 100644 --- a/engine/src/flutter/flow/layers/container_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/container_layer_unittests.cc @@ -29,6 +29,7 @@ TEST_F(ContainerLayerTest, PaintingEmptyLayerDies) { layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_EQ(layer->child_paint_bounds(), SkRect::MakeEmpty()); EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), @@ -43,6 +44,7 @@ TEST_F(ContainerLayerTest, PaintBeforePrerollDies) { layer->Add(mock_layer); EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_EQ(layer->child_paint_bounds(), SkRect::MakeEmpty()); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), "needs_painting\\(context\\)"); } @@ -62,6 +64,7 @@ TEST_F(ContainerLayerTest, Simple) { EXPECT_FALSE(preroll_context()->has_platform_view); EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer->paint_bounds(), child_path.getBounds()); + EXPECT_EQ(layer->child_paint_bounds(), layer->paint_bounds()); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); @@ -96,6 +99,7 @@ TEST_F(ContainerLayerTest, Multiple) { EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); + EXPECT_EQ(layer->child_paint_bounds(), layer->paint_bounds()); EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); @@ -132,6 +136,7 @@ TEST_F(ContainerLayerTest, MultipleWithEmpty) { EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), SkPath().getBounds()); EXPECT_EQ(layer->paint_bounds(), child_path1.getBounds()); + EXPECT_EQ(layer->child_paint_bounds(), layer->paint_bounds()); EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); EXPECT_FALSE(mock_layer2->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); @@ -169,6 +174,7 @@ TEST_F(ContainerLayerTest, NeedsSystemComposite) { EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); + EXPECT_EQ(layer->child_paint_bounds(), layer->paint_bounds()); EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); @@ -186,71 +192,6 @@ TEST_F(ContainerLayerTest, NeedsSystemComposite) { child_path2, child_paint2}}})); } -TEST_F(ContainerLayerTest, MergedOneChild) { - SkPath child_path; - child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); - SkPaint child_paint(SkColors::kGreen); - SkMatrix initial_transform = SkMatrix::Translate(-0.5f, -0.5f); - - auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(); - layer->Add(mock_layer); - - layer->Preroll(preroll_context(), initial_transform); - EXPECT_FALSE(preroll_context()->has_platform_view); - EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); - EXPECT_EQ(layer->paint_bounds(), child_path.getBounds()); - EXPECT_TRUE(mock_layer->needs_painting(paint_context())); - EXPECT_TRUE(layer->needs_painting(paint_context())); - EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); - EXPECT_EQ(mock_layer->parent_cull_rect(), kGiantRect); - - layer->Paint(paint_context()); - EXPECT_EQ(mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{child_path, child_paint}}})); -} - -TEST_F(ContainerLayerTest, MergedMultipleChildren) { - SkPath child_path1; - child_path1.addRect(5.0f, 6.0f, 20.5f, 21.5f); - SkPath child_path2; - child_path2.addRect(58.0f, 2.0f, 16.5f, 14.5f); - SkPaint child_paint1(SkColors::kGray); - SkPaint child_paint2(SkColors::kGreen); - SkMatrix initial_transform = SkMatrix::Translate(-0.5f, -0.5f); - - auto mock_layer1 = std::make_shared(child_path1, child_paint1); - auto mock_layer2 = std::make_shared(child_path2, child_paint2); - auto layer = std::make_shared(); - layer->Add(mock_layer1); - layer->Add(mock_layer2); - - SkRect expected_total_bounds = child_path1.getBounds(); - expected_total_bounds.join(child_path2.getBounds()); - layer->Preroll(preroll_context(), initial_transform); - EXPECT_FALSE(preroll_context()->has_platform_view); - EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); - EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); - EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); - EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); - EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); - EXPECT_TRUE(layer->needs_painting(paint_context())); - EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); - EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); - EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); - EXPECT_EQ(mock_layer2->parent_cull_rect(), - kGiantRect); // Siblings are independent - - layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::DrawPathData{child_path1, child_paint1}}, - MockCanvas::DrawCall{0, MockCanvas::DrawPathData{ - child_path2, child_paint2}}})); -} - using ContainerLayerDiffTest = DiffContextTest; // Insert PictureLayer amongst container layers diff --git a/engine/src/flutter/flow/layers/image_filter_layer.cc b/engine/src/flutter/flow/layers/image_filter_layer.cc index d99db54998d..fbeff92307c 100644 --- a/engine/src/flutter/flow/layers/image_filter_layer.cc +++ b/engine/src/flutter/flow/layers/image_filter_layer.cc @@ -63,7 +63,8 @@ void ImageFilterLayer::Preroll(PrerollContext* context, // times to consider its properties and children to be stable // from frame to frame so we try to cache the layer itself // for maximum performance. - TryToPrepareRasterCache(context, this, matrix); + TryToPrepareRasterCache(context, this, matrix, + RasterCacheLayerStrategy::kLayer); } else { // This ImageFilterLayer is not yet considered stable so we // increment the count to measure how many times it has been @@ -84,7 +85,8 @@ void ImageFilterLayer::Preroll(PrerollContext* context, // stable between frames and also avoiding a rendering surface // switch during the Paint phase even if they are not stable. // This benefit is seen most during animations. - TryToPrepareRasterCache(context, GetCacheableChild(), matrix); + TryToPrepareRasterCache(context, this, matrix, + RasterCacheLayerStrategy::kLayerChildren); } } } @@ -94,15 +96,16 @@ void ImageFilterLayer::Paint(PaintContext& context) const { FML_DCHECK(needs_painting(context)); if (context.raster_cache) { - if (context.raster_cache->Draw(this, *context.leaf_nodes_canvas)) { + if (context.raster_cache->Draw(this, *context.leaf_nodes_canvas, + RasterCacheLayerStrategy::kLayer)) { return; } if (transformed_filter_) { SkPaint paint; paint.setImageFilter(transformed_filter_); - - if (context.raster_cache->Draw(GetCacheableChild(), - *context.leaf_nodes_canvas, &paint)) { + if (context.raster_cache->Draw(this, *context.leaf_nodes_canvas, + RasterCacheLayerStrategy::kLayerChildren, + &paint)) { return; } } @@ -115,8 +118,8 @@ void ImageFilterLayer::Paint(PaintContext& context) const { // 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, GetChildContainer()->paint_bounds(), &paint); + Layer::AutoSaveLayer save_layer = + Layer::AutoSaveLayer::Create(context, child_paint_bounds(), &paint); PaintChildren(context); } diff --git a/engine/src/flutter/flow/layers/image_filter_layer.h b/engine/src/flutter/flow/layers/image_filter_layer.h index 48a0760d4b2..2e2b2a3c5da 100644 --- a/engine/src/flutter/flow/layers/image_filter_layer.h +++ b/engine/src/flutter/flow/layers/image_filter_layer.h @@ -10,7 +10,7 @@ namespace flutter { -class ImageFilterLayer : public MergedContainerLayer { +class ImageFilterLayer : public ContainerLayer { public: explicit ImageFilterLayer(sk_sp filter); diff --git a/engine/src/flutter/flow/layers/image_filter_layer_unittests.cc b/engine/src/flutter/flow/layers/image_filter_layer_unittests.cc index d1f2f894171..0b5898efbcc 100644 --- a/engine/src/flutter/flow/layers/image_filter_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/image_filter_layer_unittests.cc @@ -37,6 +37,7 @@ TEST_F(ImageFilterLayerTest, PaintBeforePrerollDies) { layer->Add(mock_layer); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), "needs_painting\\(context\\)"); } @@ -53,6 +54,7 @@ TEST_F(ImageFilterLayerTest, EmptyFilter) { layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); @@ -87,6 +89,7 @@ TEST_F(ImageFilterLayerTest, SimpleFilter) { layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_rounded_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); @@ -121,6 +124,7 @@ TEST_F(ImageFilterLayerTest, SimpleFilterBounds) { layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), filter_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); @@ -163,6 +167,7 @@ TEST_F(ImageFilterLayerTest, MultipleChildren) { 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); + EXPECT_EQ(layer->child_paint_bounds(), children_bounds); EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); @@ -217,7 +222,9 @@ TEST_F(ImageFilterLayerTest, Nested) { 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); + EXPECT_EQ(layer1->child_paint_bounds(), children_bounds); EXPECT_EQ(layer2->paint_bounds(), mock_layer2_rounded_bounds); + EXPECT_EQ(layer2->child_paint_bounds(), child_path2.getBounds()); EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); EXPECT_TRUE(layer1->needs_painting(paint_context())); @@ -267,7 +274,7 @@ TEST_F(ImageFilterLayerTest, Readback) { EXPECT_FALSE(preroll_context()->surface_needs_readback); } -TEST_F(ImageFilterLayerTest, ChildIsCached) { +TEST_F(ImageFilterLayerTest, CacheChild) { auto layer_filter = SkImageFilters::MatrixTransform( SkMatrix(), SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr); @@ -289,15 +296,31 @@ TEST_F(ImageFilterLayerTest, ChildIsCached) { EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas)); EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayerChildren)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayerChildren)); layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas)); - EXPECT_TRUE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); + EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayerChildren)); + EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayerChildren)); } -TEST_F(ImageFilterLayerTest, ChildrenNotCached) { +TEST_F(ImageFilterLayerTest, CacheChildren) { auto layer_filter = SkImageFilters::MatrixTransform( SkMatrix(), SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr); @@ -324,6 +347,14 @@ TEST_F(ImageFilterLayerTest, ChildrenNotCached) { EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas)); EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas)); EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayerChildren)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayerChildren)); layer->Preroll(preroll_context(), initial_transform); @@ -332,6 +363,14 @@ TEST_F(ImageFilterLayerTest, ChildrenNotCached) { EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas)); EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas)); EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayerChildren)); + EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayerChildren)); } using ImageFilterLayerDiffTest = DiffContextTest; diff --git a/engine/src/flutter/flow/layers/layer.h b/engine/src/flutter/flow/layers/layer.h index 11541f62632..5c255366f44 100644 --- a/engine/src/flutter/flow/layers/layer.h +++ b/engine/src/flutter/flow/layers/layer.h @@ -82,6 +82,7 @@ struct PrerollContext { bool subtree_can_inherit_opacity = false; }; +class ContainerLayer; class PictureLayer; class DisplayListLayer; class PerformanceOverlayLayer; @@ -330,6 +331,7 @@ class Layer { uint64_t unique_id() const { return unique_id_; } + virtual const ContainerLayer* as_container_layer() const { return nullptr; } virtual const PictureLayer* as_picture_layer() const { return nullptr; } virtual const DisplayListLayer* as_display_list_layer() const { return nullptr; diff --git a/engine/src/flutter/flow/layers/opacity_layer.cc b/engine/src/flutter/flow/layers/opacity_layer.cc index 0eb47622cd8..49fc890a632 100644 --- a/engine/src/flutter/flow/layers/opacity_layer.cc +++ b/engine/src/flutter/flow/layers/opacity_layer.cc @@ -37,7 +37,7 @@ void OpacityLayer::Diff(DiffContext* context, const Layer* old_layer) { void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "OpacityLayer::Preroll"); - FML_DCHECK(!GetChildContainer()->layers().empty()); // We can't be a leaf. + FML_DCHECK(!layers().empty()); // We can't be a leaf. SkMatrix child_matrix = matrix; child_matrix.preTranslate(offset_.fX, offset_.fY); @@ -63,7 +63,8 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { #ifndef SUPPORT_FRACTIONAL_TRANSLATION child_matrix = RasterCache::GetIntegralTransCTM(child_matrix); #endif - TryToPrepareRasterCache(context, GetCacheableChild(), child_matrix); + TryToPrepareRasterCache(context, this, child_matrix, + RasterCacheLayerStrategy::kLayerChildren); } // Restore cull_rect @@ -96,8 +97,9 @@ void OpacityLayer::Paint(PaintContext& context) const { paint.setAlphaf(subtree_opacity); if (context.raster_cache && - context.raster_cache->Draw(GetCacheableChild(), - *context.leaf_nodes_canvas, &paint)) { + context.raster_cache->Draw(this, *context.leaf_nodes_canvas, + RasterCacheLayerStrategy::kLayerChildren, + &paint)) { return; } diff --git a/engine/src/flutter/flow/layers/opacity_layer.h b/engine/src/flutter/flow/layers/opacity_layer.h index 512d33c3f32..c9cdd0738c4 100644 --- a/engine/src/flutter/flow/layers/opacity_layer.h +++ b/engine/src/flutter/flow/layers/opacity_layer.h @@ -13,7 +13,7 @@ namespace flutter { // OpacityLayer is very costly due to the saveLayer call. If there's no child, // having the OpacityLayer or not has the same effect. In debug_unopt build, // |Preroll| will assert if there are no children. -class OpacityLayer : public MergedContainerLayer { +class OpacityLayer : public ContainerLayer { public: // An offset is provided here because OpacityLayer.addToScene method in the // Flutter framework can take an optional offset argument. diff --git a/engine/src/flutter/flow/layers/opacity_layer_unittests.cc b/engine/src/flutter/flow/layers/opacity_layer_unittests.cc index acb837cf775..61ac3110381 100644 --- a/engine/src/flutter/flow/layers/opacity_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/opacity_layer_unittests.cc @@ -21,9 +21,8 @@ TEST_F(OpacityLayerTest, LeafLayer) { auto layer = std::make_shared(SK_AlphaOPAQUE, SkPoint::Make(0.0f, 0.0f)); - EXPECT_DEATH_IF_SUPPORTED( - layer->Preroll(preroll_context(), SkMatrix()), - "\\!GetChildContainer\\(\\)->layers\\(\\)\\.empty\\(\\)"); + EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), + "\\!layers\\(\\)\\.empty\\(\\)"); } TEST_F(OpacityLayerTest, PaintingEmptyLayerDies) { @@ -35,6 +34,7 @@ TEST_F(OpacityLayerTest, PaintingEmptyLayerDies) { layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(mock_layer->paint_bounds(), SkPath().getBounds()); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); + EXPECT_EQ(layer->child_paint_bounds(), mock_layer->paint_bounds()); EXPECT_FALSE(mock_layer->needs_painting(paint_context())); EXPECT_FALSE(layer->needs_painting(paint_context())); @@ -72,7 +72,7 @@ TEST_F(OpacityLayerTest, TranslateChildren) { EXPECT_EQ(layer_bounds, SkRect::MakeXYWH(40, 40, 20, 20)); } -TEST_F(OpacityLayerTest, ChildIsCached) { +TEST_F(OpacityLayerTest, CacheChild) { const SkAlpha alpha_half = 255 / 2; auto initial_transform = SkMatrix::Translate(50.0, 25.5); auto other_transform = SkMatrix::Scale(1.0, 2.0); @@ -93,15 +93,31 @@ TEST_F(OpacityLayerTest, ChildIsCached) { EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas)); EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayerChildren)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayerChildren)); layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas)); - EXPECT_TRUE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); + EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayerChildren)); + EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayerChildren)); } -TEST_F(OpacityLayerTest, ChildrenNotCached) { +TEST_F(OpacityLayerTest, CacheChildren) { const SkAlpha alpha_half = 255 / 2; auto initial_transform = SkMatrix::Translate(50.0, 25.5); auto other_transform = SkMatrix::Scale(1.0, 2.0); @@ -127,6 +143,14 @@ TEST_F(OpacityLayerTest, ChildrenNotCached) { EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas)); EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas)); EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayerChildren)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayerChildren)); layer->Preroll(preroll_context(), initial_transform); @@ -135,6 +159,14 @@ TEST_F(OpacityLayerTest, ChildrenNotCached) { EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas)); EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas)); EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayer)); + EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, + RasterCacheLayerStrategy::kLayerChildren)); + EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas, + RasterCacheLayerStrategy::kLayerChildren)); } TEST_F(OpacityLayerTest, FullyOpaque) { @@ -157,6 +189,7 @@ TEST_F(OpacityLayerTest, FullyOpaque) { layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_path.getBounds()); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), @@ -208,6 +241,7 @@ TEST_F(OpacityLayerTest, FullyTransparent) { layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_path.getBounds()); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), @@ -255,6 +289,7 @@ TEST_F(OpacityLayerTest, HalfTransparent) { layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_path.getBounds()); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), @@ -321,16 +356,18 @@ TEST_F(OpacityLayerTest, Nested) { const SkRect expected_layer2_bounds = layer2_transform.mapRect(child2_path.getBounds()); - SkRect expected_layer1_bounds = expected_layer2_bounds; - expected_layer1_bounds.join(child1_path.getBounds()); - expected_layer1_bounds.join(child3_path.getBounds()); - expected_layer1_bounds = layer1_transform.mapRect(expected_layer1_bounds); + SkRect layer1_child_bounds = expected_layer2_bounds; + layer1_child_bounds.join(child1_path.getBounds()); + layer1_child_bounds.join(child3_path.getBounds()); + SkRect expected_layer1_bounds = layer1_transform.mapRect(layer1_child_bounds); layer1->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer1->paint_bounds(), child1_path.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child2_path.getBounds()); EXPECT_EQ(mock_layer3->paint_bounds(), child3_path.getBounds()); EXPECT_EQ(layer1->paint_bounds(), expected_layer1_bounds); + EXPECT_EQ(layer1->child_paint_bounds(), layer1_child_bounds); EXPECT_EQ(layer2->paint_bounds(), expected_layer2_bounds); + EXPECT_EQ(layer2->child_paint_bounds(), child2_path.getBounds()); EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); EXPECT_TRUE(mock_layer3->needs_painting(paint_context())); diff --git a/engine/src/flutter/flow/layers/physical_shape_layer_unittests.cc b/engine/src/flutter/flow/layers/physical_shape_layer_unittests.cc index db7e966cad9..aa1b15c6651 100644 --- a/engine/src/flutter/flow/layers/physical_shape_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/physical_shape_layer_unittests.cc @@ -25,6 +25,7 @@ TEST_F(PhysicalShapeLayerTest, PaintingEmptyLayerDies) { layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_EQ(layer->child_paint_bounds(), SkRect::MakeEmpty()); EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), @@ -55,6 +56,7 @@ TEST_F(PhysicalShapeLayerTest, NonEmptyLayer) { layer_path, Clip::none); layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), layer_path.getBounds()); + EXPECT_EQ(layer->child_paint_bounds(), SkRect::MakeEmpty()); EXPECT_TRUE(layer->needs_painting(paint_context())); SkPaint layer_paint; @@ -93,6 +95,7 @@ TEST_F(PhysicalShapeLayerTest, ChildrenLargerThanPathClip) { child_paint_bounds.join(child2->paint_bounds()); EXPECT_EQ(layer->paint_bounds(), layer_path.getBounds()); EXPECT_NE(layer->paint_bounds(), child_paint_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_paint_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); SkPaint layer_paint; @@ -142,13 +145,14 @@ TEST_F(PhysicalShapeLayerTest, ChildrenLargerThanPathNoClip) { layer->Add(child1); layer->Add(child2); - SkRect total_bounds = SkRect::MakeEmpty(); layer->Preroll(preroll_context(), SkMatrix()); - total_bounds.join(child1->paint_bounds()); - total_bounds.join(child2->paint_bounds()); + SkRect child_bounds = child1->paint_bounds(); + child_bounds.join(child2->paint_bounds()); + SkRect total_bounds = child_bounds; total_bounds.join(layer_path.getBounds()); EXPECT_NE(layer->paint_bounds(), layer_path.getBounds()); EXPECT_EQ(layer->paint_bounds(), total_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); SkPaint layer_paint; diff --git a/engine/src/flutter/flow/layers/shader_mask_layer_unittests.cc b/engine/src/flutter/flow/layers/shader_mask_layer_unittests.cc index c979d54278c..06499f53632 100644 --- a/engine/src/flutter/flow/layers/shader_mask_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/shader_mask_layer_unittests.cc @@ -23,6 +23,7 @@ TEST_F(ShaderMaskLayerTest, PaintingEmptyLayerDies) { layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), @@ -38,6 +39,7 @@ TEST_F(ShaderMaskLayerTest, PaintBeforePrerollDies) { layer->Add(mock_layer); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); + EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), "needs_painting\\(context\\)"); } @@ -57,6 +59,7 @@ TEST_F(ShaderMaskLayerTest, EmptyFilter) { layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); @@ -98,6 +101,7 @@ TEST_F(ShaderMaskLayerTest, SimpleFilter) { layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->child_paint_bounds(), child_bounds); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); @@ -147,6 +151,7 @@ TEST_F(ShaderMaskLayerTest, MultipleChildren) { 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); + EXPECT_EQ(layer->child_paint_bounds(), children_bounds); EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); @@ -206,7 +211,9 @@ TEST_F(ShaderMaskLayerTest, Nested) { 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); + EXPECT_EQ(layer1->child_paint_bounds(), children_bounds); EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds()); + EXPECT_EQ(layer2->child_paint_bounds(), mock_layer2->paint_bounds()); EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); EXPECT_TRUE(layer1->needs_painting(paint_context())); diff --git a/engine/src/flutter/flow/layers/transform_layer_unittests.cc b/engine/src/flutter/flow/layers/transform_layer_unittests.cc index 345a1051f38..174944c55a0 100644 --- a/engine/src/flutter/flow/layers/transform_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/transform_layer_unittests.cc @@ -21,6 +21,7 @@ TEST_F(TransformLayerTest, PaintingEmptyLayerDies) { layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_EQ(layer->child_paint_bounds(), SkRect::MakeEmpty()); EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), @@ -51,6 +52,7 @@ TEST_F(TransformLayerTest, Identity) { layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); + EXPECT_EQ(layer->child_paint_bounds(), mock_layer->paint_bounds()); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); // identity @@ -81,6 +83,7 @@ TEST_F(TransformLayerTest, Simple) { EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer->paint_bounds(), layer_transform.mapRect(mock_layer->paint_bounds())); + EXPECT_EQ(layer->child_paint_bounds(), mock_layer->paint_bounds()); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), @@ -123,8 +126,10 @@ TEST_F(TransformLayerTest, Nested) { EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer2->paint_bounds(), layer2_transform.mapRect(mock_layer->paint_bounds())); + EXPECT_EQ(layer2->child_paint_bounds(), mock_layer->paint_bounds()); EXPECT_EQ(layer1->paint_bounds(), layer1_transform.mapRect(layer2->paint_bounds())); + EXPECT_EQ(layer1->child_paint_bounds(), layer2->paint_bounds()); EXPECT_TRUE(mock_layer->needs_painting(paint_context())); EXPECT_TRUE(layer2->needs_painting(paint_context())); EXPECT_TRUE(layer1->needs_painting(paint_context())); @@ -177,14 +182,18 @@ TEST_F(TransformLayerTest, NestedSeparated) { preroll_context()->cull_rect = cull_rect; layer1->Preroll(preroll_context(), initial_transform); - SkRect expected_layer1_bounds = layer2->paint_bounds(); - expected_layer1_bounds.join(mock_layer1->paint_bounds()); + SkRect layer1_child_bounds = layer2->paint_bounds(); + layer1_child_bounds.join(mock_layer1->paint_bounds()); + SkRect expected_layer1_bounds = layer1_child_bounds; layer1_transform.mapRect(&expected_layer1_bounds); + EXPECT_EQ(mock_layer2->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer2->paint_bounds(), layer2_transform.mapRect(mock_layer2->paint_bounds())); + EXPECT_EQ(layer2->child_paint_bounds(), mock_layer2->paint_bounds()); EXPECT_EQ(mock_layer1->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer1->paint_bounds(), expected_layer1_bounds); + EXPECT_EQ(layer1->child_paint_bounds(), layer1_child_bounds); EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); EXPECT_TRUE(layer2->needs_painting(paint_context())); EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); diff --git a/engine/src/flutter/flow/raster_cache.cc b/engine/src/flutter/flow/raster_cache.cc index f2a8dd30209..68072b6f56c 100644 --- a/engine/src/flutter/flow/raster_cache.cc +++ b/engine/src/flutter/flow/raster_cache.cc @@ -7,6 +7,7 @@ #include #include "flutter/common/constants.h" +#include "flutter/flow/layers/container_layer.h" #include "flutter/flow/layers/layer.h" #include "flutter/flow/paint_utils.h" #include "flutter/fml/logging.h" @@ -176,25 +177,59 @@ std::unique_ptr RasterCache::RasterizeDisplayList( void RasterCache::Prepare(PrerollContext* context, Layer* layer, - const SkMatrix& ctm) { - RasterCacheKey cache_key(layer->unique_id(), RasterCacheKeyType::kLayer, ctm); - Entry& entry = cache_[cache_key]; + const SkMatrix& ctm, + RasterCacheLayerStrategy strategy) { + auto cache_key_optional = + TryToMakeRasterCacheKeyForLayer(layer, strategy, ctm); + if (!cache_key_optional) { + return; + } + Entry& entry = cache_[cache_key_optional.value()]; entry.access_count++; entry.used_this_frame = true; if (!entry.image) { - entry.image = RasterizeLayer(context, layer, ctm, checkerboard_images_); + entry.image = + RasterizeLayer(context, layer, strategy, ctm, checkerboard_images_); + } +} + +std::optional RasterCache::TryToMakeRasterCacheKeyForLayer( + const Layer* layer, + RasterCacheLayerStrategy strategy, + const SkMatrix& ctm) const { + switch (strategy) { + case RasterCacheLayerStrategy::kLayer: + return RasterCacheKey(layer->unique_id(), RasterCacheKeyType::kLayer, + ctm); + case RasterCacheLayerStrategy::kLayerChildren: + FML_DCHECK(layer->as_container_layer()); + auto& children_layers = layer->as_container_layer()->layers(); + auto children_count = children_layers.size(); + if (children_count == 0) { + return std::nullopt; + } + std::vector ids; + std::transform(children_layers.begin(), children_layers.end(), + std::back_inserter(ids), [](auto& layer) -> uint64_t { + return layer->unique_id(); + }); + return RasterCacheKey(RasterCacheKeyID(std::move(ids)), + RasterCacheKeyType::kLayerChildren, ctm); } } std::unique_ptr RasterCache::RasterizeLayer( PrerollContext* context, Layer* layer, + RasterCacheLayerStrategy strategy, const SkMatrix& ctm, bool checkerboard) const { + const SkRect& paint_bounds = GetPaintBoundsFromLayer(layer, strategy); + return Rasterize( context->gr_context, ctm, context->dst_color_space, checkerboard, - layer->paint_bounds(), "RasterCacheFlow::Layer", - [layer, context](SkCanvas* canvas) { + paint_bounds, "RasterCacheFlow::Layer", + [layer, context, strategy](SkCanvas* canvas) { SkISize canvas_size = canvas->getBaseLayerSize(); SkNWayCanvas internal_nodes_canvas(canvas_size.width(), canvas_size.height()); @@ -212,12 +247,32 @@ std::unique_ptr RasterCache::RasterizeLayer( context->has_platform_view ? nullptr : context->raster_cache, context->checkerboard_offscreen_layers, context->frame_device_pixel_ratio}; - if (layer->needs_painting(paintContext)) { - layer->Paint(paintContext); + switch (strategy) { + case RasterCacheLayerStrategy::kLayer: + if (layer->needs_painting(paintContext)) { + layer->Paint(paintContext); + } + break; + case RasterCacheLayerStrategy::kLayerChildren: + FML_DCHECK(layer->as_container_layer()); + layer->as_container_layer()->PaintChildren(paintContext); + break; } }); } +const SkRect& RasterCache::GetPaintBoundsFromLayer( + Layer* layer, + RasterCacheLayerStrategy strategy) const { + switch (strategy) { + case RasterCacheLayerStrategy::kLayer: + return layer->paint_bounds(); + case RasterCacheLayerStrategy::kLayerChildren: + FML_DCHECK(layer->as_container_layer()); + return layer->as_container_layer()->child_paint_bounds(); + } +} + bool RasterCache::Prepare(PrerollContext* context, SkPicture* picture, bool is_complex, @@ -321,9 +376,15 @@ bool RasterCache::Prepare(PrerollContext* context, return true; } -void RasterCache::Touch(Layer* layer, const SkMatrix& ctm) { - RasterCacheKey cache_key(layer->unique_id(), RasterCacheKeyType::kLayer, ctm); - Touch(cache_key); +void RasterCache::Touch(Layer* layer, + const SkMatrix& ctm, + RasterCacheLayerStrategy strategey) { + auto cache_key_optional = + TryToMakeRasterCacheKeyForLayer(layer, strategey, ctm); + if (!cache_key_optional) { + return; + } + Touch(cache_key_optional.value()); } void RasterCache::Touch(SkPicture* picture, @@ -368,10 +429,14 @@ bool RasterCache::Draw(const DisplayList& display_list, bool RasterCache::Draw(const Layer* layer, SkCanvas& canvas, + RasterCacheLayerStrategy strategy, const SkPaint* paint) const { - RasterCacheKey cache_key(layer->unique_id(), RasterCacheKeyType::kLayer, - canvas.getTotalMatrix()); - return Draw(cache_key, canvas, paint); + auto cache_key_optional = + TryToMakeRasterCacheKeyForLayer(layer, strategy, canvas.getTotalMatrix()); + if (!cache_key_optional) { + return false; + } + return Draw(cache_key_optional.value(), canvas, paint); } bool RasterCache::Draw(const RasterCacheKey& cache_key, diff --git a/engine/src/flutter/flow/raster_cache.h b/engine/src/flutter/flow/raster_cache.h index 60baecdd316..51d1d17e7ff 100644 --- a/engine/src/flutter/flow/raster_cache.h +++ b/engine/src/flutter/flow/raster_cache.h @@ -21,6 +21,8 @@ class SkColorSpace; namespace flutter { +enum class RasterCacheLayerStrategy { kLayer, kLayerChildren }; + class RasterCacheResult { public: RasterCacheResult(sk_sp image, @@ -143,6 +145,7 @@ class RasterCache { virtual std::unique_ptr RasterizeLayer( PrerollContext* context, Layer* layer, + RasterCacheLayerStrategy strategy, const SkMatrix& ctm, bool checkerboard) const; @@ -202,9 +205,16 @@ class RasterCache { // away. void Touch(SkPicture* picture, const SkMatrix& transformation_matrix); void Touch(DisplayList* display_list, const SkMatrix& transformation_matrix); - void Touch(Layer* layer, const SkMatrix& ctm); + void Touch( + Layer* layer, + const SkMatrix& ctm, + RasterCacheLayerStrategy strategey = RasterCacheLayerStrategy::kLayer); - void Prepare(PrerollContext* context, Layer* layer, const SkMatrix& ctm); + void Prepare( + PrerollContext* context, + Layer* layer, + const SkMatrix& ctm, + RasterCacheLayerStrategy strategey = RasterCacheLayerStrategy::kLayer); // Find the raster cache for the picture and draw it to the canvas. // @@ -226,9 +236,11 @@ class RasterCache { // (e.g., draw the raster cache with some opacity). // // Return true if the layer raster cache is found and drawn. - bool Draw(const Layer* layer, - SkCanvas& canvas, - const SkPaint* paint = nullptr) const; + bool Draw( + const Layer* layer, + SkCanvas& canvas, + RasterCacheLayerStrategy strategey = RasterCacheLayerStrategy::kLayer, + const SkPaint* paint = nullptr) const; void PrepareNewFrame(); void CleanupAfterFrame(); @@ -310,6 +322,15 @@ class RasterCache { picture_and_display_list_cache_limit_per_frame_; } + std::optional TryToMakeRasterCacheKeyForLayer( + const Layer* layer, + RasterCacheLayerStrategy strategy, + const SkMatrix& ctm) const; + + const SkRect& GetPaintBoundsFromLayer( + Layer* layer, + RasterCacheLayerStrategy strategy) const; + const size_t access_threshold_; const size_t picture_and_display_list_cache_limit_per_frame_; size_t picture_cached_this_frame_ = 0; diff --git a/engine/src/flutter/flow/raster_cache_key.h b/engine/src/flutter/flow/raster_cache_key.h index 3c146221253..d3152046c2a 100644 --- a/engine/src/flutter/flow/raster_cache_key.h +++ b/engine/src/flutter/flow/raster_cache_key.h @@ -6,6 +6,8 @@ #define FLUTTER_FLOW_RASTER_CACHE_KEY_H_ #include +#include +#include #include "flutter/fml/hash_combine.h" #include "flutter/fml/logging.h" @@ -13,19 +15,55 @@ namespace flutter { -enum class RasterCacheKeyType { kLayer, kPicture, kDisplayList }; +class RasterCacheKeyID { + public: + RasterCacheKeyID(const std::vector ids) : ids_(ids) {} + + const std::vector& ids() { return ids_; } + + std::size_t GetHash() const { + std::size_t seed = fml::HashCombine(); + for (auto id : ids_) { + fml::HashCombineSeed(seed, id); + } + return seed; + } + + bool operator==(const RasterCacheKeyID& other) const { + return ids_ == other.ids_; + } + + bool operator!=(const RasterCacheKeyID& other) const { + return !operator==(other); + } + + private: + const std::vector ids_; +}; + +enum class RasterCacheKeyType { + kLayer, + kPicture, + kDisplayList, + kLayerChildren +}; enum class RasterCacheKeyKind { kLayerMetrics, kPictureMetrics }; class RasterCacheKey { public: RasterCacheKey(uint64_t id, RasterCacheKeyType type, const SkMatrix& ctm) - : id_(id), type_(type), matrix_(ctm) { + : RasterCacheKey(RasterCacheKeyID({id}), type, ctm) {} + + RasterCacheKey(RasterCacheKeyID id, + RasterCacheKeyType type, + const SkMatrix& ctm) + : id_(std::move(id)), type_(type), matrix_(ctm) { matrix_[SkMatrix::kMTransX] = 0; matrix_[SkMatrix::kMTransY] = 0; } - uint64_t id() const { return id_; } + const RasterCacheKeyID& id() const { return id_; } RasterCacheKeyType type() const { return type_; } const SkMatrix& matrix() const { return matrix_; } @@ -35,13 +73,14 @@ class RasterCacheKey { case RasterCacheKeyType::kDisplayList: return RasterCacheKeyKind::kPictureMetrics; case RasterCacheKeyType::kLayer: + case RasterCacheKeyType::kLayerChildren: return RasterCacheKeyKind::kLayerMetrics; } } struct Hash { std::size_t operator()(RasterCacheKey const& key) const { - return fml::HashCombine(key.id_, key.type_); + return fml::HashCombine(key.id_.GetHash(), key.type_); } }; @@ -57,7 +96,7 @@ class RasterCacheKey { using Map = std::unordered_map; private: - uint64_t id_; + RasterCacheKeyID id_; RasterCacheKeyType type_; diff --git a/engine/src/flutter/flow/raster_cache_unittests.cc b/engine/src/flutter/flow/raster_cache_unittests.cc index b62cfe5d08c..ff0234d41ca 100644 --- a/engine/src/flutter/flow/raster_cache_unittests.cc +++ b/engine/src/flutter/flow/raster_cache_unittests.cc @@ -555,17 +555,27 @@ TEST(RasterCache, RasterCacheKeyHashFunction) { RasterCacheKey layer_key(id, RasterCacheKeyType::kLayer, matrix); RasterCacheKey picture_key(id, RasterCacheKeyType::kPicture, matrix); RasterCacheKey display_list_key(id, RasterCacheKeyType::kDisplayList, matrix); + RasterCacheKey layer_children_key(id, RasterCacheKeyType::kLayerChildren, + matrix); + auto raster_cache_key_id = RasterCacheKeyID({id}); auto layer_hash_code = hash_function(layer_key); - ASSERT_EQ(layer_hash_code, fml::HashCombine(id, RasterCacheKeyType::kLayer)); + ASSERT_EQ(layer_hash_code, fml::HashCombine(raster_cache_key_id.GetHash(), + RasterCacheKeyType::kLayer)); auto picture_hash_code = hash_function(picture_key); - ASSERT_EQ(picture_hash_code, - fml::HashCombine(id, RasterCacheKeyType::kPicture)); + ASSERT_EQ(picture_hash_code, fml::HashCombine(raster_cache_key_id.GetHash(), + RasterCacheKeyType::kPicture)); auto display_list_hash_code = hash_function(display_list_key); ASSERT_EQ(display_list_hash_code, - fml::HashCombine(id, RasterCacheKeyType::kDisplayList)); + fml::HashCombine(raster_cache_key_id.GetHash(), + RasterCacheKeyType::kDisplayList)); + + auto layer_children_hash_code = hash_function(layer_children_key); + ASSERT_EQ(layer_children_hash_code, + fml::HashCombine(raster_cache_key_id.GetHash(), + RasterCacheKeyType::kLayerChildren)); } TEST(RasterCache, RasterCacheKeySameID) { @@ -575,13 +585,17 @@ TEST(RasterCache, RasterCacheKeySameID) { RasterCacheKey layer_key(id, RasterCacheKeyType::kLayer, matrix); RasterCacheKey picture_key(id, RasterCacheKeyType::kPicture, matrix); RasterCacheKey display_list_key(id, RasterCacheKeyType::kDisplayList, matrix); + RasterCacheKey layer_children_key(id, RasterCacheKeyType::kLayerChildren, + matrix); map[layer_key] = 100; map[picture_key] = 200; map[display_list_key] = 300; + map[layer_children_key] = 400; ASSERT_EQ(map[layer_key], 100); ASSERT_EQ(map[picture_key], 200); ASSERT_EQ(map[display_list_key], 300); + ASSERT_EQ(map[layer_children_key], 400); } TEST(RasterCache, RasterCacheKeySameType) { @@ -620,6 +634,46 @@ TEST(RasterCache, RasterCacheKeySameType) { ASSERT_EQ(map[display_list_first_key], 350); ASSERT_EQ(map[display_list_second_key], 400); ASSERT_EQ(map[display_list_third_key], 450); + + type = RasterCacheKeyType::kLayerChildren; + RasterCacheKey layer_children_first_key(RasterCacheKeyID({1, 2, 3}), type, + matrix); + RasterCacheKey layer_children_second_key(RasterCacheKeyID({2, 3, 1}), type, + matrix); + RasterCacheKey layer_children_third_key(RasterCacheKeyID({3, 2, 1}), type, + matrix); + map[layer_children_first_key] = 100; + map[layer_children_second_key] = 200; + map[layer_children_third_key] = 300; + ASSERT_EQ(map[layer_children_first_key], 100); + ASSERT_EQ(map[layer_children_second_key], 200); + ASSERT_EQ(map[layer_children_third_key], 300); +} + +TEST(RasterCache, RasterCacheKeyID_Equal) { + RasterCacheKeyID first = RasterCacheKeyID({1}); + RasterCacheKeyID second = RasterCacheKeyID({1}); + RasterCacheKeyID third = RasterCacheKeyID({2}); + ASSERT_EQ(first, second); + ASSERT_NE(first, third); + + RasterCacheKeyID fourth = RasterCacheKeyID({1, 2}); + RasterCacheKeyID fifth = RasterCacheKeyID({1, 2}); + RasterCacheKeyID sixth = RasterCacheKeyID({2, 1}); + ASSERT_EQ(fourth, fifth); + ASSERT_NE(fourth, sixth); +} + +TEST(RasterCache, RasterCacheKeyID_HashCode) { + uint64_t foo = 1; + uint64_t bar = 2; + RasterCacheKeyID first = RasterCacheKeyID({foo}); + RasterCacheKeyID second = RasterCacheKeyID({foo, bar}); + RasterCacheKeyID third = RasterCacheKeyID({bar, foo}); + + ASSERT_EQ(first.GetHash(), fml::HashCombine(foo)); + ASSERT_EQ(second.GetHash(), fml::HashCombine(foo, bar)); + ASSERT_EQ(third.GetHash(), fml::HashCombine(bar, foo)); } } // namespace testing diff --git a/engine/src/flutter/flow/testing/mock_raster_cache.cc b/engine/src/flutter/flow/testing/mock_raster_cache.cc index 60e01f15cc8..c1669f7e51a 100644 --- a/engine/src/flutter/flow/testing/mock_raster_cache.cc +++ b/engine/src/flutter/flow/testing/mock_raster_cache.cc @@ -28,6 +28,7 @@ std::unique_ptr MockRasterCache::RasterizePicture( std::unique_ptr MockRasterCache::RasterizeLayer( PrerollContext* context, Layer* layer, + RasterCacheLayerStrategy strategy, const SkMatrix& ctm, bool checkerboard) const { SkRect logical_rect = layer->paint_bounds(); diff --git a/engine/src/flutter/flow/testing/mock_raster_cache.h b/engine/src/flutter/flow/testing/mock_raster_cache.h index 49dec10969d..4301a8a39b8 100644 --- a/engine/src/flutter/flow/testing/mock_raster_cache.h +++ b/engine/src/flutter/flow/testing/mock_raster_cache.h @@ -66,6 +66,7 @@ class MockRasterCache : public RasterCache { std::unique_ptr RasterizeLayer( PrerollContext* context, Layer* layer, + RasterCacheLayerStrategy stategy, const SkMatrix& ctm, bool checkerboard) const override;