Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit d6e96c0

Browse files
authored
Raster cache should preserve RTree for overlay layers (#42552)
Fixes flutter/flutter#116069 When raster cache paints to a layer over platform view, it will draw the individual rectangles from the content RTree, to ensure that the destination layer RTree is not clobbered. This is necessary for raster cache to not break unobstructed platform views and/or hit testing. When raster cache paints in the root (bottom-most) layer the behavior it just copies the entire image as it did before. This is to minimise risk of performance regressions. ## Pre-launch Checklist - [X] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [X] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [X] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [X] I listed at least one issue that this PR fixes in the description above. - [X] I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test-exempt. See [testing the engine] for instructions on writing and running engine tests. - [X] I updated/added relevant documentation (doc comments with `///`). - [X] I signed the [CLA]. - [X] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat
1 parent 66b2a94 commit d6e96c0

11 files changed

+151
-27
lines changed

display_list/geometry/dl_rtree.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,4 +201,12 @@ void DlRTree::search(const Node& parent,
201201
}
202202
}
203203

204+
const SkRect& DlRTree::bounds() const {
205+
if (!nodes_.empty()) {
206+
return nodes_.back().bounds;
207+
} else {
208+
return empty_;
209+
}
210+
}
211+
204212
} // namespace flutter

display_list/geometry/dl_rtree.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ class DlRTree : public SkRefCnt {
8585
: invalid_id_;
8686
}
8787

88+
/// Returns maximum and minimum axis values of rectangles in this R-Tree.
89+
/// If R-Tree is empty returns an empty SkRect.
90+
const SkRect& bounds() const;
91+
8892
/// Return the rectangle bounds for the indicated result of a query
8993
/// or an empty rect if the index is not a valid leaf node index.
9094
const SkRect& bounds(int result_index) const {

flow/layers/display_list_layer_unittests.cc

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,64 @@ TEST_F(DisplayListLayerTest, CachedIncompatibleDisplayListOpacityInheritance) {
294294
EXPECT_TRUE(DisplayListsEQ_Verbose(expected.Build(), this->display_list()));
295295
}
296296

297+
TEST_F(DisplayListLayerTest, RasterCachePreservesRTree) {
298+
const SkRect picture1_bounds = SkRect::MakeXYWH(10, 10, 10, 10);
299+
const SkRect picture2_bounds = SkRect::MakeXYWH(15, 15, 10, 10);
300+
DisplayListBuilder builder(true);
301+
builder.DrawRect(picture1_bounds, DlPaint());
302+
builder.DrawRect(picture2_bounds, DlPaint());
303+
auto display_list = builder.Build();
304+
auto display_list_layer = std::make_shared<DisplayListLayer>(
305+
SkPoint::Make(3, 3), display_list, true, false);
306+
307+
use_skia_raster_cache();
308+
309+
auto context = preroll_context();
310+
{
311+
auto mutator = context->state_stack.save();
312+
mutator.transform(SkMatrix::Scale(2.0, 2.0));
313+
display_list_layer->Preroll(preroll_context());
314+
EXPECT_EQ(context->renderable_state_flags, 0);
315+
316+
// Pump the DisplayListLayer until it is ready to cache its DL
317+
display_list_layer->Preroll(preroll_context());
318+
display_list_layer->Preroll(preroll_context());
319+
display_list_layer->Preroll(preroll_context());
320+
LayerTree::TryToRasterCache(*preroll_context()->raster_cached_entries,
321+
&paint_context(), false);
322+
}
323+
324+
DisplayListBuilder expected_root_canvas(true);
325+
expected_root_canvas.Scale(2.0, 2.0);
326+
ASSERT_TRUE(context->raster_cache->Draw(display_list_layer->caching_key_id(),
327+
expected_root_canvas, nullptr,
328+
false));
329+
auto root_canvas_dl = expected_root_canvas.Build();
330+
const auto root_canvas_rects =
331+
root_canvas_dl->rtree()->searchAndConsolidateRects(kGiantRect, true);
332+
std::list<SkRect> root_canvas_rects_expected = {
333+
SkRect::MakeLTRB(26, 26, 56, 56),
334+
};
335+
EXPECT_EQ(root_canvas_rects_expected, root_canvas_rects);
336+
337+
DisplayListBuilder expected_overlay_canvas(true);
338+
expected_overlay_canvas.Scale(2.0, 2.0);
339+
ASSERT_TRUE(context->raster_cache->Draw(display_list_layer->caching_key_id(),
340+
expected_overlay_canvas, nullptr,
341+
true));
342+
auto overlay_canvas_dl = expected_overlay_canvas.Build();
343+
const auto overlay_canvas_rects =
344+
overlay_canvas_dl->rtree()->searchAndConsolidateRects(kGiantRect, true);
345+
346+
// Same bounds as root canvas, but preserves individual rects.
347+
std::list<SkRect> overlay_canvas_rects_expected = {
348+
SkRect::MakeLTRB(26, 26, 46, 36),
349+
SkRect::MakeLTRB(26, 36, 56, 46),
350+
SkRect::MakeLTRB(36, 46, 56, 56),
351+
};
352+
EXPECT_EQ(overlay_canvas_rects_expected, overlay_canvas_rects);
353+
};
354+
297355
using DisplayListLayerDiffTest = DiffContextTest;
298356

299357
TEST_F(DisplayListLayerDiffTest, SimpleDisplayList) {

flow/layers/display_list_raster_cache_item.cc

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ bool DisplayListRasterCacheItem::Draw(const PaintContext& context,
133133
return false;
134134
}
135135
if (cache_state_ == CacheState::kCurrent) {
136-
return context.raster_cache->Draw(key_id_, *canvas, paint);
136+
return context.raster_cache->Draw(key_id_, *canvas, paint,
137+
context.rendering_above_platform_view);
137138
}
138139
return false;
139140
}
@@ -166,8 +167,10 @@ bool DisplayListRasterCacheItem::TryToPrepareRasterCache(
166167
// clang-format on
167168
};
168169
return context.raster_cache->UpdateCacheEntry(
169-
id.value(), r_context, [display_list = display_list_](DlCanvas* canvas) {
170+
id.value(), r_context,
171+
[display_list = display_list_](DlCanvas* canvas) {
170172
canvas->DrawDisplayList(display_list);
171-
});
173+
},
174+
display_list_->rtree());
172175
}
173176
} // namespace flutter

