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

Commit cadaf84

Browse files
committed
RasterCache adds a new mechanism for particularly complex pictures or display lists
1 parent 1875f0a commit cadaf84

File tree

3 files changed

+182
-15
lines changed

3 files changed

+182
-15
lines changed

flow/raster_cache.cc

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ static bool CanRasterizeRect(const SkRect& cull_rect) {
6262

6363
static bool IsPictureWorthRasterizing(SkPicture* picture,
6464
bool will_change,
65-
bool is_complex) {
65+
bool is_complex,
66+
bool is_high_priority) {
6667
if (will_change) {
6768
// If the picture is going to change in the future, there is no point in
6869
// doing to extra work to rasterize.
@@ -75,7 +76,7 @@ static bool IsPictureWorthRasterizing(SkPicture* picture,
7576
return false;
7677
}
7778

78-
if (is_complex) {
79+
if (is_complex || is_high_priority) {
7980
// The caller seems to have extra information about the picture and thinks
8081
// the picture is always worth rasterizing.
8182
return true;
@@ -90,6 +91,7 @@ static bool IsDisplayListWorthRasterizing(
9091
DisplayList* display_list,
9192
bool will_change,
9293
bool is_complex,
94+
bool is_high_priority,
9395
DisplayListComplexityCalculator* complexity_calculator) {
9496
if (will_change) {
9597
// If the display list is going to change in the future, there is no point
@@ -103,7 +105,7 @@ static bool IsDisplayListWorthRasterizing(
103105
return false;
104106
}
105107

106-
if (is_complex) {
108+
if (is_complex || is_high_priority) {
107109
// The caller seems to have extra information about the display list and
108110
// thinks the display list is always worth rasterizing.
109111
return true;
@@ -180,6 +182,7 @@ void RasterCache::Prepare(PrerollContext* context,
180182
Entry& entry = cache_[cache_key];
181183
entry.access_count++;
182184
entry.used_this_frame = true;
185+
entry.unused_count = 0;
183186
if (!entry.image) {
184187
entry.image = RasterizeLayer(context, layer, ctm, checkerboard_images_);
185188
}
@@ -222,12 +225,14 @@ bool RasterCache::Prepare(PrerollContext* context,
222225
bool is_complex,
223226
bool will_change,
224227
const SkMatrix& untranslated_matrix,
225-
const SkPoint& offset) {
228+
const SkPoint& offset,
229+
bool is_high_priority) {
226230
if (!GenerateNewCacheInThisFrame()) {
227231
return false;
228232
}
229233

230-
if (!IsPictureWorthRasterizing(picture, will_change, is_complex)) {
234+
if (!IsPictureWorthRasterizing(picture, will_change, is_complex,
235+
is_high_priority)) {
231236
// We only deal with pictures that are worthy of rasterization.
232237
return false;
233238
}
@@ -245,7 +250,8 @@ bool RasterCache::Prepare(PrerollContext* context,
245250

246251
// Creates an entry, if not present prior.
247252
Entry& entry = cache_[cache_key];
248-
if (entry.access_count < access_threshold_) {
253+
entry.is_high_priority = is_high_priority;
254+
if (!is_high_priority && entry.access_count < access_threshold_) {
249255
// Frame threshold has not yet been reached.
250256
return false;
251257
}
@@ -270,7 +276,8 @@ bool RasterCache::Prepare(PrerollContext* context,
270276
bool is_complex,
271277
bool will_change,
272278
const SkMatrix& untranslated_matrix,
273-
const SkPoint& offset) {
279+
const SkPoint& offset,
280+
bool is_high_priority) {
274281
if (!GenerateNewCacheInThisFrame()) {
275282
return false;
276283
}
@@ -281,7 +288,7 @@ bool RasterCache::Prepare(PrerollContext* context,
281288
: DisplayListComplexityCalculator::GetForSoftware();
282289

283290
if (!IsDisplayListWorthRasterizing(display_list, will_change, is_complex,
284-
complexity_calculator)) {
291+
is_high_priority, complexity_calculator)) {
285292
// We only deal with display lists that are worthy of rasterization.
286293
return false;
287294
}
@@ -300,7 +307,8 @@ bool RasterCache::Prepare(PrerollContext* context,
300307

301308
// Creates an entry, if not present prior.
302309
Entry& entry = cache_[cache_key];
303-
if (entry.access_count < access_threshold_) {
310+
entry.is_high_priority = is_high_priority;
311+
if (!is_high_priority && entry.access_count < access_threshold_) {
304312
// Frame threshold has not yet been reached.
305313
return false;
306314
}
@@ -345,6 +353,7 @@ void RasterCache::Touch(const RasterCacheKey& cache_key) {
345353
if (it != cache_.end()) {
346354
it->second.used_this_frame = true;
347355
it->second.access_count++;
356+
it->second.unused_count = 0;
348357
}
349358
}
350359

@@ -384,6 +393,7 @@ bool RasterCache::Draw(const RasterCacheKey& cache_key,
384393
Entry& entry = it->second;
385394
entry.access_count++;
386395
entry.used_this_frame = true;
396+
entry.unused_count = 0;
387397

388398
if (entry.image) {
389399
entry.image->draw(canvas, paint);
@@ -405,9 +415,25 @@ void RasterCache::SweepOneCacheAfterFrame(RasterCacheKey::Map<Entry>& cache,
405415

406416
for (auto it = cache.begin(); it != cache.end(); ++it) {
407417
Entry& entry = it->second;
408-
409418
if (!entry.used_this_frame) {
410-
dead.push_back(it);
419+
entry.unused_count++;
420+
if (entry.unused_count < entry.unused_threshold()) {
421+
if (entry.image) {
422+
RasterCacheKeyKind kind = it->first.kind();
423+
switch (kind) {
424+
case RasterCacheKeyKind::kPictureMetrics:
425+
picture_metrics.unused_count++;
426+
picture_metrics.unused_bytes += entry.image->image_bytes();
427+
break;
428+
case RasterCacheKeyKind::kLayerMetrics:
429+
layer_metrics.unused_count++;
430+
layer_metrics.unused_bytes += entry.image->image_bytes();
431+
break;
432+
}
433+
}
434+
} else {
435+
dead.push_back(it);
436+
}
411437
} else if (entry.image) {
412438
RasterCacheKeyKind kind = it->first.kind();
413439
switch (kind) {

flow/raster_cache.h

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,32 @@ struct RasterCacheMetrics {
6767
*/
6868
size_t in_use_bytes = 0;
6969

70+
/**
71+
* The number of cache entries with images unused but keeped in this frame.
72+
*/
73+
size_t unused_count = 0;
74+
75+
/**
76+
* The size of all of the images unused but keeped in this frame.
77+
*/
78+
size_t unused_bytes = 0;
7079
/**
7180
* The total cache entries that had images during this frame whether
7281
* they were used in the frame or held memory during the frame and then
7382
* were evicted after it ended.
7483
*/
75-
size_t total_count() const { return in_use_count + eviction_count; }
84+
size_t total_count() const {
85+
return in_use_count + unused_count + eviction_count;
86+
}
7687

7788
/**
7889
* The size of all of the cached images during this frame whether
7990
* they were used in the frame or held memory during the frame and then
8091
* were evicted after it ended.
8192
*/
82-
size_t total_bytes() const { return in_use_bytes + eviction_bytes; }
93+
size_t total_bytes() const {
94+
return in_use_bytes + unused_bytes + eviction_bytes;
95+
}
8396
};
8497

8598
class RasterCache {
@@ -186,13 +199,15 @@ class RasterCache {
186199
bool is_complex,
187200
bool will_change,
188201
const SkMatrix& untranslated_matrix,
189-
const SkPoint& offset = SkPoint());
202+
const SkPoint& offset = SkPoint(),
203+
bool is_high_priority = false);
190204
bool Prepare(PrerollContext* context,
191205
DisplayList* display_list,
192206
bool is_complex,
193207
bool will_change,
194208
const SkMatrix& untranslated_matrix,
195-
const SkPoint& offset = SkPoint());
209+
const SkPoint& offset = SkPoint(),
210+
bool is_high_priority = false);
196211

197212
// If there is cache entry for this picture, display list or layer, mark it as
198213
// used for this frame in order to not get evicted. This is needed during
@@ -286,9 +301,14 @@ class RasterCache {
286301

287302
private:
288303
struct Entry {
304+
// If the entry is high priority, it will always cache on first usage and
305+
// survive 3 frames without usage.
306+
bool is_high_priority = false;
289307
bool used_this_frame = false;
290308
size_t access_count = 0;
309+
size_t unused_count = 0;
291310
std::unique_ptr<RasterCacheResult> image;
311+
size_t unused_threshold() const { return is_high_priority ? 3 : 1; }
292312
};
293313

294314
void Touch(const RasterCacheKey& cache_key);

flow/raster_cache_unittests.cc

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,26 @@ TEST(RasterCache, ThresholdIsRespectedForSkPicture) {
5858
ASSERT_TRUE(cache.Draw(*picture, dummy_canvas));
5959
}
6060

61+
TEST(RasterCache, HighPriorityIsRespectedForSkPicture) {
62+
flutter::RasterCache cache;
63+
64+
SkMatrix matrix = SkMatrix::I();
65+
66+
auto picture = GetSamplePicture();
67+
68+
SkCanvas dummy_canvas;
69+
70+
PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder();
71+
72+
cache.PrepareNewFrame();
73+
74+
// Prepare should cache it when 1st access.
75+
ASSERT_TRUE(cache.Prepare(&preroll_context_holder.preroll_context,
76+
picture.get(), true, false, matrix, SkPoint(),
77+
/**is_high_priority=*/true));
78+
ASSERT_TRUE(cache.Draw(*picture, dummy_canvas));
79+
}
80+
6181
TEST(RasterCache, MetricsOmitUnpopulatedEntries) {
6282
size_t threshold = 2;
6383
flutter::RasterCache cache(threshold);
@@ -141,6 +161,26 @@ TEST(RasterCache, ThresholdIsRespectedForDisplayList) {
141161
ASSERT_TRUE(cache.Draw(*display_list, dummy_canvas));
142162
}
143163

164+
TEST(RasterCache, HighPriorityIsRespectedForDisplayList) {
165+
flutter::RasterCache cache;
166+
167+
SkMatrix matrix = SkMatrix::I();
168+
169+
auto display_list = GetSampleDisplayList();
170+
171+
SkCanvas dummy_canvas;
172+
173+
PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder();
174+
175+
cache.PrepareNewFrame();
176+
177+
// Prepare should cache it when 1st access.
178+
ASSERT_TRUE(cache.Prepare(&preroll_context_holder.preroll_context,
179+
display_list.get(), true, false, matrix, SkPoint(),
180+
/**is_high_priority=*/true));
181+
ASSERT_TRUE(cache.Draw(*display_list, dummy_canvas));
182+
}
183+
144184
TEST(RasterCache, AccessThresholdOfZeroDisablesCachingForSkPicture) {
145185
size_t threshold = 0;
146186
flutter::RasterCache cache(threshold);
@@ -291,6 +331,87 @@ TEST(RasterCache, SweepsRemoveUnusedDisplayLists) {
291331
ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas));
292332
}
293333

334+
void PrepareAndCleanupEmptyFrame(flutter::RasterCache& cache, size_t times) {
335+
for (size_t i = 0; i < times; i++) {
336+
cache.PrepareNewFrame();
337+
cache.CleanupAfterFrame(); // Extra frame without a Get image access.
338+
}
339+
}
340+
341+
TEST(RasterCache, KeepUnusedSkPicturesIfIsHighPriority) {
342+
size_t threshold = 1;
343+
flutter::RasterCache cache(threshold);
344+
345+
SkMatrix matrix = SkMatrix::I();
346+
347+
auto picture = GetSamplePicture();
348+
349+
SkCanvas dummy_canvas;
350+
351+
PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder();
352+
353+
cache.PrepareNewFrame();
354+
355+
ASSERT_TRUE(cache.Prepare(&preroll_context_holder.preroll_context,
356+
picture.get(), true, false, matrix, SkPoint(),
357+
/**is_high_priority=*/true));
358+
ASSERT_TRUE(cache.Draw(*picture, dummy_canvas));
359+
360+
cache.CleanupAfterFrame();
361+
362+
PrepareAndCleanupEmptyFrame(cache, 1);
363+
cache.PrepareNewFrame();
364+
ASSERT_TRUE(cache.Draw(*picture, dummy_canvas));
365+
cache.CleanupAfterFrame();
366+
367+
PrepareAndCleanupEmptyFrame(cache, 2);
368+
cache.PrepareNewFrame();
369+
ASSERT_TRUE(cache.Draw(*picture, dummy_canvas));
370+
cache.CleanupAfterFrame();
371+
372+
PrepareAndCleanupEmptyFrame(cache, 3);
373+
cache.PrepareNewFrame();
374+
ASSERT_FALSE(cache.Draw(*picture, dummy_canvas));
375+
cache.CleanupAfterFrame();
376+
}
377+
378+
TEST(RasterCache, KeepUnusedDisplayListsIfIsHighPriority) {
379+
size_t threshold = 1;
380+
flutter::RasterCache cache(threshold);
381+
382+
SkMatrix matrix = SkMatrix::I();
383+
384+
auto display_list = GetSampleDisplayList();
385+
386+
SkCanvas dummy_canvas;
387+
388+
PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder();
389+
390+
cache.PrepareNewFrame();
391+
392+
ASSERT_TRUE(cache.Prepare(&preroll_context_holder.preroll_context,
393+
display_list.get(), true, false, matrix, SkPoint(),
394+
/**is_high_priority=*/true));
395+
ASSERT_TRUE(cache.Draw(*display_list, dummy_canvas));
396+
397+
cache.CleanupAfterFrame();
398+
399+
PrepareAndCleanupEmptyFrame(cache, 1);
400+
cache.PrepareNewFrame();
401+
ASSERT_TRUE(cache.Draw(*display_list, dummy_canvas));
402+
cache.CleanupAfterFrame();
403+
404+
PrepareAndCleanupEmptyFrame(cache, 2);
405+
cache.PrepareNewFrame();
406+
ASSERT_TRUE(cache.Draw(*display_list, dummy_canvas));
407+
cache.CleanupAfterFrame();
408+
409+
PrepareAndCleanupEmptyFrame(cache, 3);
410+
cache.PrepareNewFrame();
411+
ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas));
412+
cache.CleanupAfterFrame();
413+
}
414+
294415
// Construct a cache result whose device target rectangle rounds out to be one
295416
// pixel wider than the cached image. Verify that it can be drawn without
296417
// triggering any assertions.

0 commit comments

Comments
 (0)