From 4d4993b8ae843ee65a35b844ae84a4b98472810b Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 20 Dec 2020 21:46:00 -0500 Subject: [PATCH] gl: Add a path cache This is very similar to the shadow cache. We cache masks that we've rendered for paths, including the fill rule and the stroke parameters in the key. The cache is not used yet. --- gsk/gl/gskglpathcache.c | 157 +++++++++++++++++++++++++++++++++ gsk/gl/gskglpathcacheprivate.h | 31 +++++++ gsk/gl/gskglrenderer.c | 5 ++ gsk/meson.build | 1 + 4 files changed, 194 insertions(+) create mode 100644 gsk/gl/gskglpathcache.c create mode 100644 gsk/gl/gskglpathcacheprivate.h diff --git a/gsk/gl/gskglpathcache.c b/gsk/gl/gskglpathcache.c new file mode 100644 index 0000000000..1dcb0c33d8 --- /dev/null +++ b/gsk/gl/gskglpathcache.c @@ -0,0 +1,157 @@ +#include "config.h" + +#include "gskglpathcacheprivate.h" +#include "gskstrokeprivate.h" + +#define MAX_UNUSED_FRAMES (16 * 5) + +typedef struct +{ + GskPath *path; + GskFillRule fill_rule; + const GskStroke *stroke; + guint hash; + + GskStroke stroke_copy; + graphene_rect_t bounds; + int texture_id; + int unused_frames; +} CacheItem; + +static guint +compute_hash (CacheItem *item) +{ + guint hash; + + hash = GPOINTER_TO_UINT (item->path); + hash += item->fill_rule; + if (item->stroke) + hash += gsk_stroke_hash (item->stroke); + + return hash; +} + +static guint +cache_key_hash (gconstpointer v) +{ + const CacheItem *item = v; + + return item->hash; +} + +static gboolean +cache_key_equal (gconstpointer v1, + gconstpointer v2) +{ + const CacheItem *item1 = v1; + const CacheItem *item2 = v2; + + return item1->path == item2->path && + item1->fill_rule == item2->fill_rule && + (item1->stroke == item2->stroke || + (item1->stroke && item2->stroke && + gsk_stroke_equal (item1->stroke, item2->stroke))); +} + +void +gsk_gl_path_cache_init (GskGLPathCache *self) +{ + self->textures = g_hash_table_new_full (cache_key_hash, + cache_key_equal, + NULL, + g_free); +} + +void +gsk_gl_path_cache_free (GskGLPathCache *self, + GskGLDriver *gl_driver) +{ + GHashTableIter iter; + CacheItem *item; + + g_hash_table_iter_init (&iter, self->textures); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&item)) + { + gsk_gl_driver_destroy_texture (gl_driver, item->texture_id); + } + + g_hash_table_unref (self->textures); + self->textures = NULL; +} + +void +gsk_gl_path_cache_begin_frame (GskGLPathCache *self, + GskGLDriver *gl_driver) +{ + GHashTableIter iter; + CacheItem *item; + + g_hash_table_iter_init (&iter, self->textures); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&item)) + { + if (item->unused_frames > MAX_UNUSED_FRAMES) + { + gsk_gl_driver_destroy_texture (gl_driver, item->texture_id); + g_hash_table_iter_remove (&iter); + } + } +} + +int +gsk_gl_path_cache_get_texture_id (GskGLPathCache *self, + GskPath *path, + GskFillRule fill_rule, + const GskStroke *stroke, + graphene_rect_t *out_bounds) +{ + CacheItem key; + CacheItem *item; + + key.path = path; + key.fill_rule = fill_rule; + key.stroke = stroke; + key.hash = compute_hash (&key); + + item = g_hash_table_lookup (self->textures, &key); + if (item == NULL) + return 0; + + item->unused_frames = 0; + + g_assert (item->texture_id != 0); + + *out_bounds = item->bounds; + + return item->texture_id; +} + +void +gsk_gl_path_cache_commit (GskGLPathCache *self, + GskPath *path, + GskFillRule fill_rule, + const GskStroke *stroke, + int texture_id, + const graphene_rect_t *bounds) +{ + CacheItem *item; + + g_assert (self != NULL); + g_assert (texture_id > 0); + + item = g_new0 (CacheItem, 1); + + item->path = path; + item->fill_rule = fill_rule; + if (stroke) + { + gsk_stroke_init_copy (&item->stroke_copy, stroke); + item->stroke = &item->stroke_copy; + } + item->hash = compute_hash (item); + + item->unused_frames = 0; + item->texture_id = texture_id; + item->bounds = *bounds; + + g_hash_table_add (self->textures, item); +} diff --git a/gsk/gl/gskglpathcacheprivate.h b/gsk/gl/gskglpathcacheprivate.h new file mode 100644 index 0000000000..181d4be26f --- /dev/null +++ b/gsk/gl/gskglpathcacheprivate.h @@ -0,0 +1,31 @@ +#ifndef __GSK_GL_PATH_CACHE_H__ +#define __GSK_GL_PATH_CACHE_H__ + +#include +#include "gskgldriverprivate.h" +#include "gskpath.h" + +typedef struct +{ + GHashTable *textures; +} GskGLPathCache; + + +void gsk_gl_path_cache_init (GskGLPathCache *self); +void gsk_gl_path_cache_free (GskGLPathCache *self, + GskGLDriver *gl_driver); +void gsk_gl_path_cache_begin_frame (GskGLPathCache *self, + GskGLDriver *gl_driver); +int gsk_gl_path_cache_get_texture_id (GskGLPathCache *self, + GskPath *path, + GskFillRule fill_rule, + const GskStroke *stroke, + graphene_rect_t *out_bounds); +void gsk_gl_path_cache_commit (GskGLPathCache *self, + GskPath *path, + GskFillRule fill_rule, + const GskStroke *stroke, + int texture_id, + const graphene_rect_t *bounds); + +#endif diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c index c00805c6e5..09b7b4f0a1 100644 --- a/gsk/gl/gskglrenderer.c +++ b/gsk/gl/gskglrenderer.c @@ -16,6 +16,7 @@ #include "gskglrenderopsprivate.h" #include "gskcairoblurprivate.h" #include "gskglshadowcacheprivate.h" +#include "gskglpathcacheprivate.h" #include "gskglnodesampleprivate.h" #include "gsktransform.h" #include "glutilsprivate.h" @@ -542,6 +543,7 @@ struct _GskGLRenderer GskGLGlyphCache *glyph_cache; GskGLIconCache *icon_cache; GskGLShadowCache shadow_cache; + GskGLPathCache path_cache; #ifdef G_ENABLE_DEBUG struct { @@ -3636,6 +3638,7 @@ gsk_gl_renderer_realize (GskRenderer *renderer, self->glyph_cache = get_glyph_cache_for_display (gdk_surface_get_display (surface), self->atlases); self->icon_cache = get_icon_cache_for_display (gdk_surface_get_display (surface), self->atlases); gsk_gl_shadow_cache_init (&self->shadow_cache); + gsk_gl_path_cache_init (&self->path_cache); gdk_profiler_end_mark (before, "gl renderer realize", NULL); @@ -3663,6 +3666,7 @@ gsk_gl_renderer_unrealize (GskRenderer *renderer) g_clear_pointer (&self->icon_cache, gsk_gl_icon_cache_unref); g_clear_pointer (&self->atlases, gsk_gl_texture_atlases_unref); gsk_gl_shadow_cache_free (&self->shadow_cache, self->gl_driver); + gsk_gl_path_cache_free (&self->path_cache, self->gl_driver); g_clear_object (&self->gl_profiler); g_clear_object (&self->gl_driver); @@ -4304,6 +4308,7 @@ gsk_gl_renderer_do_render (GskRenderer *renderer, gsk_gl_glyph_cache_begin_frame (self->glyph_cache, self->gl_driver, removed); gsk_gl_icon_cache_begin_frame (self->icon_cache, removed); gsk_gl_shadow_cache_begin_frame (&self->shadow_cache, self->gl_driver); + gsk_gl_path_cache_begin_frame (&self->path_cache, self->gl_driver); g_ptr_array_unref (removed); /* Set up the modelview and projection matrices to fit our viewport */ diff --git a/gsk/meson.build b/gsk/meson.build index 5925594c29..618ed14651 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -59,6 +59,7 @@ gsk_private_sources = files([ 'gl/gskgliconcache.c', 'gl/opbuffer.c', 'gl/stb_rect_pack.c', + 'gl/gskglpathcache.c', ]) gsk_public_headers = files([