flow/layers/layer.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ struct PaintContext {
106106
LayerStateStack& state_stack;
107107
DlCanvas* canvas;
108108

109+
// Whether current canvas is an overlay canvas. Used to determine if the
110+
// raster cache is painting to a surface that will be displayed above a
111+
// platform view, in which case it will attempt to preserve the R-Tree.
112+
bool rendering_above_platform_view = false;
113+
109114
GrDirectContext* gr_context;
110115
SkColorSpace* dst_color_space;
111116
ExternalViewEmbedder* view_embedder;

flow/layers/layer_raster_cache_item.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,14 +182,16 @@ bool LayerRasterCacheItem::Draw(const PaintContext& context,
182182
case RasterCacheItem::kNone:
183183
return false;
184184
case RasterCacheItem::kCurrent: {
185-
return context.raster_cache->Draw(key_id_, *canvas, paint);
185+
return context.raster_cache->Draw(key_id_, *canvas, paint,
186+
context.rendering_above_platform_view);
186187
}
187188
case RasterCacheItem::kChildren: {
188189
if (!layer_children_id_.has_value()) {
189190
return false;
190191
}
191192
return context.raster_cache->Draw(layer_children_id_.value(), *canvas,
192-
paint);
193+
paint,
194+
context.rendering_above_platform_view);
193195
}
194196
}
195197
}

flow/layers/platform_view_layer.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ void PlatformViewLayer::Paint(PaintContext& context) const {
4444
DlCanvas* canvas = context.view_embedder->CompositeEmbeddedView(view_id_);
4545
context.canvas = canvas;
4646
context.state_stack.set_delegate(canvas);
47+
context.rendering_above_platform_view = true;
4748
}
4849

4950
} // namespace flutter

flow/raster_cache.cc

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <vector>
99

