[gtk/wip/matthiasc/opbuffer: 3/5] Cache glyph textures in render nodes



commit ef9a56d0e5d36277a839629e7b0704ed17b4174e
Author: Matthias Clasen <mclasen redhat com>
Date:   Tue Oct 15 00:14:43 2019 -0400

    Cache glyph textures in render nodes
    
    This is a quick implementation that avoids many
    glyph cache lookups. We keep an array of direct
    pointers in the text render node, and throw those
    cached pointers away whenever any atlases have
    been dropped (since that may invalidate the cached
    glyphs).

 gsk/gl/gskglglyphcache.c        | 22 +++++++++++------
 gsk/gl/gskglglyphcacheprivate.h |  3 +++
 gsk/gl/gskglrenderer.c          | 53 +++++++++++++++++++++++++++++++++++++----
 gsk/gskrendernodeimpl.c         | 20 ++++++++++++++++
 gsk/gskrendernodeprivate.h      |  4 ++++
 5 files changed, 90 insertions(+), 12 deletions(-)
---
diff --git a/gsk/gl/gskglglyphcache.c b/gsk/gl/gskglglyphcache.c
index 21121b1ce5..4c4cbcc825 100644
--- a/gsk/gl/gskglglyphcache.c
+++ b/gsk/gl/gskglglyphcache.c
@@ -264,13 +264,7 @@ gsk_gl_glyph_cache_lookup_or_add (GskGLGlyphCache         *cache,
 
   if (value)
     {
-      value->timestamp = cache->timestamp;
-      if (value->atlas && !value->used)
-        {
-          gsk_gl_texture_atlas_mark_used (value->atlas, value->draw_width, value->draw_height);
-          value->used = TRUE;
-        }
-
+      gsk_gl_glyph_cache_entry_validate (cache, value);
       *cached_glyph_out = value;
       return;
     }
@@ -328,6 +322,8 @@ gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self,
     {
       guint dropped = 0;
 
+      self->atlas_timestamp++;
+
       g_hash_table_iter_init (&iter, self->hash_table);
       while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
         {
@@ -358,3 +354,15 @@ gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self,
         }
     }
 }
+
+void
+gsk_gl_glyph_cache_entry_validate (GskGLGlyphCache  *cache,
+                                   GskGLCachedGlyph *value)
+{
+  value->timestamp = cache->timestamp;
+  if (value->atlas && !value->used)
+    {
+      gsk_gl_texture_atlas_mark_used (value->atlas, value->draw_width, value->draw_height);
+      value->used = TRUE;
+    }
+}
diff --git a/gsk/gl/gskglglyphcacheprivate.h b/gsk/gl/gskglglyphcacheprivate.h
index 259688fbb7..59f4491ee4 100644
--- a/gsk/gl/gskglglyphcacheprivate.h
+++ b/gsk/gl/gskglglyphcacheprivate.h
@@ -16,6 +16,7 @@ typedef struct
   GskGLTextureAtlases *atlases;
 
   guint64 timestamp;
+  int atlas_timestamp; /* incremented whenever an atlas is dropped */
 } GskGLGlyphCache;
 
 typedef struct
@@ -78,5 +79,7 @@ void                     gsk_gl_glyph_cache_lookup_or_add   (GskGLGlyphCache
                                                              GlyphCacheKey          *lookup,
                                                              GskGLDriver            *driver,
                                                              const GskGLCachedGlyph **cached_glyph_out);
+void                    gsk_gl_glyph_cache_entry_validate   (GskGLGlyphCache *cache,
+                                                             GskGLCachedGlyph *entry);
 
 #endif
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index b797ce682d..3ed1f302b0 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -536,6 +536,39 @@ render_fallback_node (GskGLRenderer       *self,
   ops_draw (builder, offscreen_vertex_data);
 }
 
