From 8cbaba6a034de05cbe4f842accfd5ca5848e2897 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Thu, 31 Aug 2023 13:32:06 -0700 Subject: [PATCH] Lazily allocate RasterCacheItems only when caching is enabled (flutter/engine#45211) Fixes https://github.com/flutter/flutter/issues/133377 The default allocation of RasterCacheItems in the layer tree was showing up in profiles of apps running on Impeller which don't actually use the raster cache. In order to eliminate the overhead of those allocations, RasterCacheItems are now lazily allocated only when the layers encounter an actual raster_cache during the Preroll phase. --- .../flutter/flow/layers/cacheable_layer.cc | 40 ++++---- .../src/flutter/flow/layers/cacheable_layer.h | 35 +++++-- .../flow/layers/clip_path_layer_unittests.cc | 55 +++++++++-- .../flow/layers/clip_rect_layer_unittests.cc | 53 ++++++++-- .../flow/layers/clip_rrect_layer_unittests.cc | 64 ++++++++++--- .../flutter/flow/layers/clip_shape_layer.h | 4 +- .../flutter/flow/layers/color_filter_layer.cc | 3 +- .../layers/color_filter_layer_unittests.cc | 96 +++++++++++++------ .../flutter/flow/layers/display_list_layer.cc | 22 ++++- .../flutter/flow/layers/display_list_layer.h | 9 +- .../layers/display_list_layer_unittests.cc | 94 ++++++++++++++---- .../flutter/flow/layers/image_filter_layer.cc | 9 +- .../layers/image_filter_layer_unittests.cc | 90 +++++++++++------ .../flow/layers/layer_raster_cache_item.h | 6 +- .../src/flutter/flow/layers/opacity_layer.cc | 5 +- .../flow/layers/opacity_layer_unittests.cc | 82 ++++++++++------ .../flutter/flow/layers/shader_mask_layer.cc | 3 +- .../layers/shader_mask_layer_unittests.cc | 61 +++++++++--- engine/src/flutter/flow/raster_cache_item.h | 2 + engine/src/flutter/flow/testing/mock_layer.cc | 20 +++- engine/src/flutter/flow/testing/mock_layer.h | 11 ++- .../flutter/flow/testing/mock_raster_cache.cc | 3 +- 22 files changed, 567 insertions(+), 200 deletions(-) diff --git a/engine/src/flutter/flow/layers/cacheable_layer.cc b/engine/src/flutter/flow/layers/cacheable_layer.cc index 7cdfacfb7d4..7ce398037dc 100644 --- a/engine/src/flutter/flow/layers/cacheable_layer.cc +++ b/engine/src/flutter/flow/layers/cacheable_layer.cc @@ -6,31 +6,39 @@ namespace flutter { -AutoCache::AutoCache(RasterCacheItem* raster_cache_item, +AutoCache::AutoCache(CacheableLayer& cacheable_layer, PrerollContext* context, - const SkMatrix& matrix) - : raster_cache_item_(raster_cache_item), - context_(context), - matrix_(matrix) { - if (IsCacheEnabled()) { - raster_cache_item->PrerollSetup(context, matrix); + bool caching_enabled) { + if (context->raster_cache && caching_enabled) { + raster_cache_item_ = cacheable_layer.realize_raster_cache_item(); + if (raster_cache_item_) { + context_ = context; + matrix_ = context->state_stack.transform_3x3(); + raster_cache_item_->PrerollSetup(context_, matrix_); + } + } else { + cacheable_layer.disable_raster_cache_item(); } } -bool AutoCache::IsCacheEnabled() { - return raster_cache_item_ && context_ && context_->raster_cache; -} - AutoCache::~AutoCache() { - if (IsCacheEnabled()) { + if (raster_cache_item_) { raster_cache_item_->PrerollFinalize(context_, matrix_); } } -CacheableContainerLayer::CacheableContainerLayer(int layer_cached_threshold, - bool can_cache_children) { - layer_raster_cache_item_ = LayerRasterCacheItem::Make( - this, layer_cached_threshold, can_cache_children); +RasterCacheItem* CacheableContainerLayer::realize_raster_cache_item() { + if (!layer_raster_cache_item_) { + layer_raster_cache_item_ = LayerRasterCacheItem::Make( + this, layer_cache_threshold_, can_cache_children_); + } + return layer_raster_cache_item_.get(); +} + +void CacheableContainerLayer::disable_raster_cache_item() { + if (layer_raster_cache_item_) { + layer_raster_cache_item_->reset_cache_state(); + } } } // namespace flutter diff --git a/engine/src/flutter/flow/layers/cacheable_layer.h b/engine/src/flutter/flow/layers/cacheable_layer.h index 856ce47546e..46c502d8a19 100644 --- a/engine/src/flutter/flow/layers/cacheable_layer.h +++ b/engine/src/flutter/flow/layers/cacheable_layer.h @@ -9,39 +9,62 @@ #include "flutter/flow/layers/container_layer.h" #include "flutter/flow/layers/layer_raster_cache_item.h" +#include "flutter/flow/raster_cache_util.h" namespace flutter { +class CacheableLayer { + protected: + virtual RasterCacheItem* realize_raster_cache_item() = 0; + virtual void disable_raster_cache_item() = 0; + + friend class AutoCache; +}; + class AutoCache { public: - AutoCache(RasterCacheItem* raster_cache_item, + AutoCache(CacheableLayer& item_provider, PrerollContext* context, - const SkMatrix& matrix); + bool caching_enabled = true); void ShouldNotBeCached() { raster_cache_item_ = nullptr; } ~AutoCache(); private: - inline bool IsCacheEnabled(); RasterCacheItem* raster_cache_item_ = nullptr; PrerollContext* context_ = nullptr; - const SkMatrix matrix_; + SkMatrix matrix_; + + FML_DISALLOW_COPY_ASSIGN_AND_MOVE(AutoCache); }; -class CacheableContainerLayer : public ContainerLayer { +class CacheableContainerLayer : public ContainerLayer, public CacheableLayer { public: explicit CacheableContainerLayer( int layer_cached_threshold = RasterCacheUtil::kMinimumRendersBeforeCachingFilterLayer, - bool can_cache_children = false); + bool can_cache_children = false) + : layer_cache_threshold_(layer_cached_threshold), + can_cache_children_(can_cache_children) {} const LayerRasterCacheItem* raster_cache_item() const { return layer_raster_cache_item_.get(); } + void MarkCanCacheChildren(bool can_cache_children) { + if (layer_raster_cache_item_) { + layer_raster_cache_item_->MarkCanCacheChildren(can_cache_children); + } + } + protected: + RasterCacheItem* realize_raster_cache_item() override; + virtual void disable_raster_cache_item() override; std::unique_ptr layer_raster_cache_item_; + + int layer_cache_threshold_; + bool can_cache_children_; }; } // namespace flutter 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 582ae97fcf0..922c71263e2 100644 --- a/engine/src/flutter/flow/layers/clip_path_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/clip_path_layer_unittests.cc @@ -519,31 +519,74 @@ TEST_F(ClipPathLayerTest, LayerCached) { use_mock_raster_cache(); preroll_context()->state_stack.set_preroll_delegate(initial_transform); - const auto* clip_cache_item = layer->raster_cache_item(); - EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); + EXPECT_EQ(layer->raster_cache_item(), nullptr); layer->Preroll(preroll_context()); + EXPECT_NE(layer->raster_cache_item(), nullptr); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); + EXPECT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::CacheState::kNone); layer->Preroll(preroll_context()); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); + EXPECT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::CacheState::kNone); layer->Preroll(preroll_context()); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); - EXPECT_EQ(clip_cache_item->cache_state(), + EXPECT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::CacheState::kCurrent); DlPaint paint; - EXPECT_TRUE(raster_cache()->Draw(clip_cache_item->GetId().value(), + EXPECT_TRUE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), cache_canvas, &paint)); } +TEST_F(ClipPathLayerTest, NullRasterCacheResetsRasterCacheItem) { + auto path1 = SkPath().addRect({10, 10, 30, 30}); + auto mock1 = MockLayer::MakeOpacityCompatible(path1); + auto layer_clip = SkPath() + .addRect(SkRect::MakeLTRB(5, 5, 25, 25)) + .addOval(SkRect::MakeLTRB(20, 20, 40, 50)); + auto layer = + std::make_shared(layer_clip, Clip::antiAliasWithSaveLayer); + layer->Add(mock1); + + ASSERT_EQ(layer->raster_cache_item(), nullptr); + + layer->Preroll(preroll_context()); + ASSERT_EQ(layer->raster_cache_item(), nullptr); + + use_mock_raster_cache(); + + int limit = RasterCacheUtil::kMinimumRendersBeforeCachingFilterLayer; + for (int i = 1; i < limit; i++) { + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::kNone); + ASSERT_FALSE( + layer->raster_cache_item()->TryToPrepareRasterCache(paint_context())); + } + + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::kCurrent); + ASSERT_TRUE( + layer->raster_cache_item()->TryToPrepareRasterCache(paint_context())); + + use_null_raster_cache(); + + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::kNone); +} + TEST_F(ClipPathLayerTest, EmptyClipDoesNotCullPlatformView) { const SkPoint view_offset = SkPoint::Make(0.0f, 0.0f); const SkSize view_size = SkSize::Make(8.0f, 8.0f); 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 55359b378a7..2614ddedaea 100644 --- a/engine/src/flutter/flow/layers/clip_rect_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/clip_rect_layer_unittests.cc @@ -499,30 +499,71 @@ TEST_F(ClipRectLayerTest, LayerCached) { use_mock_raster_cache(); preroll_context()->state_stack.set_preroll_delegate(initial_transform); - - const auto* clip_cache_item = layer->raster_cache_item(); + EXPECT_EQ(layer->raster_cache_item(), nullptr); layer->Preroll(preroll_context()); + EXPECT_NE(layer->raster_cache_item(), nullptr); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); + EXPECT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::CacheState::kNone); layer->Preroll(preroll_context()); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); + EXPECT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::CacheState::kNone); layer->Preroll(preroll_context()); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); - EXPECT_EQ(clip_cache_item->cache_state(), + EXPECT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::CacheState::kCurrent); DlPaint paint; - EXPECT_TRUE(raster_cache()->Draw(clip_cache_item->GetId().value(), + EXPECT_TRUE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), cache_canvas, &paint)); } +TEST_F(ClipRectLayerTest, NullRasterCacheResetsRasterCacheItem) { + auto path1 = SkPath().addRect({10, 10, 30, 30}); + auto mock1 = MockLayer::MakeOpacityCompatible(path1); + SkRect clip_rect = SkRect::MakeWH(500, 500); + auto layer = + std::make_shared(clip_rect, Clip::antiAliasWithSaveLayer); + layer->Add(mock1); + + ASSERT_EQ(layer->raster_cache_item(), nullptr); + + layer->Preroll(preroll_context()); + ASSERT_EQ(layer->raster_cache_item(), nullptr); + + use_mock_raster_cache(); + + int limit = RasterCacheUtil::kMinimumRendersBeforeCachingFilterLayer; + for (int i = 1; i < limit; i++) { + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::kNone); + ASSERT_FALSE( + layer->raster_cache_item()->TryToPrepareRasterCache(paint_context())); + } + + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::kCurrent); + ASSERT_TRUE( + layer->raster_cache_item()->TryToPrepareRasterCache(paint_context())); + + use_null_raster_cache(); + + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::kNone); +} + TEST_F(ClipRectLayerTest, EmptyClipDoesNotCullPlatformView) { const SkPoint view_offset = SkPoint::Make(0.0f, 0.0f); const SkSize view_size = SkSize::Make(8.0f, 8.0f); 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 98d6493ba7b..f0f43954be4 100644 --- a/engine/src/flutter/flow/layers/clip_rrect_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/clip_rrect_layer_unittests.cc @@ -512,29 +512,72 @@ TEST_F(ClipRRectLayerTest, LayerCached) { use_mock_raster_cache(); preroll_context()->state_stack.set_preroll_delegate(initial_transform); - - const auto* clip_cache_item = layer->raster_cache_item(); + EXPECT_EQ(layer->raster_cache_item(), nullptr); layer->Preroll(preroll_context()); + EXPECT_NE(layer->raster_cache_item(), nullptr); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); + EXPECT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::CacheState::kNone); layer->Preroll(preroll_context()); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); + EXPECT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::CacheState::kNone); layer->Preroll(preroll_context()); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); - EXPECT_EQ(clip_cache_item->cache_state(), + EXPECT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::CacheState::kCurrent); - EXPECT_TRUE(raster_cache()->Draw(clip_cache_item->GetId().value(), + EXPECT_TRUE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), cache_canvas, &paint)); } +TEST_F(ClipRRectLayerTest, NullRasterCacheResetsRasterCacheItem) { + auto path1 = SkPath().addRect({10, 10, 30, 30}); + DlPaint paint = DlPaint(); + auto mock1 = MockLayer::MakeOpacityCompatible(path1); + SkRect clip_rect = SkRect::MakeWH(500, 500); + SkRRect clip_rrect = SkRRect::MakeRectXY(clip_rect, 20, 20); + auto layer = std::make_shared(clip_rrect, + Clip::antiAliasWithSaveLayer); + layer->Add(mock1); + + ASSERT_EQ(layer->raster_cache_item(), nullptr); + + layer->Preroll(preroll_context()); + ASSERT_EQ(layer->raster_cache_item(), nullptr); + + use_mock_raster_cache(); + + int limit = RasterCacheUtil::kMinimumRendersBeforeCachingFilterLayer; + for (int i = 1; i < limit; i++) { + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::kNone); + ASSERT_FALSE( + layer->raster_cache_item()->TryToPrepareRasterCache(paint_context())); + } + + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::kCurrent); + ASSERT_TRUE( + layer->raster_cache_item()->TryToPrepareRasterCache(paint_context())); + + use_null_raster_cache(); + + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::kNone); +} + TEST_F(ClipRRectLayerTest, NoSaveLayerShouldNotCache) { auto path1 = SkPath().addRect({10, 10, 30, 30}); @@ -551,24 +594,23 @@ TEST_F(ClipRRectLayerTest, NoSaveLayerShouldNotCache) { use_mock_raster_cache(); preroll_context()->state_stack.set_preroll_delegate(initial_transform); - - const auto* clip_cache_item = layer->raster_cache_item(); + EXPECT_EQ(layer->raster_cache_item(), nullptr); layer->Preroll(preroll_context()); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); + EXPECT_EQ(layer->raster_cache_item(), nullptr); layer->Preroll(preroll_context()); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); + EXPECT_EQ(layer->raster_cache_item(), nullptr); layer->Preroll(preroll_context()); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); + EXPECT_EQ(layer->raster_cache_item(), nullptr); } TEST_F(ClipRRectLayerTest, EmptyClipDoesNotCullPlatformView) { diff --git a/engine/src/flutter/flow/layers/clip_shape_layer.h b/engine/src/flutter/flow/layers/clip_shape_layer.h index d33267853e3..3af0b3f61b7 100644 --- a/engine/src/flutter/flow/layers/clip_shape_layer.h +++ b/engine/src/flutter/flow/layers/clip_shape_layer.h @@ -48,9 +48,7 @@ class ClipShapeLayer : public CacheableContainerLayer { // We can use the raster_cache for children only when the use_save_layer is // true so if use_save_layer is false we pass the layer_raster_item is // nullptr which mean we don't do raster cache logic. - AutoCache cache = - AutoCache(uses_save_layer ? layer_raster_cache_item_.get() : nullptr, - context, context->state_stack.transform_3x3()); + AutoCache cache(*this, context, uses_save_layer); Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create( diff --git a/engine/src/flutter/flow/layers/color_filter_layer.cc b/engine/src/flutter/flow/layers/color_filter_layer.cc index 056efa7472d..aa25727f706 100644 --- a/engine/src/flutter/flow/layers/color_filter_layer.cc +++ b/engine/src/flutter/flow/layers/color_filter_layer.cc @@ -39,8 +39,7 @@ void ColorFilterLayer::Diff(DiffContext* context, const Layer* old_layer) { void ColorFilterLayer::Preroll(PrerollContext* context) { Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context); - AutoCache cache = AutoCache(layer_raster_cache_item_.get(), context, - context->state_stack.transform_3x3()); + AutoCache cache(*this, context); ContainerLayer::Preroll(context); 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 1ec1f35bd0b..5ecc790d5a4 100644 --- a/engine/src/flutter/flow/layers/color_filter_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/color_filter_layer_unittests.cc @@ -271,27 +271,25 @@ TEST_F(ColorFilterLayerTest, CacheChild) { other_canvas.Transform(other_transform); use_mock_raster_cache(); - const auto* cacheable_color_filter_item = layer->raster_cache_item(); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_EQ(cacheable_color_filter_item->cache_state(), - RasterCacheItem::CacheState::kNone); - EXPECT_FALSE(cacheable_color_filter_item->GetId().has_value()); + EXPECT_EQ(layer->raster_cache_item(), nullptr); preroll_context()->state_stack.set_preroll_delegate(initial_transform); layer->Preroll(preroll_context()); + EXPECT_NE(layer->raster_cache_item(), nullptr); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); - EXPECT_EQ(cacheable_color_filter_item->cache_state(), + EXPECT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::CacheState::kChildren); EXPECT_EQ( - cacheable_color_filter_item->GetId().value(), + layer->raster_cache_item()->GetId().value(), RasterCacheKeyID(RasterCacheKeyID::LayerChildrenIds(layer.get()).value(), RasterCacheKeyType::kLayerChildren)); - EXPECT_FALSE(raster_cache()->Draw( - cacheable_color_filter_item->GetId().value(), other_canvas, &paint)); - EXPECT_TRUE(raster_cache()->Draw(cacheable_color_filter_item->GetId().value(), + EXPECT_FALSE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), + other_canvas, &paint)); + EXPECT_TRUE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), cache_canvas, &paint)); } @@ -317,30 +315,65 @@ TEST_F(ColorFilterLayerTest, CacheChildren) { use_mock_raster_cache(); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - const auto* cacheable_color_filter_item = layer->raster_cache_item(); - EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - - EXPECT_EQ(cacheable_color_filter_item->cache_state(), - RasterCacheItem::CacheState::kNone); - EXPECT_FALSE(cacheable_color_filter_item->GetId().has_value()); + EXPECT_EQ(layer->raster_cache_item(), nullptr); preroll_context()->state_stack.set_preroll_delegate(initial_transform); layer->Preroll(preroll_context()); + EXPECT_NE(layer->raster_cache_item(), nullptr); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); - EXPECT_EQ(cacheable_color_filter_item->cache_state(), + EXPECT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::CacheState::kChildren); EXPECT_EQ( - cacheable_color_filter_item->GetId().value(), + layer->raster_cache_item()->GetId().value(), RasterCacheKeyID(RasterCacheKeyID::LayerChildrenIds(layer.get()).value(), RasterCacheKeyType::kLayerChildren)); - EXPECT_FALSE(raster_cache()->Draw( - cacheable_color_filter_item->GetId().value(), other_canvas, &paint)); - EXPECT_TRUE(raster_cache()->Draw(cacheable_color_filter_item->GetId().value(), + EXPECT_FALSE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), + other_canvas, &paint)); + EXPECT_TRUE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), cache_canvas, &paint)); } +TEST_F(ColorFilterLayerTest, NullRasterCacheResetsRasterCacheItem) { + auto layer_filter = DlSrgbToLinearGammaColorFilter::instance; + const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); + DlPaint paint = DlPaint(); + auto mock_layer = std::make_shared(child_path); + auto layer = std::make_shared(layer_filter); + layer->Add(mock_layer); + + ASSERT_EQ(layer->raster_cache_item(), nullptr); + + layer->Preroll(preroll_context()); + ASSERT_EQ(layer->raster_cache_item(), nullptr); + + use_mock_raster_cache(); + + int limit = RasterCacheUtil::kMinimumRendersBeforeCachingFilterLayer; + for (int i = 1; i < limit; i++) { + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::kChildren); + ASSERT_TRUE( + layer->raster_cache_item()->TryToPrepareRasterCache(paint_context())); + } + + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::kCurrent); + ASSERT_TRUE( + layer->raster_cache_item()->TryToPrepareRasterCache(paint_context())); + + use_null_raster_cache(); + + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::kNone); +} + TEST_F(ColorFilterLayerTest, CacheColorFilterLayerSelf) { auto layer_filter = DlSrgbToLinearGammaColorFilter::instance; auto initial_transform = SkMatrix::Translate(50.0, 25.5); @@ -362,25 +395,28 @@ TEST_F(ColorFilterLayerTest, CacheColorFilterLayerSelf) { use_mock_raster_cache(); preroll_context()->state_stack.set_preroll_delegate(initial_transform); - const auto* cacheable_color_filter_item = layer->raster_cache_item(); + EXPECT_EQ(layer->raster_cache_item(), nullptr); // frame 1. layer->Preroll(preroll_context()); + EXPECT_NE(layer->raster_cache_item(), nullptr); layer->Paint(paint_context()); // frame 2. layer->Preroll(preroll_context()); + EXPECT_NE(layer->raster_cache_item(), nullptr); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); // ColorFilterLayer default cache children. - EXPECT_EQ(cacheable_color_filter_item->cache_state(), + EXPECT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::CacheState::kChildren); - EXPECT_TRUE(raster_cache()->Draw(cacheable_color_filter_item->GetId().value(), + EXPECT_TRUE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), cache_canvas, &paint)); - EXPECT_FALSE(raster_cache()->Draw( - cacheable_color_filter_item->GetId().value(), other_canvas, &paint)); + EXPECT_FALSE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), + other_canvas, &paint)); layer->Paint(paint_context()); // frame 3. layer->Preroll(preroll_context()); + EXPECT_NE(layer->raster_cache_item(), nullptr); layer->Paint(paint_context()); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); @@ -389,14 +425,14 @@ TEST_F(ColorFilterLayerTest, CacheColorFilterLayerSelf) { EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)2); // ColorFilterLayer default cache itself. - EXPECT_EQ(cacheable_color_filter_item->cache_state(), + EXPECT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::CacheState::kCurrent); - EXPECT_EQ(cacheable_color_filter_item->GetId(), + EXPECT_EQ(layer->raster_cache_item()->GetId(), RasterCacheKeyID(layer->unique_id(), RasterCacheKeyType::kLayer)); - EXPECT_TRUE(raster_cache()->Draw(cacheable_color_filter_item->GetId().value(), + EXPECT_TRUE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), cache_canvas, &paint)); - EXPECT_FALSE(raster_cache()->Draw( - cacheable_color_filter_item->GetId().value(), other_canvas, &paint)); + EXPECT_FALSE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), + other_canvas, &paint)); } TEST_F(ColorFilterLayerTest, OpacityInheritance) { diff --git a/engine/src/flutter/flow/layers/display_list_layer.cc b/engine/src/flutter/flow/layers/display_list_layer.cc index 543af526be6..03eaf0c6722 100644 --- a/engine/src/flutter/flow/layers/display_list_layer.cc +++ b/engine/src/flutter/flow/layers/display_list_layer.cc @@ -19,11 +19,26 @@ DisplayListLayer::DisplayListLayer(const SkPoint& offset, sk_sp display_list, bool is_complex, bool will_change) - : offset_(offset), display_list_(std::move(display_list)) { + : offset_(offset), + display_list_(std::move(display_list)), + is_complex_(is_complex), + will_change_(will_change) { if (display_list_) { bounds_ = display_list_->bounds().makeOffset(offset_.x(), offset_.y()); + } +} + +RasterCacheItem* DisplayListLayer::realize_raster_cache_item() { + if (!display_list_raster_cache_item_) { display_list_raster_cache_item_ = DisplayListRasterCacheItem::Make( - display_list_, offset_, is_complex, will_change); + display_list_, offset_, is_complex_, will_change_); + } + return display_list_raster_cache_item_.get(); +} + +void DisplayListLayer::disable_raster_cache_item() { + if (display_list_raster_cache_item_) { + display_list_raster_cache_item_->reset_cache_state(); } } @@ -95,8 +110,7 @@ bool DisplayListLayer::Compare(DiffContext::Statistics& statistics, void DisplayListLayer::Preroll(PrerollContext* context) { DisplayList* disp_list = display_list(); - AutoCache cache = AutoCache(display_list_raster_cache_item_.get(), context, - context->state_stack.transform_3x3()); + AutoCache cache(*this, context); if (disp_list->can_apply_group_opacity()) { context->renderable_state_flags = LayerStateStack::kCallerCanApplyOpacity; } diff --git a/engine/src/flutter/flow/layers/display_list_layer.h b/engine/src/flutter/flow/layers/display_list_layer.h index d7efe4e8ee0..88243cc8f9f 100644 --- a/engine/src/flutter/flow/layers/display_list_layer.h +++ b/engine/src/flutter/flow/layers/display_list_layer.h @@ -8,13 +8,14 @@ #include #include "flutter/display_list/display_list.h" +#include "flutter/flow/layers/cacheable_layer.h" #include "flutter/flow/layers/display_list_raster_cache_item.h" #include "flutter/flow/layers/layer.h" #include "flutter/flow/raster_cache_item.h" namespace flutter { -class DisplayListLayer : public Layer { +class DisplayListLayer : public Layer, public CacheableLayer { public: static constexpr size_t kMaxBytesToCompare = 10000; @@ -47,12 +48,18 @@ class DisplayListLayer : public Layer { } private: + RasterCacheItem* realize_raster_cache_item() override; + void disable_raster_cache_item() override; std::unique_ptr display_list_raster_cache_item_; + friend class AutoCache; + SkPoint offset_; SkRect bounds_; sk_sp display_list_; + bool is_complex_; + bool will_change_; static bool Compare(DiffContext::Statistics& statistics, const DisplayListLayer* l1, diff --git a/engine/src/flutter/flow/layers/display_list_layer_unittests.cc b/engine/src/flutter/flow/layers/display_list_layer_unittests.cc index 2791577666b..286021e028a 100644 --- a/engine/src/flutter/flow/layers/display_list_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/display_list_layer_unittests.cc @@ -474,6 +474,46 @@ TEST_F(DisplayListLayerTest, NoLayerTreeSnapshotsWhenDisabledByDefault) { EXPECT_EQ(0u, snapshot_store.Size()); } +TEST_F(DisplayListLayerTest, NullRasterCacheResetsRasterCacheItem) { + const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f); + const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + DisplayListBuilder builder; + builder.DrawRect(picture_bounds, DlPaint()); + auto display_list = builder.Build(); + auto layer = std::make_shared(layer_offset, display_list, + true, false); + + ASSERT_EQ(layer->raster_cache_item(), nullptr); + + layer->Preroll(preroll_context()); + ASSERT_EQ(layer->raster_cache_item(), nullptr); + + use_mock_raster_cache(); + + size_t limit = raster_cache()->access_threshold(); + for (size_t i = 0; i < limit; i++) { + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::kNone); + ASSERT_FALSE( + layer->raster_cache_item()->TryToPrepareRasterCache(paint_context())); + } + + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::kCurrent); + ASSERT_TRUE( + layer->raster_cache_item()->TryToPrepareRasterCache(paint_context())); + + use_null_raster_cache(); + + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::kNone); +} + TEST_F(DisplayListLayerTest, DisplayListAccessCountDependsOnVisibility) { const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f); const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); @@ -485,9 +525,10 @@ TEST_F(DisplayListLayerTest, DisplayListAccessCountDependsOnVisibility) { auto layer = std::make_shared(layer_offset, display_list, true, false); - auto raster_cache_item = layer->raster_cache_item(); use_mock_raster_cache(); + EXPECT_EQ(layer->raster_cache_item(), nullptr); + // First Preroll the DisplayListLayer a few times where it does not intersect // the cull rect. No caching progress should occur during this time, the // access_count should remain 0 because the DisplayList was never "visible". @@ -496,16 +537,19 @@ TEST_F(DisplayListLayerTest, DisplayListAccessCountDependsOnVisibility) { for (int i = 0; i < 10; i++) { preroll_context()->raster_cached_entries->clear(); layer->Preroll(preroll_context()); - ASSERT_EQ(raster_cache_item->cache_state(), RasterCacheItem::kNone); - ASSERT_TRUE(raster_cache_item->GetId().has_value()); + EXPECT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::kNone); + ASSERT_TRUE(layer->raster_cache_item()->GetId().has_value()); ASSERT_EQ(preroll_context()->raster_cache->GetAccessCount( - raster_cache_item->GetId().value(), SkMatrix::I()), + layer->raster_cache_item()->GetId().value(), SkMatrix::I()), 0); ASSERT_EQ(preroll_context()->raster_cached_entries->size(), size_t(1)); ASSERT_EQ(preroll_context()->raster_cache->EstimatePictureCacheByteSize(), size_t(0)); - ASSERT_FALSE(raster_cache_item->TryToPrepareRasterCache(paint_context())); - ASSERT_FALSE(raster_cache_item->Draw(paint_context(), nullptr)); + ASSERT_FALSE( + layer->raster_cache_item()->TryToPrepareRasterCache(paint_context())); + ASSERT_FALSE(layer->raster_cache_item()->Draw(paint_context(), nullptr)); } // Next Preroll the DisplayListLayer once where it does intersect @@ -516,16 +560,18 @@ TEST_F(DisplayListLayerTest, DisplayListAccessCountDependsOnVisibility) { preroll_context()->state_stack.set_preroll_delegate(hit_cull_rect); preroll_context()->raster_cached_entries->clear(); layer->Preroll(preroll_context()); - ASSERT_EQ(raster_cache_item->cache_state(), RasterCacheItem::kNone); - ASSERT_TRUE(raster_cache_item->GetId().has_value()); + EXPECT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::kNone); + ASSERT_TRUE(layer->raster_cache_item()->GetId().has_value()); ASSERT_EQ(preroll_context()->raster_cache->GetAccessCount( - raster_cache_item->GetId().value(), SkMatrix::I()), + layer->raster_cache_item()->GetId().value(), SkMatrix::I()), 1); ASSERT_EQ(preroll_context()->raster_cached_entries->size(), size_t(1)); ASSERT_EQ(preroll_context()->raster_cache->EstimatePictureCacheByteSize(), size_t(0)); - ASSERT_FALSE(raster_cache_item->TryToPrepareRasterCache(paint_context())); - ASSERT_FALSE(raster_cache_item->Draw(paint_context(), nullptr)); + ASSERT_FALSE( + layer->raster_cache_item()->TryToPrepareRasterCache(paint_context())); + ASSERT_FALSE(layer->raster_cache_item()->Draw(paint_context(), nullptr)); // Now we can Preroll the DisplayListLayer again with a cull rect that // it does not intersect and it should continue to count these operations @@ -536,16 +582,19 @@ TEST_F(DisplayListLayerTest, DisplayListAccessCountDependsOnVisibility) { for (int i = 0; i < 10; i++) { preroll_context()->raster_cached_entries->clear(); layer->Preroll(preroll_context()); - ASSERT_EQ(raster_cache_item->cache_state(), RasterCacheItem::kNone); - ASSERT_TRUE(raster_cache_item->GetId().has_value()); + EXPECT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::kNone); + ASSERT_TRUE(layer->raster_cache_item()->GetId().has_value()); ASSERT_EQ(preroll_context()->raster_cache->GetAccessCount( - raster_cache_item->GetId().value(), SkMatrix::I()), + layer->raster_cache_item()->GetId().value(), SkMatrix::I()), i + 2); ASSERT_EQ(preroll_context()->raster_cached_entries->size(), size_t(1)); ASSERT_EQ(preroll_context()->raster_cache->EstimatePictureCacheByteSize(), size_t(0)); - ASSERT_FALSE(raster_cache_item->TryToPrepareRasterCache(paint_context())); - ASSERT_FALSE(raster_cache_item->Draw(paint_context(), nullptr)); + ASSERT_FALSE( + layer->raster_cache_item()->TryToPrepareRasterCache(paint_context())); + ASSERT_FALSE(layer->raster_cache_item()->Draw(paint_context(), nullptr)); } // Finally Preroll the DisplayListLayer again where it does intersect @@ -556,18 +605,21 @@ TEST_F(DisplayListLayerTest, DisplayListAccessCountDependsOnVisibility) { preroll_context()->state_stack.set_preroll_delegate(hit_cull_rect); preroll_context()->raster_cached_entries->clear(); layer->Preroll(preroll_context()); - ASSERT_EQ(raster_cache_item->cache_state(), RasterCacheItem::kCurrent); - ASSERT_TRUE(raster_cache_item->GetId().has_value()); + EXPECT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::kCurrent); + ASSERT_TRUE(layer->raster_cache_item()->GetId().has_value()); ASSERT_EQ(preroll_context()->raster_cache->GetAccessCount( - raster_cache_item->GetId().value(), SkMatrix::I()), + layer->raster_cache_item()->GetId().value(), SkMatrix::I()), 12); ASSERT_EQ(preroll_context()->raster_cached_entries->size(), size_t(1)); ASSERT_EQ(preroll_context()->raster_cache->EstimatePictureCacheByteSize(), size_t(0)); - ASSERT_TRUE(raster_cache_item->TryToPrepareRasterCache(paint_context())); + ASSERT_TRUE( + layer->raster_cache_item()->TryToPrepareRasterCache(paint_context())); ASSERT_GT(preroll_context()->raster_cache->EstimatePictureCacheByteSize(), size_t(0)); - ASSERT_TRUE(raster_cache_item->Draw(paint_context(), nullptr)); + ASSERT_TRUE(layer->raster_cache_item()->Draw(paint_context(), nullptr)); } TEST_F(DisplayListLayerTest, OverflowCachedDisplayListOpacityInheritance) { diff --git a/engine/src/flutter/flow/layers/image_filter_layer.cc b/engine/src/flutter/flow/layers/image_filter_layer.cc index 834df45ce85..27d8abe2314 100644 --- a/engine/src/flutter/flow/layers/image_filter_layer.cc +++ b/engine/src/flutter/flow/layers/image_filter_layer.cc @@ -56,8 +56,7 @@ void ImageFilterLayer::Preroll(PrerollContext* context) { Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context); - AutoCache cache = AutoCache(layer_raster_cache_item_.get(), context, - context->state_stack.transform_3x3()); + AutoCache cache(*this, context); SkRect child_bounds = SkRect::MakeEmpty(); @@ -87,13 +86,9 @@ void ImageFilterLayer::Preroll(PrerollContext* context) { // CacheChildren only when the transformed_filter_ doesn't equal null. // So in here we reset the LayerRasterCacheItem cache state. - layer_raster_cache_item_->MarkNotCacheChildren(); - transformed_filter_ = filter_->makeWithLocalMatrix(context->state_stack.transform_3x3()); - if (transformed_filter_) { - layer_raster_cache_item_->MarkCacheChildren(); - } + MarkCanCacheChildren(transformed_filter_ != nullptr); } void ImageFilterLayer::Paint(PaintContext& context) const { 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 ee8f08c8491..64046f5461f 100644 --- a/engine/src/flutter/flow/layers/image_filter_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/image_filter_layer_unittests.cc @@ -367,27 +367,26 @@ TEST_F(ImageFilterLayerTest, CacheChild) { DlPaint paint; use_mock_raster_cache(); - const auto* cacheable_image_filter_item = layer->raster_cache_item(); + EXPECT_EQ(layer->raster_cache_item(), nullptr); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - // ImageFilterLayer default cache itself. - EXPECT_EQ(cacheable_image_filter_item->cache_state(), - RasterCacheItem::CacheState::kNone); - EXPECT_FALSE(cacheable_image_filter_item->Draw(paint_context(), &paint)); + EXPECT_EQ(cacheable_items().size(), 0u); preroll_context()->state_stack.set_preroll_delegate(initial_transform); layer->Preroll(preroll_context()); + EXPECT_NE(layer->raster_cache_item(), nullptr); + EXPECT_EQ(cacheable_items().size(), 1u); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); // The layer_cache_item's strategy is Children, mean we will must cache // his children - EXPECT_EQ(cacheable_image_filter_item->cache_state(), + EXPECT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::CacheState::kChildren); - EXPECT_TRUE(raster_cache()->Draw(cacheable_image_filter_item->GetId().value(), + EXPECT_TRUE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), cache_canvas, &paint)); - EXPECT_FALSE(raster_cache()->Draw( - cacheable_image_filter_item->GetId().value(), other_canvas, &paint)); + EXPECT_FALSE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), + other_canvas, &paint)); } TEST_F(ImageFilterLayerTest, CacheChildren) { @@ -413,30 +412,27 @@ TEST_F(ImageFilterLayerTest, CacheChildren) { use_mock_raster_cache(); - const auto* cacheable_image_filter_item = layer->raster_cache_item(); + EXPECT_EQ(layer->raster_cache_item(), nullptr); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - // ImageFilterLayer default cache itself. - EXPECT_EQ(cacheable_image_filter_item->cache_state(), - RasterCacheItem::CacheState::kNone); - EXPECT_FALSE(cacheable_image_filter_item->Draw(paint_context(), &paint)); - preroll_context()->state_stack.set_preroll_delegate(initial_transform); layer->Preroll(preroll_context()); + EXPECT_NE(layer->raster_cache_item(), nullptr); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); // The layer_cache_item's strategy is Children, mean we will must cache his // children - EXPECT_EQ(cacheable_image_filter_item->cache_state(), + EXPECT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::CacheState::kChildren); - EXPECT_TRUE(raster_cache()->Draw(cacheable_image_filter_item->GetId().value(), + EXPECT_TRUE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), cache_canvas, &paint)); - EXPECT_FALSE(raster_cache()->Draw( - cacheable_image_filter_item->GetId().value(), other_canvas, &paint)); + EXPECT_FALSE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), + other_canvas, &paint)); layer->Preroll(preroll_context()); + EXPECT_NE(layer->raster_cache_item(), nullptr); SkRect children_bounds = child_path1.getBounds(); children_bounds.join(child_path2.getBounds()); @@ -459,7 +455,7 @@ TEST_F(ImageFilterLayerTest, CacheChildren) { expected_builder.Transform(snapped_matrix); DlPaint dl_paint; dl_paint.setImageFilter(transformed_filter.get()); - raster_cache()->Draw(cacheable_image_filter_item->GetId().value(), + raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), expected_builder, &dl_paint); } expected_builder.Restore(); @@ -468,6 +464,45 @@ TEST_F(ImageFilterLayerTest, CacheChildren) { EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); } +TEST_F(ImageFilterLayerTest, NullRasterCacheResetsRasterCacheItem) { + auto dl_image_filter = std::make_shared( + SkMatrix(), DlImageSampling::kMipmapLinear); + const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); + auto mock_layer = std::make_shared(child_path); + auto layer = std::make_shared(dl_image_filter); + layer->Add(mock_layer); + + ASSERT_EQ(layer->raster_cache_item(), nullptr); + + layer->Preroll(preroll_context()); + ASSERT_EQ(layer->raster_cache_item(), nullptr); + + use_mock_raster_cache(); + + int limit = RasterCacheUtil::kMinimumRendersBeforeCachingFilterLayer; + for (int i = 1; i < limit; i++) { + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::kChildren); + ASSERT_TRUE( + layer->raster_cache_item()->TryToPrepareRasterCache(paint_context())); + } + + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::kCurrent); + ASSERT_TRUE( + layer->raster_cache_item()->TryToPrepareRasterCache(paint_context())); + + use_null_raster_cache(); + + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::kNone); +} + TEST_F(ImageFilterLayerTest, CacheImageFilterLayerSelf) { auto dl_image_filter = std::make_shared( SkMatrix(), DlImageSampling::kMipmapLinear); @@ -495,9 +530,10 @@ TEST_F(ImageFilterLayerTest, CacheImageFilterLayerSelf) { use_mock_raster_cache(); preroll_context()->state_stack.set_preroll_delegate(initial_transform); - const auto* cacheable_image_filter_item = layer->raster_cache_item(); + EXPECT_EQ(layer->raster_cache_item(), nullptr); // frame 1. layer->Preroll(preroll_context()); + EXPECT_NE(layer->raster_cache_item(), nullptr); layer->Paint(display_list_paint_context()); { @@ -537,14 +573,14 @@ TEST_F(ImageFilterLayerTest, CacheImageFilterLayerSelf) { EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)2); // ImageFilterLayer default cache itself. - EXPECT_EQ(cacheable_image_filter_item->cache_state(), + EXPECT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::CacheState::kCurrent); - EXPECT_EQ(cacheable_image_filter_item->GetId(), + EXPECT_EQ(layer->raster_cache_item()->GetId(), RasterCacheKeyID(layer->unique_id(), RasterCacheKeyType::kLayer)); - EXPECT_TRUE(raster_cache()->Draw(cacheable_image_filter_item->GetId().value(), + EXPECT_TRUE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), cache_canvas, &paint)); - EXPECT_FALSE(raster_cache()->Draw( - cacheable_image_filter_item->GetId().value(), other_canvas, &paint)); + EXPECT_FALSE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), + other_canvas, &paint)); layer->Preroll(preroll_context()); @@ -556,7 +592,7 @@ TEST_F(ImageFilterLayerTest, CacheImageFilterLayerSelf) { expected_builder.Save(); { EXPECT_TRUE( - raster_cache()->Draw(cacheable_image_filter_item->GetId().value(), + raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), expected_builder, nullptr)); } expected_builder.Restore(); diff --git a/engine/src/flutter/flow/layers/layer_raster_cache_item.h b/engine/src/flutter/flow/layers/layer_raster_cache_item.h index 53e9a65237a..b383ac3899b 100644 --- a/engine/src/flutter/flow/layers/layer_raster_cache_item.h +++ b/engine/src/flutter/flow/layers/layer_raster_cache_item.h @@ -45,9 +45,9 @@ class LayerRasterCacheItem : public RasterCacheItem { bool TryToPrepareRasterCache(const PaintContext& context, bool parent_cached = false) const override; - void MarkCacheChildren() { can_cache_children_ = true; } - - void MarkNotCacheChildren() { can_cache_children_ = false; } + void MarkCanCacheChildren(bool can_cache_children) { + can_cache_children_ = can_cache_children; + } bool IsCacheChildren() const { return cache_state_ == CacheState::kChildren; } diff --git a/engine/src/flutter/flow/layers/opacity_layer.cc b/engine/src/flutter/flow/layers/opacity_layer.cc index 8b55dff69aa..20843bef5cb 100644 --- a/engine/src/flutter/flow/layers/opacity_layer.cc +++ b/engine/src/flutter/flow/layers/opacity_layer.cc @@ -42,8 +42,7 @@ void OpacityLayer::Preroll(PrerollContext* context) { mutator.translate(offset_); mutator.applyOpacity(SkRect(), DlColor::toOpacity(alpha_)); - AutoCache auto_cache = AutoCache(layer_raster_cache_item_.get(), context, - context->state_stack.transform_3x3()); + AutoCache auto_cache(*this, context); Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context); @@ -78,7 +77,7 @@ void OpacityLayer::Paint(PaintContext& context) const { mutator.applyOpacity(child_paint_bounds(), opacity()); - if (!children_can_accept_opacity()) { + if (context.raster_cache && !children_can_accept_opacity()) { DlPaint paint; if (layer_raster_cache_item_->Draw(context, context.state_stack.fill(paint))) { diff --git a/engine/src/flutter/flow/layers/opacity_layer_unittests.cc b/engine/src/flutter/flow/layers/opacity_layer_unittests.cc index 71ccdc15c84..3a64fc31ca4 100644 --- a/engine/src/flutter/flow/layers/opacity_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/opacity_layer_unittests.cc @@ -103,29 +103,24 @@ TEST_F(OpacityLayerTest, CacheChild) { use_mock_raster_cache(); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - - const auto* cacheable_opacity_item = layer->raster_cache_item(); - - EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_EQ(cacheable_opacity_item->cache_state(), - RasterCacheItem::CacheState::kNone); - EXPECT_FALSE(cacheable_opacity_item->GetId().has_value()); + EXPECT_EQ(layer->raster_cache_item(), nullptr); preroll_context()->state_stack.set_preroll_delegate(initial_transform); layer->Preroll(preroll_context()); + EXPECT_NE(layer->raster_cache_item(), nullptr); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); - EXPECT_EQ(cacheable_opacity_item->cache_state(), + EXPECT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::CacheState::kChildren); EXPECT_EQ( - cacheable_opacity_item->GetId().value(), + layer->raster_cache_item()->GetId().value(), RasterCacheKeyID(RasterCacheKeyID::LayerChildrenIds(layer.get()).value(), RasterCacheKeyType::kLayerChildren)); - EXPECT_FALSE(raster_cache()->Draw(cacheable_opacity_item->GetId().value(), + EXPECT_FALSE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), other_canvas, &paint)); - EXPECT_TRUE(raster_cache()->Draw(cacheable_opacity_item->GetId().value(), + EXPECT_TRUE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), cache_canvas, &paint)); } @@ -152,32 +147,61 @@ TEST_F(OpacityLayerTest, CacheChildren) { use_mock_raster_cache(); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - - const auto* cacheable_opacity_item = layer->raster_cache_item(); - - EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_EQ(cacheable_opacity_item->cache_state(), - RasterCacheItem::CacheState::kNone); - EXPECT_FALSE(cacheable_opacity_item->GetId().has_value()); + EXPECT_EQ(layer->raster_cache_item(), nullptr); preroll_context()->state_stack.set_preroll_delegate(initial_transform); layer->Preroll(preroll_context()); + EXPECT_NE(layer->raster_cache_item(), nullptr); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); - EXPECT_EQ(cacheable_opacity_item->cache_state(), + EXPECT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::CacheState::kChildren); EXPECT_EQ( - cacheable_opacity_item->GetId().value(), + layer->raster_cache_item()->GetId().value(), RasterCacheKeyID(RasterCacheKeyID::LayerChildrenIds(layer.get()).value(), RasterCacheKeyType::kLayerChildren)); - EXPECT_FALSE(raster_cache()->Draw(cacheable_opacity_item->GetId().value(), + EXPECT_FALSE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), other_canvas, &paint)); - EXPECT_TRUE(raster_cache()->Draw(cacheable_opacity_item->GetId().value(), + EXPECT_TRUE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), cache_canvas, &paint)); } +TEST_F(OpacityLayerTest, NullRasterCacheResetsRasterCacheItem) { + const SkAlpha alpha_half = 255 / 2; + const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); + auto mock_layer = std::make_shared(child_path); + mock_layer->set_fake_opacity_compatible(false); + auto layer = + std::make_shared(alpha_half, SkPoint::Make(0.0f, 0.0f)); + layer->Add(mock_layer); + + ASSERT_EQ(layer->raster_cache_item(), nullptr); + + layer->Preroll(preroll_context()); + ASSERT_EQ(layer->raster_cache_item(), nullptr); + + use_mock_raster_cache(); + + // OpacityLayer will never cache itself, only its children + int limit = 10 * RasterCacheUtil::kMinimumRendersBeforeCachingFilterLayer; + for (int i = 1; i < limit; i++) { + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::kChildren); + ASSERT_TRUE( + layer->raster_cache_item()->TryToPrepareRasterCache(paint_context())); + } + + use_null_raster_cache(); + + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::kNone); +} + TEST_F(OpacityLayerTest, ShouldNotCacheChildren) { DlPaint paint; auto opacity_layer = @@ -190,24 +214,20 @@ TEST_F(OpacityLayerTest, ShouldNotCacheChildren) { use_mock_raster_cache(); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - - const auto* cacheable_opacity_item = opacity_layer->raster_cache_item(); - - EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_EQ(cacheable_opacity_item->cache_state(), - RasterCacheItem::CacheState::kNone); - EXPECT_FALSE(cacheable_opacity_item->GetId().has_value()); + EXPECT_EQ(opacity_layer->raster_cache_item(), nullptr); opacity_layer->Preroll(preroll_context()); + EXPECT_NE(opacity_layer->raster_cache_item(), nullptr); EXPECT_EQ(context->renderable_state_flags, LayerStateStack::kCallerCanApplyOpacity); EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_EQ(cacheable_opacity_item->cache_state(), + EXPECT_EQ(opacity_layer->raster_cache_item()->cache_state(), RasterCacheItem::CacheState::kNone); - EXPECT_FALSE(cacheable_opacity_item->Draw(paint_context(), &paint)); + EXPECT_FALSE( + opacity_layer->raster_cache_item()->Draw(paint_context(), &paint)); } TEST_F(OpacityLayerTest, FullyOpaque) { diff --git a/engine/src/flutter/flow/layers/shader_mask_layer.cc b/engine/src/flutter/flow/layers/shader_mask_layer.cc index bf7b81f4005..ccf6ee390d9 100644 --- a/engine/src/flutter/flow/layers/shader_mask_layer.cc +++ b/engine/src/flutter/flow/layers/shader_mask_layer.cc @@ -37,8 +37,7 @@ void ShaderMaskLayer::Diff(DiffContext* context, const Layer* old_layer) { void ShaderMaskLayer::Preroll(PrerollContext* context) { Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context); - AutoCache cache = AutoCache(layer_raster_cache_item_.get(), context, - context->state_stack.transform_3x3()); + AutoCache cache(*this, context); ContainerLayer::Preroll(context); // We always paint with a saveLayer (or a cached rendering), 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 4354f8715a1..f72126380ec 100644 --- a/engine/src/flutter/flow/layers/shader_mask_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/shader_mask_layer_unittests.cc @@ -334,39 +334,78 @@ TEST_F(ShaderMaskLayerTest, LayerCached) { use_mock_raster_cache(); preroll_context()->state_stack.set_preroll_delegate(initial_transform); - const auto* cacheable_shader_masker_item = layer->raster_cache_item(); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_EQ(cacheable_shader_masker_item->cache_state(), - RasterCacheItem::CacheState::kNone); - EXPECT_FALSE(cacheable_shader_masker_item->GetId().has_value()); + EXPECT_EQ(layer->raster_cache_item(), nullptr); // frame 1. layer->Preroll(preroll_context()); + EXPECT_NE(layer->raster_cache_item(), nullptr); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_EQ(cacheable_shader_masker_item->cache_state(), + EXPECT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::CacheState::kNone); - EXPECT_FALSE(cacheable_shader_masker_item->GetId().has_value()); + EXPECT_FALSE(layer->raster_cache_item()->GetId().has_value()); // frame 2. layer->Preroll(preroll_context()); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_EQ(cacheable_shader_masker_item->cache_state(), + EXPECT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::CacheState::kNone); - EXPECT_FALSE(cacheable_shader_masker_item->GetId().has_value()); + EXPECT_FALSE(layer->raster_cache_item()->GetId().has_value()); // frame 3. layer->Preroll(preroll_context()); LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); - EXPECT_EQ(cacheable_shader_masker_item->cache_state(), + EXPECT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::CacheState::kCurrent); - EXPECT_TRUE(raster_cache()->Draw( - cacheable_shader_masker_item->GetId().value(), cache_canvas, &paint)); + EXPECT_TRUE(raster_cache()->Draw(layer->raster_cache_item()->GetId().value(), + cache_canvas, &paint)); +} + +TEST_F(ShaderMaskLayerTest, NullRasterCacheResetsRasterCacheItem) { + auto dl_filter = MakeFilter(DlColor::kBlue()); + DlPaint paint; + const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 20.5f, 20.5f); + const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); + auto mock_layer = std::make_shared(child_path); + auto layer = std::make_shared(dl_filter, layer_bounds, + DlBlendMode::kSrc); + layer->Add(mock_layer); + + ASSERT_EQ(layer->raster_cache_item(), nullptr); + + layer->Preroll(preroll_context()); + ASSERT_EQ(layer->raster_cache_item(), nullptr); + + use_mock_raster_cache(); + + int limit = RasterCacheUtil::kMinimumRendersBeforeCachingFilterLayer; + for (int i = 1; i < limit; i++) { + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::kNone); + ASSERT_FALSE( + layer->raster_cache_item()->TryToPrepareRasterCache(paint_context())); + } + + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), + RasterCacheItem::kCurrent); + ASSERT_TRUE( + layer->raster_cache_item()->TryToPrepareRasterCache(paint_context())); + + use_null_raster_cache(); + + layer->Preroll(preroll_context()); + ASSERT_NE(layer->raster_cache_item(), nullptr); + ASSERT_EQ(layer->raster_cache_item()->cache_state(), RasterCacheItem::kNone); } TEST_F(ShaderMaskLayerTest, OpacityInheritance) { diff --git a/engine/src/flutter/flow/raster_cache_item.h b/engine/src/flutter/flow/raster_cache_item.h index 6df3066fed7..78c7c9f4ad8 100644 --- a/engine/src/flutter/flow/raster_cache_item.h +++ b/engine/src/flutter/flow/raster_cache_item.h @@ -57,6 +57,8 @@ class RasterCacheItem { void set_matrix(const SkMatrix& matrix) { matrix_ = matrix; } + void reset_cache_state() { cache_state_ = kNone; } + CacheState cache_state() const { return cache_state_; } bool need_caching() const { return cache_state_ != CacheState::kNone; } diff --git a/engine/src/flutter/flow/testing/mock_layer.cc b/engine/src/flutter/flow/testing/mock_layer.cc index 4f0453c3835..31e5f5d41ad 100644 --- a/engine/src/flutter/flow/testing/mock_layer.cc +++ b/engine/src/flutter/flow/testing/mock_layer.cc @@ -66,17 +66,29 @@ void MockLayer::Paint(PaintContext& context) const { void MockCacheableContainerLayer::Preroll(PrerollContext* context) { Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context); - auto cache = AutoCache(layer_raster_cache_item_.get(), context, - context->state_stack.transform_3x3()); + AutoCache cache(*this, context); ContainerLayer::Preroll(context); } +RasterCacheItem* MockCacheableLayer::realize_raster_cache_item() { + if (!raster_cache_item_) { + raster_cache_item_ = + std::make_unique(this, render_limit_); + } + return raster_cache_item_.get(); +} + +void MockCacheableLayer::disable_raster_cache_item() { + if (raster_cache_item_) { + raster_cache_item_->reset_cache_state(); + } +} + void MockCacheableLayer::Preroll(PrerollContext* context) { Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context); - auto cache = AutoCache(raster_cache_item_.get(), context, - context->state_stack.transform_3x3()); + AutoCache cache(*this, context); MockLayer::Preroll(context); } diff --git a/engine/src/flutter/flow/testing/mock_layer.h b/engine/src/flutter/flow/testing/mock_layer.h index 4d2efdf3678..0ceb4f45e02 100644 --- a/engine/src/flutter/flow/testing/mock_layer.h +++ b/engine/src/flutter/flow/testing/mock_layer.h @@ -150,15 +150,12 @@ class MockLayerCacheableItem : public LayerRasterCacheItem { public: using LayerRasterCacheItem::LayerRasterCacheItem; }; -class MockCacheableLayer : public MockLayer { +class MockCacheableLayer : public MockLayer, public CacheableLayer { public: explicit MockCacheableLayer(SkPath path, DlPaint paint = DlPaint(), int render_limit = 3) - : MockLayer(path, paint) { - raster_cache_item_ = - std::make_unique(this, render_limit); - } + : MockLayer(path, paint), render_limit_(render_limit) {} const LayerRasterCacheItem* raster_cache_item() const { return raster_cache_item_.get(); @@ -167,7 +164,11 @@ class MockCacheableLayer : public MockLayer { void Preroll(PrerollContext* context) override; private: + RasterCacheItem* realize_raster_cache_item() override; + void disable_raster_cache_item() override; std::unique_ptr raster_cache_item_; + + int render_limit_; }; } // 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 9effd6af12a..3aba6b150b0 100644 --- a/engine/src/flutter/flow/testing/mock_raster_cache.cc +++ b/engine/src/flutter/flow/testing/mock_raster_cache.cc @@ -62,7 +62,8 @@ void MockRasterCache::AddMockPicture(int width, int height) { DisplayListRasterCacheItem display_list_item(display_list, SkPoint(), true, false); for (size_t i = 0; i < access_threshold(); i++) { - AutoCache(&display_list_item, &preroll_context_, ctm); + display_list_item.PrerollSetup(&preroll_context_, ctm); + display_list_item.PrerollFinalize(&preroll_context_, ctm); } RasterCache::Context r_context = { // clang-format off