Skip to content

Commit 68689a5

Browse files
authored
Fix premature DisplayList caching (flutter#34562)
1 parent 2b5d5c3 commit 68689a5

File tree

6 files changed

+145
-48
lines changed

6 files changed

+145
-48
lines changed

flow/layers/display_list_layer_unittests.cc

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,9 +238,9 @@ TEST_F(DisplayListLayerTest, CachedIncompatibleDisplayListOpacityInheritance) {
238238
EXPECT_FALSE(context->subtree_can_inherit_opacity);
239239

240240
// Pump the DisplayListLayer until it is ready to cache its DL
241-
display_list_layer->Paint(paint_context());
242-
display_list_layer->Paint(paint_context());
243-
display_list_layer->Paint(paint_context());
241+
display_list_layer->Preroll(preroll_context(), SkMatrix());
242+
display_list_layer->Preroll(preroll_context(), SkMatrix());
243+
display_list_layer->Preroll(preroll_context(), SkMatrix());
244244

245245
int opacity_alpha = 0x7F;
246246
SkPoint opacity_offset = SkPoint::Make(10, 10);
@@ -398,5 +398,97 @@ TEST_F(DisplayListLayerTest, NoLayerTreeSnapshotsWhenDisabledByDefault) {
398398
EXPECT_EQ(0u, snapshot_store.Size());
399399
}
400400

401+
TEST_F(DisplayListLayerTest, DisplayListAccessCountDependsOnVisibility) {
402+
const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f);
403+
const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
404+
const SkRect missed_cull_rect = SkRect::MakeLTRB(100, 100, 200, 200);
405+
const SkRect hit_cull_rect = SkRect::MakeLTRB(0, 0, 200, 200);
406+
DisplayListBuilder builder;
407+
builder.drawRect(picture_bounds);
408+
auto display_list = builder.Build();
409+
auto layer = std::make_shared<DisplayListLayer>(
410+
layer_offset, SkiaGPUObject(display_list, unref_queue()), true, false);
411+
412+
auto raster_cache_item = layer->raster_cache_item();
413+
use_mock_raster_cache();
414+
415+
// First Preroll the DisplayListLayer a few times where it does not intersect
416+
// the cull rect. No caching progress should occur during this time, the
417+
// access_count should remain 0 because the DisplayList was never "visible".
418+
preroll_context()->cull_rect = missed_cull_rect;
419+
for (int i = 0; i < 10; i++) {
420+
preroll_context()->raster_cached_entries->clear();
421+
layer->Preroll(preroll_context(), SkMatrix::I());
422+
ASSERT_EQ(raster_cache_item->cache_state(), RasterCacheItem::kNone);
423+
ASSERT_TRUE(raster_cache_item->GetId().has_value());
424+
ASSERT_EQ(preroll_context()->raster_cache->GetAccessCount(
425+
raster_cache_item->GetId().value(), SkMatrix::I()),
426+
0);
427+
ASSERT_EQ(preroll_context()->raster_cached_entries->size(), size_t(1));
428+
ASSERT_EQ(preroll_context()->raster_cache->EstimatePictureCacheByteSize(),
429+
size_t(0));
430+
ASSERT_FALSE(raster_cache_item->TryToPrepareRasterCache(paint_context()));
431+
ASSERT_FALSE(raster_cache_item->Draw(paint_context(), nullptr));
432+
}
433+
434+
// Next Preroll the DisplayListLayer once where it does intersect
435+
// the cull rect. No caching progress should occur during this time
436+
// since this is the first frame in which it was visible, but the
437+
// count should start incrementing.
438+
preroll_context()->cull_rect = hit_cull_rect;
439+
preroll_context()->raster_cached_entries->clear();
440+
layer->Preroll(preroll_context(), SkMatrix());
441+
ASSERT_EQ(raster_cache_item->cache_state(), RasterCacheItem::kNone);
442+
ASSERT_TRUE(raster_cache_item->GetId().has_value());
443+
ASSERT_EQ(preroll_context()->raster_cache->GetAccessCount(
444+
raster_cache_item->GetId().value(), SkMatrix::I()),
445+
1);
446+
ASSERT_EQ(preroll_context()->raster_cached_entries->size(), size_t(1));
447+
ASSERT_EQ(preroll_context()->raster_cache->EstimatePictureCacheByteSize(),
448+
size_t(0));
449+
ASSERT_FALSE(raster_cache_item->TryToPrepareRasterCache(paint_context()));
450+
ASSERT_FALSE(raster_cache_item->Draw(paint_context(), nullptr));
451+
452+
// Now we can Preroll the DisplayListLayer again with a cull rect that
453+
// it does not intersect and it should continue to count these operations
454+
// even though it is not visible. No actual caching should occur yet,
455+
// even though we will surpass its threshold.
456+
preroll_context()->cull_rect = missed_cull_rect;
457+
for (int i = 0; i < 10; i++) {
458+
preroll_context()->raster_cached_entries->clear();
459+
layer->Preroll(preroll_context(), SkMatrix());
460+
ASSERT_EQ(raster_cache_item->cache_state(), RasterCacheItem::kNone);
461+
ASSERT_TRUE(raster_cache_item->GetId().has_value());
462+
ASSERT_EQ(preroll_context()->raster_cache->GetAccessCount(
463+
raster_cache_item->GetId().value(), SkMatrix::I()),
464+
i + 2);
465+
ASSERT_EQ(preroll_context()->raster_cached_entries->size(), size_t(1));
466+
ASSERT_EQ(preroll_context()->raster_cache->EstimatePictureCacheByteSize(),
467+
size_t(0));
468+
ASSERT_FALSE(raster_cache_item->TryToPrepareRasterCache(paint_context()));
469+
ASSERT_FALSE(raster_cache_item->Draw(paint_context(), nullptr));
470+
}
471+
472+
// Finally Preroll the DisplayListLayer again where it does intersect
473+
// the cull rect. Since we should have exhausted our access count
474+
// threshold in the loop above, these operations should result in the
475+
// DisplayList being cached.
476+
preroll_context()->cull_rect = hit_cull_rect;
477+
preroll_context()->raster_cached_entries->clear();
478+
layer->Preroll(preroll_context(), SkMatrix());
479+
ASSERT_EQ(raster_cache_item->cache_state(), RasterCacheItem::kCurrent);
480+
ASSERT_TRUE(raster_cache_item->GetId().has_value());
481+
ASSERT_EQ(preroll_context()->raster_cache->GetAccessCount(
482+
raster_cache_item->GetId().value(), SkMatrix::I()),
483+
12);
484+
ASSERT_EQ(preroll_context()->raster_cached_entries->size(), size_t(1));
485+
ASSERT_EQ(preroll_context()->raster_cache->EstimatePictureCacheByteSize(),
486+
size_t(0));
487+
ASSERT_TRUE(raster_cache_item->TryToPrepareRasterCache(paint_context()));
488+
ASSERT_GT(preroll_context()->raster_cache->EstimatePictureCacheByteSize(),
489+
size_t(0));
490+
ASSERT_TRUE(raster_cache_item->Draw(paint_context(), nullptr));
491+
}
492+
401493
} // namespace testing
402494
} // namespace flutter

