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

[WIP]RasterCache adds a new mechanism for particularly complex pictures or display lists #31925

74 changes: 40 additions & 34 deletions flow/raster_cache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ void RasterCache::Prepare(PrerollContext* context,
Entry& entry = cache_[cache_key];
entry.access_count++;
entry.used_this_frame = true;
entry.unused_count = 0;
if (!entry.image) {
entry.image = RasterizeLayer(context, layer, ctm, checkerboard_images_);
}
Expand Down Expand Up @@ -222,7 +223,8 @@ bool RasterCache::Prepare(PrerollContext* context,
bool is_complex,
bool will_change,
const SkMatrix& untranslated_matrix,
const SkPoint& offset) {
const SkPoint& offset,
bool is_high_priority) {
if (!GenerateNewCacheInThisFrame()) {
return false;
}
Expand All @@ -245,7 +247,8 @@ bool RasterCache::Prepare(PrerollContext* context,

// Creates an entry, if not present prior.
Entry& entry = cache_[cache_key];
if (entry.access_count < access_threshold_) {
entry.is_high_priority = is_high_priority;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have entry.is_high_priority be a one-way latch, or a most-recently-specified flag? This might depend on what the recent request for empirical evidence turns up.

if (!is_high_priority && entry.access_count < access_threshold_) {
// Frame threshold has not yet been reached.
return false;
}
Expand All @@ -270,7 +273,8 @@ bool RasterCache::Prepare(PrerollContext* context,
bool is_complex,
bool will_change,
const SkMatrix& untranslated_matrix,
const SkPoint& offset) {
const SkPoint& offset,
bool is_high_priority) {
if (!GenerateNewCacheInThisFrame()) {
return false;
}
Expand Down Expand Up @@ -300,7 +304,8 @@ bool RasterCache::Prepare(PrerollContext* context,

// Creates an entry, if not present prior.
Entry& entry = cache_[cache_key];
if (entry.access_count < access_threshold_) {
entry.is_high_priority = is_high_priority;
if (!is_high_priority && entry.access_count < access_threshold_) {
// Frame threshold has not yet been reached.
return false;
}
Expand Down Expand Up @@ -345,6 +350,7 @@ void RasterCache::Touch(const RasterCacheKey& cache_key) {
if (it != cache_.end()) {
it->second.used_this_frame = true;
it->second.access_count++;
it->second.unused_count = 0;
}
}

Expand Down Expand Up @@ -384,6 +390,7 @@ bool RasterCache::Draw(const RasterCacheKey& cache_key,
Entry& entry = it->second;
entry.access_count++;
entry.used_this_frame = true;
entry.unused_count = 0;

if (entry.image) {
entry.image->draw(canvas, paint);
Expand All @@ -398,47 +405,37 @@ void RasterCache::PrepareNewFrame() {
display_list_cached_this_frame_ = 0;
}

void RasterCache::SweepOneCacheAfterFrame(RasterCacheKey::Map<Entry>& cache,
RasterCacheMetrics& picture_metrics,
RasterCacheMetrics& layer_metrics) {
void RasterCache::SweepCacheAfterFrame() {
std::vector<RasterCacheKey::Map<Entry>::iterator> dead;

for (auto it = cache.begin(); it != cache.end(); ++it) {
for (auto it = cache_.begin(); it != cache_.end(); ++it) {
Entry& entry = it->second;

if (!entry.used_this_frame) {
dead.push_back(it);
} else if (entry.image) {
RasterCacheKeyKind kind = it->first.kind();
switch (kind) {
case RasterCacheKeyKind::kPictureMetrics:
picture_metrics.in_use_count++;
picture_metrics.in_use_bytes += entry.image->image_bytes();
break;
case RasterCacheKeyKind::kLayerMetrics:
layer_metrics.in_use_count++;
layer_metrics.in_use_bytes += entry.image->image_bytes();
break;
if (entry.unused_count < entry.unused_threshold()) {
entry.unused_count++;
if (entry.image) {
RasterCacheMetrics& metrics = GetMetricsForKind(it->first.kind());
metrics.unused_count++;
metrics.unused_bytes += entry.image->image_bytes();
}
} else {
dead.push_back(it);
}
} else if (entry.image) {
RasterCacheMetrics& metrics = GetMetricsForKind(it->first.kind());
metrics.in_use_count++;
metrics.in_use_bytes += entry.image->image_bytes();
}
entry.used_this_frame = false;
}

for (auto it : dead) {
if (it->second.image) {
RasterCacheKeyKind kind = it->first.kind();
switch (kind) {
case RasterCacheKeyKind::kPictureMetrics:
picture_metrics.eviction_count++;
picture_metrics.eviction_bytes += it->second.image->image_bytes();
break;
case RasterCacheKeyKind::kLayerMetrics:
layer_metrics.eviction_count++;
layer_metrics.eviction_bytes += it->second.image->image_bytes();
break;
}
RasterCacheMetrics& metrics = GetMetricsForKind(it->first.kind());
metrics.eviction_count++;
metrics.eviction_bytes += it->second.image->image_bytes();
}
cache.erase(it);
cache_.erase(it);
}
}

Expand All @@ -447,7 +444,7 @@ void RasterCache::CleanupAfterFrame() {
layer_metrics_ = {};
{
TRACE_EVENT0("flutter", "RasterCache::SweepCaches");
SweepOneCacheAfterFrame(cache_, picture_metrics_, layer_metrics_);
SweepCacheAfterFrame();
}
TraceStatsToTimeline();
}
Expand All @@ -458,6 +455,15 @@ void RasterCache::Clear() {
layer_metrics_ = {};
}

RasterCacheMetrics& RasterCache::GetMetricsForKind(RasterCacheKeyKind kind) {
switch (kind) {
case RasterCacheKeyKind::kPictureMetrics:
return picture_metrics_;
case RasterCacheKeyKind::kLayerMetrics:
return layer_metrics_;
}
}

size_t RasterCache::GetCachedEntriesCount() const {
return cache_.size();
}
Expand Down
41 changes: 34 additions & 7 deletions flow/raster_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,32 @@ struct RasterCacheMetrics {
*/
size_t in_use_bytes = 0;

/**
* The number of cache entries with images unused but kept in this frame.
*/
size_t unused_count = 0;

/**
* The size of all of the images unused but kept in this frame.
*/
size_t unused_bytes = 0;
/**
* The total cache entries that had images during this frame whether
* they were used in the frame or held memory during the frame and then
* were evicted after it ended.
*/
size_t total_count() const { return in_use_count + eviction_count; }
size_t total_count() const {
return in_use_count + unused_count + eviction_count;
}

/**
* The size of all of the cached images during this frame whether
* they were used in the frame or held memory during the frame and then
* were evicted after it ended.
*/
size_t total_bytes() const { return in_use_bytes + eviction_bytes; }
size_t total_bytes() const {
return in_use_bytes + unused_bytes + eviction_bytes;
}
};

class RasterCache {
Expand All @@ -89,6 +102,9 @@ class RasterCache {
// on that frame. This limit allows us to throttle the cache and distribute
// the work across multiple frames.
static constexpr int kDefaultPictureAndDispLayListCacheLimitPerFrame = 3;
// The default number of frames the high-priority entry which is high priority
// survives if it is not used.
static constexpr int kHighPriorityEvictionThreshold = 3;

explicit RasterCache(size_t access_threshold = 3,
size_t picture_and_display_list_cache_limit_per_frame =
Expand Down Expand Up @@ -186,13 +202,15 @@ class RasterCache {
bool is_complex,
bool will_change,
const SkMatrix& untranslated_matrix,
const SkPoint& offset = SkPoint());
const SkPoint& offset = SkPoint(),
bool is_high_priority = false);
bool Prepare(PrerollContext* context,
DisplayList* display_list,
bool is_complex,
bool will_change,
const SkMatrix& untranslated_matrix,
const SkPoint& offset = SkPoint());
const SkPoint& offset = SkPoint(),
bool is_high_priority = false);

// If there is cache entry for this picture, display list or layer, mark it as
// used for this frame in order to not get evicted. This is needed during
Expand Down Expand Up @@ -286,9 +304,18 @@ class RasterCache {

private:
struct Entry {
// If the entry is high priority, it will always cache on first usage and
// survive 3 frames without usage.
bool is_high_priority = false;
bool used_this_frame = false;
size_t access_count = 0;
size_t unused_count = 0;
std::unique_ptr<RasterCacheResult> image;
// Return the number of frames the entry survives if it is not used. If the
// number is 0, then it will be evicted when not in use.
size_t unused_threshold() const {
return is_high_priority ? kHighPriorityEvictionThreshold : 0;
}
};

void Touch(const RasterCacheKey& cache_key);
Expand All @@ -297,9 +324,9 @@ class RasterCache {
SkCanvas& canvas,
const SkPaint* paint) const;

void SweepOneCacheAfterFrame(RasterCacheKey::Map<Entry>& cache,
RasterCacheMetrics& picture_metrics,
RasterCacheMetrics& layer_metrics);
void SweepCacheAfterFrame();

RasterCacheMetrics& GetMetricsForKind(RasterCacheKeyKind kind);

bool GenerateNewCacheInThisFrame() const {
// Disabling caching when access_threshold is zero is historic behavior.
Expand Down
Loading