diff --git a/display_list/geometry/dl_rtree.cc b/display_list/geometry/dl_rtree.cc index 548ff505071a4..d5fb8ad64d1ff 100644 --- a/display_list/geometry/dl_rtree.cc +++ b/display_list/geometry/dl_rtree.cc @@ -201,4 +201,12 @@ void DlRTree::search(const Node& parent, } } +const SkRect& DlRTree::bounds() const { + if (!nodes_.empty()) { + return nodes_.back().bounds; + } else { + return empty_; + } +} + } // namespace flutter diff --git a/display_list/geometry/dl_rtree.h b/display_list/geometry/dl_rtree.h index 0c2f25abc766a..0de686abc52d5 100644 --- a/display_list/geometry/dl_rtree.h +++ b/display_list/geometry/dl_rtree.h @@ -85,6 +85,10 @@ class DlRTree : public SkRefCnt { : invalid_id_; } + /// Returns maximum and minimum axis values of rectangles in this R-Tree. + /// If R-Tree is empty returns an empty SkRect. + const SkRect& bounds() const; + /// Return the rectangle bounds for the indicated result of a query /// or an empty rect if the index is not a valid leaf node index. const SkRect& bounds(int result_index) const { diff --git a/flow/layers/display_list_layer_unittests.cc b/flow/layers/display_list_layer_unittests.cc index dee93d2a673e0..2791577666b8d 100644 --- a/flow/layers/display_list_layer_unittests.cc +++ b/flow/layers/display_list_layer_unittests.cc @@ -294,6 +294,64 @@ TEST_F(DisplayListLayerTest, CachedIncompatibleDisplayListOpacityInheritance) { EXPECT_TRUE(DisplayListsEQ_Verbose(expected.Build(), this->display_list())); } +TEST_F(DisplayListLayerTest, RasterCachePreservesRTree) { + const SkRect picture1_bounds = SkRect::MakeXYWH(10, 10, 10, 10); + const SkRect picture2_bounds = SkRect::MakeXYWH(15, 15, 10, 10); + DisplayListBuilder builder(true); + builder.DrawRect(picture1_bounds, DlPaint()); + builder.DrawRect(picture2_bounds, DlPaint()); + auto display_list = builder.Build(); + auto display_list_layer = std::make_shared( + SkPoint::Make(3, 3), display_list, true, false); + + use_skia_raster_cache(); + + auto context = preroll_context(); + { + auto mutator = context->state_stack.save(); + mutator.transform(SkMatrix::Scale(2.0, 2.0)); + display_list_layer->Preroll(preroll_context()); + EXPECT_EQ(context->renderable_state_flags, 0); + + // Pump the DisplayListLayer until it is ready to cache its DL + display_list_layer->Preroll(preroll_context()); + display_list_layer->Preroll(preroll_context()); + display_list_layer->Preroll(preroll_context()); + LayerTree::TryToRasterCache(*preroll_context()->raster_cached_entries, + &paint_context(), false); + } + + DisplayListBuilder expected_root_canvas(true); + expected_root_canvas.Scale(2.0, 2.0); + ASSERT_TRUE(context->raster_cache->Draw(display_list_layer->caching_key_id(), + expected_root_canvas, nullptr, + false)); + auto root_canvas_dl = expected_root_canvas.Build(); + const auto root_canvas_rects = + root_canvas_dl->rtree()->searchAndConsolidateRects(kGiantRect, true); + std::list root_canvas_rects_expected = { + SkRect::MakeLTRB(26, 26, 56, 56), + }; + EXPECT_EQ(root_canvas_rects_expected, root_canvas_rects); + + DisplayListBuilder expected_overlay_canvas(true); + expected_overlay_canvas.Scale(2.0, 2.0); + ASSERT_TRUE(context->raster_cache->Draw(display_list_layer->caching_key_id(), + expected_overlay_canvas, nullptr, + true)); + auto overlay_canvas_dl = expected_overlay_canvas.Build(); + const auto overlay_canvas_rects = + overlay_canvas_dl->rtree()->searchAndConsolidateRects(kGiantRect, true); + + // Same bounds as root canvas, but preserves individual rects. + std::list overlay_canvas_rects_expected = { + SkRect::MakeLTRB(26, 26, 46, 36), + SkRect::MakeLTRB(26, 36, 56, 46), + SkRect::MakeLTRB(36, 46, 56, 56), + }; + EXPECT_EQ(overlay_canvas_rects_expected, overlay_canvas_rects); +}; + using DisplayListLayerDiffTest = DiffContextTest; TEST_F(DisplayListLayerDiffTest, SimpleDisplayList) { diff --git a/flow/layers/display_list_raster_cache_item.cc b/flow/layers/display_list_raster_cache_item.cc index 089fb6c0c338b..c0675785f724b 100644 --- a/flow/layers/display_list_raster_cache_item.cc +++ b/flow/layers/display_list_raster_cache_item.cc @@ -133,7 +133,8 @@ bool DisplayListRasterCacheItem::Draw(const PaintContext& context, return false; } if (cache_state_ == CacheState::kCurrent) { - return context.raster_cache->Draw(key_id_, *canvas, paint); + return context.raster_cache->Draw(key_id_, *canvas, paint, + context.rendering_above_platform_view); } return false; } @@ -166,8 +167,10 @@ bool DisplayListRasterCacheItem::TryToPrepareRasterCache( // clang-format on }; return context.raster_cache->UpdateCacheEntry( - id.value(), r_context, [display_list = display_list_](DlCanvas* canvas) { + id.value(), r_context, + [display_list = display_list_](DlCanvas* canvas) { canvas->DrawDisplayList(display_list); - }); + }, + display_list_->rtree()); } } // namespace flutter diff --git a/flow/layers/layer.h b/flow/layers/layer.h index f1f1056a5ea9f..cbaa6d1833fe6 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -106,6 +106,11 @@ struct PaintContext { LayerStateStack& state_stack; DlCanvas* canvas; + // Whether current canvas is an overlay canvas. Used to determine if the + // raster cache is painting to a surface that will be displayed above a + // platform view, in which case it will attempt to preserve the R-Tree. + bool rendering_above_platform_view = false; + GrDirectContext* gr_context; SkColorSpace* dst_color_space; ExternalViewEmbedder* view_embedder; diff --git a/flow/layers/layer_raster_cache_item.cc b/flow/layers/layer_raster_cache_item.cc index b6597414958b3..61c0f8596e303 100644 --- a/flow/layers/layer_raster_cache_item.cc +++ b/flow/layers/layer_raster_cache_item.cc @@ -182,14 +182,16 @@ bool LayerRasterCacheItem::Draw(const PaintContext& context, case RasterCacheItem::kNone: return false; case RasterCacheItem::kCurrent: { - return context.raster_cache->Draw(key_id_, *canvas, paint); + return context.raster_cache->Draw(key_id_, *canvas, paint, + context.rendering_above_platform_view); } case RasterCacheItem::kChildren: { if (!layer_children_id_.has_value()) { return false; } return context.raster_cache->Draw(layer_children_id_.value(), *canvas, - paint); + paint, + context.rendering_above_platform_view); } } } diff --git a/flow/layers/platform_view_layer.cc b/flow/layers/platform_view_layer.cc index 487bbc1e710e2..0f3fc7fc3deec 100644 --- a/flow/layers/platform_view_layer.cc +++ b/flow/layers/platform_view_layer.cc @@ -44,6 +44,7 @@ void PlatformViewLayer::Paint(PaintContext& context) const { DlCanvas* canvas = context.view_embedder->CompositeEmbeddedView(view_id_); context.canvas = canvas; context.state_stack.set_delegate(canvas); + context.rendering_above_platform_view = true; } } // namespace flutter diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index 40c87e7331ab7..90f49d0371c14 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -8,6 +8,7 @@ #include #include "flutter/common/constants.h" +#include "flutter/display_list/skia/dl_sk_dispatcher.h" #include "flutter/flow/layers/container_layer.h" #include "flutter/flow/layers/layer.h" #include "flutter/flow/paint_utils.h" @@ -26,10 +27,16 @@ namespace flutter { RasterCacheResult::RasterCacheResult(sk_sp image, const SkRect& logical_rect, - const char* type) - : image_(std::move(image)), logical_rect_(logical_rect), flow_(type) {} - -void RasterCacheResult::draw(DlCanvas& canvas, const DlPaint* paint) const { + const char* type, + sk_sp rtree) + : image_(std::move(image)), + logical_rect_(logical_rect), + flow_(type), + rtree_(std::move(rtree)) {} + +void RasterCacheResult::draw(DlCanvas& canvas, + const DlPaint* paint, + bool preserve_rtree) const { DlAutoCanvasRestore auto_restore(&canvas, true); auto matrix = RasterCacheUtil::GetIntegralTransCTM(canvas.GetTransform()); @@ -39,8 +46,26 @@ void RasterCacheResult::draw(DlCanvas& canvas, const DlPaint* paint) const { std::abs(bounds.height() - image_->dimensions().height()) <= 1); canvas.TransformReset(); flow_.Step(); - canvas.DrawImage(image_, {bounds.fLeft, bounds.fTop}, - DlImageSampling::kNearestNeighbor, paint); + if (!preserve_rtree || !rtree_) { + canvas.DrawImage(image_, {bounds.fLeft, bounds.fTop}, + DlImageSampling::kNearestNeighbor, paint); + } else { + // On some platforms RTree from overlay layers is used for unobstructed + // platform views and hit testing. To preserve the RTree raster cache must + // paint individual rects instead of the whole image. + auto rects = rtree_->searchAndConsolidateRects(kGiantRect); + + canvas.Translate(bounds.fLeft, bounds.fTop); + + SkRect rtree_bounds = + RasterCacheUtil::GetRoundedOutDeviceBounds(rtree_->bounds(), matrix); + for (auto rect : rects) { + rect = RasterCacheUtil::GetRoundedOutDeviceBounds(rect, matrix); + rect.offset(-rtree_bounds.fLeft, -rtree_bounds.fTop); + canvas.DrawImageRect(image_, rect, rect, + DlImageSampling::kNearestNeighbor, paint); + } + } } RasterCache::RasterCache(size_t access_threshold, @@ -52,6 +77,7 @@ RasterCache::RasterCache(size_t access_threshold, /// @note Procedure doesn't copy all closures. std::unique_ptr RasterCache::Rasterize( const RasterCache::Context& context, + sk_sp rtree, const std::function& draw_function, const std::function& draw_checkerboard) const { @@ -75,6 +101,7 @@ std::unique_ptr RasterCache::Rasterize( DlSkCanvasAdapter canvas(surface->getCanvas()); canvas.Clear(DlColor::kTransparent()); + canvas.Translate(-dest_rect.left(), -dest_rect.top()); canvas.Transform(matrix); draw_function(&canvas); @@ -84,19 +111,21 @@ std::unique_ptr RasterCache::Rasterize( } auto image = DlImage::Make(surface->makeImageSnapshot()); - return std::make_unique(image, context.logical_rect, - context.flow_type); + return std::make_unique( + image, context.logical_rect, context.flow_type, std::move(rtree)); } bool RasterCache::UpdateCacheEntry( const RasterCacheKeyID& id, const Context& raster_cache_context, - const std::function& render_function) const { + const std::function& render_function, + sk_sp rtree) const { RasterCacheKey key = RasterCacheKey(id, raster_cache_context.matrix); Entry& entry = cache_[key]; if (!entry.image) { void (*func)(DlCanvas*, const SkRect& rect) = DrawCheckerboard; - entry.image = Rasterize(raster_cache_context, render_function, func); + entry.image = Rasterize(raster_cache_context, std::move(rtree), + render_function, func); if (entry.image != nullptr) { switch (id.type()) { case RasterCacheKeyType::kDisplayList: { @@ -146,7 +175,8 @@ bool RasterCache::HasEntry(const RasterCacheKeyID& id, bool RasterCache::Draw(const RasterCacheKeyID& id, DlCanvas& canvas, - const DlPaint* paint) const { + const DlPaint* paint, + bool preserve_rtree) const { auto it = cache_.find(RasterCacheKey(id, canvas.GetTransform())); if (it == cache_.end()) { return false; @@ -155,7 +185,7 @@ bool RasterCache::Draw(const RasterCacheKeyID& id, Entry& entry = it->second; if (entry.image) { - entry.image->draw(canvas, paint); + entry.image->draw(canvas, paint, preserve_rtree); return true; } diff --git a/flow/raster_cache.h b/flow/raster_cache.h index ea2f5b1829e66..a683350f792dc 100644 --- a/flow/raster_cache.h +++ b/flow/raster_cache.h @@ -30,11 +30,14 @@ class RasterCacheResult { public: RasterCacheResult(sk_sp image, const SkRect& logical_rect, - const char* type); + const char* type, + sk_sp rtree = nullptr); virtual ~RasterCacheResult() = default; - virtual void draw(DlCanvas& canvas, const DlPaint* paint) const; + virtual void draw(DlCanvas& canvas, + const DlPaint* paint, + bool preserve_rtree) const; virtual SkISize image_dimensions() const { return image_ ? image_->dimensions() : SkISize::Make(0, 0); @@ -48,6 +51,7 @@ class RasterCacheResult { sk_sp image_; SkRect logical_rect_; fml::tracing::TraceFlow flow_; + sk_sp rtree_; }; class Layer; @@ -127,6 +131,7 @@ class RasterCache { std::unique_ptr Rasterize( const RasterCache::Context& context, + sk_sp rtree, const std::function& draw_function, const std::function& draw_checkerboard) const; @@ -143,9 +148,15 @@ class RasterCache { // if the item was disabled due to conditions discovered during |Preroll| // or if the attempt to populate the entry failed due to bounds overflow // conditions. + // If |preserve_rtree| is true, the raster cache will preserve the original + // RTree of cached content by blitting individual rectangles from the cached + // image to the canvas according to the original layer R-Tree (if present). + // This is to ensure that the target surface R-Tree will not be clobbered with + // one large blit as it can affect platform view overlays and hit testing. bool Draw(const RasterCacheKeyID& id, DlCanvas& canvas, - const DlPaint* paint) const; + const DlPaint* paint, + bool preserve_rtree = false) const; bool HasEntry(const RasterCacheKeyID& id, const SkMatrix&) const; @@ -234,10 +245,10 @@ class RasterCache { */ int GetAccessCount(const RasterCacheKeyID& id, const SkMatrix& matrix) const; - bool UpdateCacheEntry( - const RasterCacheKeyID& id, - const Context& raster_cache_context, - const std::function& render_function) const; + bool UpdateCacheEntry(const RasterCacheKeyID& id, + const Context& raster_cache_context, + const std::function& render_function, + sk_sp rtree = nullptr) const; private: struct Entry { diff --git a/flow/raster_cache_unittests.cc b/flow/raster_cache_unittests.cc index 43c314c7fff33..daa0146d14d95 100644 --- a/flow/raster_cache_unittests.cc +++ b/flow/raster_cache_unittests.cc @@ -178,11 +178,11 @@ TEST(RasterCache, SetCheckboardCacheImages) { }; cache.SetCheckboardCacheImages(false); - cache.Rasterize(r_context, dummy_draw_function, draw_checkerboard); + cache.Rasterize(r_context, nullptr, dummy_draw_function, draw_checkerboard); ASSERT_FALSE(did_draw_checkerboard); cache.SetCheckboardCacheImages(true); - cache.Rasterize(r_context, dummy_draw_function, draw_checkerboard); + cache.Rasterize(r_context, nullptr, dummy_draw_function, draw_checkerboard); ASSERT_TRUE(did_draw_checkerboard); } diff --git a/flow/testing/mock_raster_cache.h b/flow/testing/mock_raster_cache.h index 4f625ad6df938..e3478b38c3942 100644 --- a/flow/testing/mock_raster_cache.h +++ b/flow/testing/mock_raster_cache.h @@ -29,7 +29,9 @@ class MockRasterCacheResult : public RasterCacheResult { public: explicit MockRasterCacheResult(SkRect device_rect); - void draw(DlCanvas& canvas, const DlPaint* paint = nullptr) const override{}; + void draw(DlCanvas& canvas, + const DlPaint* paint = nullptr, + bool preserve_rtree = false) const override{}; SkISize image_dimensions() const override { return SkSize::Make(device_rect_.width(), device_rect_.height()).toCeil();