flow/layers/display_list_raster_cache_item.cc

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -108,16 +108,13 @@ void DisplayListRasterCacheItem::PrerollFinalize(PrerollContext* context,
108108
// if the rect is intersect we will get the entry access_count to confirm if
109109
// it great than the threshold. Otherwise we only increase the entry
110110
// access_count.
111-
if (context->cull_rect.intersect(bounds)) {
112-
if (raster_cache->MarkSeen(key_id_, transformation_matrix_) <
113-
raster_cache->access_threshold()) {
114-
cache_state_ = CacheState::kNone;
115-
return;
116-
}
117-
context->subtree_can_inherit_opacity = true;
118-
cache_state_ = CacheState::kCurrent;
111+
bool visible = context->cull_rect.intersect(bounds);
112+
int accesses = raster_cache->MarkSeen(key_id_, matrix, visible);
113+
if (!visible || accesses <= raster_cache->access_threshold()) {
114+
cache_state_ = kNone;
119115
} else {
120-
raster_cache->Touch(key_id_, matrix);
116+
context->subtree_can_inherit_opacity = true;
117+
cache_state_ = kCurrent;
121118
}
122119
return;
123120
}
@@ -136,9 +133,6 @@ bool DisplayListRasterCacheItem::Draw(const PaintContext& context,
136133
if (cache_state_ == CacheState::kCurrent) {
137134
return context.raster_cache->Draw(key_id_, *canvas, paint);
138135
}
139-
// This display_list doesn't cache itself, this only increase the entry
140-
// access_count;
141-
context.raster_cache->Touch(key_id_, canvas->getTotalMatrix());
142136
return false;
143137
}
144138

flow/layers/layer_raster_cache_item.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ void LayerRasterCacheItem::PrerollFinalize(PrerollContext* context,
5353
if (num_cache_attempts_ >= layer_cached_threshold_) {
5454
// the layer can be cached
5555
cache_state_ = CacheState::kCurrent;
56-
context->raster_cache->MarkSeen(key_id_, matrix_);
56+
context->raster_cache->MarkSeen(key_id_, matrix_, true);
5757
} else {
5858
num_cache_attempts_++;
5959
// access current layer
@@ -67,7 +67,8 @@ void LayerRasterCacheItem::PrerollFinalize(PrerollContext* context,
6767
RasterCacheKeyType::kLayerChildren);
6868
}
6969
cache_state_ = CacheState::kChildren;
70-
context->raster_cache->MarkSeen(layer_children_id_.value(), matrix_);
70+
context->raster_cache->MarkSeen(layer_children_id_.value(), matrix_,
71+
true);
7172
}
7273
}
7374
}

