Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[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 @@ -186,6 +186,7 @@ void RasterCache::Prepare(PrerollContext* context,
Entry& entry = cache_[cache_key_optional.value()];
entry.access_count++;
entry.used_this_frame = true;
entry.unused_count = 0;
if (!entry.image) {
entry.image =
RasterizeLayer(context, layer, strategy, ctm, checkerboard_images_);
Expand Down Expand Up @@ -263,7 +264,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 @@ -286,7 +288,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 @@ -311,7 +314,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 @@ -341,7 +345,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 @@ -392,6 +397,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 @@ -435,6 +441,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 @@ -449,47 +456,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 @@ -498,7 +495,7 @@ void RasterCache::CleanupAfterFrame() {
layer_metrics_ = {};
{
TRACE_EVENT0("flutter", "RasterCache::SweepCaches");
SweepOneCacheAfterFrame(cache_, picture_metrics_, layer_metrics_);
SweepCacheAfterFrame();
}
TraceStatsToTimeline();
}
Expand All @@ -509,6 +506,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 @@ -69,19 +69,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 @@ -91,6 +104,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 @@ -189,13 +205,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 @@ -298,9 +316,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 @@ -309,9 +336,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