1010
#include "flutter/common/constants.h"
11+
#include "flutter/display_list/skia/dl_sk_dispatcher.h"
1112
#include "flutter/flow/layers/container_layer.h"
1213
#include "flutter/flow/layers/layer.h"
1314
#include "flutter/flow/paint_utils.h"
@@ -26,10 +27,16 @@ namespace flutter {
2627

2728
RasterCacheResult::RasterCacheResult(sk_sp<DlImage> image,
2829
const SkRect& logical_rect,
29-
const char* type)
30-
: image_(std::move(image)), logical_rect_(logical_rect), flow_(type) {}
31-
32-
void RasterCacheResult::draw(DlCanvas& canvas, const DlPaint* paint) const {
30+
const char* type,
31+
sk_sp<const DlRTree> rtree)
32+
: image_(std::move(image)),
33+
logical_rect_(logical_rect),
34+
flow_(type),
35+
rtree_(std::move(rtree)) {}
36+
37+
void RasterCacheResult::draw(DlCanvas& canvas,
38+
const DlPaint* paint,
39+
bool preserve_rtree) const {
3340
DlAutoCanvasRestore auto_restore(&canvas, true);
3441

3542
auto matrix = RasterCacheUtil::GetIntegralTransCTM(canvas.GetTransform());
@@ -39,8 +46,26 @@ void RasterCacheResult::draw(DlCanvas& canvas, const DlPaint* paint) const {
3946
std::abs(bounds.height() - image_->dimensions().height()) <= 1);
4047
canvas.TransformReset();
4148
flow_.Step();
42-
canvas.DrawImage(image_, {bounds.fLeft, bounds.fTop},
43-
DlImageSampling::kNearestNeighbor, paint);
49+
if (!preserve_rtree || !rtree_) {
50+
canvas.DrawImage(image_, {bounds.fLeft, bounds.fTop},
51+
DlImageSampling::kNearestNeighbor, paint);
52+
} else {
53+
// On some platforms RTree from overlay layers is used for unobstructed
54+
// platform views and hit testing. To preserve the RTree raster cache must
55+
// paint individual rects instead of the whole image.
56+
auto rects = rtree_->searchAndConsolidateRects(kGiantRect);
57+
58+
canvas.Translate(bounds.fLeft, bounds.fTop);
59+
60+
SkRect rtree_bounds =
61+
RasterCacheUtil::GetRoundedOutDeviceBounds(rtree_->bounds(), matrix);
62+
for (auto rect : rects) {
63+
rect = RasterCacheUtil::GetRoundedOutDeviceBounds(rect, matrix);
64+
rect.offset(-rtree_bounds.fLeft, -rtree_bounds.fTop);
65+
canvas.DrawImageRect(image_, rect, rect,
66+
DlImageSampling::kNearestNeighbor, paint);
67+
}
68+
}
4469
}
4570

4671
RasterCache::RasterCache(size_t access_threshold,
@@ -52,6 +77,7 @@ RasterCache::RasterCache(size_t access_threshold,
5277
/// @note Procedure doesn't copy all closures.
5378
std::unique_ptr<RasterCacheResult> RasterCache::Rasterize(
5479
const RasterCache::Context& context,
80+
sk_sp<const DlRTree> rtree,
5581
const std::function<void(DlCanvas*)>& draw_function,
5682
const std::function<void(DlCanvas*, const SkRect& rect)>& draw_checkerboard)
5783
const {
@@ -75,6 +101,7 @@ std::unique_ptr<RasterCacheResult> RasterCache::Rasterize(
75101

76102
DlSkCanvasAdapter canvas(surface->getCanvas());
77103
canvas.Clear(DlColor::kTransparent());
104+
78105
canvas.Translate(-dest_rect.left(), -dest_rect.top());
79106
canvas.Transform(matrix);
80107
draw_function(&canvas);
@@ -84,19 +111,21 @@ std::unique_ptr<RasterCacheResult> RasterCache::Rasterize(
84111
}
85112

86113
auto image = DlImage::Make(surface->makeImageSnapshot());
87-
return std::make_unique<RasterCacheResult>(image, context.logical_rect,
88-
context.flow_type);
114+
return std::make_unique<RasterCacheResult>(
115+
image, context.logical_rect, context.flow_type, std::move(rtree));
89116
}
90117

91118
bool RasterCache::UpdateCacheEntry(
92119
const RasterCacheKeyID& id,
93120
const Context& raster_cache_context,
94-
const std::function<void(DlCanvas*)>& render_function) const {
121+
const std::function<void(DlCanvas*)>& render_function,
122+
sk_sp<const DlRTree> rtree) const {
95123
RasterCacheKey key = RasterCacheKey(id, raster_cache_context.matrix);
96124
Entry& entry = cache_[key];
97125
if (!entry.image) {
98126
void (*func)(DlCanvas*, const SkRect& rect) = DrawCheckerboard;
99-
entry.image = Rasterize(raster_cache_context, render_function, func);
127+
entry.image = Rasterize(raster_cache_context, std::move(rtree),
128+
render_function, func);
100129
if (entry.image != nullptr) {
101130
switch (id.type()) {
102131
case RasterCacheKeyType::kDisplayList: {
@@ -146,7 +175,8 @@ bool RasterCache::HasEntry(const RasterCacheKeyID& id,
146175

147176
bool RasterCache::Draw(const RasterCacheKeyID& id,
148177
DlCanvas& canvas,
149-
const DlPaint* paint) const {
178+
const DlPaint* paint,
179+
bool preserve_rtree) const {
150180
auto it = cache_.find(RasterCacheKey(id, canvas.GetTransform()));
151181
if (it == cache_.end()) {
152182
return false;
@@ -155,7 +185,7 @@ bool RasterCache::Draw(const RasterCacheKeyID& id,
155185
Entry& entry = it->second;
156186

157187
if (entry.image) {
158-
entry.image->draw(canvas, paint);
188+
entry.image->draw(canvas, paint, preserve_rtree);
159189
return true;
160190
}
161191

flow/raster_cache.h

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,14 @@ class RasterCacheResult {
3030
public:
3131
RasterCacheResult(sk_sp<DlImage> image,
3232
const SkRect& logical_rect,
33-
const char* type);
33+
const char* type,
34+
sk_sp<const DlRTree> rtree = nullptr);
3435

3536
virtual ~RasterCacheResult() = default;
3637

37-
virtual void draw(DlCanvas& canvas, const DlPaint* paint) const;
38+
virtual void draw(DlCanvas& canvas,
39+
const DlPaint* paint,
40+
bool preserve_rtree) const;
3841

3942
virtual SkISize image_dimensions() const {
4043
return image_ ? image_->dimensions() : SkISize::Make(0, 0);
@@ -48,6 +51,7 @@ class RasterCacheResult {
4851
sk_sp<DlImage> image_;
4952
SkRect logical_rect_;
5053
fml::tracing::TraceFlow flow_;
54+
sk_sp<const DlRTree> rtree_;
5155
};
5256

5357
class Layer;
@@ -127,6 +131,7 @@ class RasterCache {
127131

128132
std::unique_ptr<RasterCacheResult> Rasterize(
129133
const RasterCache::Context& context,
134+
sk_sp<const DlRTree> rtree,
130135
const std::function<void(DlCanvas*)>& draw_function,
131136
const std::function<void(DlCanvas*, const SkRect& rect)>&
132137
draw_checkerboard) const;
@@ -143,9 +148,15 @@ class RasterCache {
143148
// if the item was disabled due to conditions discovered during |Preroll|
144149
// or if the attempt to populate the entry failed due to bounds overflow
145150
// conditions.
151+
// If |preserve_rtree| is true, the raster cache will preserve the original
152+
// RTree of cached content by blitting individual rectangles from the cached
153+
// image to the canvas according to the original layer R-Tree (if present).
154+
// This is to ensure that the target surface R-Tree will not be clobbered with
155+
// one large blit as it can affect platform view overlays and hit testing.
146156
bool Draw(const RasterCacheKeyID& id,
147157
DlCanvas& canvas,
148-
const DlPaint* paint) const;
158+
const DlPaint* paint,
159+
bool preserve_rtree = false) const;
149160

150161
bool HasEntry(const RasterCacheKeyID& id, const SkMatrix&) const;
151162

@@ -234,10 +245,10 @@ class RasterCache {
234245
*/
235246
int GetAccessCount(const RasterCacheKeyID& id, const SkMatrix& matrix) const;
236247

237-
bool UpdateCacheEntry(
238-
const RasterCacheKeyID& id,
239-
const Context& raster_cache_context,
240-
const std::function<void(DlCanvas*)>& render_function) const;
248+
bool UpdateCacheEntry(const RasterCacheKeyID& id,
249+
const Context& raster_cache_context,
250+
const std::function<void(DlCanvas*)>& render_function,
251+
sk_sp<const DlRTree> rtree = nullptr) const;
241252

242253
private:
243254
struct Entry {

flow/raster_cache_unittests.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,11 @@ TEST(RasterCache, SetCheckboardCacheImages) {
178178
};
179179

180180
cache.SetCheckboardCacheImages(false);
181-
cache.Rasterize(r_context, dummy_draw_function, draw_checkerboard);
181+
cache.Rasterize(r_context, nullptr, dummy_draw_function, draw_checkerboard);
182182
ASSERT_FALSE(did_draw_checkerboard);
183183

184184
cache.SetCheckboardCacheImages(true);
185-
cache.Rasterize(r_context, dummy_draw_function, draw_checkerboard);
185+
cache.Rasterize(r_context, nullptr, dummy_draw_function, draw_checkerboard);
186186
ASSERT_TRUE(did_draw_checkerboard);
187187
}
188188

flow/testing/mock_raster_cache.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ class MockRasterCacheResult : public RasterCacheResult {
2929
public:
3030
explicit MockRasterCacheResult(SkRect device_rect);
3131

32-
void draw(DlCanvas& canvas, const DlPaint* paint = nullptr) const override{};
32+
void draw(DlCanvas& canvas,
33+
const DlPaint* paint = nullptr,
34+
bool preserve_rtree = false) const override{};
3335

3436
SkISize image_dimensions() const override {
3537
return SkSize::Make(device_rect_.width(), device_rect_.height()).toCeil();

0 commit comments

Comments
 (0)