From efe3f4569a6aa49935ffb9a688812f46a2971b89 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Thu, 28 May 2020 18:45:43 -0700 Subject: [PATCH] Fix child caching in opacity_layer (#17914) Choose a child more likely to remain stable from frame to frame as the target to cache in the OpacityLayer. --- flow/layers/opacity_layer.cc | 13 +++- flow/layers/opacity_layer.h | 50 ++++++++++++ flow/layers/opacity_layer_unittests.cc | 65 ++++++++++++++++ flow/raster_cache.cc | 103 +++++++++++++++---------- flow/raster_cache.h | 68 +++++++++++++--- flow/testing/layer_test.h | 69 ++++++++++++++++- testing/BUILD.gn | 3 + testing/mock_raster_cache.cc | 39 ++++++++++ testing/mock_raster_cache.h | 64 +++++++++++++++ 9 files changed, 419 insertions(+), 55 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 d864db354..9fd0bcc09 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 658dd1a7b..4edc61ad9 100644 --- a/flow/layers/opacity_layer.h +++ b/flow/layers/opacity_layer.h @@ -38,8 +38,58 @@ class OpacityLayer : public ContainerLayer { #endif // defined(OS_FUCHSIA) private: + /** + * @brief Returns the ContainerLayer used to hold all of the children + * of the OpacityLayer. + * + * 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 + */ ContainerLayer* GetChildContainer() const; + /** + * @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. + * + * 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. + * + * 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 + */ + Layer* GetCacheableChild() const; + SkAlpha alpha_; SkPoint offset_; SkRRect frameRRect_; diff --git a/flow/layers/opacity_layer_unittests.cc b/flow/layers/opacity_layer_unittests.cc index e08d5c9ac..735bb0ae3 100644 --- a/flow/layers/opacity_layer_unittests.cc +++ b/flow/layers/opacity_layer_unittests.cc @@ -53,6 +53,71 @@ 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; + SkCanvas cache_canvas; + cache_canvas.setMatrix(cache_ctm); + SkCanvas other_canvas; + other_canvas.setMatrix(other_transform); + + 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()->GetLayerCachedEntriesCount(), (size_t)1); + EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas)); + EXPECT_TRUE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); +} + +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; + SkCanvas cache_canvas; + cache_canvas.setMatrix(cache_ctm); + SkCanvas other_canvas; + other_canvas.setMatrix(other_transform); + + use_mock_raster_cache(); + + EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); + EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), other_canvas)); + EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas)); + EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas)); + EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas)); + + layer->Preroll(preroll_context(), initial_transform); + + EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); + EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), other_canvas)); + EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas)); + EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas)); + EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas)); +} + 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 d41408beb..89fc1ebd3 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -86,7 +86,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, @@ -105,7 +105,7 @@ static RasterCacheResult Rasterize( : SkSurface::MakeRaster(image_info); if (!surface) { - return {}; + return nullptr; } SkCanvas* canvas = surface->getCanvas(); @@ -118,14 +118,16 @@ static RasterCacheResult Rasterize( DrawCheckerboard(canvas, logical_rect); } - return {surface->makeImageSnapshot(), logical_rect}; + return std::make_unique(surface->makeImageSnapshot(), + logical_rect); } -RasterCacheResult RasterizePicture(SkPicture* picture, - GrContext* context, - const SkMatrix& ctm, - SkColorSpace* dst_color_space, - bool checkerboard) { +std::unique_ptr RasterCache::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); }); @@ -138,34 +140,41 @@ 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 = Rasterize( - context->gr_context, ctm, context->dst_color_space, - checkerboard_images_, 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); - } - }); + if (!entry.image) { + entry.image = RasterizeLayer(context, layer, ctm, checkerboard_images_); } } +std::unique_ptr RasterCache::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, // internal_nodes_canvas + canvas, // leaf_nodes_canvas + context->gr_context, // gr_context + nullptr, // view_embedder + 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); + } + }); +} + bool RasterCache::Prepare(GrContext* context, SkPicture* picture, const SkMatrix& transformation_matrix, @@ -202,7 +211,7 @@ bool RasterCache::Prepare(GrContext* context, return false; } - if (!entry.image.is_valid()) { + if (!entry.image) { entry.image = RasterizePicture(picture, context, transformation_matrix, dst_color_space, checkerboard_images_); picture_cached_this_frame_++; @@ -221,8 +230,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; } @@ -242,8 +251,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; } @@ -266,6 +275,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; @@ -287,15 +304,17 @@ 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) { + layer_cache_bytes += item.second.image->image_bytes(); + } } 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) { + picture_cache_bytes += item.second.image->image_bytes(); + } } FML_TRACE_COUNTER("flutter", "RasterCache", diff --git a/flow/raster_cache.h b/flow/raster_cache.h index 3174ed0f5..a2448462a 100644 --- a/flow/raster_cache.h +++ b/flow/raster_cache.h @@ -19,22 +19,22 @@ namespace flutter { class RasterCacheResult { public: - RasterCacheResult() = default; - - RasterCacheResult(const RasterCacheResult& other) = default; - RasterCacheResult(sk_sp image, const SkRect& logical_rect); - operator bool() const { return static_cast(image_); } + virtual ~RasterCacheResult() = default; - bool is_valid() const { return static_cast(image_); }; + virtual void draw(SkCanvas& canvas, const SkPaint* paint = nullptr) const; - void draw(SkCanvas& canvas, const SkPaint* paint = nullptr) const; - - SkISize image_dimensions() const { + 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_; @@ -54,6 +54,50 @@ class RasterCache { 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; ctm.mapRect(&device_rect, rect); @@ -123,11 +167,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 diff --git a/flow/testing/layer_test.h b/flow/testing/layer_test.h index 593dec183..a81bf3c28 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" @@ -24,6 +25,13 @@ 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 +// 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 class LayerTestBase : public CanvasTestBase { @@ -55,18 +63,77 @@ class LayerTestBase : public CanvasTestBase { 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()); + } + + /** + * @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()); + } TextureRegistry& texture_regitry() { return texture_registry_; } + RasterCache* raster_cache() { return raster_cache_.get(); } PrerollContext* preroll_context() { return &preroll_context_; } Layer::PaintContext& paint_context() { return paint_context_; } 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_; + std::unique_ptr raster_cache_; PrerollContext preroll_context_; Layer::PaintContext paint_context_; diff --git a/testing/BUILD.gn b/testing/BUILD.gn index b8c3bd0a2..69f7d3222 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 000000000..7b178ff05 --- /dev/null +++ b/testing/mock_raster_cache.cc @@ -0,0 +1,39 @@ +// 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/testing/mock_raster_cache.h" +#include "flutter/flow/layers/layer.h" + +namespace flutter { +namespace testing { + +MockRasterCacheResult::MockRasterCacheResult(SkIRect device_rect) + : 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 MockRasterCache::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); + + return std::make_unique(cache_rect); +} + +} // namespace testing +} // namespace flutter diff --git a/testing/mock_raster_cache.h b/testing/mock_raster_cache.h new file mode 100644 index 000000000..0f85118f4 --- /dev/null +++ b/testing/mock_raster_cache.h @@ -0,0 +1,64 @@ +// 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/SkImage.h" +#include "third_party/skia/include/core/SkPicture.h" + +namespace flutter { +namespace testing { + +/** + * @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); + + void draw(SkCanvas& canvas, const SkPaint* paint = nullptr) const override{}; + + 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_; +}; + +/** + * @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, + 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; +}; + +} // namespace testing +} // namespace flutter + +#endif // TESTING_MOCK_RASTER_CACHE_H_ -- GitLab