[gtk/mask-nodes: 9/11] gl: Add a path cache
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/mask-nodes: 9/11] gl: Add a path cache
- Date: Fri, 18 Dec 2020 05:49:48 +0000 (UTC)
commit 3696de353fe0870da885eba21ca762047d1eaba3
Author: Matthias Clasen <mclasen redhat com>
Date: Tue Dec 15 19:07:06 2020 -0500
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.
gsk/gl/gskglpathcache.c | 157 +++++++++++++++++++++++++++++++++++++++++
gsk/gl/gskglpathcacheprivate.h | 31 ++++++++
gsk/gl/gskglrenderer.c | 90 +++++++++++++++++------
gsk/meson.build | 1 +
4 files changed, 256 insertions(+), 23 deletions(-)
---
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 <glib.h>
+#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 a3a331e485..54ccef5659 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 {
@@ -2831,7 +2833,8 @@ render_fill_node (GskGLRenderer *self,
GskRenderNode *child = gsk_fill_node_get_child (node);
TextureRegion child_region;
gboolean is_offscreen1;
- GdkTexture *mask;
+ GskPath *path;
+ GskFillRule fill_rule;
graphene_rect_t mask_bounds;
int mask_texture_id;
OpMask *op;
@@ -2846,18 +2849,36 @@ render_fill_node (GskGLRenderer *self,
return;
}
- /* TODO: cache masks, and apply scale */
- mask = make_path_mask (gsk_fill_node_get_path (node),
- gsk_fill_node_get_fill_rule (node),
- &mask_bounds);
+ /* TODO: apply scale */
+ path = gsk_fill_node_get_path (node);
+ fill_rule = gsk_fill_node_get_fill_rule (node);
+ mask_texture_id = gsk_gl_path_cache_get_texture_id (&self->path_cache,
+ path,
+ fill_rule,
+ NULL,
+ &mask_bounds);
+ if (mask_texture_id == 0)
+ {
+ GdkTexture *mask;
- mask_texture_id =
- gsk_gl_driver_get_texture_for_texture (self->gl_driver,
- mask,
- GL_LINEAR,
- GL_LINEAR);
+ mask = make_path_mask (path, fill_rule, &mask_bounds);
- g_object_unref (mask);
+ mask_texture_id =
+ gsk_gl_driver_get_texture_for_texture (self->gl_driver,
+ mask,
+ GL_LINEAR,
+ GL_LINEAR);
+
+ gsk_gl_driver_mark_texture_permanent (self->gl_driver, mask_texture_id);
+ gsk_gl_path_cache_commit (&self->path_cache,
+ path,
+ fill_rule,
+ NULL,
+ mask_texture_id,
+ &mask_bounds);
+
+ g_object_unref (mask);
+ }
ops_set_program (builder, &self->programs->mask_program);
@@ -2882,7 +2903,7 @@ render_stroke_node (GskGLRenderer *self,
TextureRegion child_region;
gboolean is_offscreen1;
GskPath *path;
- GdkTexture *mask;
+ GskStroke *stroke;
int mask_texture_id;
graphene_rect_t mask_bounds;
OpMask *op;
@@ -2897,19 +2918,39 @@ render_stroke_node (GskGLRenderer *self,
return;
}
- /* TODO: cache masks, and apply scale */
- path = gsk_path_stroke (gsk_stroke_node_get_path (node),
- (GskStroke *)gsk_stroke_node_get_stroke (node));
- mask = make_path_mask (path, GSK_FILL_RULE_WINDING, &mask_bounds);
+ /* TODO: apply scale */
+ path = gsk_stroke_node_get_path (node);
+ stroke = (GskStroke *)gsk_stroke_node_get_stroke (node);
+
+ mask_texture_id = gsk_gl_path_cache_get_texture_id (&self->path_cache,
+ path,
+ GSK_FILL_RULE_WINDING,
+ stroke,
+ &mask_bounds);
+ if (mask_texture_id == 0)
+ {
+ GskPath *stroke_path;
+ GdkTexture *mask;
+
+ stroke_path = gsk_path_stroke (path, stroke);
+ mask = make_path_mask (stroke_path, GSK_FILL_RULE_WINDING, &mask_bounds);
- mask_texture_id =
- gsk_gl_driver_get_texture_for_texture (self->gl_driver,
- mask,
- GL_LINEAR,
- GL_LINEAR);
+ mask_texture_id =
+ gsk_gl_driver_get_texture_for_texture (self->gl_driver,
+ mask,
+ GL_LINEAR,
+ GL_LINEAR);
- g_object_unref (mask);
- gsk_path_unref (path);
+ gsk_gl_driver_mark_texture_permanent (self->gl_driver, mask_texture_id);
+ gsk_gl_path_cache_commit (&self->path_cache,
+ path,
+ GSK_FILL_RULE_WINDING,
+ stroke,
+ mask_texture_id,
+ &mask_bounds);
+ g_object_unref (mask);
+ gsk_path_unref (stroke_path);
+ }
ops_set_program (builder, &self->programs->mask_program);
@@ -3798,6 +3839,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);
@@ -3825,6 +3867,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);
@@ -4472,6 +4515,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([
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]