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 7cdfacfb7d..7ce398037d 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 856ce47546..46c502d8a1 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 582ae97fcf..922c71263e 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 55359b378a..2614ddedae 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 98d6493ba7..f0f43954be 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 d33267853e..3af0b3f61b 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 056efa7472..aa25727f70 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 1ec1f35bd0..5ecc790d5a 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 543af526be..03eaf0c672 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 d7efe4e8ee..88243cc8f9 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 2791577666..286021e028 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 834df45ce8..27d8abe231 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 ee8f08c849..64046f5461 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 53e9a65237..b383ac3899 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 8b55dff69a..20843bef5c 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 71ccdc15c8..3a64fc31ca 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 bf7b81f400..ccf6ee390d 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 4354f8715a..f72126380e 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 6df3066fed..78c7c9f4ad 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 4f0453c383..31e5f5d41a 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 4d2efdf367..0ceb4f45e0 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 9effd6af12..3aba6b150b 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