From 3e12ef592fab1eb4f322f4a154ac10d3b0fe9b3a Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 17 Apr 2020 13:28:27 -0700 Subject: [PATCH 1/7] introduce raster cache test harness and fix caching of opacity children --- flow/layers/opacity_layer.cc | 13 +- flow/layers/opacity_layer.h | 1 + flow/layers/opacity_layer_unittests.cc | 59 ++++++++ flow/raster_cache.cc | 2 +- flow/raster_cache.h | 4 +- flow/testing/layer_test.h | 9 +- testing/BUILD.gn | 3 + testing/mock_raster_cache.cc | 198 +++++++++++++++++++++++++ testing/mock_raster_cache.h | 92 ++++++++++++ 9 files changed, 373 insertions(+), 8 deletions(-) create mode 100644 testing/mock_raster_cache.cc create mode 100644 testing/mock_raster_cache.h diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index d864db354bccc..9fd0bcc09b5e7 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -55,7 +55,7 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { #ifndef SUPPORT_FRACTIONAL_TRANSLATION child_matrix = RasterCache::GetIntegralTransCTM(child_matrix); #endif - TryToPrepareRasterCache(context, container, child_matrix); + TryToPrepareRasterCache(context, GetCacheableChild(), child_matrix); } // Restore cull_rect @@ -78,7 +78,7 @@ void OpacityLayer::Paint(PaintContext& context) const { #endif if (context.raster_cache && - context.raster_cache->Draw(GetChildContainer(), + context.raster_cache->Draw(GetCacheableChild(), *context.leaf_nodes_canvas, &paint)) { return; } @@ -118,4 +118,13 @@ ContainerLayer* OpacityLayer::GetChildContainer() const { return static_cast(layers()[0].get()); } +Layer* OpacityLayer::GetCacheableChild() const { + ContainerLayer* child_container = GetChildContainer(); + if (child_container->layers().size() == 1) { + return child_container->layers()[0].get(); + } + + return child_container; +} + } // namespace flutter diff --git a/flow/layers/opacity_layer.h b/flow/layers/opacity_layer.h index 658dd1a7b8488..c85f8e05280b0 100644 --- a/flow/layers/opacity_layer.h +++ b/flow/layers/opacity_layer.h @@ -39,6 +39,7 @@ class OpacityLayer : public ContainerLayer { private: ContainerLayer* GetChildContainer() const; + Layer* GetCacheableChild() const; SkAlpha alpha_; SkPoint offset_; diff --git a/flow/layers/opacity_layer_unittests.cc b/flow/layers/opacity_layer_unittests.cc index e08d5c9accb80..4ec014cd58e3b 100644 --- a/flow/layers/opacity_layer_unittests.cc +++ b/flow/layers/opacity_layer_unittests.cc @@ -53,6 +53,65 @@ TEST_F(OpacityLayerTest, PaintBeforePreollDies) { } #endif +TEST_F(OpacityLayerTest, ChildIsCached) { + const SkAlpha alpha_half = 255 / 2; + auto initial_transform = SkMatrix::MakeTrans(50.0, 25.5); + auto other_transform = SkMatrix::MakeScale(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(alpha_half, SkPoint::Make(0.0f, 0.0f)); + layer->Add(mock_layer); + + SkMatrix cache_ctm = initial_transform; +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + cache_ctm = RasterCache::GetIntegralTransCTM(cache_ctm); +#endif + + EXPECT_EQ(raster_cache()->LayerCacheCount(), 0); + EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer.get(), other_transform)); + EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer.get(), cache_ctm)); + + layer->Preroll(preroll_context(), initial_transform); + + EXPECT_EQ(raster_cache()->LayerCacheCount(), 1); + EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer.get(), other_transform)); + EXPECT_TRUE(raster_cache()->WasPrepared(mock_layer.get(), cache_ctm)); +} + +TEST_F(OpacityLayerTest, ChildrenNotCached) { + const SkAlpha alpha_half = 255 / 2; + auto initial_transform = SkMatrix::MakeTrans(50.0, 25.5); + auto other_transform = SkMatrix::MakeScale(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(alpha_half, SkPoint::Make(0.0f, 0.0f)); + layer->Add(mock_layer1); + layer->Add(mock_layer2); + + SkMatrix cache_ctm = initial_transform; +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + cache_ctm = RasterCache::GetIntegralTransCTM(cache_ctm); +#endif + + EXPECT_EQ(raster_cache()->LayerCacheCount(), 0); + EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer1.get(), other_transform)); + EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer1.get(), cache_ctm)); + EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer2.get(), other_transform)); + EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer2.get(), cache_ctm)); + + layer->Preroll(preroll_context(), initial_transform); + + EXPECT_EQ(raster_cache()->LayerCacheCount(), 1); + EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer1.get(), other_transform)); + EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer1.get(), cache_ctm)); + EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer2.get(), other_transform)); + EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer2.get(), cache_ctm)); +} + TEST_F(OpacityLayerTest, FullyOpaque) { const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); const SkPoint layer_offset = SkPoint::Make(0.5f, 1.5f); diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index d41408beb5ab6..7b4baff0890ca 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -121,7 +121,7 @@ static RasterCacheResult Rasterize( return {surface->makeImageSnapshot(), logical_rect}; } -RasterCacheResult RasterizePicture(SkPicture* picture, +static RasterCacheResult RasterizePicture(SkPicture* picture, GrContext* context, const SkMatrix& ctm, SkColorSpace* dst_color_space, diff --git a/flow/raster_cache.h b/flow/raster_cache.h index 3174ed0f5672a..236a08a07f14f 100644 --- a/flow/raster_cache.h +++ b/flow/raster_cache.h @@ -91,14 +91,14 @@ class RasterCache { // 3. The picture is accessed too few times // 4. There are too many pictures to be cached in the current frame. // (See also kDefaultPictureCacheLimitPerFrame.) - bool Prepare(GrContext* context, + virtual bool Prepare(GrContext* context, SkPicture* picture, const SkMatrix& transformation_matrix, SkColorSpace* dst_color_space, bool is_complex, bool will_change); - void Prepare(PrerollContext* context, Layer* layer, const SkMatrix& ctm); + virtual void Prepare(PrerollContext* context, Layer* layer, const SkMatrix& ctm); // Find the raster cache for the picture and draw it to the canvas. // diff --git a/flow/testing/layer_test.h b/flow/testing/layer_test.h index 593dec1836823..2b16790e1f8b0 100644 --- a/flow/testing/layer_test.h +++ b/flow/testing/layer_test.h @@ -13,6 +13,7 @@ #include "flutter/fml/macros.h" #include "flutter/testing/canvas_test.h" #include "flutter/testing/mock_canvas.h" +#include "flutter/testing/mock_raster_cache.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/utils/SkNWayCanvas.h" @@ -32,9 +33,9 @@ class LayerTestBase : public CanvasTestBase { public: LayerTestBase() : preroll_context_({ - nullptr, /* raster_cache */ - nullptr, /* gr_context */ - nullptr, /* external_view_embedder */ + raster_cache(), /* raster_cache */ + nullptr, /* gr_context */ + nullptr, /* external_view_embedder */ mutators_stack_, TestT::mock_canvas().imageInfo().colorSpace(), kGiantRect, /* cull_rect */ false, /* layer reads from surface */ @@ -58,6 +59,7 @@ class LayerTestBase : public CanvasTestBase { }) {} TextureRegistry& texture_regitry() { return texture_registry_; } + MockRasterCache* raster_cache() { return &raster_cache_; } PrerollContext* preroll_context() { return &preroll_context_; } Layer::PaintContext& paint_context() { return paint_context_; } @@ -67,6 +69,7 @@ class LayerTestBase : public CanvasTestBase { MutatorsStack mutators_stack_; TextureRegistry texture_registry_; + MockRasterCache raster_cache_; PrerollContext preroll_context_; Layer::PaintContext paint_context_; diff --git a/testing/BUILD.gn b/testing/BUILD.gn index b8c3bd0a2d53b..69f7d322264d4 100644 --- a/testing/BUILD.gn +++ b/testing/BUILD.gn @@ -71,10 +71,13 @@ source_set("skia") { "canvas_test.h", "mock_canvas.cc", "mock_canvas.h", + "mock_raster_cache.cc", + "mock_raster_cache.h", ] public_deps = [ ":testing_lib", + "//flutter/flow", "//third_party/skia", ] } diff --git a/testing/mock_raster_cache.cc b/testing/mock_raster_cache.cc new file mode 100644 index 0000000000000..d485e292d73bb --- /dev/null +++ b/testing/mock_raster_cache.cc @@ -0,0 +1,198 @@ +// 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.h" +#include "flutter/testing/mock_raster_cache.h" + +namespace flutter { +namespace testing { + +// RasterCacheResult::RasterCacheResult(sk_sp image, +// const SkRect& logical_rect) +// : image_(std::move(image)), logical_rect_(logical_rect) {} + +// void RasterCacheResult::draw(SkCanvas& canvas, const SkPaint* paint) const { +// TRACE_EVENT0("flutter", "RasterCacheResult::draw"); +// SkAutoCanvasRestore auto_restore(&canvas, true); +// SkIRect bounds = +// RasterCache::GetDeviceBounds(logical_rect_, canvas.getTotalMatrix()); +// FML_DCHECK( +// std::abs(bounds.size().width() - image_->dimensions().width()) <= 1 && +// std::abs(bounds.size().height() - image_->dimensions().height()) <= 1); +// canvas.resetMatrix(); +// canvas.drawImage(image_, bounds.fLeft, bounds.fTop, paint); +// } + +MockRasterCache::MockRasterCache() + : RasterCache::RasterCache(1, 1000000) { } + +static bool CanRasterizePicture(SkPicture* picture) { + if (picture == nullptr) { + return false; + } + + const SkRect cull_rect = picture->cullRect(); + + if (cull_rect.isEmpty()) { + // No point in ever rasterizing an empty picture. + return false; + } + + if (!cull_rect.isFinite()) { + // Cannot attempt to rasterize into an infinitely large surface. + return false; + } + + return true; +} + +static bool IsPictureWorthRasterizing(SkPicture* picture, + bool will_change, + bool is_complex) { + if (will_change) { + // If the picture is going to change in the future, there is no point in + // doing to extra work to rasterize. + return false; + } + + if (!CanRasterizePicture(picture)) { + // No point in deciding whether the picture is worth rasterizing if it + // cannot be rasterized at all. + return false; + } + + if (is_complex) { + // The caller seems to have extra information about the picture and thinks + // the picture is always worth rasterizing. + return true; + } + + // TODO(abarth): We should find a better heuristic here that lets us avoid + // wasting memory on trivial layers that are easy to re-rasterize every frame. + return picture->approximateOpCount() > 5; +} + +/// @note Procedure doesn't copy all closures. +static RasterCacheResult Rasterize( + const SkMatrix& ctm, + SkColorSpace* dst_color_space, + const SkRect& logical_rect) { + TRACE_EVENT0("flutter", "RasterCachePopulate"); + SkIRect cache_rect = RasterCache::GetDeviceBounds(logical_rect, ctm); + + const SkImageInfo image_info = SkImageInfo::MakeN32Premul( + cache_rect.width(), cache_rect.height(), sk_ref_sp(dst_color_space)); + + sk_sp data = SkData::MakeUninitialized(image_info.computeMinByteSize()); + sk_sp image = SkImage::MakeRasterData(image_info, data, image_info.minRowBytes()); + + return {image, logical_rect}; +} + +static RasterCacheResult RasterizePicture(SkPicture* picture, + const SkMatrix& ctm, + SkColorSpace* dst_color_space) { + return Rasterize(ctm, dst_color_space, picture->cullRect()); +} + +void MockRasterCache::Prepare(PrerollContext* context, + Layer* layer, + const SkMatrix& ctm) { + LayerRasterCacheKey cache_key(layer->unique_id(), ctm); + Entry& entry = layer_cache_[cache_key]; + entry.access_count++; + entry.used_this_frame = true; + if (!entry.image.is_valid()) { + entry.image = Rasterize( + ctm, context->dst_color_space, + layer->paint_bounds()); + } +} + +bool MockRasterCache::WasPrepared(Layer* layer, const SkMatrix& ctm) { + LayerRasterCacheKey cache_key(layer->unique_id(), ctm); + // return layer_cache_.contains(cache_key); - Requires STD_VER > 17, C++20 + return layer_cache_.find(cache_key) != layer_cache_.end(); +} + +bool MockRasterCache::Prepare(GrContext* context, + SkPicture* picture, + const SkMatrix& transformation_matrix, + SkColorSpace* dst_color_space, + bool is_complex, + bool will_change) { + if (!IsPictureWorthRasterizing(picture, will_change, is_complex)) { + // We only deal with pictures that are worthy of rasterization. + return false; + } + + // Decompose the matrix (once) for all subsequent operations. We want to make + // sure to avoid volumetric distortions while accounting for scaling. + const MatrixDecomposition matrix(transformation_matrix); + + if (!matrix.IsValid()) { + // The matrix was singular. No point in going further. + return false; + } + + PictureRasterCacheKey cache_key(picture->uniqueID(), transformation_matrix); + + // Creates an entry, if not present prior. + Entry& entry = picture_cache_[cache_key]; + + if (!entry.image.is_valid()) { + entry.image = RasterizePicture(picture, transformation_matrix, + dst_color_space); + } + return true; +} + +RasterCacheResult MockRasterCache::Get(const SkPicture& picture, + const SkMatrix& ctm) const { + PictureRasterCacheKey cache_key(picture.uniqueID(), ctm); + auto it = picture_cache_.find(cache_key); + if (it == picture_cache_.end()) { + return RasterCacheResult(); + } + + Entry& entry = it->second; + entry.access_count++; + entry.used_this_frame = true; + + return entry.image; +} + +RasterCacheResult MockRasterCache::Get(Layer* layer, const SkMatrix& ctm) const { + LayerRasterCacheKey cache_key(layer->unique_id(), ctm); + auto it = layer_cache_.find(cache_key); + if (it == layer_cache_.end()) { + return RasterCacheResult(); + } + + Entry& entry = it->second; + entry.access_count++; + entry.used_this_frame = true; + + return entry.image; +} + +void MockRasterCache::SweepAfterFrame() { + SweepOneCacheAfterFrame(picture_cache_); + SweepOneCacheAfterFrame(layer_cache_); +} + +void MockRasterCache::Clear() { + picture_cache_.clear(); + layer_cache_.clear(); +} + +size_t MockRasterCache::GetCachedEntriesCount() const { + return layer_cache_.size() + picture_cache_.size(); +} + +void MockRasterCache::SetCheckboardCacheImages(bool checkerboard) { +} + +} // namespace testing +} // namespace flutter diff --git a/testing/mock_raster_cache.h b/testing/mock_raster_cache.h new file mode 100644 index 0000000000000..89ac96b7c5d7d --- /dev/null +++ b/testing/mock_raster_cache.h @@ -0,0 +1,92 @@ +// 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 TESTING_MOCK_RASTER_CACHE_H_ +#define TESTING_MOCK_RASTER_CACHE_H_ + +#include "flutter/flow/raster_cache.h" +#include "third_party/skia/include/core/SkPicture.h" +#include "third_party/skia/include/core/SkImage.h" + +namespace flutter { +namespace testing { + +// Mock |RasterCache|, useful for writing tests that impact the Flutter engine +// raster_cache without requiring a GPU. +// +// The |MockCanvas| stores a list of Engine Layers and Pictures that have been +// inserted into the cache that the test can later verify against the expected +// list of cached objects. +class MockRasterCache : public RasterCache { + public: + // Return true if the cache is generated. + // + // We may return false and not generate the cache if + // 1. The picture is not worth rasterizing + // 2. The matrix is singular + // 3. The picture is accessed too few times + // 4. There are too many pictures to be cached in the current frame. + // (See also kDefaultPictureCacheLimitPerFrame.) + bool Prepare(GrContext* context, + SkPicture* picture, + const SkMatrix& transformation_matrix, + SkColorSpace* dst_color_space, + bool is_complex, + bool will_change) override; + + void Prepare(PrerollContext* context, Layer* layer, const SkMatrix& ctm) override; + + RasterCacheResult Get(const SkPicture& picture, const SkMatrix& ctm) const override; + + RasterCacheResult Get(Layer* layer, const SkMatrix& ctm) const override; + + bool WasPrepared(Layer* layer, const SkMatrix& ctm); + + void SweepAfterFrame(); + + void Clear(); + + int PictureCacheCount() { return picture_cache_.size(); } + int LayerCacheCount() { return layer_cache_.size(); } + + void SetCheckboardCacheImages(bool checkerboard); + + size_t GetCachedEntriesCount() const; + + MockRasterCache(); + + protected: + + private: + struct Entry { + bool used_this_frame = false; + size_t access_count = 0; + RasterCacheResult image; + }; + + template + static void SweepOneCacheAfterFrame(Cache& cache) { + std::vector dead; + + for (auto it = cache.begin(); it != cache.end(); ++it) { + Entry& entry = it->second; + if (!entry.used_this_frame) { + dead.push_back(it); + } + entry.used_this_frame = false; + } + + for (auto it : dead) { + cache.erase(it); + } + } + + mutable PictureRasterCacheKey::Map picture_cache_; + mutable LayerRasterCacheKey::Map layer_cache_; +}; + +} // namespace testing +} // namespace flutter + +#endif // TESTING_MOCK_RASTER_CACHE_H_ From 9d93cf87adb10a37deb93e69b6772ad41525732a Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Thu, 23 Apr 2020 16:52:31 -0700 Subject: [PATCH 2/7] adjust to new RasterCache::Draw() API and code cleanup --- flow/layers/opacity_layer_unittests.cc | 32 +++++--- flow/raster_cache.cc | 8 +- flow/raster_cache.h | 22 +++--- testing/mock_raster_cache.cc | 101 ++++++------------------- testing/mock_raster_cache.h | 26 +++---- 5 files changed, 73 insertions(+), 116 deletions(-) diff --git a/flow/layers/opacity_layer_unittests.cc b/flow/layers/opacity_layer_unittests.cc index 4ec014cd58e3b..0064d79e86264 100644 --- a/flow/layers/opacity_layer_unittests.cc +++ b/flow/layers/opacity_layer_unittests.cc @@ -67,16 +67,20 @@ TEST_F(OpacityLayerTest, ChildIsCached) { #ifndef SUPPORT_FRACTIONAL_TRANSLATION cache_ctm = RasterCache::GetIntegralTransCTM(cache_ctm); #endif + SkCanvas cache_canvas; + cache_canvas.setMatrix(cache_ctm); + SkCanvas other_canvas; + other_canvas.setMatrix(other_transform); EXPECT_EQ(raster_cache()->LayerCacheCount(), 0); - EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer.get(), other_transform)); - EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer.get(), cache_ctm)); + EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas)); + EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(raster_cache()->LayerCacheCount(), 1); - EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer.get(), other_transform)); - EXPECT_TRUE(raster_cache()->WasPrepared(mock_layer.get(), cache_ctm)); + EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas)); + EXPECT_TRUE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); } TEST_F(OpacityLayerTest, ChildrenNotCached) { @@ -96,20 +100,24 @@ TEST_F(OpacityLayerTest, ChildrenNotCached) { #ifndef SUPPORT_FRACTIONAL_TRANSLATION cache_ctm = RasterCache::GetIntegralTransCTM(cache_ctm); #endif + SkCanvas cache_canvas; + cache_canvas.setMatrix(cache_ctm); + SkCanvas other_canvas; + other_canvas.setMatrix(other_transform); EXPECT_EQ(raster_cache()->LayerCacheCount(), 0); - EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer1.get(), other_transform)); - EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer1.get(), cache_ctm)); - EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer2.get(), other_transform)); - EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer2.get(), cache_ctm)); + 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)); layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(raster_cache()->LayerCacheCount(), 1); - EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer1.get(), other_transform)); - EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer1.get(), cache_ctm)); - EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer2.get(), other_transform)); - EXPECT_FALSE(raster_cache()->WasPrepared(mock_layer2.get(), cache_ctm)); + 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)); } TEST_F(OpacityLayerTest, FullyOpaque) { diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index 7b4baff0890ca..758d711dba7d3 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -122,10 +122,10 @@ static RasterCacheResult Rasterize( } static RasterCacheResult RasterizePicture(SkPicture* picture, - GrContext* context, - const SkMatrix& ctm, - SkColorSpace* dst_color_space, - bool checkerboard) { + GrContext* context, + const SkMatrix& ctm, + SkColorSpace* dst_color_space, + bool checkerboard) { return Rasterize(context, ctm, dst_color_space, checkerboard, picture->cullRect(), [=](SkCanvas* canvas) { canvas->drawPicture(picture); }); diff --git a/flow/raster_cache.h b/flow/raster_cache.h index 236a08a07f14f..940da5dc6a0e5 100644 --- a/flow/raster_cache.h +++ b/flow/raster_cache.h @@ -92,18 +92,20 @@ class RasterCache { // 4. There are too many pictures to be cached in the current frame. // (See also kDefaultPictureCacheLimitPerFrame.) virtual bool Prepare(GrContext* context, - SkPicture* picture, - const SkMatrix& transformation_matrix, - SkColorSpace* dst_color_space, - bool is_complex, - bool will_change); + SkPicture* picture, + const SkMatrix& transformation_matrix, + SkColorSpace* dst_color_space, + bool is_complex, + bool will_change); - virtual void Prepare(PrerollContext* context, Layer* layer, const SkMatrix& ctm); + virtual void Prepare(PrerollContext* context, + Layer* layer, + const SkMatrix& ctm); // Find the raster cache for the picture and draw it to the canvas. // // Return true if it's found and drawn. - bool Draw(const SkPicture& picture, SkCanvas& canvas) const; + virtual bool Draw(const SkPicture& picture, SkCanvas& canvas) const; // Find the raster cache for the layer and draw it to the canvas. // @@ -111,9 +113,9 @@ class RasterCache { // 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, - SkPaint* paint = nullptr) const; + virtual bool Draw(const Layer* layer, + SkCanvas& canvas, + SkPaint* paint = nullptr) const; void SweepAfterFrame(); diff --git a/testing/mock_raster_cache.cc b/testing/mock_raster_cache.cc index d485e292d73bb..1503247b175d2 100644 --- a/testing/mock_raster_cache.cc +++ b/testing/mock_raster_cache.cc @@ -2,30 +2,13 @@ // 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.h" #include "flutter/testing/mock_raster_cache.h" +#include "flutter/flow/layers/layer.h" namespace flutter { namespace testing { -// RasterCacheResult::RasterCacheResult(sk_sp image, -// const SkRect& logical_rect) -// : image_(std::move(image)), logical_rect_(logical_rect) {} - -// void RasterCacheResult::draw(SkCanvas& canvas, const SkPaint* paint) const { -// TRACE_EVENT0("flutter", "RasterCacheResult::draw"); -// SkAutoCanvasRestore auto_restore(&canvas, true); -// SkIRect bounds = -// RasterCache::GetDeviceBounds(logical_rect_, canvas.getTotalMatrix()); -// FML_DCHECK( -// std::abs(bounds.size().width() - image_->dimensions().width()) <= 1 && -// std::abs(bounds.size().height() - image_->dimensions().height()) <= 1); -// canvas.resetMatrix(); -// canvas.drawImage(image_, bounds.fLeft, bounds.fTop, paint); -// } - -MockRasterCache::MockRasterCache() - : RasterCache::RasterCache(1, 1000000) { } +MockRasterCache::MockRasterCache() : RasterCache::RasterCache(1, 1000000) {} static bool CanRasterizePicture(SkPicture* picture) { if (picture == nullptr) { @@ -73,55 +56,22 @@ static bool IsPictureWorthRasterizing(SkPicture* picture, return picture->approximateOpCount() > 5; } -/// @note Procedure doesn't copy all closures. -static RasterCacheResult Rasterize( - const SkMatrix& ctm, - SkColorSpace* dst_color_space, - const SkRect& logical_rect) { - TRACE_EVENT0("flutter", "RasterCachePopulate"); - SkIRect cache_rect = RasterCache::GetDeviceBounds(logical_rect, ctm); - - const SkImageInfo image_info = SkImageInfo::MakeN32Premul( - cache_rect.width(), cache_rect.height(), sk_ref_sp(dst_color_space)); - - sk_sp data = SkData::MakeUninitialized(image_info.computeMinByteSize()); - sk_sp image = SkImage::MakeRasterData(image_info, data, image_info.minRowBytes()); - - return {image, logical_rect}; -} - -static RasterCacheResult RasterizePicture(SkPicture* picture, - const SkMatrix& ctm, - SkColorSpace* dst_color_space) { - return Rasterize(ctm, dst_color_space, picture->cullRect()); -} - void MockRasterCache::Prepare(PrerollContext* context, Layer* layer, const SkMatrix& ctm) { LayerRasterCacheKey cache_key(layer->unique_id(), ctm); - Entry& entry = layer_cache_[cache_key]; + MockEntry& entry = layer_cache_[cache_key]; entry.access_count++; entry.used_this_frame = true; - if (!entry.image.is_valid()) { - entry.image = Rasterize( - ctm, context->dst_color_space, - layer->paint_bounds()); - } -} - -bool MockRasterCache::WasPrepared(Layer* layer, const SkMatrix& ctm) { - LayerRasterCacheKey cache_key(layer->unique_id(), ctm); - // return layer_cache_.contains(cache_key); - Requires STD_VER > 17, C++20 - return layer_cache_.find(cache_key) != layer_cache_.end(); + entry.rasterized = true; } bool MockRasterCache::Prepare(GrContext* context, - SkPicture* picture, - const SkMatrix& transformation_matrix, - SkColorSpace* dst_color_space, - bool is_complex, - bool will_change) { + SkPicture* picture, + const SkMatrix& transformation_matrix, + SkColorSpace* dst_color_space, + bool is_complex, + bool will_change) { if (!IsPictureWorthRasterizing(picture, will_change, is_complex)) { // We only deal with pictures that are worthy of rasterization. return false; @@ -139,42 +89,40 @@ bool MockRasterCache::Prepare(GrContext* context, PictureRasterCacheKey cache_key(picture->uniqueID(), transformation_matrix); // Creates an entry, if not present prior. - Entry& entry = picture_cache_[cache_key]; + MockEntry& entry = picture_cache_[cache_key]; + entry.rasterized = true; - if (!entry.image.is_valid()) { - entry.image = RasterizePicture(picture, transformation_matrix, - dst_color_space); - } return true; } -RasterCacheResult MockRasterCache::Get(const SkPicture& picture, - const SkMatrix& ctm) const { - PictureRasterCacheKey cache_key(picture.uniqueID(), ctm); +bool MockRasterCache::Draw(const SkPicture& picture, SkCanvas& canvas) const { + PictureRasterCacheKey cache_key(picture.uniqueID(), canvas.getTotalMatrix()); auto it = picture_cache_.find(cache_key); if (it == picture_cache_.end()) { - return RasterCacheResult(); + return false; } - Entry& entry = it->second; + MockEntry& entry = it->second; entry.access_count++; entry.used_this_frame = true; - return entry.image; + return entry.rasterized; } -RasterCacheResult MockRasterCache::Get(Layer* layer, const SkMatrix& ctm) const { - LayerRasterCacheKey cache_key(layer->unique_id(), ctm); +bool MockRasterCache::Draw(const Layer* layer, + SkCanvas& canvas, + SkPaint* paint) const { + LayerRasterCacheKey cache_key(layer->unique_id(), canvas.getTotalMatrix()); auto it = layer_cache_.find(cache_key); if (it == layer_cache_.end()) { - return RasterCacheResult(); + return false; } - Entry& entry = it->second; + MockEntry& entry = it->second; entry.access_count++; entry.used_this_frame = true; - return entry.image; + return entry.rasterized; } void MockRasterCache::SweepAfterFrame() { @@ -191,8 +139,7 @@ size_t MockRasterCache::GetCachedEntriesCount() const { return layer_cache_.size() + picture_cache_.size(); } -void MockRasterCache::SetCheckboardCacheImages(bool checkerboard) { -} +void MockRasterCache::SetCheckboardCacheImages(bool checkerboard) {} } // namespace testing } // namespace flutter diff --git a/testing/mock_raster_cache.h b/testing/mock_raster_cache.h index 89ac96b7c5d7d..863fc1c90bb22 100644 --- a/testing/mock_raster_cache.h +++ b/testing/mock_raster_cache.h @@ -6,8 +6,8 @@ #define TESTING_MOCK_RASTER_CACHE_H_ #include "flutter/flow/raster_cache.h" -#include "third_party/skia/include/core/SkPicture.h" #include "third_party/skia/include/core/SkImage.h" +#include "third_party/skia/include/core/SkPicture.h" namespace flutter { namespace testing { @@ -35,13 +35,15 @@ class MockRasterCache : public RasterCache { bool is_complex, bool will_change) override; - void Prepare(PrerollContext* context, Layer* layer, const SkMatrix& ctm) override; + void Prepare(PrerollContext* context, + Layer* layer, + const SkMatrix& ctm) override; - RasterCacheResult Get(const SkPicture& picture, const SkMatrix& ctm) const override; + bool Draw(const SkPicture& picture, SkCanvas& canvas) const override; - RasterCacheResult Get(Layer* layer, const SkMatrix& ctm) const override; - - bool WasPrepared(Layer* layer, const SkMatrix& ctm); + bool Draw(const Layer* layer, + SkCanvas& canvas, + SkPaint* paint = nullptr) const override; void SweepAfterFrame(); @@ -56,13 +58,11 @@ class MockRasterCache : public RasterCache { MockRasterCache(); - protected: - private: - struct Entry { + struct MockEntry { bool used_this_frame = false; size_t access_count = 0; - RasterCacheResult image; + bool rasterized; }; template @@ -70,7 +70,7 @@ class MockRasterCache : public RasterCache { std::vector dead; for (auto it = cache.begin(); it != cache.end(); ++it) { - Entry& entry = it->second; + MockEntry& entry = it->second; if (!entry.used_this_frame) { dead.push_back(it); } @@ -82,8 +82,8 @@ class MockRasterCache : public RasterCache { } } - mutable PictureRasterCacheKey::Map picture_cache_; - mutable LayerRasterCacheKey::Map layer_cache_; + mutable PictureRasterCacheKey::Map picture_cache_; + mutable LayerRasterCacheKey::Map layer_cache_; }; } // namespace testing From 2fee75061d186d896a8c1b651f75539a73eb05e7 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Mon, 4 May 2020 15:06:45 -0700 Subject: [PATCH 3/7] first pass at basing MockRasterCache more on RasterCache --- flow/raster_cache.cc | 54 ++++++++++++++++++++++++++++---------------- flow/raster_cache.h | 38 +++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 21 deletions(-) diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index 758d711dba7d3..c9b2e2ff9dfe5 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -35,7 +35,16 @@ void RasterCacheResult::draw(SkCanvas& canvas, const SkPaint* paint) const { RasterCache::RasterCache(size_t access_threshold, size_t picture_cache_limit_per_frame) - : access_threshold_(access_threshold), + : rasterizer_(&SkRasterCacheRasterizer::instance), + access_threshold_(access_threshold), + picture_cache_limit_per_frame_(picture_cache_limit_per_frame), + checkerboard_images_(false) {} + +RasterCache::RasterCache(RasterCacheRasterizer* rasterizer, + size_t access_threshold, + size_t picture_cache_limit_per_frame) + : rasterizer_(rasterizer), + access_threshold_(access_threshold), picture_cache_limit_per_frame_(picture_cache_limit_per_frame), checkerboard_images_(false) {} @@ -121,27 +130,21 @@ static RasterCacheResult Rasterize( return {surface->makeImageSnapshot(), logical_rect}; } -static RasterCacheResult RasterizePicture(SkPicture* picture, - GrContext* context, - const SkMatrix& ctm, - SkColorSpace* dst_color_space, - bool checkerboard) { +SkRasterCacheRasterizer SkRasterCacheRasterizer::instance; + +const RasterCacheResult SkRasterCacheRasterizer::RasterizePicture( + SkPicture* picture, GrContext* context, const SkMatrix& ctm, + SkColorSpace* dst_color_space, bool checkerboard) const { return Rasterize(context, ctm, dst_color_space, checkerboard, picture->cullRect(), [=](SkCanvas* canvas) { canvas->drawPicture(picture); }); } -void RasterCache::Prepare(PrerollContext* context, - Layer* layer, - const SkMatrix& ctm) { - LayerRasterCacheKey cache_key(layer->unique_id(), ctm); - Entry& entry = layer_cache_[cache_key]; - entry.access_count++; - entry.used_this_frame = true; - if (!entry.image.is_valid()) { - entry.image = Rasterize( - context->gr_context, ctm, context->dst_color_space, - checkerboard_images_, layer->paint_bounds(), +const RasterCacheResult SkRasterCacheRasterizer::RasterizeLayer( + PrerollContext* context, Layer* layer, const SkMatrix& ctm, + bool checkerboard) const { + return Rasterize(context->gr_context, ctm, context->dst_color_space, + checkerboard, layer->paint_bounds(), [layer, context](SkCanvas* canvas) { SkISize canvas_size = canvas->getBaseLayerSize(); SkNWayCanvas internal_nodes_canvas(canvas_size.width(), @@ -163,6 +166,18 @@ void RasterCache::Prepare(PrerollContext* context, layer->Paint(paintContext); } }); +} + +void RasterCache::Prepare(PrerollContext* context, + Layer* layer, + const SkMatrix& ctm) { + LayerRasterCacheKey cache_key(layer->unique_id(), ctm); + Entry& entry = layer_cache_[cache_key]; + entry.access_count++; + entry.used_this_frame = true; + if (!entry.image.is_valid()) { + entry.image = rasterizer_->RasterizeLayer(context, layer, ctm, + checkerboard_images_); } } @@ -203,8 +218,9 @@ bool RasterCache::Prepare(GrContext* context, } if (!entry.image.is_valid()) { - entry.image = RasterizePicture(picture, context, transformation_matrix, - dst_color_space, checkerboard_images_); + entry.image = rasterizer_->RasterizePicture( + picture, context, transformation_matrix, dst_color_space, + checkerboard_images_); picture_cached_this_frame_++; } return true; diff --git a/flow/raster_cache.h b/flow/raster_cache.h index 940da5dc6a0e5..c2a7b505d5776 100644 --- a/flow/raster_cache.h +++ b/flow/raster_cache.h @@ -21,7 +21,7 @@ class RasterCacheResult { public: RasterCacheResult() = default; - RasterCacheResult(const RasterCacheResult& other) = default; + // SkRasterCacheResult(const RasterCacheResult& other) = default; RasterCacheResult(sk_sp image, const SkRect& logical_rect); @@ -42,6 +42,34 @@ class RasterCacheResult { struct PrerollContext; +class RasterCacheRasterizer { + public: + const virtual RasterCacheResult RasterizePicture( + SkPicture* picture, GrContext* context, const SkMatrix& ctm, + SkColorSpace* dst_color_space, bool checkerboard) const = 0; + + const virtual RasterCacheResult RasterizeLayer(PrerollContext* context, + Layer* layer, + const SkMatrix& ctm, + bool checkerboard) const = 0; +}; + +class SkRasterCacheRasterizer : public RasterCacheRasterizer { + public: + const RasterCacheResult RasterizePicture(SkPicture* picture, + GrContext* context, + const SkMatrix& ctm, + SkColorSpace* dst_color_space, + bool checkerboard) const override; + + const RasterCacheResult RasterizeLayer(PrerollContext* context, + Layer* layer, + const SkMatrix& ctm, + bool checkerboard) const override; + + static SkRasterCacheRasterizer instance; +}; + class RasterCache { public: // The default max number of picture raster caches to be generated per frame. @@ -54,6 +82,11 @@ class RasterCache { size_t access_threshold = 3, size_t picture_cache_limit_per_frame = kDefaultPictureCacheLimitPerFrame); + explicit RasterCache( + RasterCacheRasterizer* rasterizer, + size_t access_threshold = 3, + size_t picture_cache_limit_per_frame = kDefaultPictureCacheLimitPerFrame); + static SkIRect GetDeviceBounds(const SkRect& rect, const SkMatrix& ctm) { SkRect device_rect; ctm.mapRect(&device_rect, rect); @@ -129,7 +162,7 @@ class RasterCache { struct Entry { bool used_this_frame = false; size_t access_count = 0; - RasterCacheResult image; + RasterCacheResult* image; }; template @@ -149,6 +182,7 @@ class RasterCache { } } + const RasterCacheRasterizer* rasterizer_; const size_t access_threshold_; const size_t picture_cache_limit_per_frame_; size_t picture_cached_this_frame_ = 0; From 2e53aec45203f30016f961865c0dba2d0fdeb0dd Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Wed, 13 May 2020 16:56:06 -0700 Subject: [PATCH 4/7] update for new integer translation code and simplify mock raster cache --- flow/layers/opacity_layer.h | 20 ++++ flow/layers/opacity_layer_unittests.cc | 18 ++- flow/raster_cache.cc | 124 ++++++++++++--------- flow/raster_cache.h | 132 ++++++++++++++-------- flow/testing/layer_test.h | 46 +++++++- testing/mock_raster_cache.cc | 145 ++++--------------------- testing/mock_raster_cache.h | 96 +++++----------- 7 files changed, 275 insertions(+), 306 deletions(-) diff --git a/flow/layers/opacity_layer.h b/flow/layers/opacity_layer.h index c85f8e05280b0..49596bc16e6e9 100644 --- a/flow/layers/opacity_layer.h +++ b/flow/layers/opacity_layer.h @@ -38,7 +38,27 @@ class OpacityLayer : public ContainerLayer { #endif // defined(OS_FUCHSIA) private: + // Returns the |ContainerLayer| used to hold all of the children of the + // |OpacityLayer|. Typically these layers should only have a single child + // according to the contract of the associated Flutter widget, but the + // engine API cannot enforce that contract and must be prepared to support + // the possibility of adding more than one child. In either case, the + // child container will hold all children. Note that since the child + // container is created fresh with each new |OpacityLayer|, it may not + // be the best choice to cache to speed up rendering during an animation. + // + // @see |GetCacheableChild()| ContainerLayer* GetChildContainer() const; + + // Returns the best choice for a |Layer| object that can represent all + // children and remain stable if the |OpacityLayer| is reconstructed in + // new scenes. The |OpacityLayer| needs to be recreated on frames where + // its opacity values change, but it often contains the same child on + // each such frame. Since the child container is created fresh when + // each |OpacityLayer| is constructed, that |Layer| will not be stable + // even if the same child is used. This method will determine if there + // is a stable child and return that |Layer| so that caching will be + // more successful. Layer* GetCacheableChild() const; SkAlpha alpha_; diff --git a/flow/layers/opacity_layer_unittests.cc b/flow/layers/opacity_layer_unittests.cc index 0064d79e86264..735bb0ae3cac3 100644 --- a/flow/layers/opacity_layer_unittests.cc +++ b/flow/layers/opacity_layer_unittests.cc @@ -64,21 +64,20 @@ TEST_F(OpacityLayerTest, ChildIsCached) { layer->Add(mock_layer); SkMatrix cache_ctm = initial_transform; -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - cache_ctm = RasterCache::GetIntegralTransCTM(cache_ctm); -#endif SkCanvas cache_canvas; cache_canvas.setMatrix(cache_ctm); SkCanvas other_canvas; other_canvas.setMatrix(other_transform); - EXPECT_EQ(raster_cache()->LayerCacheCount(), 0); + 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)); layer->Preroll(preroll_context(), initial_transform); - EXPECT_EQ(raster_cache()->LayerCacheCount(), 1); + EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas)); EXPECT_TRUE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); } @@ -97,15 +96,14 @@ TEST_F(OpacityLayerTest, ChildrenNotCached) { layer->Add(mock_layer2); SkMatrix cache_ctm = initial_transform; -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - cache_ctm = RasterCache::GetIntegralTransCTM(cache_ctm); -#endif SkCanvas cache_canvas; cache_canvas.setMatrix(cache_ctm); SkCanvas other_canvas; other_canvas.setMatrix(other_transform); - EXPECT_EQ(raster_cache()->LayerCacheCount(), 0); + 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)); @@ -113,7 +111,7 @@ TEST_F(OpacityLayerTest, ChildrenNotCached) { layer->Preroll(preroll_context(), initial_transform); - EXPECT_EQ(raster_cache()->LayerCacheCount(), 1); + 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)); diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index c9b2e2ff9dfe5..65b3561002231 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -17,11 +17,11 @@ namespace flutter { -RasterCacheResult::RasterCacheResult(sk_sp image, - const SkRect& logical_rect) +SkRasterCacheResult::SkRasterCacheResult(sk_sp image, + const SkRect& logical_rect) : image_(std::move(image)), logical_rect_(logical_rect) {} -void RasterCacheResult::draw(SkCanvas& canvas, const SkPaint* paint) const { +void SkRasterCacheResult::draw(SkCanvas& canvas, const SkPaint* paint) const { TRACE_EVENT0("flutter", "RasterCacheResult::draw"); SkAutoCanvasRestore auto_restore(&canvas, true); SkIRect bounds = @@ -35,15 +35,15 @@ void RasterCacheResult::draw(SkCanvas& canvas, const SkPaint* paint) const { RasterCache::RasterCache(size_t access_threshold, size_t picture_cache_limit_per_frame) - : rasterizer_(&SkRasterCacheRasterizer::instance), + : delegate_(&SkRasterCacheImageDelegate::instance), access_threshold_(access_threshold), picture_cache_limit_per_frame_(picture_cache_limit_per_frame), checkerboard_images_(false) {} -RasterCache::RasterCache(RasterCacheRasterizer* rasterizer, +RasterCache::RasterCache(RasterCacheImageDelegate* delegate, size_t access_threshold, size_t picture_cache_limit_per_frame) - : rasterizer_(rasterizer), + : delegate_(delegate), access_threshold_(access_threshold), picture_cache_limit_per_frame_(picture_cache_limit_per_frame), checkerboard_images_(false) {} @@ -95,7 +95,7 @@ static bool IsPictureWorthRasterizing(SkPicture* picture, } /// @note Procedure doesn't copy all closures. -static RasterCacheResult Rasterize( +static std::unique_ptr Rasterize( GrContext* context, const SkMatrix& ctm, SkColorSpace* dst_color_space, @@ -114,7 +114,7 @@ static RasterCacheResult Rasterize( : SkSurface::MakeRaster(image_info); if (!surface) { - return {}; + return nullptr; } SkCanvas* canvas = surface->getCanvas(); @@ -127,45 +127,51 @@ static RasterCacheResult Rasterize( DrawCheckerboard(canvas, logical_rect); } - return {surface->makeImageSnapshot(), logical_rect}; + return std::make_unique(surface->makeImageSnapshot(), + logical_rect); } -SkRasterCacheRasterizer SkRasterCacheRasterizer::instance; +SkRasterCacheImageDelegate SkRasterCacheImageDelegate::instance; -const RasterCacheResult SkRasterCacheRasterizer::RasterizePicture( - SkPicture* picture, GrContext* context, const SkMatrix& ctm, - SkColorSpace* dst_color_space, bool checkerboard) const { +std::unique_ptr SkRasterCacheImageDelegate::RasterizePicture( + SkPicture* picture, + GrContext* context, + const SkMatrix& ctm, + SkColorSpace* dst_color_space, + bool checkerboard) const { return Rasterize(context, ctm, dst_color_space, checkerboard, picture->cullRect(), [=](SkCanvas* canvas) { canvas->drawPicture(picture); }); } -const RasterCacheResult SkRasterCacheRasterizer::RasterizeLayer( - PrerollContext* context, Layer* layer, const SkMatrix& ctm, +std::unique_ptr SkRasterCacheImageDelegate::RasterizeLayer( + PrerollContext* context, + Layer* layer, + const SkMatrix& ctm, bool checkerboard) const { - return Rasterize(context->gr_context, ctm, context->dst_color_space, - checkerboard, layer->paint_bounds(), - [layer, context](SkCanvas* canvas) { - SkISize canvas_size = canvas->getBaseLayerSize(); - SkNWayCanvas internal_nodes_canvas(canvas_size.width(), - canvas_size.height()); - internal_nodes_canvas.addCanvas(canvas); - Layer::PaintContext paintContext = { - (SkCanvas*)&internal_nodes_canvas, - canvas, - context->gr_context, - nullptr, - context->raster_time, - context->ui_time, - context->texture_registry, - context->has_platform_view ? nullptr : context->raster_cache, - context->checkerboard_offscreen_layers, - context->frame_physical_depth, - context->frame_device_pixel_ratio}; - if (layer->needs_painting()) { - layer->Paint(paintContext); - } - }); + return Rasterize( + context->gr_context, ctm, context->dst_color_space, checkerboard, + layer->paint_bounds(), [layer, context](SkCanvas* canvas) { + SkISize canvas_size = canvas->getBaseLayerSize(); + SkNWayCanvas internal_nodes_canvas(canvas_size.width(), + canvas_size.height()); + internal_nodes_canvas.addCanvas(canvas); + Layer::PaintContext paintContext = { + (SkCanvas*)&internal_nodes_canvas, + canvas, + context->gr_context, + nullptr, + context->raster_time, + context->ui_time, + context->texture_registry, + context->has_platform_view ? nullptr : context->raster_cache, + context->checkerboard_offscreen_layers, + context->frame_physical_depth, + context->frame_device_pixel_ratio}; + if (layer->needs_painting()) { + layer->Paint(paintContext); + } + }); } void RasterCache::Prepare(PrerollContext* context, @@ -175,9 +181,9 @@ void RasterCache::Prepare(PrerollContext* context, Entry& entry = layer_cache_[cache_key]; entry.access_count++; entry.used_this_frame = true; - if (!entry.image.is_valid()) { - entry.image = rasterizer_->RasterizeLayer(context, layer, ctm, - checkerboard_images_); + if (!entry.image) { + entry.image = + delegate_->RasterizeLayer(context, layer, ctm, checkerboard_images_); } } @@ -217,10 +223,10 @@ bool RasterCache::Prepare(GrContext* context, return false; } - if (!entry.image.is_valid()) { - entry.image = rasterizer_->RasterizePicture( - picture, context, transformation_matrix, dst_color_space, - checkerboard_images_); + if (!entry.image) { + entry.image = + delegate_->RasterizePicture(picture, context, transformation_matrix, + dst_color_space, checkerboard_images_); picture_cached_this_frame_++; } return true; @@ -237,8 +243,8 @@ bool RasterCache::Draw(const SkPicture& picture, SkCanvas& canvas) const { entry.access_count++; entry.used_this_frame = true; - if (entry.image.is_valid()) { - entry.image.draw(canvas); + if (entry.image) { + entry.image->draw(canvas); return true; } @@ -258,8 +264,8 @@ bool RasterCache::Draw(const Layer* layer, entry.access_count++; entry.used_this_frame = true; - if (entry.image.is_valid()) { - entry.image.draw(canvas, paint); + if (entry.image) { + entry.image->draw(canvas, paint); return true; } @@ -282,6 +288,14 @@ size_t RasterCache::GetCachedEntriesCount() const { return layer_cache_.size() + picture_cache_.size(); } +size_t RasterCache::GetLayerCachedEntriesCount() const { + return layer_cache_.size(); +} + +size_t RasterCache::GetPictureCachedEntriesCount() const { + return picture_cache_.size(); +} + void RasterCache::SetCheckboardCacheImages(bool checkerboard) { if (checkerboard_images_ == checkerboard) { return; @@ -303,15 +317,19 @@ void RasterCache::TraceStatsToTimeline() const { size_t picture_cache_bytes = 0; for (const auto& item : layer_cache_) { - const auto dimensions = item.second.image.image_dimensions(); layer_cache_count++; - layer_cache_bytes += dimensions.width() * dimensions.height() * 4; + if (item.second.image) { + const auto dimensions = item.second.image->image_dimensions(); + layer_cache_bytes += dimensions.width() * dimensions.height() * 4; + } } for (const auto& item : picture_cache_) { - const auto dimensions = item.second.image.image_dimensions(); picture_cache_count++; - picture_cache_bytes += dimensions.width() * dimensions.height() * 4; + if (item.second.image) { + const auto dimensions = item.second.image->image_dimensions(); + picture_cache_bytes += dimensions.width() * dimensions.height() * 4; + } } FML_TRACE_COUNTER("flutter", "RasterCache", diff --git a/flow/raster_cache.h b/flow/raster_cache.h index c2a7b505d5776..b406b2a0fa7e1 100644 --- a/flow/raster_cache.h +++ b/flow/raster_cache.h @@ -17,21 +17,32 @@ namespace flutter { +// |RasterCacheResult| contains the result of rendering a layer or a picture +// at a given transform by an instance of |RasterCacheImageDelegate| and +// provides the capability to render the result to a given |SkCanvas|. class RasterCacheResult { public: - RasterCacheResult() = default; + virtual ~RasterCacheResult() = default; - // SkRasterCacheResult(const RasterCacheResult& other) = default; + // Draw the rendered result to the |SkCanvas| using the properties of the + // |SkPaint|. + virtual void draw(SkCanvas& canvas, const SkPaint* paint = nullptr) const = 0; - RasterCacheResult(sk_sp image, const SkRect& logical_rect); - - operator bool() const { return static_cast(image_); } + // Return the size of the cached result to help manage the cache memory + // consumption. + virtual SkISize image_dimensions() const = 0; +}; - bool is_valid() const { return static_cast(image_); }; +// |SkRasterCacheResult| is the typical implementation of |SkRasterCacheResult| +// created from an |SkRasterCacheImageDelegate| and storing the result as +// pixels in an |SkImage|. +class SkRasterCacheResult : public RasterCacheResult { + public: + SkRasterCacheResult(sk_sp image, const SkRect& logical_rect); - void draw(SkCanvas& canvas, const SkPaint* paint = nullptr) const; + void draw(SkCanvas& canvas, const SkPaint* paint = nullptr) const override; - SkISize image_dimensions() const { + SkISize image_dimensions() const override { return image_ ? image_->dimensions() : SkISize::Make(0, 0); }; @@ -42,32 +53,42 @@ class RasterCacheResult { struct PrerollContext; -class RasterCacheRasterizer { +// The |RasterCacheImageDelegate| creates and retruns a cached result of +// rendering the supplied |Layer| or |SkPicture|. +class RasterCacheImageDelegate { public: - const virtual RasterCacheResult RasterizePicture( - SkPicture* picture, GrContext* context, const SkMatrix& ctm, - SkColorSpace* dst_color_space, bool checkerboard) const = 0; - - const virtual RasterCacheResult RasterizeLayer(PrerollContext* context, - Layer* layer, - const SkMatrix& ctm, - bool checkerboard) const = 0; + virtual std::unique_ptr RasterizePicture( + SkPicture* picture, + GrContext* context, + const SkMatrix& ctm, + SkColorSpace* dst_color_space, + bool checkerboard) const = 0; + + virtual std::unique_ptr RasterizeLayer( + PrerollContext* context, + Layer* layer, + const SkMatrix& ctm, + bool checkerboard) const = 0; }; -class SkRasterCacheRasterizer : public RasterCacheRasterizer { +// The |SkRasterCacheImageDelegate| creates and returns rendered results +// for |Layer| and |SkPicture| objects stored as |SkImage| objects. +class SkRasterCacheImageDelegate : public RasterCacheImageDelegate { public: - const RasterCacheResult RasterizePicture(SkPicture* picture, - GrContext* context, - const SkMatrix& ctm, - SkColorSpace* dst_color_space, - bool checkerboard) const override; - - const RasterCacheResult RasterizeLayer(PrerollContext* context, - Layer* layer, - const SkMatrix& ctm, - bool checkerboard) const override; - - static SkRasterCacheRasterizer instance; + std::unique_ptr RasterizePicture( + SkPicture* picture, + GrContext* context, + const SkMatrix& ctm, + SkColorSpace* dst_color_space, + bool checkerboard) const override; + + std::unique_ptr RasterizeLayer( + PrerollContext* context, + Layer* layer, + const SkMatrix& ctm, + bool checkerboard) const override; + + static SkRasterCacheImageDelegate instance; }; class RasterCache { @@ -78,12 +99,29 @@ class RasterCache { // multiple frames. static constexpr int kDefaultPictureCacheLimitPerFrame = 3; + // Create a default |RasterCache| object that caches |Layer| and |SkPicture| + // objects using a |SkRasterCacheImageDelegate|. + // + // @param access_threshold + // the number of attempts to cache a given |SkPicture| before inserting + // it into the cache. The defalt value of 3 will only cache a picture + // on the 3rd time it appears in consecutive frames. + // @param picture_cache_limit_per_frame + // the maximum number of |SkPicture| objects to render into the cache + // per frame. The default value of 3 will rasterize at most 3 pictures + // in a given frame and leave the rest for caching in subsequent frames. + // + // @see |kDefaultPictureCacheLimitPerFrame| explicit RasterCache( size_t access_threshold = 3, size_t picture_cache_limit_per_frame = kDefaultPictureCacheLimitPerFrame); + // Create a |RasterCache| object that uses a specified implementation of + // |RasterCacheImageDelegate|. + // + // @see |RasterCache(size_t, size_t)| explicit RasterCache( - RasterCacheRasterizer* rasterizer, + RasterCacheImageDelegate* delegate, size_t access_threshold = 3, size_t picture_cache_limit_per_frame = kDefaultPictureCacheLimitPerFrame); @@ -124,21 +162,19 @@ class RasterCache { // 3. The picture is accessed too few times // 4. There are too many pictures to be cached in the current frame. // (See also kDefaultPictureCacheLimitPerFrame.) - virtual bool Prepare(GrContext* context, - SkPicture* picture, - const SkMatrix& transformation_matrix, - SkColorSpace* dst_color_space, - bool is_complex, - bool will_change); + bool Prepare(GrContext* context, + SkPicture* picture, + const SkMatrix& transformation_matrix, + SkColorSpace* dst_color_space, + bool is_complex, + bool will_change); - virtual void Prepare(PrerollContext* context, - Layer* layer, - const SkMatrix& ctm); + void Prepare(PrerollContext* context, Layer* layer, const SkMatrix& ctm); // Find the raster cache for the picture and draw it to the canvas. // // Return true if it's found and drawn. - virtual bool Draw(const SkPicture& picture, SkCanvas& canvas) const; + bool Draw(const SkPicture& picture, SkCanvas& canvas) const; // Find the raster cache for the layer and draw it to the canvas. // @@ -146,9 +182,9 @@ class RasterCache { // draw the raster cache with some opacity). // // Return true if the layer raster cache is found and drawn. - virtual bool Draw(const Layer* layer, - SkCanvas& canvas, - SkPaint* paint = nullptr) const; + bool Draw(const Layer* layer, + SkCanvas& canvas, + SkPaint* paint = nullptr) const; void SweepAfterFrame(); @@ -158,11 +194,15 @@ class RasterCache { size_t GetCachedEntriesCount() const; + size_t GetLayerCachedEntriesCount() const; + + size_t GetPictureCachedEntriesCount() const; + private: struct Entry { bool used_this_frame = false; size_t access_count = 0; - RasterCacheResult* image; + std::unique_ptr image; }; template @@ -182,7 +222,7 @@ class RasterCache { } } - const RasterCacheRasterizer* rasterizer_; + const RasterCacheImageDelegate* delegate_; const size_t access_threshold_; const size_t picture_cache_limit_per_frame_; size_t picture_cached_this_frame_ = 0; diff --git a/flow/testing/layer_test.h b/flow/testing/layer_test.h index 2b16790e1f8b0..f123c624dc0eb 100644 --- a/flow/testing/layer_test.h +++ b/flow/testing/layer_test.h @@ -25,6 +25,14 @@ namespace testing { // |Layer|'s. // |LayerTest| is a default implementation based on |::testing::Test|. // +// By default the preroll and paint contexts will not use a raster cache. +// If a test needs to verify the proper operation of a layer in the presence +// of a raster cache then a number of options can be enabled by using the +// following methods: +// @see |use_null_raster_cache()| +// @see |use_mock_raster_cache()| +// @see |use_skia_raster_cache()| +// // |BaseT| should be the base test type, such as |::testing::Test| below. template class LayerTestBase : public CanvasTestBase { @@ -52,24 +60,50 @@ class LayerTestBase : public CanvasTestBase { nullptr, /* gr_context */ nullptr, /* external_view_embedder */ raster_time_, ui_time_, texture_registry_, - nullptr, /* raster_cache */ - false, /* checkerboard_offscreen_layers */ - 100.0f, /* frame_physical_depth */ - 1.0f, /* frame_device_pixel_ratio */ + raster_cache(), /* raster_cache */ + false, /* checkerboard_offscreen_layers */ + 100.0f, /* frame_physical_depth */ + 1.0f, /* frame_device_pixel_ratio */ }) {} + // Use no raster cache in subsequent |Preroll| and |Paint| method calls. + // This is the default mode of operation. + void use_null_raster_cache() { set_raster_cache(nullptr); } + + // Use a mock raster cache in subsequent |Preroll| and |Paint| method calls. + // The mock raster cache behaves like a normal raster cache with respect to + // when layers and pictures are cached, but it does not incur the overhead + // of rendering the layers or caching the resulting pixels. + void use_mock_raster_cache() { + set_raster_cache( + std::make_unique(&MockRasterCacheImageDelegate::instance)); + } + + // Use a normal raster cache in subusequent |Preroll| and |Paint| method + // calls that uses Skia to render cached layers and pictures in the same + // way as happens when actually handling a frame on a device. + void use_skia_raster_cache() { + set_raster_cache(std::make_unique()); + } + TextureRegistry& texture_regitry() { return texture_registry_; } - MockRasterCache* raster_cache() { return &raster_cache_; } + RasterCache* raster_cache() { return raster_cache_.get(); } PrerollContext* preroll_context() { return &preroll_context_; } Layer::PaintContext& paint_context() { return paint_context_; } private: + void set_raster_cache(std::unique_ptr raster_cache) { + raster_cache_ = std::move(raster_cache); + preroll_context_.raster_cache = raster_cache_.get(); + paint_context_.raster_cache = raster_cache_.get(); + } + Stopwatch raster_time_; Stopwatch ui_time_; MutatorsStack mutators_stack_; TextureRegistry texture_registry_; - MockRasterCache raster_cache_; + std::unique_ptr raster_cache_; PrerollContext preroll_context_; Layer::PaintContext paint_context_; diff --git a/testing/mock_raster_cache.cc b/testing/mock_raster_cache.cc index 1503247b175d2..69d4e0af16522 100644 --- a/testing/mock_raster_cache.cc +++ b/testing/mock_raster_cache.cc @@ -8,138 +8,33 @@ namespace flutter { namespace testing { -MockRasterCache::MockRasterCache() : RasterCache::RasterCache(1, 1000000) {} +MockRasterCacheResult::MockRasterCacheResult(SkIRect device_rect) + : device_rect_(device_rect) {} -static bool CanRasterizePicture(SkPicture* picture) { - if (picture == nullptr) { - return false; - } +std::unique_ptr +MockRasterCacheImageDelegate::RasterizePicture(SkPicture* picture, + GrContext* context, + const SkMatrix& ctm, + SkColorSpace* dst_color_space, + bool checkerboard) const { + SkRect logical_rect = picture->cullRect(); + SkIRect cache_rect = RasterCache::GetDeviceBounds(logical_rect, ctm); - const SkRect cull_rect = picture->cullRect(); - - if (cull_rect.isEmpty()) { - // No point in ever rasterizing an empty picture. - return false; - } - - if (!cull_rect.isFinite()) { - // Cannot attempt to rasterize into an infinitely large surface. - return false; - } - - return true; -} - -static bool IsPictureWorthRasterizing(SkPicture* picture, - bool will_change, - bool is_complex) { - if (will_change) { - // If the picture is going to change in the future, there is no point in - // doing to extra work to rasterize. - return false; - } - - if (!CanRasterizePicture(picture)) { - // No point in deciding whether the picture is worth rasterizing if it - // cannot be rasterized at all. - return false; - } - - if (is_complex) { - // The caller seems to have extra information about the picture and thinks - // the picture is always worth rasterizing. - return true; - } - - // TODO(abarth): We should find a better heuristic here that lets us avoid - // wasting memory on trivial layers that are easy to re-rasterize every frame. - return picture->approximateOpCount() > 5; -} - -void MockRasterCache::Prepare(PrerollContext* context, - Layer* layer, - const SkMatrix& ctm) { - LayerRasterCacheKey cache_key(layer->unique_id(), ctm); - MockEntry& entry = layer_cache_[cache_key]; - entry.access_count++; - entry.used_this_frame = true; - entry.rasterized = true; -} - -bool MockRasterCache::Prepare(GrContext* context, - SkPicture* picture, - const SkMatrix& transformation_matrix, - SkColorSpace* dst_color_space, - bool is_complex, - bool will_change) { - if (!IsPictureWorthRasterizing(picture, will_change, is_complex)) { - // We only deal with pictures that are worthy of rasterization. - return false; - } - - // Decompose the matrix (once) for all subsequent operations. We want to make - // sure to avoid volumetric distortions while accounting for scaling. - const MatrixDecomposition matrix(transformation_matrix); - - if (!matrix.IsValid()) { - // The matrix was singular. No point in going further. - return false; - } - - PictureRasterCacheKey cache_key(picture->uniqueID(), transformation_matrix); - - // Creates an entry, if not present prior. - MockEntry& entry = picture_cache_[cache_key]; - entry.rasterized = true; - - return true; -} - -bool MockRasterCache::Draw(const SkPicture& picture, SkCanvas& canvas) const { - PictureRasterCacheKey cache_key(picture.uniqueID(), canvas.getTotalMatrix()); - auto it = picture_cache_.find(cache_key); - if (it == picture_cache_.end()) { - return false; - } - - MockEntry& entry = it->second; - entry.access_count++; - entry.used_this_frame = true; - - return entry.rasterized; + return std::make_unique(cache_rect); } -bool MockRasterCache::Draw(const Layer* layer, - SkCanvas& canvas, - SkPaint* paint) const { - LayerRasterCacheKey cache_key(layer->unique_id(), canvas.getTotalMatrix()); - auto it = layer_cache_.find(cache_key); - if (it == layer_cache_.end()) { - return false; - } - - MockEntry& entry = it->second; - entry.access_count++; - entry.used_this_frame = true; - - return entry.rasterized; -} - -void MockRasterCache::SweepAfterFrame() { - SweepOneCacheAfterFrame(picture_cache_); - SweepOneCacheAfterFrame(layer_cache_); -} - -void MockRasterCache::Clear() { - picture_cache_.clear(); - layer_cache_.clear(); -} +std::unique_ptr MockRasterCacheImageDelegate::RasterizeLayer( + PrerollContext* context, + Layer* layer, + const SkMatrix& ctm, + bool checkerboard) const { + SkRect logical_rect = layer->paint_bounds(); + SkIRect cache_rect = RasterCache::GetDeviceBounds(logical_rect, ctm); -size_t MockRasterCache::GetCachedEntriesCount() const { - return layer_cache_.size() + picture_cache_.size(); + return std::make_unique(cache_rect); } -void MockRasterCache::SetCheckboardCacheImages(bool checkerboard) {} +MockRasterCacheImageDelegate MockRasterCacheImageDelegate::instance; } // namespace testing } // namespace flutter diff --git a/testing/mock_raster_cache.h b/testing/mock_raster_cache.h index 863fc1c90bb22..b9ad5267389bd 100644 --- a/testing/mock_raster_cache.h +++ b/testing/mock_raster_cache.h @@ -12,78 +12,42 @@ namespace flutter { namespace testing { -// Mock |RasterCache|, useful for writing tests that impact the Flutter engine -// raster_cache without requiring a GPU. -// -// The |MockCanvas| stores a list of Engine Layers and Pictures that have been -// inserted into the cache that the test can later verify against the expected -// list of cached objects. -class MockRasterCache : public RasterCache { +// A |RasterCacheResult| implementation that represents a cached |Layer| or +// |SkPicture| without the overhead of storage. This implementation is used +// by |MockRasterCacheImageDelegate| only for testing proper usage of the +// |RasterCache| in layer unit tests. +class MockRasterCacheResult : public RasterCacheResult { public: - // Return true if the cache is generated. - // - // We may return false and not generate the cache if - // 1. The picture is not worth rasterizing - // 2. The matrix is singular - // 3. The picture is accessed too few times - // 4. There are too many pictures to be cached in the current frame. - // (See also kDefaultPictureCacheLimitPerFrame.) - bool Prepare(GrContext* context, - SkPicture* picture, - const SkMatrix& transformation_matrix, - SkColorSpace* dst_color_space, - bool is_complex, - bool will_change) override; + MockRasterCacheResult(SkIRect device_rect); - void Prepare(PrerollContext* context, - Layer* layer, - const SkMatrix& ctm) override; + void draw(SkCanvas& canvas, const SkPaint* paint = nullptr) const override{}; - bool Draw(const SkPicture& picture, SkCanvas& canvas) const override; - - bool Draw(const Layer* layer, - SkCanvas& canvas, - SkPaint* paint = nullptr) const override; - - void SweepAfterFrame(); - - void Clear(); - - int PictureCacheCount() { return picture_cache_.size(); } - int LayerCacheCount() { return layer_cache_.size(); } - - void SetCheckboardCacheImages(bool checkerboard); - - size_t GetCachedEntriesCount() const; - - MockRasterCache(); + SkISize image_dimensions() const override { return device_rect_.size(); }; private: - struct MockEntry { - bool used_this_frame = false; - size_t access_count = 0; - bool rasterized; - }; - - template - static void SweepOneCacheAfterFrame(Cache& cache) { - std::vector dead; - - for (auto it = cache.begin(); it != cache.end(); ++it) { - MockEntry& entry = it->second; - if (!entry.used_this_frame) { - dead.push_back(it); - } - entry.used_this_frame = false; - } - - for (auto it : dead) { - cache.erase(it); - } - } + SkIRect device_rect_; +}; - mutable PictureRasterCacheKey::Map picture_cache_; - mutable LayerRasterCacheKey::Map layer_cache_; +// A |RasterCacheImageDelegate| implementation that simulates the act of +// rendering a |Layer| or |SkPicture| without the overhead of rasterization +// or storage. This implementation is used only for testing proper usage of +// the |RasterCache| in layer unit tests. +class MockRasterCacheImageDelegate : public RasterCacheImageDelegate { + public: + std::unique_ptr RasterizePicture( + SkPicture* picture, + GrContext* context, + const SkMatrix& ctm, + SkColorSpace* dst_color_space, + bool checkerboard) const override; + + std::unique_ptr RasterizeLayer( + PrerollContext* context, + Layer* layer, + const SkMatrix& ctm, + bool checkerboard) const override; + + static MockRasterCacheImageDelegate instance; }; } // namespace testing From 3de8a148c82fe9e9cc1b77495f0132a21ff8d1f7 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Tue, 19 May 2020 15:52:03 -0700 Subject: [PATCH 5/7] simplify specialization of [Mock]RasterCache and doc formatting --- flow/layers/opacity_layer.h | 51 ++++++++----- flow/raster_cache.cc | 71 ++++++++----------- flow/raster_cache.h | 134 ++++++++++++++--------------------- flow/testing/layer_test.h | 86 ++++++++++++++-------- testing/mock_raster_cache.cc | 21 +++--- testing/mock_raster_cache.h | 30 +++++--- 6 files changed, 200 insertions(+), 193 deletions(-) diff --git a/flow/layers/opacity_layer.h b/flow/layers/opacity_layer.h index 49596bc16e6e9..304ccada37e7a 100644 --- a/flow/layers/opacity_layer.h +++ b/flow/layers/opacity_layer.h @@ -38,27 +38,40 @@ class OpacityLayer : public ContainerLayer { #endif // defined(OS_FUCHSIA) private: - // Returns the |ContainerLayer| used to hold all of the children of the - // |OpacityLayer|. Typically these layers should only have a single child - // according to the contract of the associated Flutter widget, but the - // engine API cannot enforce that contract and must be prepared to support - // the possibility of adding more than one child. In either case, the - // child container will hold all children. Note that since the child - // container is created fresh with each new |OpacityLayer|, it may not - // be the best choice to cache to speed up rendering during an animation. - // - // @see |GetCacheableChild()| + /** + * @brief Returns the ContainerLayer used to hold all of the children + * of the OpacityLayer. + * + * Typically these layers should only have a single child according to + * the contract of the associated Flutter widget, but the engine API + * cannot enforce that contract and must be prepared to support the + * possibility of adding more than one child. In either case, the + * child container will hold all children. Note that since the child + * container is created fresh with each new OpacityLayer, it may not + * be the best choice to cache to speed up rendering during an animation. + * + * @see GetCacheableChild() + * @return the ContainerLayer child used to hold the children + */ ContainerLayer* GetChildContainer() const; - // Returns the best choice for a |Layer| object that can represent all - // children and remain stable if the |OpacityLayer| is reconstructed in - // new scenes. The |OpacityLayer| needs to be recreated on frames where - // its opacity values change, but it often contains the same child on - // each such frame. Since the child container is created fresh when - // each |OpacityLayer| is constructed, that |Layer| will not be stable - // even if the same child is used. This method will determine if there - // is a stable child and return that |Layer| so that caching will be - // more successful. + /** + * @brief Returns the best choice for a Layer object that can be used + * in RasterCache operations to cache the children of the OpacityLayer. + * + * The returned Layer must represent all children and try to remain stable + * if the OpacityLayer is reconstructed in subsequent frames of the scene. + * The OpacityLayer needs to be recreated on frames where its opacity + * values change, but it often contains the same child on each such frame. + * Since the child container is created fresh each time the OpacityLayer + * is constructed, the return value from GetChildContainer will be different + * on each such frame even if the same child is used. This method will + * determine if there is a (single) stable child and return that Layer + * so that caching will be more successful. + * + * @see GetChildContainer() + * @return the best candidate Layer for caching the children + */ Layer* GetCacheableChild() const; SkAlpha alpha_; diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index 65b3561002231..89fc1ebd3c092 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -17,11 +17,11 @@ namespace flutter { -SkRasterCacheResult::SkRasterCacheResult(sk_sp image, - const SkRect& logical_rect) +RasterCacheResult::RasterCacheResult(sk_sp image, + const SkRect& logical_rect) : image_(std::move(image)), logical_rect_(logical_rect) {} -void SkRasterCacheResult::draw(SkCanvas& canvas, const SkPaint* paint) const { +void RasterCacheResult::draw(SkCanvas& canvas, const SkPaint* paint) const { TRACE_EVENT0("flutter", "RasterCacheResult::draw"); SkAutoCanvasRestore auto_restore(&canvas, true); SkIRect bounds = @@ -35,16 +35,7 @@ void SkRasterCacheResult::draw(SkCanvas& canvas, const SkPaint* paint) const { RasterCache::RasterCache(size_t access_threshold, size_t picture_cache_limit_per_frame) - : delegate_(&SkRasterCacheImageDelegate::instance), - access_threshold_(access_threshold), - picture_cache_limit_per_frame_(picture_cache_limit_per_frame), - checkerboard_images_(false) {} - -RasterCache::RasterCache(RasterCacheImageDelegate* delegate, - size_t access_threshold, - size_t picture_cache_limit_per_frame) - : delegate_(delegate), - access_threshold_(access_threshold), + : access_threshold_(access_threshold), picture_cache_limit_per_frame_(picture_cache_limit_per_frame), checkerboard_images_(false) {} @@ -127,13 +118,11 @@ static std::unique_ptr Rasterize( DrawCheckerboard(canvas, logical_rect); } - return std::make_unique(surface->makeImageSnapshot(), - logical_rect); + return std::make_unique(surface->makeImageSnapshot(), + logical_rect); } -SkRasterCacheImageDelegate SkRasterCacheImageDelegate::instance; - -std::unique_ptr SkRasterCacheImageDelegate::RasterizePicture( +std::unique_ptr RasterCache::RasterizePicture( SkPicture* picture, GrContext* context, const SkMatrix& ctm, @@ -144,7 +133,19 @@ std::unique_ptr SkRasterCacheImageDelegate::RasterizePicture( [=](SkCanvas* canvas) { canvas->drawPicture(picture); }); } -std::unique_ptr SkRasterCacheImageDelegate::RasterizeLayer( +void RasterCache::Prepare(PrerollContext* context, + Layer* layer, + const SkMatrix& ctm) { + LayerRasterCacheKey cache_key(layer->unique_id(), ctm); + Entry& entry = layer_cache_[cache_key]; + entry.access_count++; + entry.used_this_frame = true; + if (!entry.image) { + entry.image = RasterizeLayer(context, layer, ctm, checkerboard_images_); + } +} + +std::unique_ptr RasterCache::RasterizeLayer( PrerollContext* context, Layer* layer, const SkMatrix& ctm, @@ -157,10 +158,10 @@ std::unique_ptr SkRasterCacheImageDelegate::RasterizeLayer( canvas_size.height()); internal_nodes_canvas.addCanvas(canvas); Layer::PaintContext paintContext = { - (SkCanvas*)&internal_nodes_canvas, - canvas, - context->gr_context, - nullptr, + (SkCanvas*)&internal_nodes_canvas, // internal_nodes_canvas + canvas, // leaf_nodes_canvas + context->gr_context, // gr_context + nullptr, // view_embedder context->raster_time, context->ui_time, context->texture_registry, @@ -174,19 +175,6 @@ std::unique_ptr SkRasterCacheImageDelegate::RasterizeLayer( }); } -void RasterCache::Prepare(PrerollContext* context, - Layer* layer, - const SkMatrix& ctm) { - LayerRasterCacheKey cache_key(layer->unique_id(), ctm); - Entry& entry = layer_cache_[cache_key]; - entry.access_count++; - entry.used_this_frame = true; - if (!entry.image) { - entry.image = - delegate_->RasterizeLayer(context, layer, ctm, checkerboard_images_); - } -} - bool RasterCache::Prepare(GrContext* context, SkPicture* picture, const SkMatrix& transformation_matrix, @@ -224,9 +212,8 @@ bool RasterCache::Prepare(GrContext* context, } if (!entry.image) { - entry.image = - delegate_->RasterizePicture(picture, context, transformation_matrix, - dst_color_space, checkerboard_images_); + entry.image = RasterizePicture(picture, context, transformation_matrix, + dst_color_space, checkerboard_images_); picture_cached_this_frame_++; } return true; @@ -319,16 +306,14 @@ void RasterCache::TraceStatsToTimeline() const { for (const auto& item : layer_cache_) { layer_cache_count++; if (item.second.image) { - const auto dimensions = item.second.image->image_dimensions(); - layer_cache_bytes += dimensions.width() * dimensions.height() * 4; + layer_cache_bytes += item.second.image->image_bytes(); } } for (const auto& item : picture_cache_) { picture_cache_count++; if (item.second.image) { - const auto dimensions = item.second.image->image_dimensions(); - picture_cache_bytes += dimensions.width() * dimensions.height() * 4; + picture_cache_bytes += item.second.image->image_bytes(); } } diff --git a/flow/raster_cache.h b/flow/raster_cache.h index b406b2a0fa7e1..a2448462ac51c 100644 --- a/flow/raster_cache.h +++ b/flow/raster_cache.h @@ -17,35 +17,24 @@ namespace flutter { -// |RasterCacheResult| contains the result of rendering a layer or a picture -// at a given transform by an instance of |RasterCacheImageDelegate| and -// provides the capability to render the result to a given |SkCanvas|. class RasterCacheResult { public: - virtual ~RasterCacheResult() = default; - - // Draw the rendered result to the |SkCanvas| using the properties of the - // |SkPaint|. - virtual void draw(SkCanvas& canvas, const SkPaint* paint = nullptr) const = 0; - - // Return the size of the cached result to help manage the cache memory - // consumption. - virtual SkISize image_dimensions() const = 0; -}; + RasterCacheResult(sk_sp image, const SkRect& logical_rect); -// |SkRasterCacheResult| is the typical implementation of |SkRasterCacheResult| -// created from an |SkRasterCacheImageDelegate| and storing the result as -// pixels in an |SkImage|. -class SkRasterCacheResult : public RasterCacheResult { - public: - SkRasterCacheResult(sk_sp image, const SkRect& logical_rect); + virtual ~RasterCacheResult() = default; - void draw(SkCanvas& canvas, const SkPaint* paint = nullptr) const override; + virtual void draw(SkCanvas& canvas, const SkPaint* paint = nullptr) const; - SkISize image_dimensions() const override { + virtual SkISize image_dimensions() const { return image_ ? image_->dimensions() : SkISize::Make(0, 0); }; + virtual int64_t image_bytes() const { + return image_ ? image_->dimensions().area() * + image_->imageInfo().bytesPerPixel() + : 0; + }; + private: sk_sp image_; SkRect logical_rect_; @@ -53,44 +42,6 @@ class SkRasterCacheResult : public RasterCacheResult { struct PrerollContext; -// The |RasterCacheImageDelegate| creates and retruns a cached result of -// rendering the supplied |Layer| or |SkPicture|. -class RasterCacheImageDelegate { - public: - virtual std::unique_ptr RasterizePicture( - SkPicture* picture, - GrContext* context, - const SkMatrix& ctm, - SkColorSpace* dst_color_space, - bool checkerboard) const = 0; - - virtual std::unique_ptr RasterizeLayer( - PrerollContext* context, - Layer* layer, - const SkMatrix& ctm, - bool checkerboard) const = 0; -}; - -// The |SkRasterCacheImageDelegate| creates and returns rendered results -// for |Layer| and |SkPicture| objects stored as |SkImage| objects. -class SkRasterCacheImageDelegate : public RasterCacheImageDelegate { - public: - std::unique_ptr RasterizePicture( - SkPicture* picture, - GrContext* context, - const SkMatrix& ctm, - SkColorSpace* dst_color_space, - bool checkerboard) const override; - - std::unique_ptr RasterizeLayer( - PrerollContext* context, - Layer* layer, - const SkMatrix& ctm, - bool checkerboard) const override; - - static SkRasterCacheImageDelegate instance; -}; - class RasterCache { public: // The default max number of picture raster caches to be generated per frame. @@ -99,31 +50,53 @@ class RasterCache { // multiple frames. static constexpr int kDefaultPictureCacheLimitPerFrame = 3; - // Create a default |RasterCache| object that caches |Layer| and |SkPicture| - // objects using a |SkRasterCacheImageDelegate|. - // - // @param access_threshold - // the number of attempts to cache a given |SkPicture| before inserting - // it into the cache. The defalt value of 3 will only cache a picture - // on the 3rd time it appears in consecutive frames. - // @param picture_cache_limit_per_frame - // the maximum number of |SkPicture| objects to render into the cache - // per frame. The default value of 3 will rasterize at most 3 pictures - // in a given frame and leave the rest for caching in subsequent frames. - // - // @see |kDefaultPictureCacheLimitPerFrame| explicit RasterCache( size_t access_threshold = 3, size_t picture_cache_limit_per_frame = kDefaultPictureCacheLimitPerFrame); - // Create a |RasterCache| object that uses a specified implementation of - // |RasterCacheImageDelegate|. - // - // @see |RasterCache(size_t, size_t)| - explicit RasterCache( - RasterCacheImageDelegate* delegate, - size_t access_threshold = 3, - size_t picture_cache_limit_per_frame = kDefaultPictureCacheLimitPerFrame); + virtual ~RasterCache() = default; + + /** + * @brief Rasterize a picture object and produce a RasterCacheResult + * to be stored in the cache. + * + * @param picture the SkPicture object to be cached. + * @param context the GrContext 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 picture into + * the destination using a simple image blit + */ + virtual std::unique_ptr RasterizePicture( + SkPicture* picture, + GrContext* context, + const SkMatrix& ctm, + SkColorSpace* dst_color_space, + bool checkerboard) 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, + const SkMatrix& ctm, + bool checkerboard) const; static SkIRect GetDeviceBounds(const SkRect& rect, const SkMatrix& ctm) { SkRect device_rect; @@ -222,7 +195,6 @@ class RasterCache { } } - const RasterCacheImageDelegate* delegate_; const size_t access_threshold_; const size_t picture_cache_limit_per_frame_; size_t picture_cached_this_frame_ = 0; diff --git a/flow/testing/layer_test.h b/flow/testing/layer_test.h index f123c624dc0eb..a81bf3c28ff0f 100644 --- a/flow/testing/layer_test.h +++ b/flow/testing/layer_test.h @@ -28,10 +28,9 @@ namespace testing { // By default the preroll and paint contexts will not use a raster cache. // If a test needs to verify the proper operation of a layer in the presence // of a raster cache then a number of options can be enabled by using the -// following methods: -// @see |use_null_raster_cache()| -// @see |use_mock_raster_cache()| -// @see |use_skia_raster_cache()| +// methods |LayerTestBase::use_null_raster_cache()|, +// |LayerTestBase::use_mock_raster_cache()| or +// |LayerTestBase::use_skia_raster_cache()| // // |BaseT| should be the base test type, such as |::testing::Test| below. template @@ -41,9 +40,9 @@ class LayerTestBase : public CanvasTestBase { public: LayerTestBase() : preroll_context_({ - raster_cache(), /* raster_cache */ - nullptr, /* gr_context */ - nullptr, /* external_view_embedder */ + nullptr, /* raster_cache */ + nullptr, /* gr_context */ + nullptr, /* external_view_embedder */ mutators_stack_, TestT::mock_canvas().imageInfo().colorSpace(), kGiantRect, /* cull_rect */ false, /* layer reads from surface */ @@ -60,30 +59,61 @@ class LayerTestBase : public CanvasTestBase { nullptr, /* gr_context */ nullptr, /* external_view_embedder */ raster_time_, ui_time_, texture_registry_, - raster_cache(), /* raster_cache */ - false, /* checkerboard_offscreen_layers */ - 100.0f, /* frame_physical_depth */ - 1.0f, /* frame_device_pixel_ratio */ - }) {} - - // Use no raster cache in subsequent |Preroll| and |Paint| method calls. - // This is the default mode of operation. - void use_null_raster_cache() { set_raster_cache(nullptr); } - - // Use a mock raster cache in subsequent |Preroll| and |Paint| method calls. - // The mock raster cache behaves like a normal raster cache with respect to - // when layers and pictures are cached, but it does not incur the overhead - // of rendering the layers or caching the resulting pixels. + nullptr, /* raster_cache */ + false, /* checkerboard_offscreen_layers */ + 100.0f, /* frame_physical_depth */ + 1.0f, /* frame_device_pixel_ratio */ + }) { + use_null_raster_cache(); + } + + /** + * @brief Use no raster cache in the preroll_context() and + * paint_context() structures. + * + * This method must be called before using the preroll_context() and + * paint_context() structures in calls to the Layer::Preroll() and + * Layer::Paint() methods. This is the default mode of operation. + * + * @see use_mock_raster_cache() + * @see use_skia_raster_cache() + */ + void use_null_raster_cache() { set_raster_cache_(nullptr); } + + /** + * @brief Use a mock raster cache in the preroll_context() and + * paint_context() structures. + * + * This method must be called before using the preroll_context() and + * paint_context() structures in calls to the Layer::Preroll() and + * Layer::Paint() methods. The mock raster cache behaves like a normal + * raster cache with respect to decisions about when layers and pictures + * should be cached, but it does not incur the overhead of rendering the + * layers or caching the resulting pixels. + * + * @see use_null_raster_cache() + * @see use_skia_raster_cache() + */ void use_mock_raster_cache() { - set_raster_cache( - std::make_unique(&MockRasterCacheImageDelegate::instance)); + set_raster_cache_(std::make_unique()); } - // Use a normal raster cache in subusequent |Preroll| and |Paint| method - // calls that uses Skia to render cached layers and pictures in the same - // way as happens when actually handling a frame on a device. + /** + * @brief Use a normal raster cache in the preroll_context() and + * paint_context() structures. + * + * This method must be called before using the preroll_context() and + * paint_context() structures in calls to the Layer::Preroll() and + * Layer::Paint() methods. The Skia raster cache will behave identically + * to the raster cache typically used when handling a frame on a device + * including rendering the contents of pictures and layers to an + * SkImage, but using a software rather than a hardware renderer. + * + * @see use_null_raster_cache() + * @see use_mock_raster_cache() + */ void use_skia_raster_cache() { - set_raster_cache(std::make_unique()); + set_raster_cache_(std::make_unique()); } TextureRegistry& texture_regitry() { return texture_registry_; } @@ -92,7 +122,7 @@ class LayerTestBase : public CanvasTestBase { Layer::PaintContext& paint_context() { return paint_context_; } private: - void set_raster_cache(std::unique_ptr raster_cache) { + void set_raster_cache_(std::unique_ptr raster_cache) { raster_cache_ = std::move(raster_cache); preroll_context_.raster_cache = raster_cache_.get(); paint_context_.raster_cache = raster_cache_.get(); diff --git a/testing/mock_raster_cache.cc b/testing/mock_raster_cache.cc index 69d4e0af16522..7b178ff05358f 100644 --- a/testing/mock_raster_cache.cc +++ b/testing/mock_raster_cache.cc @@ -9,21 +9,22 @@ namespace flutter { namespace testing { MockRasterCacheResult::MockRasterCacheResult(SkIRect device_rect) - : device_rect_(device_rect) {} - -std::unique_ptr -MockRasterCacheImageDelegate::RasterizePicture(SkPicture* picture, - GrContext* context, - const SkMatrix& ctm, - SkColorSpace* dst_color_space, - bool checkerboard) const { + : RasterCacheResult(nullptr, SkRect::MakeEmpty()), + device_rect_(device_rect) {} + +std::unique_ptr MockRasterCache::RasterizePicture( + SkPicture* picture, + GrContext* context, + const SkMatrix& ctm, + SkColorSpace* dst_color_space, + bool checkerboard) const { SkRect logical_rect = picture->cullRect(); SkIRect cache_rect = RasterCache::GetDeviceBounds(logical_rect, ctm); return std::make_unique(cache_rect); } -std::unique_ptr MockRasterCacheImageDelegate::RasterizeLayer( +std::unique_ptr MockRasterCache::RasterizeLayer( PrerollContext* context, Layer* layer, const SkMatrix& ctm, @@ -34,7 +35,5 @@ std::unique_ptr MockRasterCacheImageDelegate::RasterizeLayer( return std::make_unique(cache_rect); } -MockRasterCacheImageDelegate MockRasterCacheImageDelegate::instance; - } // namespace testing } // namespace flutter diff --git a/testing/mock_raster_cache.h b/testing/mock_raster_cache.h index b9ad5267389bd..0f85118f4d0a0 100644 --- a/testing/mock_raster_cache.h +++ b/testing/mock_raster_cache.h @@ -12,10 +12,13 @@ namespace flutter { namespace testing { -// A |RasterCacheResult| implementation that represents a cached |Layer| or -// |SkPicture| without the overhead of storage. This implementation is used -// by |MockRasterCacheImageDelegate| only for testing proper usage of the -// |RasterCache| in layer unit tests. +/** + * @brief A RasterCacheResult implementation that represents a cached Layer or + * SkPicture without the overhead of storage. + * + * This implementation is used by MockRasterCache only for testing proper usage + * of the RasterCache in layer unit tests. + */ class MockRasterCacheResult : public RasterCacheResult { public: MockRasterCacheResult(SkIRect device_rect); @@ -24,15 +27,22 @@ class MockRasterCacheResult : public RasterCacheResult { SkISize image_dimensions() const override { return device_rect_.size(); }; + int64_t image_bytes() const override { + return image_dimensions().area() * + SkColorTypeBytesPerPixel(kBGRA_8888_SkColorType); + } + private: SkIRect device_rect_; }; -// A |RasterCacheImageDelegate| implementation that simulates the act of -// rendering a |Layer| or |SkPicture| without the overhead of rasterization -// or storage. This implementation is used only for testing proper usage of -// the |RasterCache| in layer unit tests. -class MockRasterCacheImageDelegate : public RasterCacheImageDelegate { +/** + * @brief A RasterCache implementation that simulates the act of rendering a + * Layer or SkPicture without the overhead of rasterization or pixel storage. + * This implementation is used only for testing proper usage of the RasterCache + * in layer unit tests. + */ +class MockRasterCache : public RasterCache { public: std::unique_ptr RasterizePicture( SkPicture* picture, @@ -46,8 +56,6 @@ class MockRasterCacheImageDelegate : public RasterCacheImageDelegate { Layer* layer, const SkMatrix& ctm, bool checkerboard) const override; - - static MockRasterCacheImageDelegate instance; }; } // namespace testing From 08b7e7dfbdc1c01ba761b7bd5f490dff6b1bf560 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Thu, 21 May 2020 11:39:04 -0700 Subject: [PATCH 6/7] update comment related to how OpacityLayer might end up with multiple children --- flow/layers/opacity_layer.h | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/flow/layers/opacity_layer.h b/flow/layers/opacity_layer.h index 304ccada37e7a..e0efef25ed17c 100644 --- a/flow/layers/opacity_layer.h +++ b/flow/layers/opacity_layer.h @@ -42,13 +42,16 @@ class OpacityLayer : public ContainerLayer { * @brief Returns the ContainerLayer used to hold all of the children * of the OpacityLayer. * - * Typically these layers should only have a single child according to - * the contract of the associated Flutter widget, but the engine API - * cannot enforce that contract and must be prepared to support the - * possibility of adding more than one child. In either case, the - * child container will hold all children. Note that since the child - * container is created fresh with each new OpacityLayer, it may not - * be the best choice to cache to speed up rendering during an animation. + * Often opacity layers will only have a single child since the associated + * Flutter widget is specified with only a single child widget pointer. + * But depending on the structure of the child tree that single widget at + * the framework level can turn into multiple children at the engine + * API level since there is no guarantee of a 1:1 correspondence of widgets + * to engine layers. This synthetic child container layer is established to + * hold all of the children in a single layer so that we can cache their + * output, but this synthetic layer will typically not be the best choice + * for the layer cache since the synthetic container is created fresh with + * each new OpacityLayer, and so may not be stable from frame to frame. * * @see GetCacheableChild() * @return the ContainerLayer child used to hold the children @@ -61,13 +64,17 @@ class OpacityLayer : public ContainerLayer { * * The returned Layer must represent all children and try to remain stable * if the OpacityLayer is reconstructed in subsequent frames of the scene. - * The OpacityLayer needs to be recreated on frames where its opacity - * values change, but it often contains the same child on each such frame. - * Since the child container is created fresh each time the OpacityLayer - * is constructed, the return value from GetChildContainer will be different - * on each such frame even if the same child is used. This method will - * determine if there is a (single) stable child and return that Layer - * so that caching will be more successful. + * + * Note that since the synthetic child container returned from the + * GetChildContainer() method is created fresh with each new OpacityLayer, + * its return value will not be a good candidate for caching. But if the + * standard recommendations for animations are followed and the child widget + * is wrapped with a RepaintBoundary widget at the framework level, then + * the synthetic child container should contain the same single child layer + * on each frame. Under those conditions, that single child of the child + * container will be the best candidate for caching in the RasterCache + * and this method will return that single child if possible to improve + * the performance of caching the children. * * @see GetChildContainer() * @return the best candidate Layer for caching the children From ac3f66e086ca2a860c1b7d808d65b787848310bd Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Thu, 21 May 2020 13:31:09 -0700 Subject: [PATCH 7/7] update GetCacheableChild docs with review suggestion --- flow/layers/opacity_layer.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/flow/layers/opacity_layer.h b/flow/layers/opacity_layer.h index e0efef25ed17c..4edc61ad9009a 100644 --- a/flow/layers/opacity_layer.h +++ b/flow/layers/opacity_layer.h @@ -76,6 +76,15 @@ class OpacityLayer : public ContainerLayer { * and this method will return that single child if possible to improve * the performance of caching the children. * + * Note that if GetCacheableChild() does not find a single stable child of + * the child container it will return the child container as a fallback. + * Even though that child is new in each frame of an animation and thus we + * cannot reuse the cached layer raster between animation frames, the single + * container child will allow us to paint the child onto an offscreen buffer + * during Preroll() which reduces one render target switch compared to + * painting the child on the fly via an AutoSaveLayer in Paint() and thus + * still improves our performance. + * * @see GetChildContainer() * @return the best candidate Layer for caching the children */