diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 4f805f0c653bd..65deba2981605 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -134,6 +134,8 @@ FILE: ../../../flutter/flow/layer_snapshot_store.h FILE: ../../../flutter/flow/layers/backdrop_filter_layer.cc FILE: ../../../flutter/flow/layers/backdrop_filter_layer.h FILE: ../../../flutter/flow/layers/backdrop_filter_layer_unittests.cc +FILE: ../../../flutter/flow/layers/cacheable_layer.cc +FILE: ../../../flutter/flow/layers/cacheable_layer.h FILE: ../../../flutter/flow/layers/checkerboard_layertree_unittests.cc FILE: ../../../flutter/flow/layers/clip_path_layer.cc FILE: ../../../flutter/flow/layers/clip_path_layer.h @@ -154,11 +156,15 @@ FILE: ../../../flutter/flow/layers/container_layer_unittests.cc FILE: ../../../flutter/flow/layers/display_list_layer.cc FILE: ../../../flutter/flow/layers/display_list_layer.h FILE: ../../../flutter/flow/layers/display_list_layer_unittests.cc +FILE: ../../../flutter/flow/layers/display_list_raster_cache_item.cc +FILE: ../../../flutter/flow/layers/display_list_raster_cache_item.h FILE: ../../../flutter/flow/layers/image_filter_layer.cc FILE: ../../../flutter/flow/layers/image_filter_layer.h FILE: ../../../flutter/flow/layers/image_filter_layer_unittests.cc FILE: ../../../flutter/flow/layers/layer.cc FILE: ../../../flutter/flow/layers/layer.h +FILE: ../../../flutter/flow/layers/layer_raster_cache_item.cc +FILE: ../../../flutter/flow/layers/layer_raster_cache_item.h FILE: ../../../flutter/flow/layers/layer_tree.cc FILE: ../../../flutter/flow/layers/layer_tree.h FILE: ../../../flutter/flow/layers/layer_tree_unittests.cc @@ -193,9 +199,12 @@ FILE: ../../../flutter/flow/paint_utils.cc FILE: ../../../flutter/flow/paint_utils.h FILE: ../../../flutter/flow/raster_cache.cc FILE: ../../../flutter/flow/raster_cache.h +FILE: ../../../flutter/flow/raster_cache_item.h FILE: ../../../flutter/flow/raster_cache_key.cc FILE: ../../../flutter/flow/raster_cache_key.h FILE: ../../../flutter/flow/raster_cache_unittests.cc +FILE: ../../../flutter/flow/raster_cache_util.cc +FILE: ../../../flutter/flow/raster_cache_util.h FILE: ../../../flutter/flow/rtree.cc FILE: ../../../flutter/flow/rtree.h FILE: ../../../flutter/flow/rtree_unittests.cc diff --git a/flow/BUILD.gn b/flow/BUILD.gn index f8377c86b8c55..c42fa8e7ab18c 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -22,6 +22,8 @@ source_set("flow") { "layer_snapshot_store.h", "layers/backdrop_filter_layer.cc", "layers/backdrop_filter_layer.h", + "layers/cacheable_layer.cc", + "layers/cacheable_layer.h", "layers/clip_path_layer.cc", "layers/clip_path_layer.h", "layers/clip_rect_layer.cc", @@ -35,10 +37,14 @@ source_set("flow") { "layers/container_layer.h", "layers/display_list_layer.cc", "layers/display_list_layer.h", + "layers/display_list_raster_cache_item.cc", + "layers/display_list_raster_cache_item.h", "layers/image_filter_layer.cc", "layers/image_filter_layer.h", "layers/layer.cc", "layers/layer.h", + "layers/layer_raster_cache_item.cc", + "layers/layer_raster_cache_item.h", "layers/layer_tree.cc", "layers/layer_tree.h", "layers/offscreen_surface.cc", @@ -63,8 +69,11 @@ source_set("flow") { "paint_utils.h", "raster_cache.cc", "raster_cache.h", + "raster_cache_item.h", "raster_cache_key.cc", "raster_cache_key.h", + "raster_cache_util.cc", + "raster_cache_util.h", "rtree.cc", "rtree.h", "skia_gpu_object.h", diff --git a/flow/layers/cacheable_layer.cc b/flow/layers/cacheable_layer.cc new file mode 100644 index 0000000000000..6c485e89dc155 --- /dev/null +++ b/flow/layers/cacheable_layer.cc @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/cacheable_layer.h" +#include "flutter/flow/raster_cache.h" +#include "flutter/flow/raster_cache_item.h" + +namespace flutter { + +AutoCache::AutoCache(RasterCacheItem* raster_cache_item, + PrerollContext* context, + const SkMatrix& matrix) + : raster_cache_item_(raster_cache_item), + context_(context), + matrix_(matrix) { + if (IsCacheEnabled()) { + raster_cache_item->PrerollSetup(context, matrix); + } +} + +bool AutoCache::IsCacheEnabled() { + return raster_cache_item_ && context_ && context_->raster_cache; +} + +AutoCache::~AutoCache() { + if (IsCacheEnabled()) { + 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); +} + +} // namespace flutter diff --git a/flow/layers/cacheable_layer.h b/flow/layers/cacheable_layer.h new file mode 100644 index 0000000000000..aac445b4bf054 --- /dev/null +++ b/flow/layers/cacheable_layer.h @@ -0,0 +1,50 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FLOW_LAYERS_CACHEABLE_LAYER_H_ +#define FLUTTER_FLOW_LAYERS_CACHEABLE_LAYER_H_ + +#include + +#include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/layers/display_list_raster_cache_item.h" +#include "flutter/flow/layers/layer_raster_cache_item.h" + +namespace flutter { + +class AutoCache { + public: + AutoCache(RasterCacheItem* raster_cache_item, + PrerollContext* context, + const SkMatrix& matrix); + + void ShouldNotBeCached() { raster_cache_item_ = nullptr; } + + ~AutoCache(); + + private: + inline bool IsCacheEnabled(); + RasterCacheItem* raster_cache_item_ = nullptr; + PrerollContext* context_ = nullptr; + const SkMatrix& matrix_; +}; + +class CacheableContainerLayer : public ContainerLayer { + public: + explicit CacheableContainerLayer( + int layer_cached_threshold = + RasterCacheUtil::kMinimumRendersBeforeCachingFilterLayer, + bool can_cache_children = false); + + const LayerRasterCacheItem* raster_cache_item() const { + return layer_raster_cache_item_.get(); + } + + protected: + std::unique_ptr layer_raster_cache_item_; +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_LAYERS_CACHEABLE_LAYER_H_ diff --git a/flow/layers/clip_path_layer_unittests.cc b/flow/layers/clip_path_layer_unittests.cc index 2268d34ff2a8a..f65af652ce5f5 100644 --- a/flow/layers/clip_path_layer_unittests.cc +++ b/flow/layers/clip_path_layer_unittests.cc @@ -4,11 +4,15 @@ #include "flutter/flow/layers/clip_path_layer.h" +#include "flutter/flow/layers/layer_tree.h" #include "flutter/flow/layers/opacity_layer.h" +#include "flutter/flow/raster_cache_item.h" #include "flutter/flow/testing/layer_test.h" #include "flutter/flow/testing/mock_layer.h" #include "flutter/fml/macros.h" #include "flutter/testing/mock_canvas.h" +#include "gtest/gtest.h" +#include "include/core/SkPaint.h" namespace flutter { namespace testing { @@ -501,24 +505,29 @@ TEST_F(ClipPathLayerTest, LayerCached) { use_mock_raster_cache(); + const auto* clip_cache_item = layer->raster_cache_item(); + EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); + EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); + EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); + EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); - EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); + EXPECT_EQ(clip_cache_item->cache_state(), + RasterCacheItem::CacheState::kCurrent); + SkPaint paint; + EXPECT_TRUE(raster_cache()->Draw(clip_cache_item->GetId().value(), + cache_canvas, &paint)); } } // namespace testing diff --git a/flow/layers/clip_rect_layer_unittests.cc b/flow/layers/clip_rect_layer_unittests.cc index 843e2a2c29ccf..35ac1fb46f469 100644 --- a/flow/layers/clip_rect_layer_unittests.cc +++ b/flow/layers/clip_rect_layer_unittests.cc @@ -4,6 +4,7 @@ #include "flutter/flow/layers/clip_rect_layer.h" +#include "flutter/flow/layers/layer_tree.h" #include "flutter/flow/layers/opacity_layer.h" #include "flutter/flow/testing/layer_test.h" #include "flutter/flow/testing/mock_layer.h" @@ -491,24 +492,27 @@ TEST_F(ClipRectLayerTest, LayerCached) { use_mock_raster_cache(); - EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); + const auto* clip_cache_item = layer->raster_cache_item(); layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); + EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); + EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); + EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); - EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); + EXPECT_EQ(clip_cache_item->cache_state(), + RasterCacheItem::CacheState::kCurrent); + SkPaint paint; + EXPECT_TRUE(raster_cache()->Draw(clip_cache_item->GetId().value(), + cache_canvas, &paint)); } } // namespace testing diff --git a/flow/layers/clip_rrect_layer_unittests.cc b/flow/layers/clip_rrect_layer_unittests.cc index fa37a6020b0ce..3f47f130ac500 100644 --- a/flow/layers/clip_rrect_layer_unittests.cc +++ b/flow/layers/clip_rrect_layer_unittests.cc @@ -4,6 +4,7 @@ #include "flutter/flow/layers/clip_rrect_layer.h" +#include "flutter/flow/layers/layer_tree.h" #include "flutter/flow/layers/opacity_layer.h" #include "flutter/flow/testing/layer_test.h" #include "flutter/flow/testing/mock_layer.h" @@ -486,6 +487,7 @@ TEST_F(ClipRRectLayerTest, OpacityInheritanceSaveLayerPainting) { TEST_F(ClipRRectLayerTest, LayerCached) { auto path1 = SkPath().addRect({10, 10, 30, 30}); + SkPaint paint = SkPaint(); auto mock1 = MockLayer::MakeOpacityCompatible(path1); SkRect clip_rect = SkRect::MakeWH(500, 500); SkRRect clip_r_rect = SkRRect::MakeRectXY(clip_rect, 20, 20); @@ -500,24 +502,61 @@ TEST_F(ClipRRectLayerTest, LayerCached) { use_mock_raster_cache(); - EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); + const auto* clip_cache_item = layer->raster_cache_item(); layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); + EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); + EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); + EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); - EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); + EXPECT_EQ(clip_cache_item->cache_state(), + RasterCacheItem::CacheState::kCurrent); + EXPECT_TRUE(raster_cache()->Draw(clip_cache_item->GetId().value(), + cache_canvas, &paint)); +} + +TEST_F(ClipRRectLayerTest, NoSaveLayerShouldNotCache) { + auto path1 = SkPath().addRect({10, 10, 30, 30}); + + auto mock1 = MockLayer::MakeOpacityCompatible(path1); + SkRect clip_rect = SkRect::MakeWH(500, 500); + SkRRect clip_r_rect = SkRRect::MakeRectXY(clip_rect, 20, 20); + auto layer = std::make_shared(clip_r_rect, Clip::antiAlias); + layer->Add(mock1); + + auto initial_transform = SkMatrix::Translate(50.0, 25.5); + SkMatrix cache_ctm = initial_transform; + SkCanvas cache_canvas; + cache_canvas.setMatrix(cache_ctm); + + use_mock_raster_cache(); + + const auto* clip_cache_item = layer->raster_cache_item(); + + layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); + + EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); + EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); + + layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); + EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); + EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); + + layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); + EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); + EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone); } } // namespace testing diff --git a/flow/layers/clip_shape_layer.h b/flow/layers/clip_shape_layer.h index 7adc9b2b9e4cc..d8729e8e83c11 100644 --- a/flow/layers/clip_shape_layer.h +++ b/flow/layers/clip_shape_layer.h @@ -5,19 +5,20 @@ #ifndef FLUTTER_FLOW_LAYERS_CLIP_SHAPE_LAYER_H_ #define FLUTTER_FLOW_LAYERS_CLIP_SHAPE_LAYER_H_ +#include "flutter/flow/layers/cacheable_layer.h" #include "flutter/flow/layers/container_layer.h" #include "flutter/flow/paint_utils.h" namespace flutter { template -class ClipShapeLayer : public ContainerLayer { +class ClipShapeLayer : public CacheableContainerLayer { public: using ClipShape = T; ClipShapeLayer(const ClipShape& clip_shape, Clip clip_behavior) - : clip_shape_(clip_shape), - clip_behavior_(clip_behavior), - render_count_(1) { + : CacheableContainerLayer(), + clip_shape_(clip_shape), + clip_behavior_(clip_behavior) { FML_DCHECK(clip_behavior != Clip::none); } @@ -31,7 +32,6 @@ class ClipShapeLayer : public ContainerLayer { context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer)); } } - if (context->PushCullRect(clip_shape_bounds())) { DiffChildren(context, prev); } @@ -40,9 +40,18 @@ class ClipShapeLayer : public ContainerLayer { void Preroll(PrerollContext* context, const SkMatrix& matrix) override { SkRect previous_cull_rect = context->cull_rect; + bool uses_save_layer = UsesSaveLayer(); + if (!context->cull_rect.intersect(clip_shape_bounds())) { context->cull_rect.setEmpty(); } + // 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, matrix); + Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); OnMutatorsStackPushClipShape(context->mutators_stack); @@ -59,15 +68,8 @@ class ClipShapeLayer : public ContainerLayer { // If we use a SaveLayer then we can accept opacity on behalf // of our children and apply it in the saveLayer. - if (UsesSaveLayer()) { + if (uses_save_layer) { context->subtree_can_inherit_opacity = true; - if (render_count_ >= kMinimumRendersBeforeCachingLayer) { - SkMatrix child_matrix(matrix); - TryToPrepareRasterCache(context, this, child_matrix, - RasterCacheLayerStrategy::kLayer); - } else { - render_count_++; - } } context->mutators_stack.Pop(); @@ -86,11 +88,10 @@ class ClipShapeLayer : public ContainerLayer { } AutoCachePaint cache_paint(context); - if (context.raster_cache && - context.raster_cache->Draw(this, *context.leaf_nodes_canvas, - RasterCacheLayerStrategy::kLayer, - cache_paint.paint())) { - return; + if (context.raster_cache) { + if (layer_raster_cache_item_->Draw(context, cache_paint.paint())) { + return; + } } Layer::AutoSaveLayer save_layer = Layer::AutoSaveLayer::Create( @@ -115,9 +116,6 @@ class ClipShapeLayer : public ContainerLayer { const ClipShape clip_shape_; Clip clip_behavior_; - static constexpr int kMinimumRendersBeforeCachingLayer = 3; - int render_count_; - FML_DISALLOW_COPY_AND_ASSIGN(ClipShapeLayer); }; diff --git a/flow/layers/color_filter_layer.cc b/flow/layers/color_filter_layer.cc index 2109dc62c8ea9..3c1f50157ccd0 100644 --- a/flow/layers/color_filter_layer.cc +++ b/flow/layers/color_filter_layer.cc @@ -3,11 +3,16 @@ // found in the LICENSE file. #include "flutter/flow/layers/color_filter_layer.h" +#include "flutter/flow/raster_cache_item.h" +#include "flutter/flow/raster_cache_util.h" namespace flutter { ColorFilterLayer::ColorFilterLayer(sk_sp filter) - : filter_(std::move(filter)), render_count_(1) {} + : CacheableContainerLayer( + RasterCacheUtil::kMinimumRendersBeforeCachingFilterLayer, + true), + filter_(std::move(filter)) {} void ColorFilterLayer::Diff(DiffContext* context, const Layer* old_layer) { DiffContext::AutoSubtreeRestore subtree(context); @@ -28,22 +33,12 @@ void ColorFilterLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context); - ContainerLayer::Preroll(context, matrix); + AutoCache cache = AutoCache(layer_raster_cache_item_.get(), context, matrix); + ContainerLayer::Preroll(context, matrix); // We always use a saveLayer (or a cached rendering), so we // can always apply opacity in those cases. context->subtree_can_inherit_opacity = true; - - SkMatrix child_matrix(matrix); - - if (render_count_ >= kMinimumRendersBeforeCachingFilterLayer) { - TryToPrepareRasterCache(context, this, child_matrix, - RasterCacheLayerStrategy::kLayer); - } else { - render_count_++; - TryToPrepareRasterCache(context, this, child_matrix, - RasterCacheLayerStrategy::kLayerChildren); - } } void ColorFilterLayer::Paint(PaintContext& context) const { @@ -53,16 +48,10 @@ void ColorFilterLayer::Paint(PaintContext& context) const { AutoCachePaint cache_paint(context); if (context.raster_cache) { - if (context.raster_cache->Draw(this, *context.leaf_nodes_canvas, - RasterCacheLayerStrategy::kLayer, - cache_paint.paint())) { - return; + if (layer_raster_cache_item_->IsCacheChildren()) { + cache_paint.setColorFilter(filter_); } - - cache_paint.setColorFilter(filter_); - if (context.raster_cache->Draw(this, *context.leaf_nodes_canvas, - RasterCacheLayerStrategy::kLayerChildren, - cache_paint.paint())) { + if (layer_raster_cache_item_->Draw(context, cache_paint.paint())) { return; } } @@ -71,6 +60,7 @@ void ColorFilterLayer::Paint(PaintContext& context) const { Layer::AutoSaveLayer save = Layer::AutoSaveLayer::Create( context, paint_bounds(), cache_paint.paint()); + PaintChildren(context); } diff --git a/flow/layers/color_filter_layer.h b/flow/layers/color_filter_layer.h index fc28295d457d7..d896ff34fb7b8 100644 --- a/flow/layers/color_filter_layer.h +++ b/flow/layers/color_filter_layer.h @@ -5,12 +5,12 @@ #ifndef FLUTTER_FLOW_LAYERS_COLOR_FILTER_LAYER_H_ #define FLUTTER_FLOW_LAYERS_COLOR_FILTER_LAYER_H_ -#include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/layers/cacheable_layer.h" +#include "flutter/flow/layers/layer.h" #include "third_party/skia/include/core/SkColorFilter.h" - namespace flutter { -class ColorFilterLayer : public ContainerLayer { +class ColorFilterLayer : public CacheableContainerLayer { public: explicit ColorFilterLayer(sk_sp filter); @@ -22,10 +22,6 @@ class ColorFilterLayer : public ContainerLayer { private: sk_sp filter_; - - static constexpr int kMinimumRendersBeforeCachingFilterLayer = 3; - int render_count_; - FML_DISALLOW_COPY_AND_ASSIGN(ColorFilterLayer); }; diff --git a/flow/layers/color_filter_layer_unittests.cc b/flow/layers/color_filter_layer_unittests.cc index ccc9d9201b5db..5ec709f1ab54b 100644 --- a/flow/layers/color_filter_layer_unittests.cc +++ b/flow/layers/color_filter_layer_unittests.cc @@ -2,10 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "flutter/flow/compositor_context.h" #include "flutter/flow/layers/color_filter_layer.h" #include "flutter/display_list/display_list_color_filter.h" +#include "flutter/flow/layers/layer_tree.h" #include "flutter/flow/layers/opacity_layer.h" +#include "flutter/flow/raster_cache.h" +#include "flutter/flow/raster_cache_key.h" #include "flutter/flow/testing/layer_test.h" #include "flutter/flow/testing/mock_layer.h" #include "flutter/fml/macros.h" @@ -228,6 +232,7 @@ TEST_F(ColorFilterLayerTest, CacheChild) { auto initial_transform = SkMatrix::Translate(50.0, 25.5); auto other_transform = SkMatrix::Scale(1.0, 2.0); const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); + SkPaint paint = SkPaint(); auto mock_layer = std::make_shared(child_path); auto layer = std::make_shared(layer_filter); layer->Add(mock_layer); @@ -239,32 +244,27 @@ TEST_F(ColorFilterLayerTest, CacheChild) { other_canvas.setMatrix(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_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayerChildren)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayerChildren)); + EXPECT_EQ(cacheable_color_filter_item->cache_state(), + RasterCacheItem::CacheState::kNone); + EXPECT_FALSE(cacheable_color_filter_item->GetId().has_value()); layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); - EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayerChildren)); - EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayerChildren)); + EXPECT_EQ(cacheable_color_filter_item->cache_state(), + RasterCacheItem::CacheState::kChildren); + EXPECT_EQ( + cacheable_color_filter_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(), + cache_canvas, &paint)); } TEST_F(ColorFilterLayerTest, CacheChildren) { @@ -279,6 +279,7 @@ TEST_F(ColorFilterLayerTest, CacheChildren) { auto layer = std::make_shared(layer_filter); layer->Add(mock_layer1); layer->Add(mock_layer2); + SkPaint paint = SkPaint(); SkMatrix cache_ctm = initial_transform; SkCanvas cache_canvas; @@ -289,34 +290,85 @@ TEST_F(ColorFilterLayerTest, CacheChildren) { use_mock_raster_cache(); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), other_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayerChildren)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayerChildren)); + 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()); layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); - EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), other_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayerChildren)); - EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayerChildren)); + EXPECT_EQ(cacheable_color_filter_item->cache_state(), + RasterCacheItem::CacheState::kChildren); + EXPECT_EQ( + cacheable_color_filter_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(), + cache_canvas, &paint)); +} + +TEST_F(ColorFilterLayerTest, CacheColorFilterLayerSelf) { + auto layer_filter = + SkColorMatrixFilter::MakeLightingFilter(SK_ColorGREEN, SK_ColorYELLOW); + auto initial_transform = SkMatrix::Translate(50.0, 25.5); + auto other_transform = SkMatrix::Scale(1.0, 2.0); + const SkPath child_path1 = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); + const SkPath child_path2 = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); + auto mock_layer1 = std::make_shared(child_path1); + auto mock_layer2 = std::make_shared(child_path2); + auto layer = std::make_shared(layer_filter); + layer->Add(mock_layer1); + layer->Add(mock_layer2); + SkPaint paint = SkPaint(); + + SkMatrix cache_ctm = initial_transform; + SkCanvas cache_canvas; + cache_canvas.setMatrix(cache_ctm); + SkCanvas other_canvas; + other_canvas.setMatrix(other_transform); + + use_mock_raster_cache(); + const auto* cacheable_color_filter_item = layer->raster_cache_item(); + + // frame 1. + layer->Preroll(preroll_context(), initial_transform); + layer->Paint(paint_context()); + // frame 2. + layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); + // ColorFilterLayer default cache children. + EXPECT_EQ(cacheable_color_filter_item->cache_state(), + RasterCacheItem::CacheState::kChildren); + EXPECT_TRUE(raster_cache()->Draw(cacheable_color_filter_item->GetId().value(), + cache_canvas, &paint)); + EXPECT_FALSE(raster_cache()->Draw( + cacheable_color_filter_item->GetId().value(), other_canvas, &paint)); + layer->Paint(paint_context()); + + // frame 3. + layer->Preroll(preroll_context(), initial_transform); + layer->Paint(paint_context()); + + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); + // frame1,2 cache the ColorFilterLayer's children layer, frame3 cache the + // ColorFilterLayer + EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)2); + + // ColorFilterLayer default cache itself. + EXPECT_EQ(cacheable_color_filter_item->cache_state(), + RasterCacheItem::CacheState::kCurrent); + EXPECT_EQ(cacheable_color_filter_item->GetId(), + RasterCacheKeyID(layer->unique_id(), RasterCacheKeyType::kLayer)); + EXPECT_TRUE(raster_cache()->Draw(cacheable_color_filter_item->GetId().value(), + cache_canvas, &paint)); + EXPECT_FALSE(raster_cache()->Draw( + cacheable_color_filter_item->GetId().value(), other_canvas, &paint)); } TEST_F(ColorFilterLayerTest, OpacityInheritance) { @@ -339,7 +391,7 @@ TEST_F(ColorFilterLayerTest, OpacityInheritance) { PrerollContext* context = preroll_context(); context->subtree_can_inherit_opacity = false; color_filter_layer->Preroll(preroll_context(), initial_transform); - // ImageFilterLayers can always inherit opacity whether or not their + // ColorFilterLayer can always inherit opacity whether or not their // children are compatible. EXPECT_TRUE(context->subtree_can_inherit_opacity); diff --git a/flow/layers/container_layer.cc b/flow/layers/container_layer.cc index 7e9820e4aa1ac..ad6fd9126e200 100644 --- a/flow/layers/container_layer.cc +++ b/flow/layers/container_layer.cc @@ -189,19 +189,4 @@ void ContainerLayer::PaintChildren(PaintContext& context) const { } } -void ContainerLayer::TryToPrepareRasterCache( - PrerollContext* context, - Layer* layer, - const SkMatrix& matrix, - RasterCacheLayerStrategy strategy) { - if (!context->has_platform_view && !context->has_texture_layer && - context->raster_cache && - SkRect::Intersects(context->cull_rect, layer->paint_bounds())) { - context->raster_cache->Prepare(context, layer, matrix, strategy); - } else if (context->raster_cache) { - // Don't evict raster cache entry during partial repaint - context->raster_cache->Touch(layer, matrix, strategy); - } -} - } // namespace flutter diff --git a/flow/layers/container_layer.h b/flow/layers/container_layer.h index 937c4e586da15..cb3d497aa642a 100644 --- a/flow/layers/container_layer.h +++ b/flow/layers/container_layer.h @@ -27,7 +27,9 @@ class ContainerLayer : public Layer { virtual void DiffChildren(DiffContext* context, const ContainerLayer* old_layer); - void PaintChildren(PaintContext& context) const; + + void PaintChildren(PaintContext& context) const override; + const ContainerLayer* as_container_layer() const override { return this; } const SkRect& child_paint_bounds() const { return child_paint_bounds_; } @@ -37,21 +39,6 @@ class ContainerLayer : public Layer { const SkMatrix& child_matrix, SkRect* child_paint_bounds); - // Try to prepare the raster cache for a given layer. - // - // The raster cache would fail if either of the followings is true: - // 1. The context has a platform view. - // 2. The context does not have a valid raster cache. - // 3. The layer's paint bounds does not intersect with the cull rect. - // - // We make this a static function instead of a member function that directly - // uses the "this" pointer as the layer because we sometimes need to raster - // cache a child layer and one can't access its child's protected method. - static void TryToPrepareRasterCache(PrerollContext* context, - Layer* layer, - const SkMatrix& matrix, - RasterCacheLayerStrategy strategy); - private: std::vector> layers_; SkRect child_paint_bounds_; diff --git a/flow/layers/container_layer_unittests.cc b/flow/layers/container_layer_unittests.cc index bdb2661d428c5..89f6477a4ec4b 100644 --- a/flow/layers/container_layer_unittests.cc +++ b/flow/layers/container_layer_unittests.cc @@ -4,11 +4,16 @@ #include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/layers/layer.h" +#include "flutter/flow/layers/layer_tree.h" #include "flutter/flow/testing/diff_context_test.h" #include "flutter/flow/testing/layer_test.h" #include "flutter/flow/testing/mock_layer.h" #include "flutter/fml/macros.h" #include "flutter/testing/mock_canvas.h" +#include "gtest/gtest.h" +#include "include/core/SkCanvas.h" +#include "include/core/SkMatrix.h" namespace flutter { namespace testing { @@ -192,6 +197,224 @@ TEST_F(ContainerLayerTest, NeedsSystemComposite) { child_path2, child_paint2}}})); } +TEST_F(ContainerLayerTest, RasterCacheTest) { + // LTRB + const SkPath child_path1 = SkPath().addRect(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path2 = SkPath().addRect(21.0f, 6.0f, 25.5f, 21.5f); + const SkPath child_path3 = SkPath().addRect(26.0f, 6.0f, 30.5f, 21.5f); + const SkPaint child_paint1(SkColors::kGray); + const SkPaint child_paint2(SkColors::kGreen); + const SkPaint paint; + auto cacheable_container_layer1 = + MockCacheableContainerLayer::CacheLayerOrChildren(); + auto cacheable_container_layer2 = + MockCacheableContainerLayer::CacheLayerOnly(); + auto cacheable_container_layer11 = + MockCacheableContainerLayer::CacheLayerOrChildren(); + + auto cacheable_layer111 = + std::make_shared(child_path3, paint); + // if the frame had rendered 2 frames, we will cache the cacheable_layer21 + // layer + auto cacheable_layer21 = + std::make_shared(child_path1, paint, 2); + + // clang-format off +// layer +// | +// ________________________________ ________________________________ +// | | | +// cacheable_container_layer1 mock_layer2 cacheable_container_layer2 +// | | +// cacheable_container_layer11 cacheable_layer21 +// | +// cacheable_layer111 + // clang-format on + + auto mock_layer1 = std::make_shared(child_path1, child_paint1); + auto mock_layer2 = std::make_shared(SkPath(), child_paint2); + auto mock_layer3 = std::make_shared(child_path2, paint); + + cacheable_container_layer1->Add(mock_layer1); + cacheable_container_layer1->Add(mock_layer3); + + cacheable_container_layer1->Add(cacheable_container_layer11); + cacheable_container_layer11->Add(cacheable_layer111); + + cacheable_container_layer2->Add(cacheable_layer21); + auto layer = std::make_shared(); + layer->Add(cacheable_container_layer1); + layer->Add(mock_layer2); + layer->Add(cacheable_container_layer2); + + SkCanvas cache_canvas; + cache_canvas.setMatrix(SkMatrix::I()); + + // Initial Preroll for check the layer paint bounds + layer->Preroll(preroll_context(), SkMatrix::I()); + + EXPECT_EQ(mock_layer1->paint_bounds(), + SkRect::MakeLTRB(5.f, 6.f, 20.5f, 21.5f)); + EXPECT_EQ(mock_layer3->paint_bounds(), + SkRect::MakeLTRB(21.0f, 6.0f, 25.5f, 21.5f)); + EXPECT_EQ(cacheable_layer111->paint_bounds(), + SkRect::MakeLTRB(26.0f, 6.0f, 30.5f, 21.5f)); + EXPECT_EQ(cacheable_container_layer1->paint_bounds(), + SkRect::MakeLTRB(5.f, 6.f, 30.5f, 21.5f)); + + // the preroll context's raster cache is nullptr + EXPECT_EQ(preroll_context()->raster_cached_entries->size(), + static_cast(0)); + { + // frame1 + use_mock_raster_cache(); + preroll_context()->raster_cache->PrepareNewFrame(); + layer->Preroll(preroll_context(), SkMatrix::I()); + // Cache the cacheable entries + LayerTree::TryToRasterCache(*(preroll_context()->raster_cached_entries), + &paint_context()); + + EXPECT_EQ(preroll_context()->raster_cached_entries->size(), + static_cast(5)); + + // cacheable_container_layer1 will cache his children + EXPECT_EQ(cacheable_container_layer1->raster_cache_item()->cache_state(), + RasterCacheItem::CacheState::kChildren); + EXPECT_TRUE(raster_cache()->HasEntry( + cacheable_container_layer1->raster_cache_item()->GetId().value(), + SkMatrix::I())); + + EXPECT_EQ(cacheable_container_layer11->raster_cache_item()->cache_state(), + RasterCacheItem::CacheState::kChildren); + EXPECT_TRUE(raster_cache()->HasEntry( + cacheable_container_layer11->raster_cache_item()->GetId().value(), + SkMatrix::I())); + EXPECT_FALSE(raster_cache()->Draw( + cacheable_container_layer11->raster_cache_item()->GetId().value(), + cache_canvas, &paint)); + + // The cacheable_layer111 should be cached when rended 3 frames + EXPECT_EQ(cacheable_layer111->raster_cache_item()->cache_state(), + RasterCacheItem::CacheState::kNone); + // render count < 2 don't cache it + EXPECT_EQ(cacheable_container_layer2->raster_cache_item()->cache_state(), + RasterCacheItem::CacheState::kNone); + preroll_context()->raster_cache->CleanupAfterFrame(); + } + + { + // frame2 + // new frame the layer tree will create new PrerollContext, so in here we + // clear the cached_entries + preroll_context()->raster_cached_entries->clear(); + preroll_context()->raster_cache->PrepareNewFrame(); + layer->Preroll(preroll_context(), SkMatrix::I()); + + // Cache the cacheable entries + LayerTree::TryToRasterCache(*(preroll_context()->raster_cached_entries), + &paint_context()); + EXPECT_EQ(preroll_context()->raster_cached_entries->size(), + static_cast(5)); + EXPECT_EQ(cacheable_container_layer1->raster_cache_item()->cache_state(), + RasterCacheItem::CacheState::kChildren); + EXPECT_TRUE(raster_cache()->HasEntry( + cacheable_container_layer1->raster_cache_item()->GetId().value(), + SkMatrix::I())); + + EXPECT_EQ(cacheable_container_layer11->raster_cache_item()->cache_state(), + RasterCacheItem::CacheState::kChildren); + EXPECT_TRUE(raster_cache()->HasEntry( + cacheable_container_layer11->raster_cache_item()->GetId().value(), + SkMatrix::I())); + EXPECT_FALSE(raster_cache()->Draw( + cacheable_container_layer11->raster_cache_item()->GetId().value(), + cache_canvas, &paint)); + + EXPECT_EQ(cacheable_container_layer2->raster_cache_item()->cache_state(), + RasterCacheItem::CacheState::kNone); + + // render count == 2 cache it + EXPECT_EQ(cacheable_layer21->raster_cache_item()->cache_state(), + RasterCacheItem::CacheState::kCurrent); + EXPECT_TRUE(raster_cache()->HasEntry( + cacheable_layer21->raster_cache_item()->GetId().value(), + SkMatrix::I())); + EXPECT_TRUE(raster_cache()->Draw( + cacheable_layer21->raster_cache_item()->GetId().value(), cache_canvas, + &paint)); + preroll_context()->raster_cache->CleanupAfterFrame(); + } + + { + // frame3 + // new frame the layer tree will create new PrerollContext, so in here we + // clear the cached_entries + preroll_context()->raster_cache->PrepareNewFrame(); + preroll_context()->raster_cached_entries->clear(); + layer->Preroll(preroll_context(), SkMatrix::I()); + + // Cache the cacheable entries + LayerTree::TryToRasterCache(*(preroll_context()->raster_cached_entries), + &paint_context()); + EXPECT_EQ(preroll_context()->raster_cached_entries->size(), + static_cast(5)); + EXPECT_EQ(cacheable_container_layer1->raster_cache_item()->cache_state(), + RasterCacheItem::CacheState::kCurrent); + EXPECT_TRUE(raster_cache()->HasEntry( + cacheable_container_layer1->raster_cache_item()->GetId().value(), + SkMatrix::I())); + EXPECT_TRUE(raster_cache()->HasEntry( + cacheable_container_layer11->raster_cache_item()->GetId().value(), + SkMatrix::I())); + EXPECT_FALSE(raster_cache()->Draw( + cacheable_container_layer11->raster_cache_item()->GetId().value(), + cache_canvas, &paint)); + // The 3td frame, we will cache the cacheable_layer111, but his ancestor has + // been cached, so cacheable_layer111 Draw is false + EXPECT_TRUE(raster_cache()->HasEntry( + cacheable_layer111->raster_cache_item()->GetId().value(), + SkMatrix::I())); + EXPECT_FALSE(raster_cache()->Draw( + cacheable_layer111->raster_cache_item()->GetId().value(), cache_canvas, + &paint)); + + // The third frame, we will cache the cacheable_container_layer2 + EXPECT_EQ(cacheable_container_layer2->raster_cache_item()->cache_state(), + RasterCacheItem::CacheState::kCurrent); + + EXPECT_TRUE(raster_cache()->HasEntry( + cacheable_layer21->raster_cache_item()->GetId().value(), + SkMatrix::I())); + preroll_context()->raster_cache->CleanupAfterFrame(); + } + + { + preroll_context()->raster_cache->PrepareNewFrame(); + // frame4 + preroll_context()->raster_cached_entries->clear(); + layer->Preroll(preroll_context(), SkMatrix::I()); + LayerTree::TryToRasterCache(*(preroll_context()->raster_cached_entries), + &paint_context()); + preroll_context()->raster_cache->CleanupAfterFrame(); + + // frame5 + preroll_context()->raster_cache->PrepareNewFrame(); + preroll_context()->raster_cached_entries->clear(); + layer->Preroll(preroll_context(), SkMatrix::I()); + LayerTree::TryToRasterCache(*(preroll_context()->raster_cached_entries), + &paint_context()); + preroll_context()->raster_cache->CleanupAfterFrame(); + + // frame6 + preroll_context()->raster_cache->PrepareNewFrame(); + preroll_context()->raster_cached_entries->clear(); + layer->Preroll(preroll_context(), SkMatrix::I()); + LayerTree::TryToRasterCache(*(preroll_context()->raster_cached_entries), + &paint_context()); + preroll_context()->raster_cache->CleanupAfterFrame(); + } +} + TEST_F(ContainerLayerTest, OpacityInheritance) { auto path1 = SkPath().addRect({10, 10, 30, 30}); auto mock1 = MockLayer::MakeOpacityCompatible(path1); @@ -246,6 +469,39 @@ TEST_F(ContainerLayerTest, OpacityInheritance) { container2->Preroll(context, SkMatrix::I()); EXPECT_FALSE(context->subtree_can_inherit_opacity); } +TEST_F(ContainerLayerTest, CollectionCacheableLayer) { + SkPath child_path; + child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); + SkPaint child_paint(SkColors::kGreen); + SkMatrix initial_transform = SkMatrix::Translate(-0.5f, -0.5f); + + auto mock_layer1 = std::make_shared(SkPath(), child_paint); + auto mock_cacheable_container_layer1 = + std::make_shared(); + auto mock_container_layer = std::make_shared(); + auto mock_cacheable_layer = + std::make_shared(child_path, child_paint); + mock_cacheable_container_layer1->Add(mock_cacheable_layer); + + // ContainerLayer + // |- MockLayer + // |- MockCacheableContainerLayer + // |- MockCacheableLayer + auto layer = std::make_shared(); + layer->Add(mock_cacheable_container_layer1); + layer->Add(mock_layer1); + + layer->Preroll(preroll_context(), initial_transform); + // raster cache is null, so no entry + ASSERT_EQ(preroll_context()->raster_cached_entries->size(), + static_cast(0)); + + use_mock_raster_cache(); + // preroll_context()->raster_cache = raster_cache(); + layer->Preroll(preroll_context(), initial_transform); + ASSERT_EQ(preroll_context()->raster_cached_entries->size(), + static_cast(2)); +} using ContainerLayerDiffTest = DiffContextTest; diff --git a/flow/layers/display_list_layer.cc b/flow/layers/display_list_layer.cc index 71f79da4591a0..5fb06d57e4ebd 100644 --- a/flow/layers/display_list_layer.cc +++ b/flow/layers/display_list_layer.cc @@ -4,11 +4,15 @@ #include "flutter/flow/layers/display_list_layer.h" +#include + #include "flutter/display_list/display_list_builder.h" #include "flutter/display_list/display_list_flags.h" #include "flutter/flow/layer_snapshot_store.h" +#include "flutter/flow/layers/cacheable_layer.h" #include "flutter/flow/layers/offscreen_surface.h" #include "flutter/flow/raster_cache.h" +#include "flutter/flow/raster_cache_util.h" namespace flutter { @@ -16,10 +20,14 @@ DisplayListLayer::DisplayListLayer(const SkPoint& offset, SkiaGPUObject display_list, bool is_complex, bool will_change) - : offset_(offset), - display_list_(std::move(display_list)), - is_complex_(is_complex), - will_change_(will_change) {} + : offset_(offset), display_list_(std::move(display_list)) { + if (display_list_.skia_object() != nullptr) { + bounds_ = display_list_.skia_object()->bounds().makeOffset(offset_.x(), + offset_.y()); + display_list_raster_cache_item_ = DisplayListRasterCacheItem::Make( + display_list_.skia_object().get(), offset_, is_complex, will_change); + } +} bool DisplayListLayer::IsReplacing(DiffContext* context, const Layer* layer) const { @@ -86,28 +94,14 @@ bool DisplayListLayer::Compare(DiffContext::Statistics& statistics, void DisplayListLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "DisplayListLayer::Preroll"); - DisplayList* disp_list = display_list(); - SkRect bounds = disp_list->bounds().makeOffset(offset_.x(), offset_.y()); - + AutoCache cache = + AutoCache(display_list_raster_cache_item_.get(), context, matrix); if (disp_list->can_apply_group_opacity()) { context->subtree_can_inherit_opacity = true; } - - if (auto* cache = context->raster_cache) { - TRACE_EVENT0("flutter", "DisplayListLayer::RasterCache (Preroll)"); - if (context->cull_rect.intersects(bounds)) { - if (cache->Prepare(context, disp_list, is_complex_, will_change_, matrix, - offset_)) { - context->subtree_can_inherit_opacity = true; - } - } else { - // Don't evict raster cache entry during partial repaint - cache->Touch(disp_list, matrix); - } - } - set_paint_bounds(bounds); + set_paint_bounds(bounds_); } void DisplayListLayer::Paint(PaintContext& context) const { @@ -118,10 +112,9 @@ void DisplayListLayer::Paint(PaintContext& context) const { SkAutoCanvasRestore save(context.leaf_nodes_canvas, true); context.leaf_nodes_canvas->translate(offset_.x(), offset_.y()); - if (context.raster_cache) { + if (context.raster_cache && display_list_raster_cache_item_) { AutoCachePaint cache_paint(context); - if (context.raster_cache->Draw(*display_list(), *context.leaf_nodes_canvas, - cache_paint.paint())) { + if (display_list_raster_cache_item_->Draw(context, cache_paint.paint())) { TRACE_EVENT_INSTANT0("flutter", "raster cache hit"); return; } @@ -148,7 +141,7 @@ void DisplayListLayer::Paint(PaintContext& context) const { fml::TimePoint::Now() - start_time; const SkRect device_bounds = - RasterCache::GetDeviceBounds(paint_bounds(), ctm); + RasterCacheUtil::GetDeviceBounds(paint_bounds(), ctm); sk_sp raster_data = offscreen_surface->GetRasterData(true); LayerSnapshotData snapshot_data(unique_id(), offscreen_render_time, raster_data, device_bounds); diff --git a/flow/layers/display_list_layer.h b/flow/layers/display_list_layer.h index 347077455a205..3eaef523e7d58 100644 --- a/flow/layers/display_list_layer.h +++ b/flow/layers/display_list_layer.h @@ -5,8 +5,12 @@ #ifndef FLUTTER_FLOW_LAYERS_DISPLAY_LIST_LAYER_H_ #define FLUTTER_FLOW_LAYERS_DISPLAY_LIST_LAYER_H_ +#include + #include "flutter/display_list/display_list.h" +#include "flutter/flow/layers/display_list_raster_cache_item.h" #include "flutter/flow/layers/layer.h" +#include "flutter/flow/raster_cache_item.h" #include "flutter/flow/skia_gpu_object.h" namespace flutter { @@ -36,11 +40,17 @@ class DisplayListLayer : public Layer { void Paint(PaintContext& context) const override; + const DisplayListRasterCacheItem* raster_cache_item() const { + return display_list_raster_cache_item_.get(); + } + private: + std::unique_ptr display_list_raster_cache_item_; + SkPoint offset_; + SkRect bounds_; + flutter::SkiaGPUObject display_list_; - bool is_complex_ = false; - bool will_change_ = false; static bool Compare(DiffContext::Statistics& statistics, const DisplayListLayer* l1, diff --git a/flow/layers/display_list_layer_unittests.cc b/flow/layers/display_list_layer_unittests.cc index 54fc20a8f9aa9..941c20702368b 100644 --- a/flow/layers/display_list_layer_unittests.cc +++ b/flow/layers/display_list_layer_unittests.cc @@ -265,11 +265,11 @@ TEST_F(DisplayListLayerTest, CachedIncompatibleDisplayListOpacityInheritance) { // display_list_bounds.makeOffset(layer_offset.fX, layer_offset.fY); // save_layer_bounds.roundOut(&save_layer_bounds); // auto opacity_integral_matrix = - // RasterCache::GetIntegralTransCTM(SkMatrix::Translate(opacity_offset)); + // RasterCacheUtil::GetIntegralTransCTM(SkMatrix::Translate(opacity_offset)); // SkMatrix layer_offset_matrix = opacity_integral_matrix; // layer_offset_matrix.postTranslate(layer_offset.fX, layer_offset.fY); // auto layer_offset_integral_matrix = - // RasterCache::GetIntegralTransCTM(layer_offset_matrix); + // RasterCacheUtil::GetIntegralTransCTM(layer_offset_matrix); // // Using a recorder instead of a DisplayListBuilder so we can hand it // // off to the RasterCache::Draw() method // DisplayListCanvasRecorder recorder(SkRect::MakeWH(1000, 1000)); diff --git a/flow/layers/display_list_raster_cache_item.cc b/flow/layers/display_list_raster_cache_item.cc new file mode 100644 index 0000000000000..75a79bcf33c14 --- /dev/null +++ b/flow/layers/display_list_raster_cache_item.cc @@ -0,0 +1,177 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/display_list_raster_cache_item.h" + +#include +#include + +#include "flutter/display_list/display_list.h" +#include "flutter/flow/layers/layer.h" +#include "flutter/flow/raster_cache.h" +#include "flutter/flow/raster_cache_item.h" +#include "flutter/flow/raster_cache_key.h" +#include "flutter/flow/raster_cache_util.h" +#include "flutter/flow/skia_gpu_object.h" + +namespace flutter { + +static bool IsDisplayListWorthRasterizing( + DisplayList* display_list, + bool will_change, + bool is_complex, + DisplayListComplexityCalculator* complexity_calculator) { + if (will_change) { + // If the display list is going to change in the future, there is no point + // in doing to extra work to rasterize. + return false; + } + + if (display_list == nullptr || + !RasterCacheUtil::CanRasterizeRect(display_list->bounds())) { + // No point in deciding whether the display list is worth rasterizing if it + // cannot be rasterized at all. + return false; + } + + if (is_complex) { + // The caller seems to have extra information about the display list and + // thinks the display list is always worth rasterizing. + return true; + } + + unsigned int complexity_score = complexity_calculator->Compute(display_list); + return complexity_calculator->ShouldBeCached(complexity_score); +} + +DisplayListRasterCacheItem::DisplayListRasterCacheItem( + DisplayList* display_list, + const SkPoint& offset, + bool is_complex, + bool will_change) + : RasterCacheItem(RasterCacheKeyID(display_list->unique_id(), + RasterCacheKeyType::kDisplayList), + CacheState::kCurrent), + display_list_(display_list), + offset_(offset), + is_complex_(is_complex), + will_change_(will_change) {} + +std::unique_ptr DisplayListRasterCacheItem::Make( + DisplayList* display_list, + const SkPoint& offset, + bool is_complex, + bool will_change) { + return std::make_unique(display_list, offset, + is_complex, will_change); +} + +void DisplayListRasterCacheItem::PrerollSetup(PrerollContext* context, + const SkMatrix& matrix) { + cache_state_ = CacheState::kNone; + DisplayListComplexityCalculator* complexity_calculator = + context->gr_context ? DisplayListComplexityCalculator::GetForBackend( + context->gr_context->backend()) + : DisplayListComplexityCalculator::GetForSoftware(); + + if (!IsDisplayListWorthRasterizing(display_list_, will_change_, is_complex_, + complexity_calculator)) { + // We only deal with display lists that are worthy of rasterization. + return; + } + + transformation_matrix_ = matrix; + transformation_matrix_.preTranslate(offset_.x(), offset_.y()); + + if (!transformation_matrix_.invert(nullptr)) { + // The matrix was singular. No point in going further. + return; + } + + if (context->raster_cached_entries && context->raster_cache) { + context->raster_cached_entries->push_back(this); + cache_state_ = CacheState::kCurrent; + } + return; +} + +void DisplayListRasterCacheItem::PrerollFinalize(PrerollContext* context, + const SkMatrix& matrix) { + if (cache_state_ == CacheState::kNone || !context->raster_cache || + !context->raster_cached_entries) { + return; + } + auto* raster_cache = context->raster_cache; + SkRect bounds = display_list_->bounds().makeOffset(offset_.x(), offset_.y()); + // We must to create an entry whenever if the react is intersect. + // if the rect is intersect we will get the entry access_count to confirm if + // it great than the threshold. Otherwise we only increase the entry + // access_count. + if (context->cull_rect.intersect(bounds)) { + if (raster_cache->MarkSeen(key_id_, transformation_matrix_) < + raster_cache->access_threshold()) { + cache_state_ = CacheState::kNone; + return; + } + context->subtree_can_inherit_opacity = true; + cache_state_ = CacheState::kCurrent; + } else { + raster_cache->Touch(key_id_, matrix); + } + return; +} + +bool DisplayListRasterCacheItem::Draw(const PaintContext& context, + const SkPaint* paint) const { + return Draw(context, context.leaf_nodes_canvas, paint); +} + +bool DisplayListRasterCacheItem::Draw(const PaintContext& context, + SkCanvas* canvas, + const SkPaint* paint) const { + if (!context.raster_cache || !canvas) { + return false; + } + if (cache_state_ == CacheState::kCurrent) { + return context.raster_cache->Draw(key_id_, *canvas, paint); + } + // This display_list doesn't cache itself, this only increase the entry + // access_count; + context.raster_cache->Touch(key_id_, canvas->getTotalMatrix()); + return false; +} + +static const auto* flow_type = "RasterCacheFlow::DisplayList"; + +bool DisplayListRasterCacheItem::TryToPrepareRasterCache( + const PaintContext& context, + bool parent_cached) const { + // If we don't have raster_cache we should not cache the current display_list. + // If the current node's ancestor has been cached we also should not cache the + // current node. In the current frame, the raster_cache will collect all + // display_list or picture_list to calculate the memory they used, we + // shouldn't cache the current node if the memory is more significant than the + // limit. + if (cache_state_ == kNone || !context.raster_cache || parent_cached || + !context.raster_cache->GenerateNewCacheInThisFrame()) { + return false; + } + SkRect bounds = display_list_->bounds().makeOffset(offset_.x(), offset_.y()); + RasterCache::Context r_context = { + // clang-format off + .gr_context = context.gr_context, + .dst_color_space = context.dst_color_space, + .matrix = transformation_matrix_, + .logical_rect = bounds, + .flow_type = flow_type, + .checkerboard = context.checkerboard_offscreen_layers, + // clang-format on + }; + return context.raster_cache->UpdateCacheEntry( + GetId().value(), r_context, + [display_list = display_list_](SkCanvas* canvas) { + display_list->RenderTo(canvas); + }); +} +} // namespace flutter diff --git a/flow/layers/display_list_raster_cache_item.h b/flow/layers/display_list_raster_cache_item.h new file mode 100644 index 0000000000000..65d838939e92d --- /dev/null +++ b/flow/layers/display_list_raster_cache_item.h @@ -0,0 +1,66 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FLOW_DISPLAY_LIST_RASTER_CACHE_ITEM_H_ +#define FLUTTER_FLOW_DISPLAY_LIST_RASTER_CACHE_ITEM_H_ + +#include +#include + +#include "flutter/display_list/display_list.h" +#include "flutter/display_list/display_list_utils.h" +#include "flutter/flow/embedded_views.h" +#include "flutter/flow/raster_cache_item.h" +#include "include/core/SkCanvas.h" +#include "include/core/SkColorSpace.h" +#include "include/core/SkMatrix.h" +#include "include/core/SkPicture.h" +#include "include/core/SkPoint.h" +#include "include/core/SkRect.h" + +namespace flutter { + +class DisplayListRasterCacheItem : public RasterCacheItem { + public: + DisplayListRasterCacheItem(DisplayList* display_list, + const SkPoint& offset, + bool is_complex = true, + bool will_change = false); + + static std::unique_ptr Make(DisplayList*, + const SkPoint& offset, + bool is_complex, + bool will_change); + + void PrerollSetup(PrerollContext* context, const SkMatrix& matrix) override; + + void PrerollFinalize(PrerollContext* context, + const SkMatrix& matrix) override; + + bool Draw(const PaintContext& context, const SkPaint* paint) const override; + + bool Draw(const PaintContext& context, + SkCanvas* canvas, + const SkPaint* paint) const override; + + bool TryToPrepareRasterCache(const PaintContext& context, + bool parent_cached = false) const override; + + void ModifyMatrix(SkPoint offset) const { + matrix_ = matrix_.preTranslate(offset.x(), offset.y()); + } + + const DisplayList* display_list() const { return display_list_; } + + private: + SkMatrix transformation_matrix_; + DisplayList* display_list_; + SkPoint offset_; + bool is_complex_; + bool will_change_; +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_DISPLAY_LIST_RASTER_CACHE_ITEM_H_ diff --git a/flow/layers/image_filter_layer.cc b/flow/layers/image_filter_layer.cc index c39505dea320a..e210309ac95ab 100644 --- a/flow/layers/image_filter_layer.cc +++ b/flow/layers/image_filter_layer.cc @@ -3,13 +3,16 @@ // found in the LICENSE file. #include "flutter/flow/layers/image_filter_layer.h" +#include "flutter/flow/layers/layer.h" +#include "flutter/flow/raster_cache_util.h" namespace flutter { ImageFilterLayer::ImageFilterLayer(sk_sp filter) - : filter_(std::move(filter)), - transformed_filter_(nullptr), - render_count_(1) {} + : CacheableContainerLayer( + RasterCacheUtil::kMinimumRendersBeforeCachingFilterLayer), + filter_(std::move(filter)), + transformed_filter_(nullptr) {} void ImageFilterLayer::Diff(DiffContext* context, const Layer* old_layer) { DiffContext::AutoSubtreeRestore subtree(context); @@ -39,9 +42,12 @@ void ImageFilterLayer::Diff(DiffContext* context, const Layer* old_layer) { void ImageFilterLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "ImageFilterLayer::Preroll"); + Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context); + AutoCache cache = AutoCache(layer_raster_cache_item_.get(), context, matrix); + SkRect child_bounds = SkRect::MakeEmpty(); PrerollChildren(context, matrix, &child_bounds); context->subtree_can_inherit_opacity = true; @@ -62,39 +68,13 @@ void ImageFilterLayer::Preroll(PrerollContext* context, set_paint_bounds(child_bounds); - SkMatrix child_matrix(matrix); - - transformed_filter_ = nullptr; - if (render_count_ >= kMinimumRendersBeforeCachingFilterLayer) { - // We have rendered this same ImageFilterLayer object enough - // times to consider its properties and children to be stable - // from frame to frame so we try to cache the layer itself - // for maximum performance. - TryToPrepareRasterCache(context, this, child_matrix, - RasterCacheLayerStrategy::kLayer); - } else { - // This ImageFilterLayer is not yet considered stable so we - // increment the count to measure how many times it has been - // seen from frame to frame. - render_count_++; - - // Now we will try to pre-render the children into the cache. - // To apply the filter to pre-rendered children, we must first - // modify the filter to be aware of the transform under which - // the cached bitmap was produced. Some SkImageFilter - // instances can do this operation on some transforms and some - // (filters or transforms) cannot. We can only cache the children - // and apply the filter on the fly if this operation succeeds. - transformed_filter_ = filter_->makeWithLocalMatrix(child_matrix); - if (transformed_filter_) { - // With a modified SkImageFilter we can now try to cache the - // children to avoid their rendering costs if they remain - // stable between frames and also avoiding a rendering surface - // switch during the Paint phase even if they are not stable. - // This benefit is seen most during animations. - TryToPrepareRasterCache(context, this, matrix, - RasterCacheLayerStrategy::kLayerChildren); - } + // 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(matrix); + if (transformed_filter_) { + layer_raster_cache_item_->MarkCacheChildren(); } } @@ -104,20 +84,11 @@ void ImageFilterLayer::Paint(PaintContext& context) const { AutoCachePaint cache_paint(context); - if (context.raster_cache) { - if (context.raster_cache->Draw(this, *context.leaf_nodes_canvas, - RasterCacheLayerStrategy::kLayer, - cache_paint.paint())) { - return; - } - if (transformed_filter_) { - cache_paint.setImageFilter(transformed_filter_); - if (context.raster_cache->Draw(this, *context.leaf_nodes_canvas, - RasterCacheLayerStrategy::kLayerChildren, - cache_paint.paint())) { - return; - } - } + if (layer_raster_cache_item_->IsCacheChildren()) { + cache_paint.setImageFilter(transformed_filter_); + } + if (layer_raster_cache_item_->Draw(context, cache_paint.paint())) { + return; } cache_paint.setImageFilter(filter_); diff --git a/flow/layers/image_filter_layer.h b/flow/layers/image_filter_layer.h index 2e2b2a3c5da9a..bb2f3a6d60335 100644 --- a/flow/layers/image_filter_layer.h +++ b/flow/layers/image_filter_layer.h @@ -5,12 +5,13 @@ #ifndef FLUTTER_FLOW_LAYERS_IMAGE_FILTER_LAYER_H_ #define FLUTTER_FLOW_LAYERS_IMAGE_FILTER_LAYER_H_ -#include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/layers/cacheable_layer.h" +#include "flutter/flow/layers/layer.h" #include "third_party/skia/include/core/SkImageFilter.h" namespace flutter { -class ImageFilterLayer : public ContainerLayer { +class ImageFilterLayer : public CacheableContainerLayer { public: explicit ImageFilterLayer(sk_sp filter); @@ -21,25 +22,8 @@ class ImageFilterLayer : public ContainerLayer { void Paint(PaintContext& context) const override; private: - // The ImageFilterLayer might cache the filtered output of this layer - // if the layer remains stable (if it is not animating for instance). - // If the ImageFilterLayer is not the same between rendered frames, - // though, it will cache its children instead and filter their cached - // output on the fly. - // Caching just the children saves the time to render them and also - // avoids a rendering surface switch to draw them. - // Caching the layer itself avoids all of that and additionally avoids - // the cost of applying the filter, but can be worse than caching the - // children if the filter itself is not stable from frame to frame. - // This constant controls how many times we will Preroll and Paint this - // same ImageFilterLayer before we consider the layer and filter to be - // stable enough to switch from caching the children to caching the - // filtered output of this layer. - static constexpr int kMinimumRendersBeforeCachingFilterLayer = 3; - sk_sp filter_; sk_sp transformed_filter_; - int render_count_; FML_DISALLOW_COPY_AND_ASSIGN(ImageFilterLayer); }; diff --git a/flow/layers/image_filter_layer_unittests.cc b/flow/layers/image_filter_layer_unittests.cc index 880f7fda628c8..546331452a9fa 100644 --- a/flow/layers/image_filter_layer_unittests.cc +++ b/flow/layers/image_filter_layer_unittests.cc @@ -4,12 +4,16 @@ #include "flutter/flow/layers/image_filter_layer.h" +#include "flutter/flow/layers/layer_tree.h" #include "flutter/flow/layers/transform_layer.h" #include "flutter/flow/testing/diff_context_test.h" #include "flutter/flow/testing/layer_test.h" #include "flutter/flow/testing/mock_layer.h" #include "flutter/fml/macros.h" #include "flutter/testing/mock_canvas.h" +#include "gtest/gtest.h" +#include "include/core/SkPaint.h" +#include "include/core/SkPath.h" #include "third_party/skia/include/effects/SkImageFilters.h" namespace flutter { @@ -290,34 +294,29 @@ TEST_F(ImageFilterLayerTest, CacheChild) { cache_canvas.setMatrix(cache_ctm); SkCanvas other_canvas; other_canvas.setMatrix(other_transform); + SkPaint paint = SkPaint(); use_mock_raster_cache(); + const auto* cacheable_image_filter_item = layer->raster_cache_item(); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayerChildren)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayerChildren)); + // 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)); layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); - EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayerChildren)); - EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayerChildren)); + // The layer_cache_item's strategy is Children, mean we will must cache + // his children + EXPECT_EQ(cacheable_image_filter_item->cache_state(), + RasterCacheItem::CacheState::kChildren); + EXPECT_TRUE(raster_cache()->Draw(cacheable_image_filter_item->GetId().value(), + cache_canvas, &paint)); + EXPECT_FALSE(raster_cache()->Draw( + cacheable_image_filter_item->GetId().value(), other_canvas, &paint)); } TEST_F(ImageFilterLayerTest, CacheChildren) { @@ -326,6 +325,7 @@ TEST_F(ImageFilterLayerTest, CacheChildren) { SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr); auto initial_transform = SkMatrix::Translate(50.0, 25.5); auto other_transform = SkMatrix::Scale(1.0, 2.0); + SkPaint paint = SkPaint(); const SkPath child_path1 = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); const SkPath child_path2 = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); auto mock_layer1 = std::make_shared(child_path1); @@ -342,35 +342,73 @@ TEST_F(ImageFilterLayerTest, CacheChildren) { use_mock_raster_cache(); + const auto* cacheable_image_filter_item = layer->raster_cache_item(); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), other_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayerChildren)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayerChildren)); + + // 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)); layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); - EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), other_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayerChildren)); - EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayerChildren)); + + // The layer_cache_item's strategy is Children, mean we will must cache his + // children + EXPECT_EQ(cacheable_image_filter_item->cache_state(), + RasterCacheItem::CacheState::kChildren); + EXPECT_TRUE(raster_cache()->Draw(cacheable_image_filter_item->GetId().value(), + cache_canvas, &paint)); + EXPECT_FALSE(raster_cache()->Draw( + cacheable_image_filter_item->GetId().value(), other_canvas, &paint)); +} + +TEST_F(ImageFilterLayerTest, CacheImageFilterLayerSelf) { + auto layer_filter = SkImageFilters::MatrixTransform( + SkMatrix(), + SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr); + auto initial_transform = SkMatrix::Translate(50.0, 25.5); + auto other_transform = SkMatrix::Scale(1.0, 2.0); + 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(layer_filter); + layer->Add(mock_layer); + + SkMatrix cache_ctm = initial_transform; + SkCanvas cache_canvas; + cache_canvas.setMatrix(cache_ctm); + SkCanvas other_canvas; + other_canvas.setMatrix(other_transform); + SkPaint paint = SkPaint(); + + use_mock_raster_cache(); + const auto* cacheable_image_filter_item = layer->raster_cache_item(); + // frame 1. + layer->Preroll(preroll_context(), initial_transform); + layer->Paint(paint_context()); + // frame 2. + layer->Preroll(preroll_context(), initial_transform); + layer->Paint(paint_context()); + // frame 3. + layer->Preroll(preroll_context(), initial_transform); + layer->Paint(paint_context()); + + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); + // frame1,2 cache the ImageFilter's children layer, frame3 cache the + // ImageFilterLayer + EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)2); + + // ImageFilterLayer default cache itself. + EXPECT_EQ(cacheable_image_filter_item->cache_state(), + RasterCacheItem::CacheState::kCurrent); + EXPECT_EQ(cacheable_image_filter_item->GetId(), + RasterCacheKeyID(layer->unique_id(), RasterCacheKeyType::kLayer)); + EXPECT_TRUE(raster_cache()->Draw(cacheable_image_filter_item->GetId().value(), + cache_canvas, &paint)); + EXPECT_FALSE(raster_cache()->Draw( + cacheable_image_filter_item->GetId().value(), other_canvas, &paint)); } TEST_F(ImageFilterLayerTest, OpacityInheritance) { diff --git a/flow/layers/layer.h b/flow/layers/layer.h index 898fa103152a3..f36c13200e367 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -5,7 +5,9 @@ #ifndef FLUTTER_FLOW_LAYERS_LAYER_H_ #define FLUTTER_FLOW_LAYERS_LAYER_H_ +#include #include +#include #include #include "flutter/common/graphics/texture.h" @@ -24,17 +26,20 @@ #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/core/SkPath.h" -#include "third_party/skia/include/core/SkPicture.h" #include "third_party/skia/include/core/SkRRect.h" #include "third_party/skia/include/core/SkRect.h" #include "third_party/skia/include/utils/SkNWayCanvas.h" - namespace flutter { - namespace testing { class MockLayer; } // namespace testing +class ContainerLayer; +class DisplayListLayer; +class PerformanceOverlayLayer; +class TextureLayer; +class RasterCacheItem; + static constexpr SkRect kGiantRect = SkRect::MakeLTRB(-1E9F, -1E9F, 1E9F, 1E9F); // This should be an exact copy of the Clip enum in painting.dart. @@ -99,12 +104,47 @@ struct PrerollContext { // from your Preroll method. (eg. layers that always apply a // saveLayer when rendering anyway can apply the opacity there) bool subtree_can_inherit_opacity = false; + + std::vector* raster_cached_entries; }; -class ContainerLayer; -class DisplayListLayer; -class PerformanceOverlayLayer; -class TextureLayer; +struct PaintContext { + // When splitting the scene into multiple canvases (e.g when embedding + // a platform view on iOS) during the paint traversal we apply the non leaf + // flow layers to all canvases, and leaf layers just to the "current" + // canvas. Applying the non leaf layers to all canvases ensures that when + // we switch a canvas (when painting a PlatformViewLayer) the next canvas + // has the exact same state as the current canvas. + // The internal_nodes_canvas is a SkNWayCanvas which is used by non leaf + // and applies the operations to all canvases. + // The leaf_nodes_canvas is the "current" canvas and is used by leaf + // layers. + SkCanvas* internal_nodes_canvas; + SkCanvas* leaf_nodes_canvas; + GrDirectContext* gr_context; + SkColorSpace* dst_color_space; + ExternalViewEmbedder* view_embedder; + const Stopwatch& raster_time; + const Stopwatch& ui_time; + TextureRegistry& texture_registry; + const RasterCache* raster_cache; + const bool checkerboard_offscreen_layers; + const float frame_device_pixel_ratio = 1.0f; + + // Snapshot store to collect leaf layer snapshots. The store is non-null + // only when leaf layer tracing is enabled. + LayerSnapshotStore* layer_snapshot_store = nullptr; + bool enable_leaf_layer_tracing = false; + + // The following value should be used to modulate the opacity of the + // layer during |Paint|. If the layer does not set the corresponding + // |layer_can_inherit_opacity()| flag, then this value should always + // be |SK_Scalar1|. The value is to be applied as if by using a + // |saveLayer| with an |SkPaint| initialized to this alphaf value and + // a |kSrcOver| blend mode. + SkScalar inherited_opacity = SK_Scalar1; + DisplayListBuilder* leaf_nodes_builder = nullptr; +}; // Represents a single composited layer. Created on the UI thread but then // subquently used on the Rasterizer thread. @@ -164,43 +204,6 @@ class Layer { bool prev_surface_needs_readback_; }; - struct PaintContext { - // When splitting the scene into multiple canvases (e.g when embedding - // a platform view on iOS) during the paint traversal we apply the non leaf - // flow layers to all canvases, and leaf layers just to the "current" - // canvas. Applying the non leaf layers to all canvases ensures that when - // we switch a canvas (when painting a PlatformViewLayer) the next canvas - // has the exact same state as the current canvas. - // The internal_nodes_canvas is a SkNWayCanvas which is used by non leaf - // and applies the operations to all canvases. - // The leaf_nodes_canvas is the "current" canvas and is used by leaf - // layers. - SkCanvas* internal_nodes_canvas; - SkCanvas* leaf_nodes_canvas; - GrDirectContext* gr_context; - ExternalViewEmbedder* view_embedder; - const Stopwatch& raster_time; - const Stopwatch& ui_time; - TextureRegistry& texture_registry; - const RasterCache* raster_cache; - const bool checkerboard_offscreen_layers; - const float frame_device_pixel_ratio = 1.0f; - - // Snapshot store to collect leaf layer snapshots. The store is non-null - // only when leaf layer tracing is enabled. - LayerSnapshotStore* layer_snapshot_store = nullptr; - bool enable_leaf_layer_tracing = false; - - // The following value should be used to modulate the opacity of the - // layer during |Paint|. If the layer does not set the corresponding - // |layer_can_inherit_opacity()| flag, then this value should always - // be |SK_Scalar1|. The value is to be applied as if by using a - // |saveLayer| with an |SkPaint| initialized to this alphaf value and - // a |kSrcOver| blend mode. - SkScalar inherited_opacity = SK_Scalar1; - DisplayListBuilder* leaf_nodes_builder = nullptr; - }; - class AutoCachePaint { public: explicit AutoCachePaint(PaintContext& context) : context_(context) { @@ -299,6 +302,8 @@ class Layer { virtual void Paint(PaintContext& context) const = 0; + virtual void PaintChildren(PaintContext& context) const { FML_DCHECK(false); } + bool subtree_has_platform_view() const { return subtree_has_platform_view_; } void set_subtree_has_platform_view(bool value) { subtree_has_platform_view_ = value; diff --git a/flow/layers/layer_raster_cache_item.cc b/flow/layers/layer_raster_cache_item.cc new file mode 100644 index 0000000000000..d0d987fcaa793 --- /dev/null +++ b/flow/layers/layer_raster_cache_item.cc @@ -0,0 +1,193 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/layer_raster_cache_item.h" +#include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/raster_cache_item.h" + +namespace flutter { + +LayerRasterCacheItem::LayerRasterCacheItem(Layer* layer, + int layer_cached_threshold, + bool can_cache_children) + : RasterCacheItem( + RasterCacheKeyID(layer->unique_id(), RasterCacheKeyType::kLayer), + // The layer raster_cache_item's cache state default value is none. + CacheState::kNone), + layer_(layer), + layer_cached_threshold_(layer_cached_threshold), + can_cache_children_(can_cache_children) {} + +void LayerRasterCacheItem::PrerollSetup(PrerollContext* context, + const SkMatrix& matrix) { + cache_state_ = CacheState::kNone; + if (context->raster_cache && context->raster_cached_entries) { + context->raster_cached_entries->push_back(this); + child_items_ = context->raster_cached_entries->size(); + matrix_ = matrix; + } +} + +std::unique_ptr LayerRasterCacheItem::Make( + Layer* layer, + int layer_cache_threshold, + bool can_cache_children) { + return std::make_unique(layer, layer_cache_threshold, + can_cache_children); +} + +void LayerRasterCacheItem::PrerollFinalize(PrerollContext* context, + const SkMatrix& matrix) { + if (!context->raster_cache || !context->raster_cached_entries) { + return; + } + // We've marked the cache entry that we would like to cache so it stays + // alive, but if the following conditions apply then we need to set our + // state back to kDoNotCache so that we don't populate the entry later. + if (context->has_platform_view || context->has_texture_layer || + !SkRect::Intersects(context->cull_rect, layer_->paint_bounds())) { + return; + } + child_items_ = context->raster_cached_entries->size() - child_items_; + if (num_cache_attempts_ >= layer_cached_threshold_) { + // the layer can be cached + cache_state_ = CacheState::kCurrent; + context->raster_cache->MarkSeen(key_id_, matrix_); + } else { + num_cache_attempts_++; + // access current layer + if (can_cache_children_) { + if (!layer_children_id_.has_value()) { + auto ids = RasterCacheKeyID::LayerChildrenIds(layer_); + if (!ids.has_value()) { + return; + } + layer_children_id_.emplace(std::move(ids.value()), + RasterCacheKeyType::kLayerChildren); + } + cache_state_ = CacheState::kChildren; + context->raster_cache->MarkSeen(layer_children_id_.value(), matrix_); + } + } +} + +std::optional LayerRasterCacheItem::GetId() const { + switch (cache_state_) { + case kCurrent: + return key_id_; + case kChildren: + return layer_children_id_; + default: + return {}; + } +} + +const SkRect* LayerRasterCacheItem::GetPaintBoundsFromLayer() const { + switch (cache_state_) { + case CacheState::kCurrent: + return &(layer_->paint_bounds()); + case CacheState::kChildren: + FML_DCHECK(layer_->as_container_layer()); + return &(layer_->as_container_layer()->child_paint_bounds()); + default: + FML_DCHECK(cache_state_ != CacheState::kNone); + return nullptr; + } +} + +bool Rasterize(RasterCacheItem::CacheState cache_state, + Layer* layer, + const PaintContext& paint_context, + SkCanvas* canvas) { + FML_DCHECK(cache_state != RasterCacheItem::CacheState::kNone); + SkISize canvas_size = canvas->getBaseLayerSize(); + SkNWayCanvas internal_nodes_canvas(canvas_size.width(), canvas_size.height()); + internal_nodes_canvas.setMatrix(canvas->getTotalMatrix()); + internal_nodes_canvas.addCanvas(canvas); + PaintContext context = { + // clang-format off + .internal_nodes_canvas = static_cast(&internal_nodes_canvas), + .leaf_nodes_canvas = canvas, + .gr_context = paint_context.gr_context, + .dst_color_space = paint_context.dst_color_space, + .view_embedder = paint_context.view_embedder, + .raster_time = paint_context.raster_time, + .ui_time = paint_context.ui_time, + .texture_registry = paint_context.texture_registry, + .raster_cache = paint_context.raster_cache, + .checkerboard_offscreen_layers = paint_context.checkerboard_offscreen_layers, + .frame_device_pixel_ratio = paint_context.frame_device_pixel_ratio, + // clang-format on + }; + + switch (cache_state) { + case RasterCacheItem::CacheState::kCurrent: + FML_DCHECK(layer->needs_painting(context)); + layer->Paint(context); + break; + case RasterCacheItem::CacheState::kChildren: + layer->PaintChildren(context); + break; + case RasterCacheItem::CacheState::kNone: + FML_DCHECK(cache_state != RasterCacheItem::CacheState::kNone); + return false; + } + return true; +} + +static const auto* flow_type = "RasterCacheFlow::Layer"; + +bool LayerRasterCacheItem::TryToPrepareRasterCache(const PaintContext& context, + bool parent_cached) const { + if (!context.raster_cache || parent_cached) { + return false; + } + if (cache_state_ != kNone) { + if (const SkRect* paint_bounds = GetPaintBoundsFromLayer()) { + RasterCache::Context r_context = { + // clang-format off + .gr_context = context.gr_context, + .dst_color_space = context.dst_color_space, + .matrix = matrix_, + .logical_rect = *paint_bounds, + .flow_type = flow_type, + .checkerboard = context.checkerboard_offscreen_layers, + // clang-format on + }; + return context.raster_cache->UpdateCacheEntry( + GetId().value(), r_context, + [ctx = context, cache_state = cache_state_, + layer = layer_](SkCanvas* canvas) { + Rasterize(cache_state, layer, ctx, canvas); + }); + } + } + return false; +} + +bool LayerRasterCacheItem::Draw(const PaintContext& context, + const SkPaint* paint) const { + return Draw(context, context.leaf_nodes_canvas, paint); +} + +bool LayerRasterCacheItem::Draw(const PaintContext& context, + SkCanvas* canvas, + const SkPaint* paint) const { + if (!context.raster_cache || !canvas) { + return false; + } + switch (cache_state_) { + case RasterCacheItem::kNone: + return false; + case RasterCacheItem::kCurrent: { + return context.raster_cache->Draw(key_id_, *canvas, paint); + } + case RasterCacheItem::kChildren: { + return context.raster_cache->Draw(layer_children_id_.value(), *canvas, + paint); + } + } +} + +} // namespace flutter diff --git a/flow/layers/layer_raster_cache_item.h b/flow/layers/layer_raster_cache_item.h new file mode 100644 index 0000000000000..3588abdf3b627 --- /dev/null +++ b/flow/layers/layer_raster_cache_item.h @@ -0,0 +1,72 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FLOW_LAYER_RASTER_CACHE_ITEM_H_ +#define FLUTTER_FLOW_LAYER_RASTER_CACHE_ITEM_H_ + +#include +#include + +#include "flutter/flow/raster_cache_item.h" + +namespace flutter { + +class LayerRasterCacheItem : public RasterCacheItem { + public: + explicit LayerRasterCacheItem(Layer* layer, + int layer_cached_threshold = 1, + bool can_cache_children = false); + + /** + * @brief Create a LayerRasterCacheItem, connect a layer and manage the + * Layer's raster cache + * + * @param layer_cache_threshold after how many frames to start trying to + * cache the layer self + * @param can_cache_children the layer can do a cache for his children + */ + static std::unique_ptr + Make(Layer*, int layer_cache_threshold, bool can_cache_children = false); + + std::optional GetId() const override; + + void PrerollSetup(PrerollContext* context, const SkMatrix& matrix) override; + + void PrerollFinalize(PrerollContext* context, + const SkMatrix& matrix) override; + + bool Draw(const PaintContext& context, const SkPaint* paint) const override; + + bool Draw(const PaintContext& context, + SkCanvas* canvas, + const SkPaint* paint) const override; + + bool TryToPrepareRasterCache(const PaintContext& context, + bool parent_cached = false) const override; + + void MarkCacheChildren() { can_cache_children_ = true; } + + void MarkNotCacheChildren() { can_cache_children_ = false; } + + bool IsCacheChildren() const { return cache_state_ == CacheState::kChildren; } + + protected: + const SkRect* GetPaintBoundsFromLayer() const; + + Layer* layer_; + + // The id for cache the layer's children. + std::optional layer_children_id_; + + int layer_cached_threshold_ = 1; + + // if the layer's children can be directly cache, set the param is true; + bool can_cache_children_ = false; + + mutable int num_cache_attempts_ = 1; +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_LAYER_RASTER_CACHE_ITEM_H_ diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index 5d209bf7d737f..4d9ee26be17f1 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -4,11 +4,15 @@ #include "flutter/flow/layers/layer_tree.h" +#include "flutter/flow/embedded_views.h" #include "flutter/flow/frame_timings.h" #include "flutter/flow/layer_snapshot_store.h" +#include "flutter/flow/layers/cacheable_layer.h" #include "flutter/flow/layers/layer.h" +#include "flutter/flow/raster_cache.h" #include "flutter/fml/time/time_point.h" #include "flutter/fml/trace_event.h" +#include "include/core/SkMatrix.h" #include "third_party/skia/include/core/SkPictureRecorder.h" #include "third_party/skia/include/utils/SkNWayCanvas.h" @@ -23,6 +27,10 @@ LayerTree::LayerTree(const SkISize& frame_size, float device_pixel_ratio) FML_CHECK(device_pixel_ratio_ != 0.0f); } +inline SkColorSpace* GetColorSpace(SkCanvas* canvas) { + return canvas ? canvas->imageInfo().colorSpace() : nullptr; +} + bool LayerTree::Preroll(CompositorContext::ScopedFrame& frame, bool ignore_raster_cache, SkRect cull_rect) { @@ -33,13 +41,14 @@ bool LayerTree::Preroll(CompositorContext::ScopedFrame& frame, return false; } - SkColorSpace* color_space = - frame.canvas() ? frame.canvas()->imageInfo().colorSpace() : nullptr; + SkColorSpace* color_space = GetColorSpace(frame.canvas()); frame.context().raster_cache().SetCheckboardCacheImages( checkerboard_raster_cache_images_); MutatorsStack stack; RasterCache* cache = ignore_raster_cache ? nullptr : &frame.context().raster_cache(); + raster_cache_items_.clear(); + PrerollContext context = { // clang-format off .raster_cache = cache, @@ -54,13 +63,43 @@ bool LayerTree::Preroll(CompositorContext::ScopedFrame& frame, .texture_registry = frame.context().texture_registry(), .checkerboard_offscreen_layers = checkerboard_offscreen_layers_, .frame_device_pixel_ratio = device_pixel_ratio_, + .raster_cached_entries = &raster_cache_items_, // clang-format on }; root_layer_->Preroll(&context, frame.root_surface_transformation()); + return context.surface_needs_readback; } +void LayerTree::TryToRasterCache( + const std::vector& raster_cached_items, + const PaintContext* paint_context, + bool ignore_raster_cache) { + unsigned i = 0; + const auto item_size = raster_cached_items.size(); + while (i < item_size) { + auto* item = raster_cached_items[i]; + if (item->need_caching()) { + // try to cache current layer + // If parent failed to cache, just proceed to the next entry + // cache current entry, this entry's parent must not cache + if (item->TryToPrepareRasterCache(*paint_context, false)) { + // if parent cached, then foreach child layer to touch them. + for (unsigned j = 0; j < item->child_items(); j++) { + auto* child_item = raster_cached_items[i + j + 1]; + if (child_item->need_caching()) { + child_item->TryToPrepareRasterCache(*paint_context, true); + } + } + i += item->child_items() + 1; + continue; + } + } + i++; + } +} + void LayerTree::Paint(CompositorContext::ScopedFrame& frame, bool ignore_raster_cache) const { TRACE_EVENT0("flutter", "LayerTree::Paint"); @@ -87,13 +126,15 @@ void LayerTree::Paint(CompositorContext::ScopedFrame& frame, snapshot_store = &frame.context().snapshot_store(); } + SkColorSpace* color_space = GetColorSpace(frame.canvas()); RasterCache* cache = ignore_raster_cache ? nullptr : &frame.context().raster_cache(); - Layer::PaintContext context = { + PaintContext context = { // clang-format off .internal_nodes_canvas = &internal_nodes_canvas, .leaf_nodes_canvas = frame.canvas(), .gr_context = frame.gr_context(), + .dst_color_space = color_space, .view_embedder = frame.view_embedder(), .raster_time = frame.context().raster_time(), .ui_time = frame.context().ui_time(), @@ -108,6 +149,10 @@ void LayerTree::Paint(CompositorContext::ScopedFrame& frame, // clang-format on }; + if (cache) { + TryToRasterCache(raster_cache_items_, &context, ignore_raster_cache); + } + if (root_layer_->needs_painting(context)) { root_layer_->Paint(context); } @@ -146,11 +191,12 @@ sk_sp LayerTree::Flatten(const SkRect& bounds) { SkNWayCanvas internal_nodes_canvas(canvas_size.width(), canvas_size.height()); internal_nodes_canvas.addCanvas(&builder); - Layer::PaintContext paint_context = { + PaintContext paint_context = { // clang-format off .internal_nodes_canvas = &internal_nodes_canvas, .leaf_nodes_canvas = &builder, .gr_context = nullptr, + .dst_color_space = nullptr, .view_embedder = nullptr, .raster_time = unused_stopwatch, .ui_time = unused_stopwatch, diff --git a/flow/layers/layer_tree.h b/flow/layers/layer_tree.h index 6b1db0bc54fd4..c7daa5f809e9f 100644 --- a/flow/layers/layer_tree.h +++ b/flow/layers/layer_tree.h @@ -10,6 +10,7 @@ #include "flutter/flow/compositor_context.h" #include "flutter/flow/layers/layer.h" +#include "flutter/flow/raster_cache.h" #include "flutter/fml/macros.h" #include "flutter/fml/time/time_delta.h" #include "third_party/skia/include/core/SkPicture.h" @@ -32,6 +33,11 @@ class LayerTree { bool ignore_raster_cache = false, SkRect cull_rect = kGiantRect); + static void TryToRasterCache( + const std::vector& raster_cached_entries, + const PaintContext* paint_context, + bool ignore_raster_cache = false); + void Paint(CompositorContext::ScopedFrame& frame, bool ignore_raster_cache = false) const; @@ -91,6 +97,8 @@ class LayerTree { PaintRegionMap paint_region_map_; + std::vector raster_cache_items_; + FML_DISALLOW_COPY_AND_ASSIGN(LayerTree); }; diff --git a/flow/layers/layer_tree_unittests.cc b/flow/layers/layer_tree_unittests.cc index 289139b1b8067..fa3bb00b455eb 100644 --- a/flow/layers/layer_tree_unittests.cc +++ b/flow/layers/layer_tree_unittests.cc @@ -2,10 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include "flutter/flow/layers/layer_tree.h" #include "flutter/flow/compositor_context.h" #include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/raster_cache.h" #include "flutter/flow/testing/mock_layer.h" #include "flutter/fml/macros.h" #include "flutter/testing/canvas_test.h" @@ -14,7 +16,6 @@ namespace flutter { namespace testing { - class LayerTreeTest : public CanvasTest { public: LayerTreeTest() @@ -198,7 +199,7 @@ TEST_F(LayerTreeTest, PrerollContextInitialization) { // PrerollContext that this test must be revisited and updated. // If any fields get removed or replaced, then the expect_defaults closure // will fail to compile, again bringing attention to updating this test. - EXPECT_EQ(sizeof(PrerollContext), size_t(104)); + EXPECT_EQ(sizeof(PrerollContext), size_t(112)); MutatorsStack mock_mutators; FixedRefreshRateStopwatch mock_raster_time; @@ -225,6 +226,7 @@ TEST_F(LayerTreeTest, PrerollContextInitialization) { EXPECT_EQ(context.has_texture_layer, false); EXPECT_EQ(context.subtree_can_inherit_opacity, false); + EXPECT_EQ(context.raster_cached_entries, nullptr); }; // These 4 initializers are required because they are handled by reference @@ -242,14 +244,14 @@ TEST_F(LayerTreeTest, PaintContextInitialization) { // PaintContext that this test must be revisited and updated. // If any fields get removed or replaced, then the expect_defaults closure // will fail to compile, again bringing attention to updating this test. - EXPECT_EQ(sizeof(Layer::PaintContext), size_t(96)); + EXPECT_EQ(sizeof(PaintContext), size_t(104)); FixedRefreshRateStopwatch mock_raster_time; FixedRefreshRateStopwatch mock_ui_time; TextureRegistry mock_registry; auto expect_defaults = [&mock_raster_time, &mock_ui_time, - &mock_registry](const Layer::PaintContext& context) { + &mock_registry](const PaintContext& context) { EXPECT_EQ(context.internal_nodes_canvas, nullptr); EXPECT_EQ(context.leaf_nodes_canvas, nullptr); EXPECT_EQ(context.gr_context, nullptr); @@ -269,7 +271,7 @@ TEST_F(LayerTreeTest, PaintContextInitialization) { }; // These 4 initializers are required because they are handled by reference - Layer::PaintContext context{ + PaintContext context{ .raster_time = mock_raster_time, .ui_time = mock_ui_time, .texture_registry = mock_registry, diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index 21acc35007ba9..e9702629052ca 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -4,13 +4,18 @@ #include "flutter/flow/layers/opacity_layer.h" -#include "flutter/fml/trace_event.h" +#include "flutter/flow/layers/cacheable_layer.h" #include "third_party/skia/include/core/SkPaint.h" namespace flutter { +// the opacity_layer couldn't cache itself, so the cache_threshold is the +// max_int OpacityLayer::OpacityLayer(SkAlpha alpha, const SkPoint& offset) - : alpha_(alpha), offset_(offset), children_can_accept_opacity_(false) {} + : CacheableContainerLayer(std::numeric_limits::max(), true), + alpha_(alpha), + offset_(offset), + children_can_accept_opacity_(false) {} void OpacityLayer::Diff(DiffContext* context, const Layer* old_layer) { DiffContext::AutoSubtreeRestore subtree(context); @@ -40,32 +45,34 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { context->mutators_stack.PushTransform( SkMatrix::Translate(offset_.fX, offset_.fY)); context->mutators_stack.PushOpacity(alpha_); + + AutoCache auto_cache = + AutoCache(layer_raster_cache_item_.get(), context, matrix); Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context); // Collect inheritance information on our children in Preroll so that // we can decide whether or not to use a saveLayer in Paint. context->subtree_can_inherit_opacity = true; - // ContainerLayer will turn the flag off if any children are // incompatible or if they overlap ContainerLayer::Preroll(context, child_matrix); - // We store the inheritance ability of our children for |Paint| set_children_can_accept_opacity(context->subtree_can_inherit_opacity); // Now we let our parent layers know that we, too, can inherit opacity // regardless of what our children are capable of context->subtree_can_inherit_opacity = true; - context->mutators_stack.Pop(); context->mutators_stack.Pop(); set_paint_bounds(paint_bounds().makeOffset(offset_.fX, offset_.fY)); - if (!children_can_accept_opacity()) { - TryToPrepareRasterCache(context, this, child_matrix, - RasterCacheLayerStrategy::kLayerChildren); + if (children_can_accept_opacity()) { + // For opacity layer, we can use raster_cache children only when the + // children can't accept opacity so if the children_can_accept_opacity we + // should tell the AutoCache object don't do raster_cache. + auto_cache.ShouldNotBeCached(); } // Restore cull_rect @@ -92,10 +99,7 @@ void OpacityLayer::Paint(PaintContext& context) const { SkPaint paint; paint.setAlphaf(subtree_opacity); - if (context.raster_cache && - context.raster_cache->Draw(this, *context.leaf_nodes_canvas, - RasterCacheLayerStrategy::kLayerChildren, - &paint)) { + if (layer_raster_cache_item_->Draw(context, &paint)) { return; } diff --git a/flow/layers/opacity_layer.h b/flow/layers/opacity_layer.h index 32c25c5ba1948..aed2e7c7b0fd6 100644 --- a/flow/layers/opacity_layer.h +++ b/flow/layers/opacity_layer.h @@ -5,7 +5,9 @@ #ifndef FLUTTER_FLOW_LAYERS_OPACITY_LAYER_H_ #define FLUTTER_FLOW_LAYERS_OPACITY_LAYER_H_ -#include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/layers/cacheable_layer.h" +#include "flutter/flow/layers/layer.h" +#include "include/core/SkMatrix.h" namespace flutter { @@ -13,7 +15,7 @@ namespace flutter { // OpacityLayer is very costly due to the saveLayer call. If there's no child, // having the OpacityLayer or not has the same effect. In debug_unopt build, // |Preroll| will assert if there are no children. -class OpacityLayer : public ContainerLayer { +class OpacityLayer : public CacheableContainerLayer { public: // An offset is provided here because OpacityLayer.addToScene method in the // Flutter framework can take an optional offset argument. diff --git a/flow/layers/opacity_layer_unittests.cc b/flow/layers/opacity_layer_unittests.cc index eafef0e52636c..c662e73284ec4 100644 --- a/flow/layers/opacity_layer_unittests.cc +++ b/flow/layers/opacity_layer_unittests.cc @@ -6,6 +6,7 @@ #include "flutter/flow/layers/clip_rect_layer.h" #include "flutter/flow/layers/image_filter_layer.h" +#include "flutter/flow/layers/layer_tree.h" #include "flutter/flow/layers/transform_layer.h" #include "flutter/flow/testing/diff_context_test.h" #include "flutter/flow/testing/layer_test.h" @@ -13,6 +14,8 @@ #include "flutter/fml/macros.h" #include "flutter/testing/display_list_testing.h" #include "flutter/testing/mock_canvas.h" +#include "gtest/gtest.h" +#include "include/core/SkPaint.h" namespace flutter { namespace testing { @@ -84,6 +87,7 @@ TEST_F(OpacityLayerTest, CacheChild) { auto layer = std::make_shared(alpha_half, SkPoint::Make(0.0f, 0.0f)); layer->Add(mock_layer); + SkPaint paint; SkMatrix cache_ctm = initial_transform; SkCanvas cache_canvas; @@ -94,30 +98,29 @@ TEST_F(OpacityLayerTest, CacheChild) { use_mock_raster_cache(); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayerChildren)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayerChildren)); + + 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()); layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); - EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayerChildren)); - EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayerChildren)); + + EXPECT_EQ(cacheable_opacity_item->cache_state(), + RasterCacheItem::CacheState::kChildren); + EXPECT_EQ( + cacheable_opacity_item->GetId().value(), + RasterCacheKeyID(RasterCacheKeyID::LayerChildrenIds(layer.get()).value(), + RasterCacheKeyType::kLayerChildren)); + EXPECT_FALSE(raster_cache()->Draw(cacheable_opacity_item->GetId().value(), + other_canvas, &paint)); + EXPECT_TRUE(raster_cache()->Draw(cacheable_opacity_item->GetId().value(), + cache_canvas, &paint)); } TEST_F(OpacityLayerTest, CacheChildren) { @@ -126,6 +129,7 @@ TEST_F(OpacityLayerTest, CacheChildren) { auto other_transform = SkMatrix::Scale(1.0, 2.0); const SkPath child_path1 = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); const SkPath child_path2 = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); + SkPaint paint; auto mock_layer1 = std::make_shared(child_path1); auto mock_layer2 = std::make_shared(child_path2); auto layer = @@ -142,34 +146,61 @@ TEST_F(OpacityLayerTest, CacheChildren) { use_mock_raster_cache(); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), other_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayerChildren)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayerChildren)); + + 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()); layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); - EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), other_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas)); - EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas, - RasterCacheLayerStrategy::kLayerChildren)); - EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayerChildren)); + + EXPECT_EQ(cacheable_opacity_item->cache_state(), + RasterCacheItem::CacheState::kChildren); + EXPECT_EQ( + cacheable_opacity_item->GetId().value(), + RasterCacheKeyID(RasterCacheKeyID::LayerChildrenIds(layer.get()).value(), + RasterCacheKeyType::kLayerChildren)); + EXPECT_FALSE(raster_cache()->Draw(cacheable_opacity_item->GetId().value(), + other_canvas, &paint)); + EXPECT_TRUE(raster_cache()->Draw(cacheable_opacity_item->GetId().value(), + cache_canvas, &paint)); +} + +TEST_F(OpacityLayerTest, ShouldNotCacheChildren) { + SkPaint paint; + auto opacityLayer = + std::make_shared(128, SkPoint::Make(20, 20)); + auto mockLayer = MockLayer::MakeOpacityCompatible(SkPath()); + opacityLayer->Add(mockLayer); + + PrerollContext* context = preroll_context(); + context->subtree_can_inherit_opacity = false; + + use_mock_raster_cache(); + + EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); + + const auto* cacheable_opacity_item = opacityLayer->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()); + + opacityLayer->Preroll(preroll_context(), SkMatrix::I()); + + EXPECT_TRUE(context->subtree_can_inherit_opacity); + EXPECT_TRUE(opacityLayer->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(), + RasterCacheItem::CacheState::kNone); + EXPECT_FALSE(cacheable_opacity_item->Draw(paint_context(), &paint)); } TEST_F(OpacityLayerTest, FullyOpaque) { diff --git a/flow/layers/performance_overlay_layer_unittests.cc b/flow/layers/performance_overlay_layer_unittests.cc index 88ca5b9ab08f8..ae9eec8b767e4 100644 --- a/flow/layers/performance_overlay_layer_unittests.cc +++ b/flow/layers/performance_overlay_layer_unittests.cc @@ -59,7 +59,7 @@ static void TestPerformanceOverlayLayerGold(int refresh_rate) { ASSERT_TRUE(surface != nullptr); flutter::TextureRegistry unused_texture_registry; - flutter::Layer::PaintContext paintContext = { + flutter::PaintContext paintContext = { // clang-format off .internal_nodes_canvas = nullptr, .leaf_nodes_canvas = surface->getCanvas(), diff --git a/flow/layers/shader_mask_layer.cc b/flow/layers/shader_mask_layer.cc index 936acb281c20f..ee1dacae86c7a 100644 --- a/flow/layers/shader_mask_layer.cc +++ b/flow/layers/shader_mask_layer.cc @@ -3,16 +3,18 @@ // found in the LICENSE file. #include "flutter/flow/layers/shader_mask_layer.h" +#include "flutter/flow/raster_cache_util.h" namespace flutter { ShaderMaskLayer::ShaderMaskLayer(sk_sp shader, const SkRect& mask_rect, SkBlendMode blend_mode) - : shader_(shader), + : CacheableContainerLayer( + RasterCacheUtil::kMinimumRendersBeforeCachingFilterLayer), + shader_(shader), mask_rect_(mask_rect), - blend_mode_(blend_mode), - render_count_(1) {} + blend_mode_(blend_mode) {} void ShaderMaskLayer::Diff(DiffContext* context, const Layer* old_layer) { DiffContext::AutoSubtreeRestore subtree(context); @@ -24,7 +26,6 @@ void ShaderMaskLayer::Diff(DiffContext* context, const Layer* old_layer) { context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer)); } } - DiffChildren(context, prev); context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); @@ -33,19 +34,13 @@ void ShaderMaskLayer::Diff(DiffContext* context, const Layer* old_layer) { void ShaderMaskLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context); - ContainerLayer::Preroll(context, matrix); + AutoCache cache = AutoCache(layer_raster_cache_item_.get(), context, matrix); + + ContainerLayer::Preroll(context, matrix); // We always paint with a saveLayer (or a cached rendering), // so we can always apply opacity in any of those cases. context->subtree_can_inherit_opacity = true; - - SkMatrix child_matrix(matrix); - if (render_count_ >= kMinimumRendersBeforeCachingFilterLayer) { - TryToPrepareRasterCache(context, this, child_matrix, - RasterCacheLayerStrategy::kLayer); - } else { - render_count_++; - } } void ShaderMaskLayer::Paint(PaintContext& context) const { @@ -54,11 +49,10 @@ void ShaderMaskLayer::Paint(PaintContext& context) const { AutoCachePaint cache_paint(context); - if (context.raster_cache && - context.raster_cache->Draw(this, *context.leaf_nodes_canvas, - RasterCacheLayerStrategy::kLayer, - cache_paint.paint())) { - return; + if (context.raster_cache) { + if (layer_raster_cache_item_->Draw(context, cache_paint.paint())) { + return; + } } Layer::AutoSaveLayer save = Layer::AutoSaveLayer::Create( diff --git a/flow/layers/shader_mask_layer.h b/flow/layers/shader_mask_layer.h index 06bd1622458cb..0dd44e3dedec7 100644 --- a/flow/layers/shader_mask_layer.h +++ b/flow/layers/shader_mask_layer.h @@ -5,12 +5,12 @@ #ifndef FLUTTER_FLOW_LAYERS_SHADER_MASK_LAYER_H_ #define FLUTTER_FLOW_LAYERS_SHADER_MASK_LAYER_H_ -#include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/layers/cacheable_layer.h" #include "third_party/skia/include/core/SkShader.h" namespace flutter { -class ShaderMaskLayer : public ContainerLayer { +class ShaderMaskLayer : public CacheableContainerLayer { public: ShaderMaskLayer(sk_sp shader, const SkRect& mask_rect, @@ -27,9 +27,6 @@ class ShaderMaskLayer : public ContainerLayer { SkRect mask_rect_; SkBlendMode blend_mode_; - static constexpr int kMinimumRendersBeforeCachingFilterLayer = 3; - int render_count_; - FML_DISALLOW_COPY_AND_ASSIGN(ShaderMaskLayer); }; diff --git a/flow/layers/shader_mask_layer_unittests.cc b/flow/layers/shader_mask_layer_unittests.cc index 532ab78425e89..d57a2753d4bea 100644 --- a/flow/layers/shader_mask_layer_unittests.cc +++ b/flow/layers/shader_mask_layer_unittests.cc @@ -4,11 +4,14 @@ #include "flutter/flow/layers/shader_mask_layer.h" +#include "flutter/flow/layers/layer_tree.h" #include "flutter/flow/layers/opacity_layer.h" +#include "flutter/flow/raster_cache.h" #include "flutter/flow/testing/layer_test.h" #include "flutter/flow/testing/mock_layer.h" #include "flutter/fml/macros.h" #include "flutter/testing/mock_canvas.h" +#include "gtest/gtest.h" #include "third_party/skia/include/core/SkShader.h" #include "third_party/skia/include/effects/SkPerlinNoiseShader.h" @@ -286,6 +289,7 @@ TEST_F(ShaderMaskLayerTest, Readback) { TEST_F(ShaderMaskLayerTest, LayerCached) { auto layer_filter = SkPerlinNoiseShader::MakeFractalNoise(1.0f, 1.0f, 1, 1.0f); + SkPaint paint; const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 20.5f, 20.5f); auto initial_transform = SkMatrix::Translate(50.0, 25.5); const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); @@ -299,25 +303,39 @@ TEST_F(ShaderMaskLayerTest, LayerCached) { cache_canvas.setMatrix(cache_ctm); use_mock_raster_cache(); + const auto* cacheable_shader_masker_item = layer->raster_cache_item(); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); + EXPECT_EQ(cacheable_shader_masker_item->cache_state(), + RasterCacheItem::CacheState::kNone); + EXPECT_FALSE(cacheable_shader_masker_item->GetId().has_value()); + // frame 1. layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); + EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); + EXPECT_EQ(cacheable_shader_masker_item->cache_state(), + RasterCacheItem::CacheState::kNone); + EXPECT_FALSE(cacheable_shader_masker_item->GetId().has_value()); + // frame 2. layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); - EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); + EXPECT_EQ(cacheable_shader_masker_item->cache_state(), + RasterCacheItem::CacheState::kNone); + EXPECT_FALSE(cacheable_shader_masker_item->GetId().has_value()); + // frame 3. layer->Preroll(preroll_context(), initial_transform); + LayerTree::TryToRasterCache(cacheable_items(), &paint_context()); EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); - EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas, - RasterCacheLayerStrategy::kLayer)); + EXPECT_EQ(cacheable_shader_masker_item->cache_state(), + RasterCacheItem::CacheState::kCurrent); + + EXPECT_TRUE(raster_cache()->Draw( + cacheable_shader_masker_item->GetId().value(), cache_canvas, &paint)); } TEST_F(ShaderMaskLayerTest, OpacityInheritance) { diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index f65a9b3842a85..720efea1866ff 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -4,17 +4,20 @@ #include "flutter/flow/raster_cache.h" +#include #include #include "flutter/common/constants.h" #include "flutter/flow/layers/container_layer.h" #include "flutter/flow/layers/layer.h" #include "flutter/flow/paint_utils.h" +#include "flutter/flow/raster_cache_util.h" #include "flutter/fml/logging.h" #include "flutter/fml/trace_event.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkColorSpace.h" #include "third_party/skia/include/core/SkImage.h" +#include "third_party/skia/include/core/SkPicture.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrDirectContext.h" @@ -30,7 +33,7 @@ void RasterCacheResult::draw(SkCanvas& canvas, const SkPaint* paint) const { SkAutoCanvasRestore auto_restore(&canvas, true); SkRect bounds = - RasterCache::GetDeviceBounds(logical_rect_, canvas.getTotalMatrix()); + RasterCacheUtil::GetDeviceBounds(logical_rect_, canvas.getTotalMatrix()); FML_DCHECK(std::abs(bounds.width() - image_->dimensions().width()) <= 1 && std::abs(bounds.height() - image_->dimensions().height()) <= 1); canvas.resetMatrix(); @@ -45,71 +48,25 @@ RasterCache::RasterCache(size_t access_threshold, display_list_cache_limit_per_frame_(display_list_cache_limit_per_frame), checkerboard_images_(false) {} -static bool CanRasterizeRect(const SkRect& cull_rect) { - if (cull_rect.isEmpty()) { - // No point in ever rasterizing an empty display list. - return false; - } - - if (!cull_rect.isFinite()) { - // Cannot attempt to rasterize into an infinitely large surface. - FML_LOG(INFO) << "Attempted to raster cache non-finite display list"; - return false; - } - - return true; -} - -static bool IsDisplayListWorthRasterizing( - DisplayList* display_list, - bool will_change, - bool is_complex, - DisplayListComplexityCalculator* complexity_calculator) { - if (will_change) { - // If the display list is going to change in the future, there is no point - // in doing to extra work to rasterize. - return false; - } - - if (display_list == nullptr || !CanRasterizeRect(display_list->bounds())) { - // No point in deciding whether the display list is worth rasterizing if it - // cannot be rasterized at all. - return false; - } - - if (is_complex) { - // The caller seems to have extra information about the display list and - // thinks the display list is always worth rasterizing. - return true; - } - - unsigned int complexity_score = complexity_calculator->Compute(display_list); - return complexity_calculator->ShouldBeCached(complexity_score); -} - /// @note Procedure doesn't copy all closures. -static std::unique_ptr Rasterize( - GrDirectContext* context, - const SkMatrix& ctm, - SkColorSpace* dst_color_space, - bool checkerboard, - const SkRect& logical_rect, - const char* type, +std::unique_ptr RasterCache::Rasterize( + const RasterCache::Context& context, const std::function& draw_function) { TRACE_EVENT0("flutter", "RasterCachePopulate"); - SkRect dest_rect = RasterCache::GetDeviceBounds(logical_rect, ctm); + SkRect dest_rect = + RasterCacheUtil::GetDeviceBounds(context.logical_rect, context.matrix); // we always round out here so that the texture is integer sized. int width = SkScalarCeilToInt(dest_rect.width()); int height = SkScalarCeilToInt(dest_rect.height()); - const SkImageInfo image_info = - SkImageInfo::MakeN32Premul(width, height, sk_ref_sp(dst_color_space)); + const SkImageInfo image_info = SkImageInfo::MakeN32Premul( + width, height, sk_ref_sp(context.dst_color_space)); sk_sp surface = - context - ? SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, image_info) - : SkSurface::MakeRaster(image_info); + context.gr_context ? SkSurface::MakeRenderTarget( + context.gr_context, SkBudgeted::kYes, image_info) + : SkSurface::MakeRaster(image_info); if (!surface) { return nullptr; @@ -118,230 +75,74 @@ static std::unique_ptr Rasterize( SkCanvas* canvas = surface->getCanvas(); canvas->clear(SK_ColorTRANSPARENT); canvas->translate(-dest_rect.left(), -dest_rect.top()); - canvas->concat(ctm); + canvas->concat(context.matrix); draw_function(canvas); - if (checkerboard) { - DrawCheckerboard(canvas, logical_rect); + if (context.checkerboard) { + DrawCheckerboard(canvas, context.logical_rect); } - return std::make_unique(surface->makeImageSnapshot(), - logical_rect, type); + return std::make_unique( + surface->makeImageSnapshot(), context.logical_rect, context.flow_type); } -std::unique_ptr RasterCache::RasterizeDisplayList( - DisplayList* display_list, - GrDirectContext* context, - const SkMatrix& ctm, - SkColorSpace* dst_color_space, - bool checkerboard) const { - return Rasterize(context, ctm, dst_color_space, checkerboard, - display_list->bounds(), "RasterCacheFlow::DisplayList", - [=](SkCanvas* canvas) { display_list->RenderTo(canvas); }); -} - -void RasterCache::Prepare(PrerollContext* context, - Layer* layer, - const SkMatrix& ctm, - RasterCacheLayerStrategy strategy) { - auto cache_key_optional = - TryToMakeRasterCacheKeyForLayer(layer, strategy, ctm); - if (!cache_key_optional) { - return; - } - Entry& entry = cache_[cache_key_optional.value()]; - entry.access_count++; +bool RasterCache::UpdateCacheEntry( + const RasterCacheKeyID& id, + const Context& raster_cache_context, + const std::function& render_function) const { + RasterCacheKey key = RasterCacheKey(id, raster_cache_context.matrix); + Entry& entry = cache_[key]; entry.used_this_frame = true; if (!entry.image) { - entry.image = - RasterizeLayer(context, layer, strategy, ctm, checkerboard_images_); - } -} - -std::optional RasterCache::TryToMakeRasterCacheKeyForLayer( - const Layer* layer, - RasterCacheLayerStrategy strategy, - const SkMatrix& ctm) const { - switch (strategy) { - case RasterCacheLayerStrategy::kLayer: - return RasterCacheKey(layer->unique_id(), RasterCacheKeyType::kLayer, - ctm); - case RasterCacheLayerStrategy::kLayerChildren: - FML_DCHECK(layer->as_container_layer()); - auto& children_layers = layer->as_container_layer()->layers(); - auto children_count = children_layers.size(); - if (children_count == 0) { - return std::nullopt; - } - std::vector ids; - std::transform(children_layers.begin(), children_layers.end(), - std::back_inserter(ids), [](auto& layer) -> uint64_t { - return layer->unique_id(); - }); - return RasterCacheKey(RasterCacheKeyID(std::move(ids)), - RasterCacheKeyType::kLayerChildren, ctm); - } -} - -std::unique_ptr RasterCache::RasterizeLayer( - PrerollContext* context, - Layer* layer, - RasterCacheLayerStrategy strategy, - const SkMatrix& ctm, - bool checkerboard) const { - const SkRect& paint_bounds = GetPaintBoundsFromLayer(layer, strategy); - - return Rasterize( - context->gr_context, ctm, context->dst_color_space, checkerboard, - paint_bounds, "RasterCacheFlow::Layer", - [layer, context, strategy](SkCanvas* canvas) { - SkISize canvas_size = canvas->getBaseLayerSize(); - SkNWayCanvas internal_nodes_canvas(canvas_size.width(), - canvas_size.height()); - internal_nodes_canvas.setMatrix(canvas->getTotalMatrix()); - internal_nodes_canvas.addCanvas(canvas); - Layer::PaintContext paintContext = { - /* internal_nodes_canvas= */ static_cast( - &internal_nodes_canvas), - /* leaf_nodes_canvas= */ canvas, - /* gr_context= */ context->gr_context, - /* view_embedder= */ nullptr, - context->raster_time, - context->ui_time, - context->texture_registry, - context->has_platform_view ? nullptr : context->raster_cache, - context->checkerboard_offscreen_layers, - context->frame_device_pixel_ratio}; - switch (strategy) { - case RasterCacheLayerStrategy::kLayer: - if (layer->needs_painting(paintContext)) { - layer->Paint(paintContext); - } - break; - case RasterCacheLayerStrategy::kLayerChildren: - FML_DCHECK(layer->as_container_layer()); - layer->as_container_layer()->PaintChildren(paintContext); - break; + entry.image = Rasterize(raster_cache_context, render_function); + if (entry.image != nullptr) { + switch (id.type()) { + case RasterCacheKeyType::kDisplayList: { + display_list_cached_this_frame_++; + break; } - }); -} - -const SkRect& RasterCache::GetPaintBoundsFromLayer( - Layer* layer, - RasterCacheLayerStrategy strategy) const { - switch (strategy) { - case RasterCacheLayerStrategy::kLayer: - return layer->paint_bounds(); - case RasterCacheLayerStrategy::kLayerChildren: - FML_DCHECK(layer->as_container_layer()); - return layer->as_container_layer()->child_paint_bounds(); - } -} - -bool RasterCache::Prepare(PrerollContext* context, - DisplayList* display_list, - bool is_complex, - bool will_change, - const SkMatrix& untranslated_matrix, - const SkPoint& offset) { - if (!GenerateNewCacheInThisFrame()) { - return false; - } - - DisplayListComplexityCalculator* complexity_calculator = - context->gr_context ? DisplayListComplexityCalculator::GetForBackend( - context->gr_context->backend()) - : DisplayListComplexityCalculator::GetForSoftware(); - - if (!IsDisplayListWorthRasterizing(display_list, will_change, is_complex, - complexity_calculator)) { - // We only deal with display lists that are worthy of rasterization. - return false; - } - - SkMatrix transformation_matrix = untranslated_matrix; - transformation_matrix.preTranslate(offset.x(), offset.y()); - - if (!transformation_matrix.invert(nullptr)) { - // The matrix was singular. No point in going further. - return false; - } - - RasterCacheKey cache_key(display_list->unique_id(), - RasterCacheKeyType::kDisplayList, - transformation_matrix); - - // Creates an entry, if not present prior. - Entry& entry = cache_[cache_key]; - if (entry.access_count < access_threshold_) { - // Frame threshold has not yet been reached. - return false; - } - - if (!entry.image) { - // GetIntegralTransCTM effect for matrix which only contains scale, - // translate, so it won't affect result of matrix decomposition and cache - // key. - entry.image = RasterizeDisplayList( - display_list, context->gr_context, transformation_matrix, - context->dst_color_space, checkerboard_images_); - display_list_cached_this_frame_++; - } - return true; -} - -void RasterCache::Touch(Layer* layer, - const SkMatrix& ctm, - RasterCacheLayerStrategy strategey) { - auto cache_key_optional = - TryToMakeRasterCacheKeyForLayer(layer, strategey, ctm); - if (!cache_key_optional) { - return; + default: + break; + } + return true; + } } - Touch(cache_key_optional.value()); -} - -void RasterCache::Touch(DisplayList* display_list, - const SkMatrix& transformation_matrix) { - RasterCacheKey cache_key(display_list->unique_id(), - RasterCacheKeyType::kDisplayList, - transformation_matrix); - Touch(cache_key); + return entry.image != nullptr; } -void RasterCache::Touch(const RasterCacheKey& cache_key) { +bool RasterCache::Touch(const RasterCacheKeyID& id, + const SkMatrix& matrix) const { + RasterCacheKey cache_key = RasterCacheKey(id, matrix); auto it = cache_.find(cache_key); if (it != cache_.end()) { - it->second.used_this_frame = true; it->second.access_count++; + it->second.used_this_frame = true; + return true; } + return false; } -bool RasterCache::Draw(const DisplayList& display_list, - SkCanvas& canvas, - const SkPaint* paint) const { - RasterCacheKey cache_key(display_list.unique_id(), - RasterCacheKeyType::kDisplayList, - canvas.getTotalMatrix()); - return Draw(cache_key, canvas, paint); +int RasterCache::MarkSeen(const RasterCacheKeyID& id, + const SkMatrix& matrix) const { + RasterCacheKey key = RasterCacheKey(id, matrix); + Entry& entry = cache_[key]; + entry.used_this_frame = true; + return entry.access_count; } -bool RasterCache::Draw(const Layer* layer, - SkCanvas& canvas, - RasterCacheLayerStrategy strategy, - const SkPaint* paint) const { - auto cache_key_optional = - TryToMakeRasterCacheKeyForLayer(layer, strategy, canvas.getTotalMatrix()); - if (!cache_key_optional) { - return false; +bool RasterCache::HasEntry(const RasterCacheKeyID& id, + const SkMatrix& matrix) const { + RasterCacheKey key = RasterCacheKey(id, matrix); + if (cache_.find(key) != cache_.cend()) { + return true; } - return Draw(cache_key_optional.value(), canvas, paint); + return false; } -bool RasterCache::Draw(const RasterCacheKey& cache_key, +bool RasterCache::Draw(const RasterCacheKeyID& id, SkCanvas& canvas, const SkPaint* paint) const { - auto it = cache_.find(cache_key); + auto it = cache_.find(RasterCacheKey(id, canvas.getTotalMatrix())); if (it == cache_.end()) { return false; } @@ -375,7 +176,7 @@ void RasterCache::SweepOneCacheAfterFrame(RasterCacheKey::Map& cache, } else if (entry.image) { RasterCacheKeyKind kind = it->first.kind(); switch (kind) { - case RasterCacheKeyKind::kPictureMetrics: + case RasterCacheKeyKind::kDisplayListMetrics: picture_metrics.in_use_count++; picture_metrics.in_use_bytes += entry.image->image_bytes(); break; @@ -392,7 +193,7 @@ void RasterCache::SweepOneCacheAfterFrame(RasterCacheKey::Map& cache, if (it->second.image) { RasterCacheKeyKind kind = it->first.kind(); switch (kind) { - case RasterCacheKeyKind::kPictureMetrics: + case RasterCacheKeyKind::kDisplayListMetrics: picture_metrics.eviction_count++; picture_metrics.eviction_bytes += it->second.image->image_bytes(); break; @@ -434,13 +235,13 @@ size_t RasterCache::GetLayerCachedEntriesCount() const { } size_t RasterCache::GetPictureCachedEntriesCount() const { - size_t picture_cached_entries_count = 0; + size_t display_list_cached_entries_count = 0; for (const auto& item : cache_) { - if (item.first.kind() == RasterCacheKeyKind::kPictureMetrics) { - picture_cached_entries_count++; + if (item.first.kind() == RasterCacheKeyKind::kDisplayListMetrics) { + display_list_cached_entries_count++; } } - return picture_cached_entries_count; + return display_list_cached_entries_count; } void RasterCache::SetCheckboardCacheImages(bool checkerboard) { @@ -482,7 +283,7 @@ size_t RasterCache::EstimateLayerCacheByteSize() const { size_t RasterCache::EstimatePictureCacheByteSize() const { size_t picture_cache_bytes = 0; for (const auto& item : cache_) { - if (item.first.kind() == RasterCacheKeyKind::kPictureMetrics && + if (item.first.kind() == RasterCacheKeyKind::kDisplayListMetrics && item.second.image) { picture_cache_bytes += item.second.image->image_bytes(); } diff --git a/flow/raster_cache.h b/flow/raster_cache.h index 2212efac88ecc..95feec68dbf0c 100644 --- a/flow/raster_cache.h +++ b/flow/raster_cache.h @@ -11,9 +11,13 @@ #include "flutter/display_list/display_list.h" #include "flutter/display_list/display_list_complexity.h" #include "flutter/flow/raster_cache_key.h" +#include "flutter/flow/raster_cache_util.h" #include "flutter/fml/macros.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/fml/trace_event.h" +#include "include/core/SkCanvas.h" +#include "include/core/SkMatrix.h" +#include "include/core/SkRect.h" #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkSize.h" @@ -48,7 +52,9 @@ class RasterCacheResult { }; class Layer; +class RasterCacheItem; struct PrerollContext; +struct PaintContext; struct RasterCacheMetrics { /** @@ -88,118 +94,41 @@ struct RasterCacheMetrics { class RasterCache { public: - // The default max number of picture and display list raster caches to be - // generated per frame. Generating too many caches in one frame may cause jank - // on that frame. This limit allows us to throttle the cache and distribute - // the work across multiple frames. - static constexpr int kDefaultDispLayListCacheLimitPerFrame = 3; + struct Context { + GrDirectContext* gr_context; + const SkColorSpace* dst_color_space; + const SkMatrix& matrix; + const SkRect& logical_rect; + const char* flow_type; + bool checkerboard; + }; + + static std::unique_ptr Rasterize( + const RasterCache::Context& context, + const std::function& draw_function); - explicit RasterCache(size_t access_threshold = 3, - size_t display_list_cache_limit_per_frame = - kDefaultDispLayListCacheLimitPerFrame); + explicit RasterCache( + size_t access_threshold = 3, + size_t picture_and_display_list_cache_limit_per_frame = + RasterCacheUtil::kDefaultPictureAndDispLayListCacheLimitPerFrame); virtual ~RasterCache() = default; - /** - * @brief Rasterize a display list object and produce a RasterCacheResult - * to be stored in the cache. - * - * @param display_list the DisplayList object to be cached. - * @param context the GrDirectContext used for rendering. - * @param ctm the transformation matrix used for rendering. - * @param dst_color_space the destination color space that the cached - * rendering will be drawn into - * @param checkerboard a flag indicating whether or not a checkerboard - * pattern should be rendered into the cached image for debug - * analysis - * @return a RasterCacheResult that can draw the rendered display list into - * the destination using a simple image blit - */ - virtual std::unique_ptr RasterizeDisplayList( - DisplayList* display_list, - GrDirectContext* context, - const SkMatrix& ctm, - SkColorSpace* dst_color_space, - bool checkerboard) const; + // Draws this item if it should be rendered from the cache and returns + // true iff it was successfully drawn. Typically this should only fail + // if the item was disabled due to conditions discovered during |Preroll| + // or if the attempt to populate the entry failed due to bounds overflow + // conditions. + bool Draw(const RasterCacheKeyID& id, + SkCanvas& canvas, + const SkPaint* paint) const; - /** - * @brief Rasterize an engine Layer and produce a RasterCacheResult - * to be stored in the cache. - * - * @param context the PrerollContext containing important information - * needed for rendering a layer. - * @param layer the Layer object to be cached. - * @param ctm the transformation matrix used for rendering. - * @param checkerboard a flag indicating whether or not a checkerboard - * pattern should be rendered into the cached image for debug - * analysis - * @return a RasterCacheResult that can draw the rendered layer into - * the destination using a simple image blit - */ - virtual std::unique_ptr RasterizeLayer( - PrerollContext* context, - Layer* layer, - RasterCacheLayerStrategy strategy, - const SkMatrix& ctm, - bool checkerboard) const; - - static SkRect GetDeviceBounds(const SkRect& rect, const SkMatrix& ctm) { - SkRect device_rect; - ctm.mapRect(&device_rect, rect); - return device_rect; - } + bool Touch(const RasterCacheKeyID& id, const SkMatrix& matrix) const; - // Return true if the cache is generated. - // - // We may return false and not generate the cache if - // 1. There are too many display lists to be cached in the current frame. - // (See also kDefaultDispLayListCacheLimitPerFrame.) - // 2. The display list is not worth rasterizing - // 3. The matrix is singular - // 4. The display list is accessed too few times - bool Prepare(PrerollContext* context, - DisplayList* display_list, - bool is_complex, - bool will_change, - const SkMatrix& untranslated_matrix, - const SkPoint& offset = SkPoint()); - - // If there is cache entry for this display list or layer, mark it as - // used for this frame in order to not get evicted. This is needed during - // partial repaint for layers that are outside of current clip and are culled - // away. - void Touch(DisplayList* display_list, const SkMatrix& transformation_matrix); - void Touch( - Layer* layer, - const SkMatrix& ctm, - RasterCacheLayerStrategy strategey = RasterCacheLayerStrategy::kLayer); - - void Prepare( - PrerollContext* context, - Layer* layer, - const SkMatrix& ctm, - RasterCacheLayerStrategy strategey = RasterCacheLayerStrategy::kLayer); - - // Find the raster cache for the display list and draw it to the canvas. - // - // Return true if it's found and drawn. - bool Draw(const DisplayList& display_list, - SkCanvas& canvas, - const SkPaint* paint = nullptr) const; - - // Find the raster cache for the layer and draw it to the canvas. - // - // Additional paint can be given to change how the raster cache is drawn - // (e.g., draw the raster cache with some opacity). - // - // Return true if the layer raster cache is found and drawn. - bool Draw( - const Layer* layer, - SkCanvas& canvas, - RasterCacheLayerStrategy strategey = RasterCacheLayerStrategy::kLayer, - const SkPaint* paint = nullptr) const; + bool HasEntry(const RasterCacheKeyID& id, const SkMatrix&) const; void PrepareNewFrame(); + void CleanupAfterFrame(); void Clear(); @@ -218,14 +147,16 @@ class RasterCache { size_t GetLayerCachedEntriesCount() const; /** - * Return the number of map entries in the picture cache regardless of whether - * the entries have been populated with an image. + * Return the number of map entries in the picture caches (SkPicture and + * DisplayList) regardless of whether the entries have been populated with + * an image. */ size_t GetPictureCachedEntriesCount() const; /** * @brief Estimate how much memory is used by picture raster cache entries in - * bytes, including cache entries in the DisplayList cache. + * bytes, including cache entries in the SkPicture cache and the DisplayList + * cache. * * Only SkImage's memory usage is counted as other objects are often much * smaller compared to SkImage. SkImageInfo::computeMinByteSize is used to @@ -253,6 +184,26 @@ class RasterCache { */ int access_threshold() const { return access_threshold_; } + bool GenerateNewCacheInThisFrame() const { + // Disabling caching when access_threshold is zero is historic behavior. + return access_threshold_ != 0 && display_list_cached_this_frame_ < + display_list_cache_limit_per_frame_; + } + + /** + * @brief The entry which RasterCacheId is generated by RasterCacheKeyID and + * matrix, that will be used by the current frame. If current frame doesn't + * have this entry we will create a new entry. + * @return the number of times the entry has been hit since it was created. If + * a new entry that will be 1. + */ + int MarkSeen(const RasterCacheKeyID& id, const SkMatrix& matrix) const; + + bool UpdateCacheEntry( + const RasterCacheKeyID& id, + const Context& raster_cache_context, + const std::function& render_function) const; + private: struct Entry { bool used_this_frame = false; @@ -260,34 +211,13 @@ class RasterCache { std::unique_ptr image; }; - void Touch(const RasterCacheKey& cache_key); - - bool Draw(const RasterCacheKey& cache_key, - SkCanvas& canvas, - const SkPaint* paint) const; - void SweepOneCacheAfterFrame(RasterCacheKey::Map& cache, RasterCacheMetrics& picture_metrics, RasterCacheMetrics& layer_metrics); - bool GenerateNewCacheInThisFrame() const { - // Disabling caching when access_threshold is zero is historic behavior. - return access_threshold_ != 0 && display_list_cached_this_frame_ < - display_list_cache_limit_per_frame_; - } - - std::optional TryToMakeRasterCacheKeyForLayer( - const Layer* layer, - RasterCacheLayerStrategy strategy, - const SkMatrix& ctm) const; - - const SkRect& GetPaintBoundsFromLayer( - Layer* layer, - RasterCacheLayerStrategy strategy) const; - const size_t access_threshold_; const size_t display_list_cache_limit_per_frame_; - size_t display_list_cached_this_frame_ = 0; + mutable size_t display_list_cached_this_frame_ = 0; RasterCacheMetrics layer_metrics_; RasterCacheMetrics picture_metrics_; mutable RasterCacheKey::Map cache_; @@ -295,6 +225,9 @@ class RasterCache { void TraceStatsToTimeline() const; + friend class RasterCacheItem; + friend class LayerRasterCacheItem; + FML_DISALLOW_COPY_AND_ASSIGN(RasterCache); }; diff --git a/flow/raster_cache_item.h b/flow/raster_cache_item.h new file mode 100644 index 0000000000000..8509775589077 --- /dev/null +++ b/flow/raster_cache_item.h @@ -0,0 +1,80 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FLOW_RASTER_CACHE_ITEM_H_ +#define FLUTTER_FLOW_RASTER_CACHE_ITEM_H_ + +#include +#include + +#include "flutter/flow/raster_cache_key.h" +#include "include/core/SkCanvas.h" +#include "include/core/SkPaint.h" +#include "include/core/SkPicture.h" +#include "include/core/SkPoint.h" + +namespace flutter { + +struct PrerollContext; +struct PaintContext; +class DisplayList; +class RasterCache; +class LayerRasterCacheItem; +class DisplayListRasterCacheItem; +class SkPictureRasterCacheItem; + +class RasterCacheItem { + public: + enum CacheState { + kNone = 0, + kCurrent, + kChildren, + }; + + explicit RasterCacheItem(RasterCacheKeyID key_id, + CacheState cache_state = CacheState::kNone, + unsigned child_entries = 0) + : key_id_(key_id), + cache_state_(cache_state), + child_items_(child_entries) {} + + virtual void PrerollSetup(PrerollContext* context, + const SkMatrix& matrix) = 0; + + virtual void PrerollFinalize(PrerollContext* context, + const SkMatrix& matrix) = 0; + + virtual bool Draw(const PaintContext& context, + const SkPaint* paint) const = 0; + + virtual bool Draw(const PaintContext& context, + SkCanvas* canvas, + const SkPaint* paint) const = 0; + + virtual std::optional GetId() const { return key_id_; } + + virtual bool TryToPrepareRasterCache(const PaintContext& context, + bool parent_cached = false) const = 0; + + unsigned child_items() const { return child_items_; } + + void set_matrix(const SkMatrix& matrix) { matrix_ = matrix; } + + CacheState cache_state() const { return cache_state_; } + + bool need_caching() const { return cache_state_ != CacheState::kNone; } + + virtual ~RasterCacheItem() = default; + + protected: + // The id for cache the layer self. + RasterCacheKeyID key_id_; + CacheState cache_state_ = CacheState::kNone; + mutable SkMatrix matrix_; + unsigned child_items_; +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_RASTER_CACHE_ITEM_H_ diff --git a/flow/raster_cache_key.cc b/flow/raster_cache_key.cc index 0a20cde72dcc6..c8eedf9b641c5 100644 --- a/flow/raster_cache_key.cc +++ b/flow/raster_cache_key.cc @@ -3,9 +3,25 @@ // found in the LICENSE file. #include "flutter/flow/raster_cache_key.h" - +#include +#include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/layers/layer.h" namespace flutter { // +std::optional> RasterCacheKeyID::LayerChildrenIds( + Layer* layer) { + auto& children_layers = layer->as_container_layer()->layers(); + auto children_count = children_layers.size(); + if (children_count == 0) { + return std::nullopt; + } + std::vector ids; + std::transform(children_layers.begin(), children_layers.end(), + std::back_inserter(ids), + [](auto& layer) -> uint64_t { return layer->unique_id(); }); + return ids; +} + } // namespace flutter diff --git a/flow/raster_cache_key.h b/flow/raster_cache_key.h index d3152046c2afa..2f61f8f6d8ba1 100644 --- a/flow/raster_cache_key.h +++ b/flow/raster_cache_key.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_FLOW_RASTER_CACHE_KEY_H_ #define FLUTTER_FLOW_RASTER_CACHE_KEY_H_ +#include #include #include #include @@ -15,22 +16,34 @@ namespace flutter { +class Layer; + +enum class RasterCacheKeyType { kLayer, kDisplayList, kLayerChildren }; + class RasterCacheKeyID { public: - RasterCacheKeyID(const std::vector ids) : ids_(ids) {} + RasterCacheKeyID(uint16_t id, RasterCacheKeyType type) + : ids_({id}), type_(type) {} + + RasterCacheKeyID(const std::vector ids, RasterCacheKeyType type) + : ids_(ids), type_(type) {} + + const std::vector& ids() const { return ids_; } + + RasterCacheKeyType type() const { return type_; } - const std::vector& ids() { return ids_; } + static std::optional> LayerChildrenIds(Layer* layer); std::size_t GetHash() const { std::size_t seed = fml::HashCombine(); for (auto id : ids_) { fml::HashCombineSeed(seed, id); } - return seed; + return fml::HashCombine(seed, type_); } bool operator==(const RasterCacheKeyID& other) const { - return ids_ == other.ids_; + return type_ == other.type_ && ids_ == other.ids_; } bool operator!=(const RasterCacheKeyID& other) const { @@ -39,39 +52,30 @@ class RasterCacheKeyID { private: const std::vector ids_; + const RasterCacheKeyType type_; }; -enum class RasterCacheKeyType { - kLayer, - kPicture, - kDisplayList, - kLayerChildren -}; - -enum class RasterCacheKeyKind { kLayerMetrics, kPictureMetrics }; +enum class RasterCacheKeyKind { kLayerMetrics, kDisplayListMetrics }; class RasterCacheKey { public: RasterCacheKey(uint64_t id, RasterCacheKeyType type, const SkMatrix& ctm) - : RasterCacheKey(RasterCacheKeyID({id}), type, ctm) {} + : RasterCacheKey(RasterCacheKeyID(id, type), ctm) {} - RasterCacheKey(RasterCacheKeyID id, - RasterCacheKeyType type, - const SkMatrix& ctm) - : id_(std::move(id)), type_(type), matrix_(ctm) { + RasterCacheKey(RasterCacheKeyID id, const SkMatrix& ctm) + : id_(std::move(id)), matrix_(ctm) { matrix_[SkMatrix::kMTransX] = 0; matrix_[SkMatrix::kMTransY] = 0; } const RasterCacheKeyID& id() const { return id_; } - RasterCacheKeyType type() const { return type_; } + RasterCacheKeyType type() const { return id_.type(); } const SkMatrix& matrix() const { return matrix_; } RasterCacheKeyKind kind() const { - switch (type_) { - case RasterCacheKeyType::kPicture: + switch (id_.type()) { case RasterCacheKeyType::kDisplayList: - return RasterCacheKeyKind::kPictureMetrics; + return RasterCacheKeyKind::kDisplayListMetrics; case RasterCacheKeyType::kLayer: case RasterCacheKeyType::kLayerChildren: return RasterCacheKeyKind::kLayerMetrics; @@ -80,15 +84,14 @@ class RasterCacheKey { struct Hash { std::size_t operator()(RasterCacheKey const& key) const { - return fml::HashCombine(key.id_.GetHash(), key.type_); + return key.id_.GetHash(); } }; struct Equal { constexpr bool operator()(const RasterCacheKey& lhs, const RasterCacheKey& rhs) const { - return lhs.id_ == rhs.id_ && lhs.type_ == rhs.type_ && - lhs.matrix_ == rhs.matrix_; + return lhs.id_ == rhs.id_ && lhs.matrix_ == rhs.matrix_; } }; @@ -98,8 +101,6 @@ class RasterCacheKey { private: RasterCacheKeyID id_; - RasterCacheKeyType type_; - // ctm where only fractional (0-1) translations are preserved: // matrix_ = ctm; // matrix_[SkMatrix::kMTransX] = SkScalarFraction(ctm.getTranslateX()); diff --git a/flow/raster_cache_unittests.cc b/flow/raster_cache_unittests.cc index 19199ab9b75c4..067e045fa91b6 100644 --- a/flow/raster_cache_unittests.cc +++ b/flow/raster_cache_unittests.cc @@ -6,10 +6,15 @@ #include "flutter/display_list/display_list_builder.h" #include "flutter/display_list/display_list_test_utils.h" #include "flutter/flow/raster_cache.h" +#include "flutter/flow/raster_cache_item.h" #include "flutter/flow/testing/mock_raster_cache.h" #include "gtest/gtest.h" +#include "include/core/SkMatrix.h" +#include "include/core/SkPoint.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkPaint.h" +#include "third_party/skia/include/core/SkPicture.h" +#include "third_party/skia/include/core/SkPictureRecorder.h" namespace flutter { namespace testing { @@ -28,26 +33,32 @@ TEST(RasterCache, MetricsOmitUnpopulatedEntries) { auto display_list = GetSampleDisplayList(); SkCanvas dummy_canvas; + SkPaint paint; - PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder(); + PrerollContextHolder preroll_context_holder = + GetSamplePrerollContextHolder(&cache); + PaintContextHolder paint_context_holder = GetSamplePaintContextHolder(&cache); + auto& preroll_context = preroll_context_holder.preroll_context; + auto& paint_context = paint_context_holder.paint_context; cache.PrepareNewFrame(); + DisplayListRasterCacheItem display_list_item(display_list.get(), SkPoint(), + true, false); - ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context, - display_list.get(), true, false, matrix)); // 1st access. - ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas)); + ASSERT_FALSE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrix)); + ASSERT_FALSE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); cache.CleanupAfterFrame(); ASSERT_EQ(cache.picture_metrics().total_count(), 0u); ASSERT_EQ(cache.picture_metrics().total_bytes(), 0u); cache.PrepareNewFrame(); - ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context, - display_list.get(), true, false, matrix)); - // 2nd access. - ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas)); + ASSERT_FALSE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrix)); + ASSERT_FALSE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); cache.CleanupAfterFrame(); ASSERT_EQ(cache.picture_metrics().total_count(), 0u); @@ -55,9 +66,9 @@ TEST(RasterCache, MetricsOmitUnpopulatedEntries) { cache.PrepareNewFrame(); // Now Prepare should cache it. - ASSERT_TRUE(cache.Prepare(&preroll_context_holder.preroll_context, - display_list.get(), true, false, matrix)); - ASSERT_TRUE(cache.Draw(*display_list, dummy_canvas)); + ASSERT_TRUE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrix)); + ASSERT_TRUE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); cache.CleanupAfterFrame(); ASSERT_EQ(cache.picture_metrics().total_count(), 1u); @@ -74,32 +85,65 @@ TEST(RasterCache, ThresholdIsRespectedForDisplayList) { auto display_list = GetSampleDisplayList(); SkCanvas dummy_canvas; + SkPaint paint; - PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder(); + PrerollContextHolder preroll_context_holder = + GetSamplePrerollContextHolder(&cache); + PaintContextHolder paint_context_holder = GetSamplePaintContextHolder(&cache); + auto& preroll_context = preroll_context_holder.preroll_context; + auto& paint_context = paint_context_holder.paint_context; cache.PrepareNewFrame(); - ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context, - display_list.get(), true, false, matrix)); + DisplayListRasterCacheItem display_list_item(display_list.get(), SkPoint(), + true, false); + // 1st access. - ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas)); + ASSERT_FALSE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrix)); + ASSERT_FALSE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); cache.CleanupAfterFrame(); cache.PrepareNewFrame(); - ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context, - display_list.get(), true, false, matrix)); - // 2nd access. - ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas)); + ASSERT_FALSE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrix)); + ASSERT_FALSE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); cache.CleanupAfterFrame(); cache.PrepareNewFrame(); // Now Prepare should cache it. - ASSERT_TRUE(cache.Prepare(&preroll_context_holder.preroll_context, - display_list.get(), true, false, matrix)); - ASSERT_TRUE(cache.Draw(*display_list, dummy_canvas)); + ASSERT_TRUE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrix)); + ASSERT_TRUE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); +} + +TEST(RasterCache, AccessThresholdOfZeroDisablesCachingForSkPicture) { + size_t threshold = 0; + flutter::RasterCache cache(threshold); + + SkMatrix matrix = SkMatrix::I(); + + auto display_list = GetSampleDisplayList(); + ; + + SkCanvas dummy_canvas; + SkPaint paint; + + PrerollContextHolder preroll_context_holder = + GetSamplePrerollContextHolder(&cache); + PaintContextHolder paint_context_holder = GetSamplePaintContextHolder(&cache); + auto& preroll_context = preroll_context_holder.preroll_context; + auto& paint_context = paint_context_holder.paint_context; + + cache.PrepareNewFrame(); + DisplayListRasterCacheItem display_list_item(display_list.get(), SkPoint(), + true, false); + ASSERT_FALSE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrix)); + ASSERT_FALSE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); } TEST(RasterCache, AccessThresholdOfZeroDisablesCachingForDisplayList) { @@ -111,15 +155,54 @@ TEST(RasterCache, AccessThresholdOfZeroDisablesCachingForDisplayList) { auto display_list = GetSampleDisplayList(); SkCanvas dummy_canvas; + SkPaint paint; - PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder(); + PrerollContextHolder preroll_context_holder = + GetSamplePrerollContextHolder(&cache); + PaintContextHolder paint_context_holder = GetSamplePaintContextHolder(&cache); + auto& preroll_context = preroll_context_holder.preroll_context; + auto& paint_context = paint_context_holder.paint_context; cache.PrepareNewFrame(); - ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context, - display_list.get(), true, false, matrix)); + DisplayListRasterCacheItem display_list_item(display_list.get(), SkPoint(), + true, false); + ASSERT_FALSE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrix)); + ASSERT_FALSE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); +} + +TEST(RasterCache, PictureCacheLimitPerFrameIsRespectedWhenZeroForSkPicture) { + size_t picture_cache_limit_per_frame = 0; + flutter::RasterCache cache(3, picture_cache_limit_per_frame); + + SkMatrix matrix = SkMatrix::I(); + + auto display_list = GetSampleDisplayList(); + ; - ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas)); + SkCanvas dummy_canvas; + SkPaint paint; + + PrerollContextHolder preroll_context_holder = + GetSamplePrerollContextHolder(&cache); + PaintContextHolder paint_context_holder = GetSamplePaintContextHolder(&cache); + auto& preroll_context = preroll_context_holder.preroll_context; + auto& paint_context = paint_context_holder.paint_context; + + cache.PrepareNewFrame(); + + DisplayListRasterCacheItem display_list_item(display_list.get(), SkPoint(), + true, false); + ASSERT_FALSE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrix)); + ASSERT_FALSE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); + ASSERT_FALSE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrix)); + ASSERT_FALSE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); + ASSERT_FALSE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrix)); + ASSERT_FALSE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); } TEST(RasterCache, PictureCacheLimitPerFrameIsRespectedWhenZeroForDisplayList) { @@ -131,15 +214,73 @@ TEST(RasterCache, PictureCacheLimitPerFrameIsRespectedWhenZeroForDisplayList) { auto display_list = GetSampleDisplayList(); SkCanvas dummy_canvas; + SkPaint paint; - PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder(); + PrerollContextHolder preroll_context_holder = + GetSamplePrerollContextHolder(&cache); + PaintContextHolder paint_context_holder = GetSamplePaintContextHolder(&cache); + auto& preroll_context = preroll_context_holder.preroll_context; + auto& paint_context = paint_context_holder.paint_context; cache.PrepareNewFrame(); - ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context, - display_list.get(), true, false, matrix)); + DisplayListRasterCacheItem display_list_item(display_list.get(), SkPoint(), + true, false); + // 1st access. + ASSERT_FALSE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrix)); + ASSERT_FALSE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); + // 2nd access. + ASSERT_FALSE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrix)); + ASSERT_FALSE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); + // the picture_cache_limit_per_frame = 0, so don't cache it + ASSERT_FALSE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrix)); + ASSERT_FALSE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); +} - ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas)); +TEST(RasterCache, SweepsRemoveUnusedSkPictures) { + size_t threshold = 1; + flutter::RasterCache cache(threshold); + + SkMatrix matrix = SkMatrix::I(); + + auto display_list = GetSampleDisplayList(); + ; + + SkCanvas dummy_canvas; + SkPaint paint; + + PrerollContextHolder preroll_context_holder = + GetSamplePrerollContextHolder(&cache); + PaintContextHolder paint_context_holder = GetSamplePaintContextHolder(&cache); + auto& preroll_context = preroll_context_holder.preroll_context; + auto& paint_context = paint_context_holder.paint_context; + + DisplayListRasterCacheItem display_item(display_list.get(), SkPoint(), true, + false); + // 1. + ASSERT_FALSE(DisplayListRasterCacheItemTryToRasterCache( + display_item, preroll_context, paint_context, matrix)); + ASSERT_FALSE(display_item.Draw(paint_context, &dummy_canvas, &paint)); + + cache.CleanupAfterFrame(); + cache.PrepareNewFrame(); + // 2. + ASSERT_TRUE(DisplayListRasterCacheItemTryToRasterCache( + display_item, preroll_context, paint_context, matrix)); + ASSERT_TRUE(display_item.Draw(paint_context, &dummy_canvas, &paint)); + + cache.CleanupAfterFrame(); + + cache.PrepareNewFrame(); + cache.CleanupAfterFrame(); // Extra frame without a Get image access. + + cache.PrepareNewFrame(); + + ASSERT_FALSE(cache.Draw(display_item.GetId().value(), dummy_canvas, &paint)); + ASSERT_FALSE(display_item.Draw(paint_context, &dummy_canvas, &paint)); } TEST(RasterCache, SweepsRemoveUnusedDisplayLists) { @@ -151,21 +292,29 @@ TEST(RasterCache, SweepsRemoveUnusedDisplayLists) { auto display_list = GetSampleDisplayList(); SkCanvas dummy_canvas; + SkPaint paint; - PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder(); + PrerollContextHolder preroll_context_holder = + GetSamplePrerollContextHolder(&cache); + PaintContextHolder paint_context_holder = GetSamplePaintContextHolder(&cache); + auto& preroll_context = preroll_context_holder.preroll_context; + auto& paint_context = paint_context_holder.paint_context; cache.PrepareNewFrame(); - ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context, - display_list.get(), true, false, matrix)); // 1 - ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas)); + DisplayListRasterCacheItem display_list_item(display_list.get(), SkPoint(), + true, false); + + ASSERT_FALSE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrix)); + ASSERT_FALSE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); cache.CleanupAfterFrame(); cache.PrepareNewFrame(); - ASSERT_TRUE(cache.Prepare(&preroll_context_holder.preroll_context, - display_list.get(), true, false, matrix)); // 2 - ASSERT_TRUE(cache.Draw(*display_list, dummy_canvas)); + ASSERT_TRUE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrix)); + ASSERT_TRUE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); cache.CleanupAfterFrame(); @@ -173,14 +322,15 @@ TEST(RasterCache, SweepsRemoveUnusedDisplayLists) { cache.CleanupAfterFrame(); // Extra frame without a Get image access. cache.PrepareNewFrame(); - - ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas)); + ASSERT_FALSE( + cache.Draw(display_list_item.GetId().value(), dummy_canvas, &paint)); + ASSERT_FALSE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); } TEST(RasterCache, ComputeDeviceRectBasedOnFractionalTranslation) { SkRect logical_rect = SkRect::MakeLTRB(0, 0, 300.2, 300.3); SkMatrix ctm = SkMatrix::MakeAll(2.0, 0, 0, 0, 2.0, 0, 0, 0, 1); - auto result = RasterCache::GetDeviceBounds(logical_rect, ctm); + auto result = RasterCacheUtil::GetDeviceBounds(logical_rect, ctm); ASSERT_EQ(result, SkRect::MakeLTRB(0.0, 0.0, 600.4, 600.6)); } @@ -198,27 +348,35 @@ TEST(RasterCache, DeviceRectRoundOutForDisplayList) { sk_sp display_list = builder.Build(); SkMatrix ctm = SkMatrix::MakeAll(1.3312, 0, 233, 0, 1.3312, 206, 0, 0, 1); + SkPaint paint; SkCanvas canvas(100, 100, nullptr); canvas.setMatrix(ctm); - PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder(); + PrerollContextHolder preroll_context_holder = + GetSamplePrerollContextHolder(&cache); + PaintContextHolder paint_context_holder = GetSamplePaintContextHolder(&cache); + auto& preroll_context = preroll_context_holder.preroll_context; + auto& paint_context = paint_context_holder.paint_context; cache.PrepareNewFrame(); + DisplayListRasterCacheItem display_list_item(display_list.get(), SkPoint(), + true, false); - ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context, - display_list.get(), true, false, ctm)); - ASSERT_FALSE(cache.Draw(*display_list, canvas)); + ASSERT_FALSE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, ctm)); + ASSERT_FALSE(display_list_item.Draw(paint_context, &canvas, &paint)); cache.CleanupAfterFrame(); cache.PrepareNewFrame(); - ASSERT_TRUE(cache.Prepare(&preroll_context_holder.preroll_context, - display_list.get(), true, false, ctm)); - ASSERT_TRUE(cache.Draw(*display_list, canvas)); + ASSERT_TRUE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, ctm)); + ASSERT_TRUE(display_list_item.Draw(paint_context, &canvas, &paint)); canvas.translate(248, 0); - ASSERT_TRUE(cache.Draw(*display_list, canvas)); + ASSERT_TRUE(cache.Draw(display_list_item.GetId().value(), canvas, &paint)); + ASSERT_TRUE(display_list_item.Draw(paint_context, &canvas, &paint)); } TEST(RasterCache, NestedOpCountMetricUsedForDisplayList) { @@ -232,21 +390,29 @@ TEST(RasterCache, NestedOpCountMetricUsedForDisplayList) { ASSERT_EQ(display_list->op_count(true), 36u); SkCanvas dummy_canvas; + SkPaint paint; - PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder(); + PrerollContextHolder preroll_context_holder = + GetSamplePrerollContextHolder(&cache); + PaintContextHolder paint_context_holder = GetSamplePaintContextHolder(&cache); + auto& preroll_context = preroll_context_holder.preroll_context; + auto& paint_context = paint_context_holder.paint_context; cache.PrepareNewFrame(); - ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context, - display_list.get(), false, false, matrix)); - ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas)); + DisplayListRasterCacheItem display_list_item(display_list.get(), SkPoint(), + false, false); + + ASSERT_FALSE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrix)); + ASSERT_FALSE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); cache.CleanupAfterFrame(); cache.PrepareNewFrame(); - ASSERT_TRUE(cache.Prepare(&preroll_context_holder.preroll_context, - display_list.get(), false, false, matrix)); - ASSERT_TRUE(cache.Draw(*display_list, dummy_canvas)); + ASSERT_TRUE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrix)); + ASSERT_TRUE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); } TEST(RasterCache, NaiveComplexityScoringDisplayList) { @@ -267,21 +433,29 @@ TEST(RasterCache, NaiveComplexityScoringDisplayList) { ASSERT_FALSE(calculator->ShouldBeCached(complexity_score)); SkCanvas dummy_canvas; + SkPaint paint; - PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder(); + PrerollContextHolder preroll_context_holder = + GetSamplePrerollContextHolder(&cache); + PaintContextHolder paint_context_holder = GetSamplePaintContextHolder(&cache); + auto& preroll_context = preroll_context_holder.preroll_context; + auto& paint_context = paint_context_holder.paint_context; cache.PrepareNewFrame(); - ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context, - display_list.get(), false, false, matrix)); - ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas)); + DisplayListRasterCacheItem display_list_item(display_list.get(), SkPoint(), + false, false); + + ASSERT_FALSE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrix)); + ASSERT_FALSE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); cache.CleanupAfterFrame(); cache.PrepareNewFrame(); - ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context, - display_list.get(), false, false, matrix)); - ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas)); + ASSERT_FALSE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrix)); + ASSERT_FALSE(display_list_item.Draw(paint_context, &dummy_canvas, &paint)); // Six raster ops should be cached display_list = GetSampleDisplayList(6); @@ -291,18 +465,20 @@ TEST(RasterCache, NaiveComplexityScoringDisplayList) { ASSERT_EQ(display_list->op_count(), 6u); ASSERT_TRUE(calculator->ShouldBeCached(complexity_score)); + DisplayListRasterCacheItem display_list_item_2 = + DisplayListRasterCacheItem(display_list.get(), SkPoint(), false, false); cache.PrepareNewFrame(); - ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context, - display_list.get(), false, false, matrix)); - ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas)); + ASSERT_FALSE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item_2, preroll_context, paint_context, matrix)); + ASSERT_FALSE(display_list_item_2.Draw(paint_context, &dummy_canvas, &paint)); cache.CleanupAfterFrame(); cache.PrepareNewFrame(); - ASSERT_TRUE(cache.Prepare(&preroll_context_holder.preroll_context, - display_list.get(), false, false, matrix)); - ASSERT_TRUE(cache.Draw(*display_list, dummy_canvas)); + ASSERT_TRUE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item_2, preroll_context, paint_context, matrix)); + ASSERT_TRUE(display_list_item_2.Draw(paint_context, &dummy_canvas, &paint)); } TEST(RasterCache, DisplayListWithSingularMatrixIsNotCached) { @@ -319,20 +495,30 @@ TEST(RasterCache, DisplayListWithSingularMatrixIsNotCached) { auto display_list = GetSampleDisplayList(); SkCanvas dummy_canvas; + SkPaint paint; + + PrerollContextHolder preroll_context_holder = + GetSamplePrerollContextHolder(&cache); + PaintContextHolder paint_context_holder = GetSamplePaintContextHolder(&cache); + auto& preroll_context = preroll_context_holder.preroll_context; + auto& paint_context = paint_context_holder.paint_context; - PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder(); + DisplayListRasterCacheItem display_list_item(display_list.get(), SkPoint(), + true, false); for (int i = 0; i < 10; i++) { cache.PrepareNewFrame(); for (int j = 0; j < matrixCount; j++) { - ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context, - display_list.get(), true, false, matrices[j])); + display_list_item.set_matrix(matrices[j]); + ASSERT_FALSE(DisplayListRasterCacheItemTryToRasterCache( + display_list_item, preroll_context, paint_context, matrices[j])); } for (int j = 0; j < matrixCount; j++) { dummy_canvas.setMatrix(matrices[j]); - ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas)); + ASSERT_FALSE( + display_list_item.Draw(paint_context, &dummy_canvas, &paint)); } cache.CleanupAfterFrame(); @@ -345,29 +531,23 @@ TEST(RasterCache, RasterCacheKeyHashFunction) { SkMatrix matrix = SkMatrix::I(); uint64_t id = 5; RasterCacheKey layer_key(id, RasterCacheKeyType::kLayer, matrix); - RasterCacheKey picture_key(id, RasterCacheKeyType::kPicture, matrix); RasterCacheKey display_list_key(id, RasterCacheKeyType::kDisplayList, matrix); RasterCacheKey layer_children_key(id, RasterCacheKeyType::kLayerChildren, matrix); - auto raster_cache_key_id = RasterCacheKeyID({id}); + auto layer_cache_key_id = RasterCacheKeyID(id, RasterCacheKeyType::kLayer); auto layer_hash_code = hash_function(layer_key); - ASSERT_EQ(layer_hash_code, fml::HashCombine(raster_cache_key_id.GetHash(), - RasterCacheKeyType::kLayer)); - - auto picture_hash_code = hash_function(picture_key); - ASSERT_EQ(picture_hash_code, fml::HashCombine(raster_cache_key_id.GetHash(), - RasterCacheKeyType::kPicture)); + ASSERT_EQ(layer_hash_code, layer_cache_key_id.GetHash()); + auto display_list_cache_key_id = + RasterCacheKeyID(id, RasterCacheKeyType::kDisplayList); auto display_list_hash_code = hash_function(display_list_key); - ASSERT_EQ(display_list_hash_code, - fml::HashCombine(raster_cache_key_id.GetHash(), - RasterCacheKeyType::kDisplayList)); + ASSERT_EQ(display_list_hash_code, display_list_cache_key_id.GetHash()); + auto layer_children_cache_key_id = + RasterCacheKeyID(id, RasterCacheKeyType::kLayerChildren); auto layer_children_hash_code = hash_function(layer_children_key); - ASSERT_EQ(layer_children_hash_code, - fml::HashCombine(raster_cache_key_id.GetHash(), - RasterCacheKeyType::kLayerChildren)); + ASSERT_EQ(layer_children_hash_code, layer_children_cache_key_id.GetHash()); } TEST(RasterCache, RasterCacheKeySameID) { @@ -375,17 +555,14 @@ TEST(RasterCache, RasterCacheKeySameID) { SkMatrix matrix = SkMatrix::I(); uint64_t id = 5; RasterCacheKey layer_key(id, RasterCacheKeyType::kLayer, matrix); - RasterCacheKey picture_key(id, RasterCacheKeyType::kPicture, matrix); RasterCacheKey display_list_key(id, RasterCacheKeyType::kDisplayList, matrix); RasterCacheKey layer_children_key(id, RasterCacheKeyType::kLayerChildren, matrix); map[layer_key] = 100; - map[picture_key] = 200; map[display_list_key] = 300; map[layer_children_key] = 400; ASSERT_EQ(map[layer_key], 100); - ASSERT_EQ(map[picture_key], 200); ASSERT_EQ(map[display_list_key], 300); ASSERT_EQ(map[layer_children_key], 400); } @@ -405,7 +582,7 @@ TEST(RasterCache, RasterCacheKeySameType) { ASSERT_EQ(map[layer_second_key], 100); ASSERT_EQ(map[layer_third_key], 150); - type = RasterCacheKeyType::kPicture; + type = RasterCacheKeyType::kDisplayList; RasterCacheKey picture_first_key(20, type, matrix); RasterCacheKey picture_second_key(25, type, matrix); RasterCacheKey picture_third_key(30, type, matrix); @@ -428,11 +605,11 @@ TEST(RasterCache, RasterCacheKeySameType) { ASSERT_EQ(map[display_list_third_key], 450); type = RasterCacheKeyType::kLayerChildren; - RasterCacheKey layer_children_first_key(RasterCacheKeyID({1, 2, 3}), type, + RasterCacheKey layer_children_first_key(RasterCacheKeyID({1, 2, 3}, type), matrix); - RasterCacheKey layer_children_second_key(RasterCacheKeyID({2, 3, 1}), type, + RasterCacheKey layer_children_second_key(RasterCacheKeyID({2, 3, 1}, type), matrix); - RasterCacheKey layer_children_third_key(RasterCacheKeyID({3, 2, 1}), type, + RasterCacheKey layer_children_third_key(RasterCacheKeyID({3, 2, 1}, type), matrix); map[layer_children_first_key] = 100; map[layer_children_second_key] = 200; @@ -443,29 +620,46 @@ TEST(RasterCache, RasterCacheKeySameType) { } TEST(RasterCache, RasterCacheKeyID_Equal) { - RasterCacheKeyID first = RasterCacheKeyID({1}); - RasterCacheKeyID second = RasterCacheKeyID({1}); - RasterCacheKeyID third = RasterCacheKeyID({2}); - ASSERT_EQ(first, second); + RasterCacheKeyID first = RasterCacheKeyID(1, RasterCacheKeyType::kLayer); + RasterCacheKeyID second = + RasterCacheKeyID(1, RasterCacheKeyType::kLayerChildren); + RasterCacheKeyID third = RasterCacheKeyID(2, RasterCacheKeyType::kLayer); + ASSERT_NE(first, second); ASSERT_NE(first, third); + ASSERT_NE(second, third); + + RasterCacheKeyID fourth = + RasterCacheKeyID({1, 2}, RasterCacheKeyType::kLayer); + RasterCacheKeyID fifth = + RasterCacheKeyID({1, 2}, RasterCacheKeyType::kLayerChildren); + RasterCacheKeyID sixth = + RasterCacheKeyID({2, 1}, RasterCacheKeyType::kLayerChildren); + ASSERT_NE(fourth, fifth); + ASSERT_NE(fifth, sixth); +} - RasterCacheKeyID fourth = RasterCacheKeyID({1, 2}); - RasterCacheKeyID fifth = RasterCacheKeyID({1, 2}); - RasterCacheKeyID sixth = RasterCacheKeyID({2, 1}); - ASSERT_EQ(fourth, fifth); - ASSERT_NE(fourth, sixth); +size_t HashIds(std::vector ids, RasterCacheKeyType type) { + std::size_t seed = fml::HashCombine(); + for (auto id : ids) { + fml::HashCombineSeed(seed, id); + } + return fml::HashCombine(seed, type); } TEST(RasterCache, RasterCacheKeyID_HashCode) { uint64_t foo = 1; uint64_t bar = 2; - RasterCacheKeyID first = RasterCacheKeyID({foo}); - RasterCacheKeyID second = RasterCacheKeyID({foo, bar}); - RasterCacheKeyID third = RasterCacheKeyID({bar, foo}); - - ASSERT_EQ(first.GetHash(), fml::HashCombine(foo)); - ASSERT_EQ(second.GetHash(), fml::HashCombine(foo, bar)); - ASSERT_EQ(third.GetHash(), fml::HashCombine(bar, foo)); + RasterCacheKeyID first = RasterCacheKeyID(foo, RasterCacheKeyType::kLayer); + RasterCacheKeyID second = + RasterCacheKeyID({foo, bar}, RasterCacheKeyType::kLayerChildren); + RasterCacheKeyID third = + RasterCacheKeyID({bar, foo}, RasterCacheKeyType::kLayerChildren); + + ASSERT_EQ(first.GetHash(), HashIds({foo}, RasterCacheKeyType::kLayer)); + ASSERT_EQ(second.GetHash(), + HashIds({foo, bar}, RasterCacheKeyType::kLayerChildren)); + ASSERT_EQ(third.GetHash(), + HashIds({bar, foo}, RasterCacheKeyType::kLayerChildren)); } } // namespace testing diff --git a/flow/raster_cache_util.cc b/flow/raster_cache_util.cc new file mode 100644 index 0000000000000..25061f29f07b8 --- /dev/null +++ b/flow/raster_cache_util.cc @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/raster_cache_util.h" + +namespace flutter {} diff --git a/flow/raster_cache_util.h b/flow/raster_cache_util.h new file mode 100644 index 0000000000000..35abaf2d5f56c --- /dev/null +++ b/flow/raster_cache_util.h @@ -0,0 +1,61 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FLOW_RASTER_CACHE_UTIL_H_ +#define FLUTTER_FLOW_RASTER_CACHE_UTIL_H_ + +#include "flutter/fml/logging.h" +#include "include/core/SkMatrix.h" +#include "include/core/SkRect.h" + +namespace flutter { + +struct RasterCacheUtil { + // The default max number of picture and display list raster caches to be + // generated per frame. Generating too many caches in one frame may cause jank + // on that frame. This limit allows us to throttle the cache and distribute + // the work across multiple frames. + static constexpr int kDefaultPictureAndDispLayListCacheLimitPerFrame = 3; + + // The ImageFilterLayer might cache the filtered output of this layer + // if the layer remains stable (if it is not animating for instance). + // If the ImageFilterLayer is not the same between rendered frames, + // though, it will cache its children instead and filter their cached + // output on the fly. + // Caching just the children saves the time to render them and also + // avoids a rendering surface switch to draw them. + // Caching the layer itself avoids all of that and additionally avoids + // the cost of applying the filter, but can be worse than caching the + // children if the filter itself is not stable from frame to frame. + // This constant controls how many times we will Preroll and Paint this + // same ImageFilterLayer before we consider the layer and filter to be + // stable enough to switch from caching the children to caching the + // filtered output of this layer. + static constexpr int kMinimumRendersBeforeCachingFilterLayer = 3; + + static bool CanRasterizeRect(const SkRect& cull_rect) { + if (cull_rect.isEmpty()) { + // No point in ever rasterizing an empty display list. + return false; + } + + if (!cull_rect.isFinite()) { + // Cannot attempt to rasterize into an infinitely large surface. + FML_LOG(INFO) << "Attempted to raster cache non-finite display list"; + return false; + } + + return true; + } + + static SkRect GetDeviceBounds(const SkRect& rect, const SkMatrix& ctm) { + SkRect device_rect; + ctm.mapRect(&device_rect, rect); + return device_rect; + } +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_RASTER_CACHE_UTIL_H_ diff --git a/flow/testing/layer_test.h b/flow/testing/layer_test.h index 650f50ca96649..e5672e1eacdd1 100644 --- a/flow/testing/layer_test.h +++ b/flow/testing/layer_test.h @@ -10,6 +10,7 @@ #include #include +#include #include "flutter/flow/testing/mock_raster_cache.h" #include "flutter/fml/macros.h" @@ -58,6 +59,7 @@ class LayerTestBase : public CanvasTestBase { .checkerboard_offscreen_layers = false, .frame_device_pixel_ratio = 1.0f, .has_platform_view = false, + .raster_cached_entries = &cacheable_items_, // clang-format on }, paint_context_{ @@ -158,14 +160,16 @@ class LayerTestBase : public CanvasTestBase { set_raster_cache_(std::make_unique()); } + std::vector& cacheable_items() { return cacheable_items_; } + TextureRegistry& texture_regitry() { return texture_registry_; } RasterCache* raster_cache() { return raster_cache_.get(); } PrerollContext* preroll_context() { return &preroll_context_; } - Layer::PaintContext& paint_context() { return paint_context_; } - Layer::PaintContext& display_list_paint_context() { + PaintContext& paint_context() { return paint_context_; } + PaintContext& display_list_paint_context() { return display_list_paint_context_; } - Layer::PaintContext& check_board_context() { return check_board_context_; } + PaintContext& check_board_context() { return check_board_context_; } LayerSnapshotStore& layer_snapshot_store() { return snapshot_store_; } sk_sp display_list() { @@ -205,14 +209,16 @@ class LayerTestBase : public CanvasTestBase { std::unique_ptr raster_cache_; PrerollContext preroll_context_; - Layer::PaintContext paint_context_; + PaintContext paint_context_; DisplayListCanvasRecorder display_list_recorder_; sk_sp display_list_; SkNWayCanvas internal_display_list_canvas_; - Layer::PaintContext display_list_paint_context_; - Layer::PaintContext check_board_context_; + PaintContext display_list_paint_context_; + PaintContext check_board_context_; LayerSnapshotStore snapshot_store_; + std::vector cacheable_items_; + FML_DISALLOW_COPY_AND_ASSIGN(LayerTestBase); }; using LayerTest = LayerTestBase<::testing::Test>; diff --git a/flow/testing/mock_layer.cc b/flow/testing/mock_layer.cc index 09b7c20ccde5f..6ff049469c2b1 100644 --- a/flow/testing/mock_layer.cc +++ b/flow/testing/mock_layer.cc @@ -4,6 +4,9 @@ #include "flutter/flow/testing/mock_layer.h" +#include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/layers/layer.h" +#include "flutter/flow/testing/mock_raster_cache.h" namespace flutter { namespace testing { @@ -63,5 +66,23 @@ void MockLayer::Paint(PaintContext& context) const { } } +void MockCacheableContainerLayer::Preroll(PrerollContext* context, + const SkMatrix& matrix) { + Layer::AutoPrerollSaveLayerState save = + Layer::AutoPrerollSaveLayerState::Create(context); + auto cache = AutoCache(layer_raster_cache_item_.get(), context, matrix); + + ContainerLayer::Preroll(context, matrix); +} + +void MockCacheableLayer::Preroll(PrerollContext* context, + const SkMatrix& matrix) { + Layer::AutoPrerollSaveLayerState save = + Layer::AutoPrerollSaveLayerState::Create(context); + auto cache = AutoCache(raster_cache_item_.get(), context, matrix); + + MockLayer::Preroll(context, matrix); +} + } // namespace testing } // namespace flutter diff --git a/flow/testing/mock_layer.h b/flow/testing/mock_layer.h index 54a555c8dc87a..4e34fa1639d77 100644 --- a/flow/testing/mock_layer.h +++ b/flow/testing/mock_layer.h @@ -5,7 +5,15 @@ #ifndef FLOW_TESTING_MOCK_LAYER_H_ #define FLOW_TESTING_MOCK_LAYER_H_ +#include +#include +#include "flutter/flow/diff_context.h" +#include "flutter/flow/layers/cacheable_layer.h" +#include "flutter/flow/layers/container_layer.h" #include "flutter/flow/layers/layer.h" +#include "flutter/flow/layers/layer_raster_cache_item.h" +#include "flutter/flow/raster_cache.h" +#include "flutter/flow/raster_cache_item.h" namespace flutter { namespace testing { @@ -57,6 +65,57 @@ class MockLayer : public Layer { FML_DISALLOW_COPY_AND_ASSIGN(MockLayer); }; +class MockCacheableContainerLayer : public CacheableContainerLayer { + public: + // if render more than 3 frames, try to cache itself. + // if less 3 frames, cache his children + static std::shared_ptr CacheLayerOrChildren() { + return std::make_shared(true); + } + + // if render more than 3 frames, try to cache itself. + // if less 3 frames, cache nothing + static std::shared_ptr CacheLayerOnly() { + return std::make_shared(); + } + + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; + + explicit MockCacheableContainerLayer(bool cache_children = false) + : CacheableContainerLayer(3, cache_children) {} +}; + +class MockLayerCacheableItem : public LayerRasterCacheItem { + public: + using LayerRasterCacheItem::LayerRasterCacheItem; +}; +class MockCacheableLayer : public MockLayer { + public: + explicit MockCacheableLayer(SkPath path, + SkPaint paint = SkPaint(), + int render_limit = 3, + bool fake_has_platform_view = false, + bool fake_reads_surface = false, + bool fake_opacity_compatible = false) + : MockLayer(path, + paint, + fake_has_platform_view, + fake_reads_surface, + fake_opacity_compatible) { + raster_cache_item_ = + std::make_unique(this, render_limit); + } + + const LayerRasterCacheItem* raster_cache_item() const { + return raster_cache_item_.get(); + } + + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; + + private: + std::unique_ptr raster_cache_item_; +}; + } // namespace testing } // namespace flutter diff --git a/flow/testing/mock_raster_cache.cc b/flow/testing/mock_raster_cache.cc index b9907d4c77187..9b69eb39103c2 100644 --- a/flow/testing/mock_raster_cache.cc +++ b/flow/testing/mock_raster_cache.cc @@ -4,6 +4,15 @@ #include "flutter/flow/testing/mock_raster_cache.h" +#include "flutter/flow/layers/display_list_raster_cache_item.h" +#include "flutter/flow/layers/layer.h" +#include "flutter/flow/raster_cache.h" +#include "flutter/flow/raster_cache_item.h" +#include "include/core/SkCanvas.h" +#include "include/core/SkMatrix.h" +#include "include/core/SkPoint.h" +#include "third_party/skia/include/core/SkPictureRecorder.h" + namespace flutter { namespace testing { @@ -11,37 +20,29 @@ MockRasterCacheResult::MockRasterCacheResult(SkRect device_rect) : RasterCacheResult(nullptr, SkRect::MakeEmpty(), "RasterCacheFlow::test"), device_rect_(device_rect) {} -std::unique_ptr MockRasterCache::RasterizeDisplayList( - DisplayList* display_list, - GrDirectContext* context, - const SkMatrix& ctm, - SkColorSpace* dst_color_space, - bool checkerboard) const { - SkRect logical_rect = display_list->bounds(); - SkRect cache_rect = RasterCache::GetDeviceBounds(logical_rect, ctm); - - return std::make_unique(cache_rect); -} - -std::unique_ptr MockRasterCache::RasterizeLayer( - PrerollContext* context, - Layer* layer, - RasterCacheLayerStrategy strategy, - const SkMatrix& ctm, - bool checkerboard) const { - SkRect logical_rect = layer->paint_bounds(); - SkRect cache_rect = RasterCache::GetDeviceBounds(logical_rect, ctm); - - return std::make_unique(cache_rect); -} - void MockRasterCache::AddMockLayer(int width, int height) { SkMatrix ctm = SkMatrix::I(); SkPath path; path.addRect(100, 100, 100 + width, 100 + height); - MockLayer layer = MockLayer(path); + MockCacheableLayer layer = MockCacheableLayer(path); layer.Preroll(&preroll_context_, ctm); - Prepare(&preroll_context_, &layer, ctm); + layer.raster_cache_item()->TryToPrepareRasterCache(paint_context_); + RasterCache::Context r_context = { + // clang-format off + .gr_context = preroll_context_.gr_context, + .dst_color_space = preroll_context_.dst_color_space, + .matrix = ctm, + .logical_rect = layer.paint_bounds(), + .checkerboard = preroll_context_.checkerboard_offscreen_layers, + // clang-format on + }; + UpdateCacheEntry( + RasterCacheKeyID(layer.unique_id(), RasterCacheKeyType::kLayer), + r_context, [&](SkCanvas* canvas) { + SkRect cache_rect = RasterCacheUtil::GetDeviceBounds( + r_context.logical_rect, r_context.matrix); + return std::make_unique(cache_rect); + }); } void MockRasterCache::AddMockPicture(int width, int height) { @@ -53,38 +54,108 @@ void MockRasterCache::AddMockPicture(int width, int height) { path.addRect(100, 100, 100 + width, 100 + height); recorder.drawPath(path, SkPaint()); sk_sp display_list = recorder.Build(); - PrerollContextHolder holder = GetSamplePrerollContextHolder(); - holder.preroll_context.dst_color_space = color_space_; + + PaintContextHolder holder = GetSamplePaintContextHolder(this); + holder.paint_context.dst_color_space = color_space_; + + DisplayListRasterCacheItem display_list_item(display_list.get(), SkPoint(), + true, false); for (int i = 0; i < access_threshold(); i++) { - Prepare(&holder.preroll_context, display_list.get(), true, false, ctm); - Draw(*display_list, mock_canvas_); + MarkSeen(display_list_item.GetId().value(), ctm); + Draw(display_list_item.GetId().value(), mock_canvas_, nullptr); } - Prepare(&holder.preroll_context, display_list.get(), true, false, ctm); + MarkSeen(display_list_item.GetId().value(), ctm); + RasterCache::Context r_context = { + // clang-format off + .gr_context = preroll_context_.gr_context, + .dst_color_space = preroll_context_.dst_color_space, + .matrix = ctm, + .logical_rect = display_list->bounds(), + .checkerboard = preroll_context_.checkerboard_offscreen_layers, + // clang-format on + }; + UpdateCacheEntry(RasterCacheKeyID(display_list->unique_id(), + RasterCacheKeyType::kDisplayList), + r_context, [&](SkCanvas* canvas) { + SkRect cache_rect = RasterCacheUtil::GetDeviceBounds( + r_context.logical_rect, r_context.matrix); + return std::make_unique(cache_rect); + }); } -PrerollContextHolder GetSamplePrerollContextHolder() { +static std::vector raster_cache_items_; +PrerollContextHolder GetSamplePrerollContextHolder(RasterCache* raster_cache) { FixedRefreshRateStopwatch raster_time; FixedRefreshRateStopwatch ui_time; MutatorsStack mutators_stack; TextureRegistry texture_registry; sk_sp srgb = SkColorSpace::MakeSRGB(); + PrerollContextHolder holder = { { - nullptr, /* raster_cache */ - nullptr, /* gr_context */ - nullptr, /* external_view_embedder */ - mutators_stack, srgb.get(), /* color_space */ - kGiantRect, /* cull_rect */ - false, /* layer reads from surface */ - raster_time, ui_time, texture_registry, - false, /* checkerboard_offscreen_layers */ - 1.0f, /* frame_device_pixel_ratio */ - false, /* has_platform_view */ + // clang-format off + .raster_cache = raster_cache, + .gr_context = nullptr, + .view_embedder = nullptr, + .mutators_stack = mutators_stack, + .dst_color_space = srgb.get(), + .cull_rect = kGiantRect, + .surface_needs_readback = false, + .raster_time = raster_time, + .ui_time = ui_time, + .texture_registry = texture_registry, + .checkerboard_offscreen_layers = false, + .frame_device_pixel_ratio = 1.0f, + .has_platform_view = false, + .has_texture_layer = false, + .raster_cached_entries = &raster_cache_items_, + // clang-format on }, srgb}; return holder; } +PaintContextHolder GetSamplePaintContextHolder(RasterCache* raster_cache) { + FixedRefreshRateStopwatch raster_time; + FixedRefreshRateStopwatch ui_time; + MutatorsStack mutators_stack; + TextureRegistry texture_registry; + sk_sp srgb = SkColorSpace::MakeSRGB(); + PaintContextHolder holder = {// clang-format off + { + .internal_nodes_canvas = nullptr, + .leaf_nodes_canvas = nullptr, + .gr_context = nullptr, + .dst_color_space = srgb.get(), + .view_embedder = nullptr, + .raster_time = raster_time, + .ui_time = ui_time, + .texture_registry = texture_registry, + .raster_cache = raster_cache, + .checkerboard_offscreen_layers = false, + .frame_device_pixel_ratio = 1.0f, + .inherited_opacity = SK_Scalar1, + }, + // clang-format on + srgb}; + + return holder; +} + +bool DisplayListRasterCacheItemTryToRasterCache( + DisplayListRasterCacheItem& display_list_item, + PrerollContext& context, + PaintContext& paint_context, + const SkMatrix& matrix) { + display_list_item.PrerollSetup(&context, matrix); + display_list_item.PrerollFinalize(&context, matrix); + if (display_list_item.cache_state() == + RasterCacheItem::CacheState::kCurrent) { + return display_list_item.TryToPrepareRasterCache(paint_context); + } + return false; +} + } // namespace testing } // namespace flutter diff --git a/flow/testing/mock_raster_cache.h b/flow/testing/mock_raster_cache.h index bd99b94cb3682..0a240985f850d 100644 --- a/flow/testing/mock_raster_cache.h +++ b/flow/testing/mock_raster_cache.h @@ -5,13 +5,17 @@ #ifndef FLOW_TESTING_MOCK_RASTER_CACHE_H_ #define FLOW_TESTING_MOCK_RASTER_CACHE_H_ +#include #include "flutter/flow/layers/layer.h" #include "flutter/flow/raster_cache.h" +#include "flutter/flow/raster_cache_item.h" #include "flutter/flow/testing/mock_layer.h" #include "flutter/testing/mock_canvas.h" +#include "include/core/SkCanvas.h" #include "third_party/skia/include/core/SkColorSpace.h" #include "third_party/skia/include/core/SkColorType.h" #include "third_party/skia/include/core/SkImage.h" +#include "third_party/skia/include/core/SkPicture.h" namespace flutter { namespace testing { @@ -50,24 +54,12 @@ class MockRasterCacheResult : public RasterCacheResult { */ class MockRasterCache : public RasterCache { public: - explicit MockRasterCache(size_t access_threshold = 3, - size_t display_list_cache_limit_per_frame = - kDefaultDispLayListCacheLimitPerFrame) - : RasterCache(access_threshold, display_list_cache_limit_per_frame) {} - - std::unique_ptr RasterizeDisplayList( - DisplayList* display_list, - GrDirectContext* context, - const SkMatrix& ctm, - SkColorSpace* dst_color_space, - bool checkerboard) const override; - - std::unique_ptr RasterizeLayer( - PrerollContext* context, - Layer* layer, - RasterCacheLayerStrategy stategy, - const SkMatrix& ctm, - bool checkerboard) const override; + explicit MockRasterCache( + size_t access_threshold = 3, + size_t picture_and_display_list_cache_limit_per_frame = + RasterCacheUtil::kDefaultPictureAndDispLayListCacheLimitPerFrame) + : RasterCache(access_threshold, + picture_and_display_list_cache_limit_per_frame) {} void AddMockLayer(int width, int height); void AddMockPicture(int width, int height); @@ -97,6 +89,23 @@ class MockRasterCache : public RasterCache { .has_texture_layer = false, // clang-format on }; + + PaintContext paint_context_ = { + // clang-format off + .internal_nodes_canvas = nullptr, + .leaf_nodes_canvas = nullptr, + .gr_context = nullptr, + .dst_color_space = color_space_, + .view_embedder = nullptr, + .raster_time = raster_time_, + .ui_time = ui_time_, + .texture_registry = texture_registry_, + .raster_cache = nullptr, + .checkerboard_offscreen_layers = false, + .frame_device_pixel_ratio = 1.0f, + .inherited_opacity = SK_Scalar1, + // clang-format on + }; }; struct PrerollContextHolder { @@ -104,7 +113,22 @@ struct PrerollContextHolder { sk_sp srgb; }; -PrerollContextHolder GetSamplePrerollContextHolder(); +struct PaintContextHolder { + PaintContext paint_context; + sk_sp srgb; +}; + +PrerollContextHolder GetSamplePrerollContextHolder( + RasterCache* raster_cache = nullptr); + +PaintContextHolder GetSamplePaintContextHolder( + RasterCache* raster_cache = nullptr); + +bool DisplayListRasterCacheItemTryToRasterCache( + DisplayListRasterCacheItem& display_list_item, + PrerollContext& context, + PaintContext& paint_context, + const SkMatrix& matrix); } // namespace testing } // namespace flutter diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 18737e57f6d6f..5b97ac0c27b46 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "include/core/SkMatrix.h" #define FML_USED_ON_EMBEDDER #include @@ -13,6 +14,7 @@ #include "assets/directory_asset_bundle.h" #include "common/graphics/persistent_cache.h" #include "flutter/flow/layers/display_list_layer.h" +#include "flutter/flow/layers/layer_raster_cache_item.h" #include "flutter/flow/layers/transform_layer.h" #include "flutter/fml/command_line.h" #include "flutter/fml/dart/dart_converter.h" @@ -2370,8 +2372,10 @@ TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) { // 2. Rasterize the picture and the picture layer in the raster cache. std::promise rasterized; + shell->GetTaskRunners().GetRasterTaskRunner()->PostTask( [&shell, &rasterized, &display_list, &display_list_layer] { + std::vector raster_cache_items; auto* compositor_context = shell->GetRasterizer()->compositor_context(); auto& raster_cache = compositor_context->raster_cache(); @@ -2379,9 +2383,26 @@ TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) { FixedRefreshRateStopwatch ui_time; MutatorsStack mutators_stack; TextureRegistry texture_registry; + PaintContext paint_context = { + // clang-format off + .internal_nodes_canvas = nullptr, + .leaf_nodes_canvas = nullptr, + .gr_context = nullptr, + .dst_color_space = nullptr, + .view_embedder = nullptr, + .raster_time = raster_time, + .ui_time = ui_time, + .texture_registry = texture_registry, + .raster_cache = &raster_cache, + .checkerboard_offscreen_layers = false, + .frame_device_pixel_ratio = 1.0f, + .inherited_opacity = SK_Scalar1, + // clang-format on + }; + PrerollContext preroll_context = { // clang-format off - .raster_cache = nullptr, + .raster_cache = &raster_cache, .gr_context = nullptr, .view_embedder = nullptr, .mutators_stack = mutators_stack, @@ -2394,25 +2415,38 @@ TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) { .checkerboard_offscreen_layers = false, .frame_device_pixel_ratio = 1.0f, .has_platform_view = false, + .has_texture_layer = false, + .raster_cached_entries = &raster_cache_items, // clang-format on }; // 2.1. Rasterize the picture. Call Draw multiple times to pass the // access threshold (default to 3) so a cache can be generated. SkCanvas dummy_canvas; + SkPaint paint; bool picture_cache_generated; + DisplayListRasterCacheItem display_list_raster_cache_item( + display_list.get(), SkPoint(), true, false); for (int i = 0; i < 4; i += 1) { SkMatrix matrix = SkMatrix::I(); - - picture_cache_generated = raster_cache.Prepare( - &preroll_context, display_list.get(), true, false, matrix); - raster_cache.Draw(*display_list, dummy_canvas); + display_list_raster_cache_item.PrerollSetup(&preroll_context, matrix); + display_list_raster_cache_item.PrerollFinalize(&preroll_context, + matrix); + picture_cache_generated = + display_list_raster_cache_item.need_caching(); + display_list_raster_cache_item.TryToPrepareRasterCache(paint_context); + display_list_raster_cache_item.Draw(paint_context, &dummy_canvas, + &paint); } ASSERT_TRUE(picture_cache_generated); // 2.2. Rasterize the picture layer. - raster_cache.Prepare(&preroll_context, display_list_layer.get(), - SkMatrix::I()); + LayerRasterCacheItem layer_raster_cache_item(display_list_layer.get()); + layer_raster_cache_item.PrerollSetup(&preroll_context, SkMatrix::I()); + layer_raster_cache_item.PrerollFinalize(&preroll_context, + SkMatrix::I()); + layer_raster_cache_item.TryToPrepareRasterCache(paint_context); + layer_raster_cache_item.Draw(paint_context, &dummy_canvas, &paint); rasterized.set_value(true); }); rasterized.get_future().wait();