gpu: Rework glyph aging

Instead of counting atlas occupancy by itesm, count how many
dead pixels we have, and free the atlas if more than half of its
pixels are dead. This matches what the GL renderer does.

As part of this, change when glyphs are freed. We now keep them
in the hash table until their atlas is freed and we only do dead
pixel accounting when should_collect is called. This keeps the
glyphs available for use from the cache as long as are in the atlas.

If a stale glyph is sused, we 'revive' itby removing its pixels
from the dead.

This matches more closely what the gl renderer does.
This commit is contained in:
Matthias Clasen
2024-01-19 22:21:55 -05:00
parent d4963ba2e5
commit 65455cdb91

View File

@@ -3,6 +3,7 @@
#include "gskgpudeviceprivate.h"
#include "gskgpuframeprivate.h"
#include "gskgpuimageprivate.h"
#include "gskgpuuploadopprivate.h"
#include "gdk/gdkdisplayprivate.h"
@@ -18,7 +19,9 @@
G_STATIC_ASSERT (MAX_ATLAS_ITEM_SIZE < ATLAS_SIZE);
#define CACHE_GC_TIMEOUT 15 /* seconds */
#define MAX_DEAD_PIXELS (ATLAS_SIZE * ATLAS_SIZE / 2)
#define CACHE_GC_TIMEOUT 1 /* seconds */
#define CACHE_MAX_AGE (G_TIME_SPAN_SECOND * 4) /* 4 seconds, in µs */
@@ -68,8 +71,28 @@ struct _GskGpuCached
GskGpuCached *prev;
gint64 timestamp;
gboolean stale;
guint pixels; /* For glyphs, pixels. For atlases, dead pixels */
};
static inline void
mark_as_stale (GskGpuCached *cached,
gboolean stale)
{
if (cached->stale != stale)
{
cached->stale = stale;
if (cached->atlas)
{
if (stale)
((GskGpuCached *) cached->atlas)->pixels += cached->pixels;
else
((GskGpuCached *) cached->atlas)->pixels -= cached->pixels;
}
}
}
static void
gsk_gpu_cached_free (GskGpuDevice *device,
GskGpuCached *cached)
@@ -85,6 +108,8 @@ gsk_gpu_cached_free (GskGpuDevice *device,
else
priv->first_cached = cached->next;
mark_as_stale (cached, TRUE);
cached->class->free (device, cached);
}
@@ -125,6 +150,7 @@ gsk_gpu_cached_use (GskGpuDevice *device,
gint64 timestamp)
{
cached->timestamp = timestamp;
mark_as_stale (cached, FALSE);
}
/* }}} */
@@ -136,8 +162,6 @@ struct _GskGpuCachedAtlas
GskGpuImage *image;
gsize n_items;
gsize n_slices;
struct {
gsize width;
@@ -174,9 +198,7 @@ gsk_gpu_cached_atlas_should_collect (GskGpuDevice *device,
GskGpuCached *cached,
gint64 timestamp)
{
GskGpuCachedAtlas *self = (GskGpuCachedAtlas *) cached;
return self->n_items == 0;
return cached->pixels > MAX_DEAD_PIXELS;
}
static const GskGpuCachedClass GSK_GPU_CACHED_ATLAS_CLASS =
@@ -279,8 +301,6 @@ gsk_gpu_cached_atlas_allocate (GskGpuCachedAtlas *atlas,
g_assert (atlas->slices[best_slice].width <= ATLAS_SIZE);
g_assert (best_y + atlas->slices[best_slice].height <= ATLAS_SIZE);
atlas->n_items++;
return TRUE;
}
@@ -391,12 +411,6 @@ gsk_gpu_cached_glyph_free (GskGpuDevice *device,
g_hash_table_remove (priv->glyph_cache, self);
if (cached->atlas)
{
g_assert (cached->atlas->n_items > 0);
cached->atlas->n_items--;
}
g_object_unref (self->font);
g_object_unref (self->image);
@@ -408,7 +422,11 @@ gsk_gpu_cached_glyph_should_collect (GskGpuDevice *device,
GskGpuCached *cached,
gint64 timestamp)
{
return timestamp - cached->timestamp > CACHE_MAX_AGE;
if (timestamp - cached->timestamp > CACHE_MAX_AGE)
mark_as_stale (cached, TRUE);
/* Glyphs are only collected when their atlas is freed */
return FALSE;
}
static guint
@@ -476,11 +494,14 @@ gsk_gpu_device_gc (GskGpuDevice *self,
gint64 timestamp)
{
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
GskGpuCached *cached, *next;
GskGpuCached *cached, *prev;
for (cached = priv->first_cached; cached != NULL; cached = next)
/* We walk the cache from the end so we don't end up with prev
* begin a leftover glyph on the atlas we are freeing
*/
for (cached = priv->last_cached; cached != NULL; cached = prev)
{
next = cached->next;
prev = cached->prev;
if (gsk_gpu_cached_should_collect (self, cached, timestamp))
gsk_gpu_cached_free (self, cached);
}
@@ -773,6 +794,7 @@ gsk_gpu_device_lookup_glyph_image (GskGpuDevice *self,
cache->scale = scale,
cache->bounds = rect,
cache->image = image,
((GskGpuCached *) cache)->pixels = (rect.size.width + 2 * padding) * (rect.size.height + 2 * padding);
cache->origin = GRAPHENE_POINT_INIT (- origin.x + (flags & 3) / 4.f,
- origin.y + ((flags >> 2) & 3) / 4.f);