Cache glyph textures in render nodes
authorMatthias Clasen <mclasen@redhat.com>
Tue, 15 Oct 2019 04:14:43 +0000 (00:14 -0400)
committerMatthias Clasen <mclasen@redhat.com>
Tue, 15 Oct 2019 23:44:26 +0000 (19:44 -0400)
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
gsk/gl/gskglglyphcacheprivate.h
gsk/gl/gskglrenderer.c
gsk/gskrendernodeimpl.c
gsk/gskrendernodeprivate.h

index 7cc899e82c286f760be3a3a9dfe9237865b6dd93..5571293ee541582c4335d739c4f2eb1015c2dede 100644 (file)
@@ -269,13 +269,7 @@ gsk_gl_glyph_cache_lookup_or_add (GskGLGlyphCache         *cache,
 
   if (value)
     {
-      if (value->atlas && !value->used)
-        {
-          gsk_gl_texture_atlas_mark_used (value->atlas, value->draw_width, value->draw_height);
-          value->used = TRUE;
-        }
-      value->accessed = TRUE;
-
+      gsk_gl_glyph_cache_entry_validate (cache, value);
       *cached_glyph_out = value;
       return;
     }
@@ -334,6 +328,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))
         {
@@ -375,3 +371,15 @@ gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self,
       GSK_NOTE(GLYPH_CACHE, g_message ("%d glyphs cached", g_hash_table_size (self->hash_table)));
     }
 }
+
+void
+gsk_gl_glyph_cache_entry_validate (GskGLGlyphCache  *cache,
+                                   GskGLCachedGlyph *value)
+{
+  value->accessed = TRUE;
+  if (value->atlas && !value->used)
+    {
+      gsk_gl_texture_atlas_mark_used (value->atlas, value->draw_width, value->draw_height);
+      value->used = TRUE;
+    }
+}
index 16a6feb47108371d1293ace9ba22b7ed104311ff..4c2920bee6120e2b5d3d3c7e3e91b5fa248a13d1 100644 (file)
@@ -16,6 +16,7 @@ typedef struct
   GskGLTextureAtlases *atlases;
 
   int timestamp;
+  int atlas_timestamp; /* incremented whenever an atlas is dropped */
 } GskGLGlyphCache;
 
 typedef struct
@@ -79,5 +80,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
index 229d63402317e81e1c68c531f408a3b5e62de843..886b9fdc4e51360acb4cfce315667c59659da95a 100644 (file)
@@ -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;
index 8c7be01555248a28087d0034fabdcb03b8a77cc5..b8bd749fa5397510c474c5526eeac9b87452b0d2 100644 (file)
@@ -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;
index 2ced79738b9c150adf75ecac36a4cef91baa78b8..f2fe03444431e290d0b46d8939d06a84fbd58f22 100644 (file)
@@ -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