summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2017-09-20 17:52:40 -0400
committerMatthias Clasen <mclasen@redhat.com>2017-09-20 23:26:14 -0400
commitb5eeb98374d39c62d51dbc0bcb707c595471cf24 (patch)
tree4594e859e7e41b3e48f7bd531040bb7f33d9aeee
parent7d7f8e5f8ec5049d4a91da90117390ebc27e2fca (diff)
downloadgtk+-b5eeb98374d39c62d51dbc0bcb707c595471cf24.tar.gz
Add simple aging for the glyph cache
This should prevent the cache from growing out of bounds.
-rw-r--r--gsk/gskvulkanglyphcache.c116
-rw-r--r--gsk/gskvulkanglyphcacheprivate.h2
-rw-r--r--gsk/gskvulkanrendererprivate.h2
3 files changed, 118 insertions, 2 deletions
diff --git a/gsk/gskvulkanglyphcache.c b/gsk/gskvulkanglyphcache.c
index bf0a0621f7..a390e0617d 100644
--- a/gsk/gskvulkanglyphcache.c
+++ b/gsk/gskvulkanglyphcache.c
@@ -8,6 +8,20 @@
#include <graphene.h>
+/* Parameters for our cache eviction strategy.
+ *
+ * Each cached glyph has an age that gets reset every time a cached glyph gets used.
+ * Glyphs that have not been used for the MAX_AGE frames are considered old. We keep
+ * count of the pixels of each atlas that are taken up by old glyphs. We check the
+ * fraction of old pixels every CHECK_INTERVAL frames, and if it is above MAX_OLD, then
+ * we drop the atlas an all the glyphs contained in it from the cache.
+ */
+
+#define MAX_AGE 60
+#define CHECK_INTERVAL 10
+#define MAX_OLD 0.333
+
+
typedef struct {
cairo_surface_t *surface;
GskVulkanImage *image;
@@ -15,6 +29,7 @@ typedef struct {
int x, y, y0;
int num_glyphs;
GList *dirty_glyphs;
+ guint old_pixels;
} Atlas;
struct _GskVulkanGlyphCache {
@@ -24,6 +39,8 @@ struct _GskVulkanGlyphCache {
GHashTable *hash_table;
GPtrArray *atlases;
+
+ guint64 timestamp;
};
struct _GskVulkanGlyphCacheClass {
@@ -43,7 +60,7 @@ create_atlas (GskVulkanGlyphCache *cache)
{
Atlas *atlas;
- atlas = g_new (Atlas, 1);
+ atlas = g_new0 (Atlas, 1);
atlas->width = 512;
atlas->height = 512;
atlas->y0 = 1;
@@ -202,9 +219,10 @@ add_to_cache (GskVulkanGlyphCache *cache,
for (i = 0; i < cache->atlases->len; i++)
{
atlas = g_ptr_array_index (cache->atlases, i);
- g_print ("\tAtlas %d (%dx%d): %d glyphs (%d dirty), filled to %d, %d / %d\n",
+ g_print ("\tAtlas %d (%dx%d): %d glyphs (%d dirty), %.2g%% old pixels, filled to %d, %d / %d\n",
i, atlas->width, atlas->height,
atlas->num_glyphs, g_list_length (atlas->dirty_glyphs),
+ 100.0 * (double)atlas->old_pixels / (double)(atlas->width * atlas->height),
atlas->x, atlas->y0, atlas->y);
}
}
@@ -281,6 +299,17 @@ gsk_vulkan_glyph_cache_lookup (GskVulkanGlyphCache *cache,
value = g_hash_table_lookup (cache->hash_table, &lookup_key);
+ if (value)
+ {
+ if (cache->timestamp - value->timestamp >= MAX_AGE)
+ {
+ Atlas *atlas = g_ptr_array_index (cache->atlases, value->texture_index);
+
+ atlas->old_pixels -= value->draw_width * value->draw_height;
+ value->timestamp = cache->timestamp;
+ }
+ }
+
if (create && value == NULL)
{
GlyphCacheKey *key;
@@ -296,6 +325,7 @@ gsk_vulkan_glyph_cache_lookup (GskVulkanGlyphCache *cache,
value->draw_y = ink_rect.y;
value->draw_width = ink_rect.width;
value->draw_height = ink_rect.height;
+ value->timestamp = cache->timestamp;
key->font = g_object_ref (font);
key->glyph = glyph;
@@ -334,3 +364,85 @@ gsk_vulkan_glyph_cache_get_glyph_image (GskVulkanGlyphCache *cache,
return atlas->image;
}
+
+void
+gsk_vulkan_glyph_cache_begin_frame (GskVulkanGlyphCache *cache)
+{
+ int i, j;
+ guint *drops;
+ guint *shifts;
+ guint len;
+ GHashTableIter iter;
+ GlyphCacheKey *key;
+ GskVulkanCachedGlyph *value;
+ guint dropped = 0;
+
+ cache->timestamp++;
+
+ if (cache->timestamp % CHECK_INTERVAL != 0)
+ return;
+
+ len = cache->atlases->len;
+
+ /* look for glyphs that have grown old since last time */
+ g_hash_table_iter_init (&iter, cache->hash_table);
+ while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
+ {
+ guint age;
+
+ age = cache->timestamp - value->timestamp;
+ if (MAX_AGE <= age && age < MAX_AGE + CHECK_INTERVAL)
+ {
+ Atlas *atlas = g_ptr_array_index (cache->atlases, value->texture_index);
+ atlas->old_pixels += value->draw_width * value->draw_height;
+ }
+ }
+
+ drops = g_alloca (sizeof (guint) * len);
+ shifts = g_alloca (sizeof (guint) * len);
+
+ for (i = 0; i < len; i++)
+ {
+ drops[i] = 0;
+ shifts[i] = i;
+ }
+
+ /* look for atlases to drop, and create a mapping of updated texture indices */
+ for (i = cache->atlases->len - 1; i >= 0; i--)
+ {
+ Atlas *atlas = g_ptr_array_index (cache->atlases, i);
+
+ if (atlas->old_pixels > MAX_OLD * atlas->width * atlas->height)
+ {
+ GSK_NOTE(GLYPH_CACHE,
+ g_print ("Dropping atlas %d (%g.2%% old)\n", i, 100.0 * (double)atlas->old_pixels / (double)(atlas->width * atlas->height)));
+ g_ptr_array_remove_index (cache->atlases, i);
+
+ drops[i] = 1;
+ for (j = i; j + 1 < len; j++)
+ shifts[j + 1] = shifts[j];
+ }
+ }
+
+ /* no atlas dropped, we're done */
+ if (len == cache->atlases->len)
+ return;
+
+ /* purge glyphs and update texture indices */
+ g_hash_table_iter_init (&iter, cache->hash_table);
+
+ while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
+ {
+ if (drops[value->texture_index])
+ {
+ dropped++;
+ g_hash_table_iter_remove (&iter);
+ }
+ else
+ {
+ value->texture_index = shifts[value->texture_index];
+ }
+ }
+
+ GSK_NOTE(GLYPH_CACHE, g_print ("Dropped %d glyphs\n", dropped));
+}
diff --git a/gsk/gskvulkanglyphcacheprivate.h b/gsk/gskvulkanglyphcacheprivate.h
index 56866851f1..3a9cddfbee 100644
--- a/gsk/gskvulkanglyphcacheprivate.h
+++ b/gsk/gskvulkanglyphcacheprivate.h
@@ -22,4 +22,6 @@ GskVulkanCachedGlyph *gsk_vulkan_glyph_cache_lookup (GskVulkanGlyphCache
PangoFont *font,
PangoGlyph glyph);
+void gsk_vulkan_glyph_cache_begin_frame (GskVulkanGlyphCache *cache);
+
#endif /* __GSK_VULKAN_GLYPH_CACHE_PRIVATE_H__ */
diff --git a/gsk/gskvulkanrendererprivate.h b/gsk/gskvulkanrendererprivate.h
index 0f954e3cbb..19f0d3baa5 100644
--- a/gsk/gskvulkanrendererprivate.h
+++ b/gsk/gskvulkanrendererprivate.h
@@ -38,6 +38,8 @@ typedef struct
int draw_y;
int draw_width;
int draw_height;
+
+ guint64 timestamp;
} GskVulkanCachedGlyph;
guint gsk_vulkan_renderer_cache_glyph (GskVulkanRenderer *renderer,