flow/raster_cache.cc

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ bool RasterCache::UpdateCacheEntry(
9292
const std::function<void(SkCanvas*)>& render_function) const {
9393
RasterCacheKey key = RasterCacheKey(id, raster_cache_context.matrix);
9494
Entry& entry = cache_[key];
95-
entry.used_this_frame = true;
9695
if (!entry.image) {
9796
entry.image = Rasterize(raster_cache_context, render_function);
9897
if (entry.image != nullptr) {
@@ -110,24 +109,27 @@ bool RasterCache::UpdateCacheEntry(
110109
return entry.image != nullptr;
111110
}
112111

113-
bool RasterCache::Touch(const RasterCacheKeyID& id,
114-
const SkMatrix& matrix) const {
115-
RasterCacheKey cache_key = RasterCacheKey(id, matrix);
116-
auto it = cache_.find(cache_key);
117-
if (it != cache_.end()) {
118-
it->second.access_count++;
119-
it->second.used_this_frame = true;
120-
return true;
112+
int RasterCache::MarkSeen(const RasterCacheKeyID& id,
113+
const SkMatrix& matrix,
114+
bool visible) const {
115+
RasterCacheKey key = RasterCacheKey(id, matrix);
116+
Entry& entry = cache_[key];
117+
entry.encountered_this_frame = true;
118+
entry.visible_this_frame = visible;
119+
if (visible || entry.accesses_since_visible > 0) {
120+
entry.accesses_since_visible++;
121121
}
122-
return false;
122+
return entry.accesses_since_visible;
123123
}
124124

125-
int RasterCache::MarkSeen(const RasterCacheKeyID& id,
126-
const SkMatrix& matrix) const {
125+
int RasterCache::GetAccessCount(const RasterCacheKeyID& id,
126+
const SkMatrix& matrix) const {
127127
RasterCacheKey key = RasterCacheKey(id, matrix);
128-
Entry& entry = cache_[key];
129-
entry.used_this_frame = true;
130-
return entry.access_count;
128+
auto entry = cache_.find(key);
129+
if (entry != cache_.cend()) {
130+
return entry->second.accesses_since_visible;
131+
}
132+
return -1;
131133
}
132134

133135
bool RasterCache::HasEntry(const RasterCacheKeyID& id,
@@ -148,8 +150,6 @@ bool RasterCache::Draw(const RasterCacheKeyID& id,
148150
}
149151

150152
Entry& entry = it->second;
151-
entry.access_count++;
152-
entry.used_this_frame = true;
153153

154154
if (entry.image) {
155155
entry.image->draw(canvas, paint);
@@ -171,7 +171,7 @@ void RasterCache::SweepOneCacheAfterFrame(RasterCacheKey::Map<Entry>& cache,
171171
for (auto it = cache.begin(); it != cache.end(); ++it) {
172172
Entry& entry = it->second;
173173

174-
if (!entry.used_this_frame) {
174+
if (!entry.encountered_this_frame) {
175175
dead.push_back(it);
176176
} else if (entry.image) {
177177
RasterCacheKeyKind kind = it->first.kind();
@@ -186,7 +186,7 @@ void RasterCache::SweepOneCacheAfterFrame(RasterCacheKey::Map<Entry>& cache,
186186
break;
187187
}
188188
}
189-
entry.used_this_frame = false;
189+
entry.encountered_this_frame = false;
190190
}
191191

192192
for (auto it : dead) {

flow/raster_cache.h

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,6 @@ class RasterCache {
123123
SkCanvas& canvas,
124124
const SkPaint* paint) const;
125125

126-
bool Touch(const RasterCacheKeyID& id, const SkMatrix& matrix) const;
127-
128126
bool HasEntry(const RasterCacheKeyID& id, const SkMatrix&) const;
129127

130128
void PrepareNewFrame();
@@ -191,13 +189,24 @@ class RasterCache {
191189
}
192190

193191
/**
194-
* @brief The entry which RasterCacheId is generated by RasterCacheKeyID and
195-
* matrix, that will be used by the current frame. If current frame doesn't
196-
* have this entry we will create a new entry.
197-
* @return the number of times the entry has been hit since it was created. If
198-
* a new entry that will be 1.
192+
* @brief The entry whose RasterCacheKey is generated by RasterCacheKeyID
193+
* and matrix is marked as encountered by the current frame. The entry
194+
* will be created if it does not exist. Optionally the entry will be marked
195+
* as visible in the current frame if the caller determines that it
196+
* intersects the cull rect. The access_count of the entry will be
197+
* increased if it is visible, or if it was ever visible.
198+
* @return the number of times the entry has been hit since it was created.
199+
* For a new entry that will be 1 if it is visible, or zero if non-visible.
200+
*/
201+
int MarkSeen(const RasterCacheKeyID& id,
202+
const SkMatrix& matrix,
203+
bool visible) const;
204+
205+
/**
206+
* Returns the access count (i.e. accesses_since_visible) for the given
207+
* entry in the cache, or -1 if no such entry exists.
199208
*/
200-
int MarkSeen(const RasterCacheKeyID& id, const SkMatrix& matrix) const;
209+
int GetAccessCount(const RasterCacheKeyID& id, const SkMatrix& matrix) const;
201210

202211
bool UpdateCacheEntry(
203212
const RasterCacheKeyID& id,
@@ -206,8 +215,9 @@ class RasterCache {
206215

207216
private:
208217
struct Entry {
209-
bool used_this_frame = false;
210-
size_t access_count = 0;
218+
bool encountered_this_frame = false;
219+
bool visible_this_frame = false;
220+
size_t accesses_since_visible = 0;
211221
std::unique_ptr<RasterCacheResult> image;
212222
};
213223

flow/testing/mock_raster_cache.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,10 @@ void MockRasterCache::AddMockPicture(int width, int height) {
6161
DisplayListRasterCacheItem display_list_item(display_list.get(), SkPoint(),
6262
true, false);
6363
for (int i = 0; i < access_threshold(); i++) {
64-
MarkSeen(display_list_item.GetId().value(), ctm);
64+
MarkSeen(display_list_item.GetId().value(), ctm, true);
6565
Draw(display_list_item.GetId().value(), mock_canvas_, nullptr);
6666
}
67-
MarkSeen(display_list_item.GetId().value(), ctm);
67+
MarkSeen(display_list_item.GetId().value(), ctm, true);
6868
RasterCache::Context r_context = {
6969
// clang-format off
7070
.gr_context = preroll_context_.gr_context,

0 commit comments

Comments
 (0)