[gtk+/wip/alexl/broadway4] broadway: Add node cache for text



commit 9c550f75e6fe5c9913ac78e84a93789584f86b16
Author: Alexander Larsson <alexl redhat com>
Date:   Wed Nov 22 15:39:09 2017 +0100

    broadway: Add node cache for text

 gdk/broadway/broadway.js  |    2 +-
 gsk/gskbroadwayrenderer.c |  220 +++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 205 insertions(+), 17 deletions(-)
---
diff --git a/gdk/broadway/broadway.js b/gdk/broadway/broadway.js
index 45dae02..dd9ea1c 100644
--- a/gdk/broadway/broadway.js
+++ b/gdk/broadway/broadway.js
@@ -438,7 +438,7 @@ SwapNodes.prototype.handle_node = function(parent, offset_x, offset_y)
     {
         case 0:  // TEXTURE
         {
-            var rect = this.decode_irect();
+            var rect = this.decode_rect();
             var texture_id = this.decode_uint32();
             var image = new Image();
             image.width = rect.width;
diff --git a/gsk/gskbroadwayrenderer.c b/gsk/gskbroadwayrenderer.c
index b2ead65..07d8d18 100644
--- a/gsk/gskbroadwayrenderer.c
+++ b/gsk/gskbroadwayrenderer.c
@@ -152,6 +152,197 @@ add_color_stop (GArray *nodes, const GskColorStop *stop)
   add_rgba (nodes, &stop->color);
 }
 
+static gboolean
+float_is_int32 (float f)
+{
+  gint32 i = (gint32)f;
+  float f2 = (float)i;
+  return f2 == f;
+}
+
+static GHashTable *gsk_broadway_node_cache;
+
+typedef struct {
+  GdkTexture *texture;
+  GskRenderNode *node;
+  float off_x;
+  float off_y;
+} NodeCacheElement;
+
+static void
+node_cache_element_free (NodeCacheElement *element)
+{
+  gsk_render_node_unref (element->node);
+  g_free (element);
+}
+
+static guint
+glyph_info_hash (const PangoGlyphInfo *info)
+{
+  return info->glyph ^
+    info->geometry.width << 6 ^
+    info->geometry.x_offset << 12 ^
+    info->geometry.y_offset << 18 ^
+    info->attr.is_cluster_start << 30;
+}
+
+static gboolean
+glyph_info_equal (const PangoGlyphInfo *a,
+                  const PangoGlyphInfo *b)
+{
+  return
+    a->glyph == b->glyph &&
+    a->geometry.width == b->geometry.width &&
+    a->geometry.x_offset == b->geometry.x_offset &&
+    a->geometry.y_offset == b->geometry.y_offset &&
+    a->attr.is_cluster_start == b->attr.is_cluster_start;
+ }
+
+static guint
+node_cache_hash (GskRenderNode *node)
+{
+  if (gsk_render_node_get_node_type (node) == GSK_TEXT_NODE &&
+      float_is_int32 (gsk_text_node_get_x (node)) &&
+      float_is_int32 (gsk_text_node_get_y (node)))
+    {
+      guint i;
+      const PangoFont *font = gsk_text_node_peek_font (node);
+      guint n_glyphs = gsk_text_node_get_num_glyphs (node);
+      const PangoGlyphInfo *infos = gsk_text_node_peek_glyphs (node);
+      const GdkRGBA *color = gsk_text_node_peek_color (node);
+      guint h;
+
+      h = g_direct_hash (font) ^ n_glyphs << 16 ^ gdk_rgba_hash (color);
+      for (i = 0; i < n_glyphs; i++)
+        h ^= glyph_info_hash (&infos[i]);
+
+      return h;
+    }
+
+  return 0;
+}
+
+static gboolean
+node_cache_equal (GskRenderNode *a,
+                  GskRenderNode *b)
+{
+  if (gsk_render_node_get_node_type (a) != gsk_render_node_get_node_type (b))
+    return FALSE;
+
+  if (gsk_render_node_get_node_type (a) == GSK_TEXT_NODE &&
+      float_is_int32 (gsk_text_node_get_x (a)) &&
+      float_is_int32 (gsk_text_node_get_y (a)) &&
+      float_is_int32 (gsk_text_node_get_x (b)) &&
+      float_is_int32 (gsk_text_node_get_y (b)))
+    {
+      const PangoFont *a_font = gsk_text_node_peek_font (a);
+      guint a_n_glyphs = gsk_text_node_get_num_glyphs (a);
+      const PangoGlyphInfo *a_infos = gsk_text_node_peek_glyphs (a);
+      const GdkRGBA *a_color = gsk_text_node_peek_color (a);
+      const PangoFont *b_font = gsk_text_node_peek_font (b);
+      guint b_n_glyphs = gsk_text_node_get_num_glyphs (b);
+      const PangoGlyphInfo *b_infos = gsk_text_node_peek_glyphs (b);
+      const GdkRGBA *b_color = gsk_text_node_peek_color (a);
+      guint i;
+
+      if (a_font != b_font)
+        return FALSE;
+
+      if (a_n_glyphs != b_n_glyphs)
+        return FALSE;
+
+      for (i = 0; i < a_n_glyphs; i++)
+        {
+          if (!glyph_info_equal (&a_infos[i], &b_infos[i]))
+            return FALSE;
+        }
+
+      if (!gdk_rgba_equal (a_color, b_color))
+        return FALSE;
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static GdkTexture *
+node_cache_lookup (GskRenderNode *node,
+                   float *off_x, float *off_y)
+{
+  NodeCacheElement *hit;
+
+  if (gsk_broadway_node_cache == NULL)
+    gsk_broadway_node_cache = g_hash_table_new_full ((GHashFunc)node_cache_hash,
+                                                     (GEqualFunc)node_cache_equal,
+                                                     NULL,
+                                                     (GDestroyNotify)node_cache_element_free);
+
+  hit = g_hash_table_lookup (gsk_broadway_node_cache, node);
+  if (hit)
+    {
+      *off_x = hit->off_x;
+      *off_y = hit->off_y;
+      return g_object_ref (hit->texture);
+    }
+
+  return NULL;
+}
+
+static void
+cached_texture_gone (gpointer data,
+                     GObject *where_the_object_was)
+{
+  NodeCacheElement *element = data;
+  g_hash_table_remove (gsk_broadway_node_cache, element->node);
+}
+
+static void
+node_cache_store (GskRenderNode *node,
+                  GdkTexture *texture,
+                  float off_x,
+                  float off_y)
+{
+  if (gsk_render_node_get_node_type (node) == GSK_TEXT_NODE &&
+      float_is_int32 (gsk_text_node_get_x (node)) &&
+      float_is_int32 (gsk_text_node_get_y (node)))
+    {
+      NodeCacheElement *element = g_new0 (NodeCacheElement, 1);
+      element->texture = texture;
+      element->node = gsk_render_node_ref (node);
+      element->off_x = off_x;
+      element->off_y = off_y;
+      g_object_weak_ref (G_OBJECT (texture), cached_texture_gone, element);
+      g_hash_table_insert (gsk_broadway_node_cache, element->node, element);
+    }
+}
+
+static GdkTexture *
+node_texture_fallback (GskRenderNode *node,
+                       float *off_x,
+                       float *off_y)
+{
+  cairo_surface_t *surface;
+  cairo_t *cr;
+  int x = floorf (node->bounds.origin.x);
+  int y = floorf (node->bounds.origin.y);
+  int width = ceil (node->bounds.origin.x + node->bounds.size.width) - x;
+  int height = ceil (node->bounds.origin.y + node->bounds.size.height) - y;
+  GdkTexture *texture;
+
+  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+  cr = cairo_create (surface);
+  cairo_translate (cr, -x, -y);
+  gsk_render_node_draw (node, cr);
+  cairo_destroy (cr);
+
+  texture = gdk_texture_new_for_surface (surface);
+  *off_x =  x - node->bounds.origin.x;
+  *off_y =  y - node->bounds.origin.y;
+
+  return texture;
+}
+
 static void
 gsk_broadway_renderer_add_node (GskRenderer *self,
                                 GArray *nodes,
@@ -159,10 +350,6 @@ gsk_broadway_renderer_add_node (GskRenderer *self,
                                 GskRenderNode *node)
 {
   GdkDisplay *display = gsk_renderer_get_display (self);
-  int x = floorf (node->bounds.origin.x);
-  int y = floorf (node->bounds.origin.y);
-  int width = ceil (node->bounds.origin.x + node->bounds.size.width) - x;
-  int height = ceil (node->bounds.origin.y + node->bounds.size.height) - y;
 
   switch (gsk_render_node_get_node_type (node))
     {
@@ -250,27 +437,28 @@ gsk_broadway_renderer_add_node (GskRenderer *self,
       }
       return;
 
+    case GSK_TEXT_NODE:
     default:
       {
-        cairo_surface_t *surface;
         GdkTexture *texture;
         guint32 texture_id;
-        cairo_t *cr;
+        float off_x = 0, off_y = 0;
+
+        texture = node_cache_lookup (node, &off_x, &off_y);
 
-        surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
-        cr = cairo_create (surface);
-        cairo_translate (cr, -x, -y);
-        gsk_render_node_draw (node, cr);
-        cairo_destroy (cr);
+        if (!texture)
+          {
+            texture = node_texture_fallback (node, &off_x, &off_y);
+            node_cache_store (node, texture, off_x, off_y);
+          }
 
-        texture = gdk_texture_new_for_surface (surface);
         g_ptr_array_add (node_textures, texture); /* Transfers ownership to node_textures */
         texture_id = gdk_broadway_display_ensure_texture (display, texture);
         add_uint32 (nodes, BROADWAY_NODE_TEXTURE);
-        add_uint32 (nodes, x);
-        add_uint32 (nodes, y);
-        add_uint32 (nodes, width);
-        add_uint32 (nodes, height);
+        add_float (nodes, node->bounds.origin.x + off_x);
+        add_float (nodes, node->bounds.origin.y + off_y);
+        add_float (nodes, gdk_texture_get_width (texture));
+        add_float (nodes, gdk_texture_get_height (texture));
         add_uint32 (nodes, texture_id);
       }
       return;


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