+typedef struct {
+  int timestamp;
+  GskGLCachedGlyph *glyphs[];
+} TextRenderData;
+
+static inline TextRenderData *
+ensure_render_data (GskRenderNode *node,
+                    GskGLGlyphCache *cache)
+{
+  TextRenderData *data;
+  int num_glyphs;
+
+  num_glyphs = gsk_text_node_get_num_glyphs (node);
+  data = gsk_text_node_get_render_data (node);
+  if (data)
+    {
+      if (data->timestamp < cache->atlas_timestamp)
+        {
+          memset (data->glyphs, 0, sizeof (gpointer) * num_glyphs);
+          data->timestamp = cache->atlas_timestamp;
+        }
+    }
+  else
+    {
+      data = g_new0 (TextRenderData, sizeof (TextRenderData) + sizeof (gpointer) * num_glyphs);
+      data->timestamp = cache->atlas_timestamp;
+
+      gsk_text_node_set_render_data (node, data);
+    }
+
+  return data;
+}
+
 static inline void
 render_text_node (GskGLRenderer   *self,
                   GskRenderNode   *node,
@@ -553,6 +586,7 @@ render_text_node (GskGLRenderer   *self,
   float x = offset->x + builder->dx;
   float y = offset->y + builder->dy;
   GlyphCacheKey lookup;
+  TextRenderData *render_data;
 
   /* If the font has color glyphs, we don't need to recolor anything */
   if (!force_color && gsk_text_node_has_color_glyphs (node))
@@ -565,6 +599,8 @@ render_text_node (GskGLRenderer   *self,
       ops_set_color (builder, color);
     }
 
+  render_data = ensure_render_data (node, self->glyph_cache);
+
   lookup.font = (PangoFont *)font;
   lookup.scale = (guint) (text_scale * 1024);
 
@@ -585,12 +621,19 @@ render_text_node (GskGLRenderer   *self,
       cx = (float)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
       cy = (float)(gi->geometry.y_offset) / PANGO_SCALE;
 
-      glyph_cache_key_set_glyph_and_shift (&lookup, gi->glyph, x + cx, y + cy);
+      glyph = render_data->glyphs[i];
+      if (!glyph)
+        {
+          glyph_cache_key_set_glyph_and_shift (&lookup, gi->glyph, x + cx, y + cy);
+
+          gsk_gl_glyph_cache_lookup_or_add (self->glyph_cache,
+                                            &lookup,
+                                            self->gl_driver,
+                                            &glyph);
+          render_data->glyphs[i] = (GskGLCachedGlyph *)glyph;
+        }
 
-      gsk_gl_glyph_cache_lookup_or_add (self->glyph_cache,
-                                        &lookup,
-                                        self->gl_driver,
-                                        &glyph);
+      gsk_gl_glyph_cache_entry_validate (self->glyph_cache, render_data->glyphs[i]);
 
       if (glyph->texture_id == 0)
         goto next;
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index 8c7be01555..b8bd749fa5 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -3414,6 +3414,7 @@ struct _GskTextNode
   GdkRGBA color;
   graphene_point_t offset;
 
+  gpointer render_data;
   guint num_glyphs;
   PangoGlyphInfo glyphs[];
 };
@@ -3423,6 +3424,7 @@ gsk_text_node_finalize (GskRenderNode *node)
 {
   GskTextNode *self = (GskTextNode *) node;
 
+  g_free (self->render_data);
   g_object_unref (self->font);
 }
 
@@ -3625,6 +3627,24 @@ gsk_text_node_get_offset (GskRenderNode *node)
   return &self->offset;
 }
 
+void
+gsk_text_node_set_render_data (GskRenderNode *node,
+                               gpointer       data)
+{
+  GskTextNode *self = (GskTextNode *) node;
+
+  self->render_data = data;
+}
+
+gpointer
+gsk_text_node_get_render_data (GskRenderNode *node)
+{
+  GskTextNode *self = (GskTextNode *) node;
+
+  return self->render_data;
+}
+
+
 /*** GSK_BLUR_NODE ***/
 
 typedef struct _GskBlurNode GskBlurNode;
diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h
index 2ced79738b..f2fe034444 100644
--- a/gsk/gskrendernodeprivate.h
+++ b/gsk/gskrendernodeprivate.h
@@ -46,6 +46,10 @@ void            gsk_render_node_diff             (GskRenderNode             *nod
 void            gsk_render_node_diff_impossible  (GskRenderNode             *node1,
                                                   GskRenderNode             *node2,
                                                   cairo_region_t            *region);
+void            gsk_text_node_set_render_data    (GskRenderNode *node,
+                                                  gpointer       data);
+gpointer        gsk_text_node_get_render_data    (GskRenderNode *node);
+                                                  
 
 
 G_END_DECLS


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]