LibGfx: Make Skia image cache eviction pressure-based
DecodedImageFrameSkiaImageCache used to treat each display-list flush as a cache generation. That worked poorly for the compositor, where all contexts share one DisplayListPlayerSkia and therefore one decoded-frame cache. A busy context could advance the generation counter enough to evict images from another context even when the shared cache was still comfortably under its entry and byte limits. Track recency from actual image-cache hits and inserts instead, and evict the least-recently used entries only when the existing global entry or byte caps are exceeded. This keeps cross-context reuse while making eviction respond to memory pressure rather than unrelated presentation churn. Prune the decoded-image cache before handing the surface to the shared SkiaBackendContext flush cleanup, so any SkImage references released by the cache are visible to Skia's resource cleanup in the same flush path.
This commit is contained in:
parent
1eb541ce9e
commit
d341ed5448
2 changed files with 20 additions and 22 deletions
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
namespace Gfx {
|
||||
|
||||
static constexpr u64 image_cache_max_unused_generations = 120;
|
||||
static constexpr size_t image_cache_max_entries = 128;
|
||||
static constexpr size_t image_cache_max_bytes = 64 * MiB;
|
||||
|
||||
|
|
@ -56,35 +55,41 @@ struct DecodedImageFrameSkiaImageCache::Impl {
|
|||
|
||||
struct CachedImage {
|
||||
sk_sp<SkImage> image;
|
||||
u64 last_used_generation { 0 };
|
||||
u64 last_used_sequence_number { 0 };
|
||||
size_t approximate_byte_size { 0 };
|
||||
};
|
||||
|
||||
u64 next_use_sequence_number()
|
||||
{
|
||||
return ++use_sequence_number;
|
||||
}
|
||||
|
||||
void prune_to_limits()
|
||||
{
|
||||
while (images.size() > image_cache_max_entries || approximate_byte_size > image_cache_max_bytes) {
|
||||
Optional<DecodedImageFrame> oldest_frame;
|
||||
Optional<u64> oldest_generation;
|
||||
Optional<DecodedImageFrame> least_recently_used_frame;
|
||||
Optional<u64> least_recently_used_sequence_number;
|
||||
for (auto const& image : images) {
|
||||
if (!oldest_generation.has_value() || image.value.last_used_generation < oldest_generation.value()) {
|
||||
oldest_frame = image.key;
|
||||
oldest_generation = image.value.last_used_generation;
|
||||
if (!least_recently_used_sequence_number.has_value()
|
||||
|| image.value.last_used_sequence_number < least_recently_used_sequence_number.value()) {
|
||||
least_recently_used_frame = image.key;
|
||||
least_recently_used_sequence_number = image.value.last_used_sequence_number;
|
||||
}
|
||||
}
|
||||
|
||||
if (!oldest_frame.has_value())
|
||||
if (!least_recently_used_frame.has_value())
|
||||
break;
|
||||
|
||||
auto cached_image = images.get(oldest_frame.value()).release_value();
|
||||
auto cached_image = images.get(least_recently_used_frame.value()).release_value();
|
||||
approximate_byte_size -= min(approximate_byte_size, cached_image.approximate_byte_size);
|
||||
images.remove(oldest_frame.value());
|
||||
images.remove(least_recently_used_frame.value());
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<SkiaBackendContext> skia_backend_context;
|
||||
HashMap<DecodedImageFrame, CachedImage, DecodedImageFrameKeyTraits> images;
|
||||
size_t approximate_byte_size { 0 };
|
||||
u64 generation { 0 };
|
||||
u64 use_sequence_number { 0 };
|
||||
};
|
||||
|
||||
DecodedImageFrameSkiaImageCache::DecodedImageFrameSkiaImageCache()
|
||||
|
|
@ -103,7 +108,7 @@ sk_sp<SkImage> DecodedImageFrameSkiaImageCache::image_for_frame(DecodedImageFram
|
|||
{
|
||||
auto const& bitmap = frame.bitmap();
|
||||
if (auto it = m_impl->images.find(frame); it != m_impl->images.end()) {
|
||||
it->value.last_used_generation = m_impl->generation;
|
||||
it->value.last_used_sequence_number = m_impl->next_use_sequence_number();
|
||||
return it->value.image;
|
||||
}
|
||||
|
||||
|
|
@ -123,7 +128,7 @@ sk_sp<SkImage> DecodedImageFrameSkiaImageCache::image_for_frame(DecodedImageFram
|
|||
|
||||
Impl::CachedImage cached_image {
|
||||
.image = image,
|
||||
.last_used_generation = m_impl->generation,
|
||||
.last_used_sequence_number = m_impl->next_use_sequence_number(),
|
||||
.approximate_byte_size = bitmap.size_in_bytes(),
|
||||
};
|
||||
m_impl->approximate_byte_size += cached_image.approximate_byte_size;
|
||||
|
|
@ -134,14 +139,7 @@ sk_sp<SkImage> DecodedImageFrameSkiaImageCache::image_for_frame(DecodedImageFram
|
|||
|
||||
void DecodedImageFrameSkiaImageCache::prune()
|
||||
{
|
||||
m_impl->images.remove_all_matching([this](auto const&, auto const& cached_image) {
|
||||
if (m_impl->generation - cached_image.last_used_generation <= image_cache_max_unused_generations)
|
||||
return false;
|
||||
m_impl->approximate_byte_size -= min(m_impl->approximate_byte_size, cached_image.approximate_byte_size);
|
||||
return true;
|
||||
});
|
||||
m_impl->prune_to_limits();
|
||||
++m_impl->generation;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,20 +106,20 @@ static SkM44 to_skia_matrix4x4(Gfx::FloatMatrix4x4 const& matrix)
|
|||
|
||||
void DisplayListPlayerSkia::flush(Gfx::PaintingSurface& surface)
|
||||
{
|
||||
m_image_cache.prune();
|
||||
if (auto context = surface.skia_backend_context())
|
||||
context->flush_and_submit(&surface.sk_surface());
|
||||
surface.flush();
|
||||
m_image_cache.prune();
|
||||
}
|
||||
|
||||
void DisplayListPlayerSkia::flush_async(Gfx::PaintingSurface& surface, Function<void()>&& callback)
|
||||
{
|
||||
m_image_cache.prune();
|
||||
if (auto context = surface.skia_backend_context())
|
||||
context->flush_and_submit_async(&surface.sk_surface(), move(callback));
|
||||
else
|
||||
callback();
|
||||
surface.flush();
|
||||
m_image_cache.prune();
|
||||
}
|
||||
|
||||
static void paint_scrollbar_into_surface(Gfx::PaintingSurface& surface, PaintScrollBar const& command)
|
||||
|
|
|
|||
Loading…
Reference in a new issue