[gtk/mask-nodes: 9/11] gl: Add a path cache




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]