[gtk/wip/alexl/broadway5: 32/35] Broadway: Add id for nodes and reuse old ones



commit fbefec52a56b45da4e77cefca16aea9e9db38f47
Author: Alexander Larsson <alexl redhat com>
Date:   Mon Mar 25 14:15:49 2019 +0100

    Broadway: Add id for nodes and reuse old ones
    
    When sending render nodes from the client to the daemon we add an id,
    and whenever we're about to re-send the entire tree node we instead
    send the old id. We track all the nodes for the previous frame
    of the surface this way.
    
    Having the id on the daemon side will allow us do to much better deltas.

 gdk/broadway/broadway-protocol.h       |   1 +
 gdk/broadway/broadway-server.c         | 269 +++++++++++++--
 gdk/broadway/broadway-server.h         |  10 +-
 gdk/broadway/broadwayd.c               | 128 +-------
 gdk/broadway/gdkdisplay-broadway.c     |   2 +-
 gdk/broadway/gdkdrawcontext-broadway.c |   2 +
 gsk/gskbroadwayrenderer.c              | 581 +++++++++++++++++++--------------
 7 files changed, 596 insertions(+), 397 deletions(-)
---
diff --git a/gdk/broadway/broadway-protocol.h b/gdk/broadway/broadway-protocol.h
index 5c2d923a10..70c5c18522 100644
--- a/gdk/broadway/broadway-protocol.h
+++ b/gdk/broadway/broadway-protocol.h
@@ -24,6 +24,7 @@ typedef enum { /* Sync changes with broadway.js */
   BROADWAY_NODE_KEEP_THIS = 12,
   BROADWAY_NODE_TRANSLATE = 13,
   BROADWAY_NODE_DEBUG = 14,
+  BROADWAY_NODE_REUSE = 15,
 } BroadwayNodeType;
 
 static const char *broadway_node_type_names[] G_GNUC_UNUSED =  {
diff --git a/gdk/broadway/broadway-server.c b/gdk/broadway/broadway-server.c
index 951c7035c1..ac9c424cf9 100644
--- a/gdk/broadway/broadway-server.c
+++ b/gdk/broadway/broadway-server.c
@@ -35,7 +35,6 @@
 #include <string.h>
 #endif
 
-
 typedef struct {
   int id;
   guint32 tag;
@@ -126,23 +125,56 @@ struct BroadwaySurface {
   gint32 transient_for;
   guint32 texture;
   BroadwayNode *nodes;
+  GHashTable *node_lookup;
+};
+
+struct _BroadwayTexture {
+  grefcount refcount;
+  guint32 id;
+  GBytes *bytes;
 };
 
 static void broadway_server_resync_surfaces (BroadwayServer *server);
 static void send_outstanding_roundtrips (BroadwayServer *server);
 
+static void broadway_server_ref_texture (BroadwayServer   *server,
+                                         guint32           id);
+
 static GType broadway_server_get_type (void);
 
 G_DEFINE_TYPE (BroadwayServer, broadway_server, G_TYPE_OBJECT)
 
 static void
-broadway_node_free (BroadwayNode *node)
+broadway_texture_free (BroadwayTexture *texture)
+{
+  g_bytes_unref (texture->bytes);
+  g_free (texture);
+}
+
+static void
+broadway_node_unref (BroadwayServer *server,
+                     BroadwayNode *node)
 {
   int i;
-  for (i = 0; i < node->n_children; i++)
-    broadway_node_free (node->children[i]);
 
-  g_free (node);
+  if (g_ref_count_dec (&node->refcount))
+    {
+      for (i = 0; i < node->n_children; i++)
+        broadway_node_unref (server, node->children[i]);
+
+      if (node->texture_id)
+        broadway_server_release_texture (server, node->texture_id);
+
+      g_free (node);
+    }
+}
+
+static BroadwayNode *
+broadway_node_ref (BroadwayNode *node)
+{
+  g_ref_count_inc (&node->refcount);
+
+  return node;
 }
 
 gboolean
@@ -204,7 +236,7 @@ broadway_server_init (BroadwayServer *server)
   server->surface_id_hash = g_hash_table_new (NULL, NULL);
   server->id_counter = 0;
   server->textures = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
-                                            (GDestroyNotify)g_bytes_unref);
+                                            (GDestroyNotify)broadway_texture_free);
 
   root = g_new0 (BroadwaySurface, 1);
   root->id = server->id_counter++;
@@ -241,10 +273,12 @@ broadway_server_class_init (BroadwayServerClass * class)
 }
 
 static void
-broadway_surface_free (BroadwaySurface *surface)
+broadway_surface_free (BroadwayServer *server,
+                       BroadwaySurface *surface)
 {
   if (surface->nodes)
-    broadway_node_free (surface->nodes);
+    broadway_node_unref (server, surface->nodes);
+  g_hash_table_unref (surface->node_lookup);
   g_free (surface);
 }
 
@@ -1477,7 +1511,7 @@ broadway_server_destroy_surface (BroadwayServer *server,
       server->surfaces = g_list_remove (server->surfaces, surface);
       g_hash_table_remove (server->surface_id_hash,
                            GINT_TO_POINTER (id));
-      broadway_surface_free (surface);
+      broadway_surface_free (server, surface);
     }
 }
 
@@ -1605,53 +1639,232 @@ broadway_server_has_client (BroadwayServer *server)
   return server->output != NULL;
 }
 
+#define NODE_SIZE_COLOR 1
+#define NODE_SIZE_FLOAT 1
+#define NODE_SIZE_POINT 2
+#define NODE_SIZE_SIZE 2
+#define NODE_SIZE_RECT (NODE_SIZE_POINT + NODE_SIZE_SIZE)
+#define NODE_SIZE_RRECT (NODE_SIZE_RECT + 4 * NODE_SIZE_SIZE)
+#define NODE_SIZE_COLOR_STOP (NODE_SIZE_FLOAT + NODE_SIZE_COLOR)
+#define NODE_SIZE_SHADOW (NODE_SIZE_COLOR + 3 * NODE_SIZE_FLOAT)
+
+static guint32
+rotl (guint32 value, int shift)
+{
+  if ((shift &= 32 - 1) == 0)
+    return value;
+  return (value << shift) | (value >> (32 - shift));
+}
+
+static BroadwayNode *
+decode_nodes (BroadwayServer *server,
+              BroadwaySurface *surface,
+              int len,
+              guint32 data[],
+              GHashTable  *client_texture_map,
+              int *pos)
+{
+  BroadwayNode *node;
+  guint32 type, id;
+  guint32 i, n_stops, n_shadows, n_chars;
+  guint32 size, n_children;
+  gint32 texture_offset;
+  guint32 hash;
+
+  g_assert (*pos < len);
+
+  size = 0;
+  n_children = 0;
+  texture_offset = -1;
+
+  type = data[(*pos)++];
+  id = data[(*pos)++];
+  switch (type) {
+  case BROADWAY_NODE_REUSE:
+    node = g_hash_table_lookup (surface->node_lookup, GINT_TO_POINTER(id));
+    g_assert (node != NULL);
+    return broadway_node_ref (node);
+    break;
+  case BROADWAY_NODE_COLOR:
+    size = NODE_SIZE_RECT + NODE_SIZE_COLOR;
+    break;
+  case BROADWAY_NODE_BORDER:
+    size = NODE_SIZE_RRECT + 4 * NODE_SIZE_FLOAT + 4 * NODE_SIZE_COLOR;
+    break;
+  case BROADWAY_NODE_INSET_SHADOW:
+  case BROADWAY_NODE_OUTSET_SHADOW:
+    size = NODE_SIZE_RRECT + NODE_SIZE_COLOR + 4 * NODE_SIZE_FLOAT;
+    break;
+  case BROADWAY_NODE_TEXTURE:
+    texture_offset = 4;
+    size = 5;
+    break;
+  case BROADWAY_NODE_CONTAINER:
+    size = 1;
+    n_children = data[*pos];
+    break;
+  case BROADWAY_NODE_ROUNDED_CLIP:
+    size = NODE_SIZE_RRECT;
+    n_children = 1;
+    break;
+  case BROADWAY_NODE_CLIP:
+    size = NODE_SIZE_RECT;
+    n_children = 1;
+    break;
+  case BROADWAY_NODE_TRANSLATE:
+    size = NODE_SIZE_POINT;
+    n_children = 1;
+    break;
+  case BROADWAY_NODE_LINEAR_GRADIENT:
+    size = NODE_SIZE_RECT + 2 * NODE_SIZE_POINT;
+    n_stops = data[*pos + size++];
+    size += n_stops * NODE_SIZE_COLOR_STOP;
+    break;
+  case BROADWAY_NODE_SHADOW:
+    size = 1;
+    n_shadows = data[*pos];
+    size += n_shadows * NODE_SIZE_SHADOW;
+    n_children = 1;
+    break;
+  case BROADWAY_NODE_OPACITY:
+    size = NODE_SIZE_FLOAT;
+    n_children = 1;
+    break;
+  case BROADWAY_NODE_DEBUG:
+    n_chars = data[*pos];
+    size = 1 + (n_chars + 3) / 4;
+    n_children = 1;
+    break;
+  default:
+    g_assert_not_reached ();
+  }
+
+  node = g_malloc (sizeof(BroadwayNode) + (size - 1) * sizeof(guint32) + n_children * sizeof (BroadwayNode 
*));
+  g_ref_count_init (&node->refcount);
+  node->type = type;
+  node->id = id;
+  node->texture_id = 0;
+  node->n_children = n_children;
+  node->children = (BroadwayNode **)((char *)node + sizeof(BroadwayNode) + (size - 1) * sizeof(guint32));
+  node->n_data = size;
+  for (i = 0; i < size; i++)
+    {
+      node->data[i] = data[(*pos)++];
+      if (i == texture_offset)
+        {
+          node->texture_id = GPOINTER_TO_INT (g_hash_table_lookup (client_texture_map, GINT_TO_POINTER 
(node->data[i])));
+          broadway_server_ref_texture (server, node->texture_id);
+          node->data[i] = node->texture_id;
+        }
+    }
+
+  for (i = 0; i < n_children; i++)
+    node->children[i] = decode_nodes (server, surface, len, data, client_texture_map, pos);
+
+  hash = node->type << 16;
+
+  for (i = 0; i < size; i++)
+    hash ^= rotl (node->data[i], i);
+
+  for (i = 0; i < n_children; i++)
+    hash ^= rotl (node->children[i]->hash, i);
+
+  node->hash = hash;
+
+  return node;
+}
+
+static void
+init_node_lookup (BroadwaySurface *surface,
+                  BroadwayNode *node)
+{
+  int i;
+
+  g_hash_table_insert (surface->node_lookup, GINT_TO_POINTER(node->id), node);
+  for (i = 0; i < node->n_children; i++)
+    init_node_lookup (surface, node->children[i]);
+}
+
 /* passes ownership of nodes */
 void
-broadway_server_surface_set_nodes (BroadwayServer   *server,
-                                   gint              id,
-                                   BroadwayNode     *root)
+broadway_server_surface_update_nodes (BroadwayServer   *server,
+                                      gint              id,
+                                      guint32          data[],
+                                      int              len,
+                                      GHashTable      *client_texture_map)
 {
   BroadwaySurface *surface;
+  int pos = 0;
+  BroadwayNode *root;
 
   surface = broadway_server_lookup_surface (server, id);
   if (surface == NULL)
     return;
 
+  root = decode_nodes (server, surface, len, data, client_texture_map, &pos);
+
   if (server->output != NULL)
     broadway_output_surface_set_nodes (server->output, surface->id,
                                        root,
                                        surface->nodes);
 
   if (surface->nodes)
-    broadway_node_free (surface->nodes);
+    broadway_node_unref (server, surface->nodes);
+
   surface->nodes = root;
+
+  g_hash_table_remove_all (surface->node_lookup);
+
+  init_node_lookup (surface, surface->nodes);
 }
 
 guint32
 broadway_server_upload_texture (BroadwayServer   *server,
-                                GBytes           *texture)
+                                GBytes           *bytes)
 {
-  guint32 id;
+  BroadwayTexture *texture;
+
+  texture = g_new0 (BroadwayTexture, 1);
+  g_ref_count_init (&texture->refcount);
+  texture->id = ++server->next_texture_id;
+  texture->bytes = g_bytes_ref (bytes);
 
-  id = ++server->next_texture_id;
   g_hash_table_replace (server->textures,
-                        GINT_TO_POINTER (id),
-                        g_bytes_ref (texture));
+                        GINT_TO_POINTER (texture->id),
+                        texture);
 
   if (server->output)
-    broadway_output_upload_texture (server->output, id, texture);
+    broadway_output_upload_texture (server->output, texture->id, texture->bytes);
+
+  return texture->id;
+}
+
+static void
+broadway_server_ref_texture (BroadwayServer   *server,
+                             guint32           id)
+{
+  BroadwayTexture *texture;
 
-  return id;
+  texture = g_hash_table_lookup (server->textures, GINT_TO_POINTER (id));
+  if (texture)
+    g_ref_count_inc (&texture->refcount);
 }
 
 void
 broadway_server_release_texture (BroadwayServer   *server,
                                  guint32           id)
 {
-  g_hash_table_remove (server->textures, GINT_TO_POINTER (id));
+  BroadwayTexture *texture;
 
-  if (server->output)
-    broadway_output_release_texture (server->output, id);
+  texture = g_hash_table_lookup (server->textures, GINT_TO_POINTER (id));
+
+  if (texture && g_ref_count_dec (&texture->refcount))
+    {
+      g_hash_table_remove (server->textures, GINT_TO_POINTER (id));
+
+      if (server->output)
+        broadway_output_release_texture (server->output, id);
+    }
 }
 
 gboolean
@@ -1801,6 +2014,7 @@ broadway_server_new_surface (BroadwayServer *server,
   surface->width = width;
   surface->height = height;
   surface->is_temp = is_temp;
+  surface->node_lookup = g_hash_table_new (g_direct_hash, g_direct_equal);
 
   g_hash_table_insert (server->surface_id_hash,
                        GINT_TO_POINTER (surface->id),
@@ -1835,9 +2049,12 @@ broadway_server_resync_surfaces (BroadwayServer *server)
   /* First upload all textures */
   g_hash_table_iter_init (&iter, server->textures);
   while (g_hash_table_iter_next (&iter, &key, &value))
-    broadway_output_upload_texture (server->output,
-                                    GPOINTER_TO_INT (key),
-                                    (GBytes *)value);
+    {
+      BroadwayTexture *texture = value;
+      broadway_output_upload_texture (server->output,
+                                      GPOINTER_TO_INT (key),
+                                      texture->bytes);
+    }
 
   /* Then create all surfaces */
   for (l = server->surfaces; l != NULL; l = l->next)
diff --git a/gdk/broadway/broadway-server.h b/gdk/broadway/broadway-server.h
index 73ec89e051..1e125bd065 100644
--- a/gdk/broadway/broadway-server.h
+++ b/gdk/broadway/broadway-server.h
@@ -19,12 +19,16 @@ typedef struct _BroadwayServerClass BroadwayServerClass;
 #define BROADWAY_SERVER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), BROADWAY_TYPE_SERVER, 
BroadwayServerClass))
 
 typedef struct _BroadwayNode BroadwayNode;
+typedef struct _BroadwayTexture BroadwayTexture;
 
 struct _BroadwayNode {
+  grefcount refcount;
   guint32 type;
+  guint32 id;
   guint32 hash; /* deep hash */
   guint32 n_children;
   BroadwayNode **children;
+  guint32 texture_id;
   guint32 n_data;
   guint32 data[1];
 };
@@ -99,9 +103,11 @@ void                broadway_server_release_texture           (BroadwayServer  *
                                                                guint32          id);
 cairo_surface_t   * broadway_server_create_surface            (int              width,
                                                                int              height);
-void                broadway_server_surface_set_nodes         (BroadwayServer  *server,
+void                broadway_server_surface_update_nodes      (BroadwayServer  *server,
                                                                gint             id,
-                                                               BroadwayNode    *root);
+                                                               guint32          data[],
+                                                               int              len,
+                                                               GHashTable      *client_texture_map);
 gboolean            broadway_server_surface_move_resize       (BroadwayServer  *server,
                                                                gint             id,
                                                                gboolean         with_move,
diff --git a/gdk/broadway/broadwayd.c b/gdk/broadway/broadwayd.c
index e691f3ce90..a3b402f1f6 100644
--- a/gdk/broadway/broadwayd.c
+++ b/gdk/broadway/broadwayd.c
@@ -215,125 +215,6 @@ get_client_serial (BroadwayClient *client, guint32 daemon_serial)
   return client_serial;
 }
 
-#define NODE_SIZE_COLOR 1
-#define NODE_SIZE_FLOAT 1
-#define NODE_SIZE_POINT 2
-#define NODE_SIZE_SIZE 2
-#define NODE_SIZE_RECT (NODE_SIZE_POINT + NODE_SIZE_SIZE)
-#define NODE_SIZE_RRECT (NODE_SIZE_RECT + 4 * NODE_SIZE_SIZE)
-#define NODE_SIZE_COLOR_STOP (NODE_SIZE_FLOAT + NODE_SIZE_COLOR)
-#define NODE_SIZE_SHADOW (NODE_SIZE_COLOR + 3 * NODE_SIZE_FLOAT)
-
-static guint32
-rotl (guint32 value, int shift)
-{
-  if ((shift &= 32 - 1) == 0)
-    return value;
-  return (value << shift) | (value >> (32 - shift));
-}
-
-static BroadwayNode *
-decode_nodes (BroadwayClient *client,
-              int len, guint32 data[], int *pos)
-{
-  BroadwayNode *node;
-  guint32 type;
-  guint32 i, n_stops, n_shadows, n_chars;
-  guint32 size, n_children;
-  gint32 texture_offset;
-  guint32 hash;
-
-  g_assert (*pos < len);
-
-  size = 0;
-  n_children = 0;
-  texture_offset = -1;
-
-  type = data[(*pos)++];
-  switch (type) {
-  case BROADWAY_NODE_COLOR:
-    size = NODE_SIZE_RECT + NODE_SIZE_COLOR;
-    break;
-  case BROADWAY_NODE_BORDER:
-    size = NODE_SIZE_RRECT + 4 * NODE_SIZE_FLOAT + 4 * NODE_SIZE_COLOR;
-    break;
-  case BROADWAY_NODE_INSET_SHADOW:
-  case BROADWAY_NODE_OUTSET_SHADOW:
-    size = NODE_SIZE_RRECT + NODE_SIZE_COLOR + 4 * NODE_SIZE_FLOAT;
-    break;
-  case BROADWAY_NODE_TEXTURE:
-    texture_offset = 4;
-    size = 5;
-    break;
-  case BROADWAY_NODE_CONTAINER:
-    size = 1;
-    n_children = data[*pos];
-    break;
-  case BROADWAY_NODE_ROUNDED_CLIP:
-    size = NODE_SIZE_RRECT;
-    n_children = 1;
-    break;
-  case BROADWAY_NODE_CLIP:
-    size = NODE_SIZE_RECT;
-    n_children = 1;
-    break;
-  case BROADWAY_NODE_TRANSLATE:
-    size = NODE_SIZE_POINT;
-    n_children = 1;
-    break;
-  case BROADWAY_NODE_LINEAR_GRADIENT:
-    size = NODE_SIZE_RECT + 2 * NODE_SIZE_POINT;
-    n_stops = data[*pos + size++];
-    size += n_stops * NODE_SIZE_COLOR_STOP;
-    break;
-  case BROADWAY_NODE_SHADOW:
-    size = 1;
-    n_shadows = data[*pos];
-    size += n_shadows * NODE_SIZE_SHADOW;
-    n_children = 1;
-    break;
-  case BROADWAY_NODE_OPACITY:
-    size = NODE_SIZE_FLOAT;
-    n_children = 1;
-    break;
-  case BROADWAY_NODE_DEBUG:
-    n_chars = data[*pos];
-    size = 1 + (n_chars + 3) / 4;
-    n_children = 1;
-    break;
-  default:
-    g_assert_not_reached ();
-  }
-
-  node = g_malloc (sizeof(BroadwayNode) + (size - 1) * sizeof(guint32) + n_children * sizeof (BroadwayNode 
*));
-  node->type = type;
-  node->n_children = n_children;
-  node->children = (BroadwayNode **)((char *)node + sizeof(BroadwayNode) + (size - 1) * sizeof(guint32));
-  node->n_data = size;
-  for (i = 0; i < size; i++)
-    {
-      node->data[i] = data[(*pos)++];
-      if (i == texture_offset)
-        node->data[i] = GPOINTER_TO_INT (g_hash_table_lookup (client->textures,
-                                                              GINT_TO_POINTER (node->data[i])));
-    }
-
-  for (i = 0; i < n_children; i++)
-    node->children[i] = decode_nodes (client, len, data, pos);
-
-  hash = node->type << 16;
-
-  for (i = 0; i < size; i++)
-    hash ^= rotl (node->data[i], i);
-
-  for (i = 0; i < n_children; i++)
-    hash ^= rotl (node->children[i]->hash, i);
-
-  node->hash = hash;
-
-  return node;
-}
-
 static void
 client_handle_request (BroadwayClient *client,
                        BroadwayRequest *request)
@@ -409,13 +290,10 @@ client_handle_request (BroadwayClient *client,
       {
         gsize array_size = request->base.size - sizeof (BroadwayRequestSetNodes) + sizeof(guint32);
         int n_data = array_size / sizeof(guint32);
-        int pos = 0;
-        BroadwayNode *node;
-
-        node = decode_nodes (client, n_data, request->set_nodes.data, &pos);
 
-        broadway_server_surface_set_nodes (server, request->set_nodes.id,
-                                           node);
+        broadway_server_surface_update_nodes (server, request->set_nodes.id,
+                                              request->set_nodes.data, n_data,
+                                              client->textures);
       }
       break;
     case BROADWAY_REQUEST_UPLOAD_TEXTURE:
diff --git a/gdk/broadway/gdkdisplay-broadway.c b/gdk/broadway/gdkdisplay-broadway.c
index f27af0b3ef..9a80ec8d55 100644
--- a/gdk/broadway/gdkdisplay-broadway.c
+++ b/gdk/broadway/gdkdisplay-broadway.c
@@ -380,7 +380,7 @@ gdk_broadway_display_ensure_texture (GdkDisplay *display,
       data = g_new0 (BroadwayTextureData, 1);
       data->id = id;
       data->display = g_object_ref (display);
-      g_object_set_data_full (G_OBJECT (texture), "broadway-data", data, 
(GDestroyNotify)broadway_texture_data_free);
+     g_object_set_data_full (G_OBJECT (texture), "broadway-data", data, 
(GDestroyNotify)broadway_texture_data_free);
     }
 
   return data->id;
diff --git a/gdk/broadway/gdkdrawcontext-broadway.c b/gdk/broadway/gdkdrawcontext-broadway.c
index 889b52fb30..473488396a 100644
--- a/gdk/broadway/gdkdrawcontext-broadway.c
+++ b/gdk/broadway/gdkdrawcontext-broadway.c
@@ -64,6 +64,8 @@ gdk_broadway_draw_context_end_frame (GdkDrawContext *draw_context,
 
   g_array_unref (self->nodes);
   self->nodes = NULL;
+
+  /* We now sent all new texture refs to the daemon via the nodes, so we can drop them here */
   g_ptr_array_unref (self->node_textures);
   self->node_textures = NULL;
 }
diff --git a/gsk/gskbroadwayrenderer.c b/gsk/gskbroadwayrenderer.c
index 0ebee3702d..0730ca8a73 100644
--- a/gsk/gskbroadwayrenderer.c
+++ b/gsk/gskbroadwayrenderer.c
@@ -13,8 +13,16 @@ struct _GskBroadwayRenderer
 {
   GskRenderer parent_instance;
   GdkBroadwayDrawContext *draw_context;
-  GHashTable *fallback_cache;
-  guint32 frame_nr;
+  guint32 next_node_id;
+
+  /* Set during rendering */
+  GArray *nodes;              /* Owned by draw_contex */
+  GPtrArray *node_textures;   /* Owned by draw_contex */
+  GHashTable *node_lookup;
+
+  /* Kept from last frame */
+  GHashTable *last_node_lookup;
+  GskRenderNode *last_root; /* Owning refs to the things in last_node_lookup */
 };
 
 struct _GskBroadwayRendererClass
@@ -179,46 +187,132 @@ add_string (GArray *nodes, const char *str)
     add_uint32 (nodes, v);
 }
 
-typedef struct {
-  GskRenderNode *node;
-  GdkTexture *texture;
-  float off_x;
-  float off_y;
-  int used_in_frame;
-} FallbackCacheElement;
+static void
+collect_reused_child_nodes (GskRenderer *renderer,
+                            GskRenderNode *node);
 
 static void
-fallback_cache_element_free (FallbackCacheElement *element)
+collect_reused_node (GskRenderer *renderer,
+                     GskRenderNode *node)
 {
-  gsk_render_node_unref (element->node);
-  g_object_unref (element->texture);
-  g_free (element);
+  GskBroadwayRenderer *self = GSK_BROADWAY_RENDERER (renderer);
+  guint32 old_id;
+
+  if (self->last_node_lookup &&
+      (old_id = GPOINTER_TO_INT(g_hash_table_lookup (self->last_node_lookup, node))) != 0)
+    {
+      g_hash_table_insert (self->node_lookup, node, GINT_TO_POINTER (old_id));
+      collect_reused_child_nodes (renderer, node);
+    }
 }
 
-static GdkTexture *
-node_texture_fallback (GskRenderNode *node,
-                       float *off_x,
-                       float *off_y)
+
+static void
+collect_reused_child_nodes (GskRenderer *renderer,
+                            GskRenderNode *node)
 {
-  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;
+  guint i;
 
-  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);
+  switch (gsk_render_node_get_node_type (node))
+    {
+    case GSK_NOT_A_RENDER_NODE:
+      g_assert_not_reached ();
+      return;
 
-  texture = gdk_texture_new_for_surface (surface);
-  *off_x =  x - node->bounds.origin.x;
-  *off_y =  y - node->bounds.origin.y;
+      /* Leaf nodes */
 
-  return texture;
+    case GSK_TEXTURE_NODE:
+    case GSK_CAIRO_NODE:
+    case GSK_COLOR_NODE:
+    case GSK_BORDER_NODE:
+    case GSK_OUTSET_SHADOW_NODE:
+    case GSK_INSET_SHADOW_NODE:
+    case GSK_LINEAR_GRADIENT_NODE:
+
+      /* Fallbacks (=> leaf for now */
+    case GSK_COLOR_MATRIX_NODE:
+    case GSK_TEXT_NODE:
+    case GSK_REPEATING_LINEAR_GRADIENT_NODE:
+    case GSK_REPEAT_NODE:
+    case GSK_BLEND_NODE:
+    case GSK_CROSS_FADE_NODE:
+    case GSK_BLUR_NODE:
+
+    default:
+
+      break;
+
+      /* Bin nodes */
+
+    case GSK_SHADOW_NODE:
+      collect_reused_node (renderer,
+                           gsk_shadow_node_get_child (node));
+      break;
+
+    case GSK_OPACITY_NODE:
+      collect_reused_node (renderer,
+                           gsk_opacity_node_get_child (node));
+      break;
+
+    case GSK_ROUNDED_CLIP_NODE:
+      collect_reused_node (renderer,
+                           gsk_rounded_clip_node_get_child (node));
+      break;
+
+    case GSK_CLIP_NODE:
+      collect_reused_node (renderer,
+                           gsk_clip_node_get_child (node));
+      break;
+
+    case GSK_TRANSFORM_NODE:
+      collect_reused_node (renderer,
+                           gsk_transform_node_get_child (node));
+      break;
+
+    case GSK_DEBUG_NODE:
+      collect_reused_node (renderer,
+                           gsk_debug_node_get_child (node));
+      break;
+
+      /* Generic nodes */
+
+    case GSK_CONTAINER_NODE:
+      for (i = 0; i < gsk_container_node_get_n_children (node); i++)
+        collect_reused_node (renderer,
+                             gsk_container_node_get_child (node, i));
+      break;
+
+      break; /* Fallback */
+    }
+}
+
+static gboolean
+add_new_node (GskRenderer *renderer,
+              GskRenderNode *node,
+              BroadwayNodeType type)
+{
+  GskBroadwayRenderer *self = GSK_BROADWAY_RENDERER (renderer);
+  guint32 id, old_id;
+
+  if (self->last_node_lookup &&
+      (old_id = GPOINTER_TO_INT (g_hash_table_lookup (self->last_node_lookup, node))) != 0)
+    {
+      add_uint32 (self->nodes, BROADWAY_NODE_REUSE);
+      add_uint32 (self->nodes, old_id);
+
+      g_hash_table_insert (self->node_lookup, node, GINT_TO_POINTER(old_id));
+      collect_reused_child_nodes (renderer, node);
+
+      return FALSE;
+    }
+
+  id = ++self->next_node_id;
+  g_hash_table_insert (self->node_lookup, node, GINT_TO_POINTER(id));
+
+  add_uint32 (self->nodes, type);
+  add_uint32 (self->nodes, id);
+
+  return TRUE;
 }
 
 /* Note: This tracks the offset so that we can convert
@@ -227,14 +321,13 @@ node_texture_fallback (GskRenderNode *node,
    which is good for re-using subtrees. */
 static void
 gsk_broadway_renderer_add_node (GskRenderer *renderer,
-                                GArray *nodes,
-                                GPtrArray *node_textures,
                                 GskRenderNode *node,
                                 float offset_x,
                                 float offset_y)
 {
   GdkDisplay *display = gdk_surface_get_display (gsk_renderer_get_surface (renderer));
   GskBroadwayRenderer *self = GSK_BROADWAY_RENDERER (renderer);
+  GArray *nodes = self->nodes;
 
   switch (gsk_render_node_get_node_type (node))
     {
@@ -245,214 +338,218 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
     /* Leaf nodes */
 
     case GSK_TEXTURE_NODE:
-      {
-        GdkTexture *texture = gsk_texture_node_get_texture (node);
-        guint32 texture_id;
+      if (add_new_node (renderer, node, BROADWAY_NODE_TEXTURE))
+        {
+          GdkTexture *texture = gsk_texture_node_get_texture (node);
+          guint32 texture_id;
 
-        g_ptr_array_add (node_textures, g_object_ref (texture)); /* Transfers ownership to node_textures */
-        texture_id = gdk_broadway_display_ensure_texture (display, texture);
+          /* No need to add to self->node_textures here, the node will keep it alive until end of frame. */
 
-        add_uint32 (nodes, BROADWAY_NODE_TEXTURE);
-        add_rect (nodes, &node->bounds, offset_x, offset_y);
-        add_uint32 (nodes, texture_id);
-      }
+          texture_id = gdk_broadway_display_ensure_texture (display, texture);
+
+          add_rect (nodes, &node->bounds, offset_x, offset_y);
+          add_uint32 (nodes, texture_id);
+        }
       return;
 
     case GSK_CAIRO_NODE:
-      {
-        cairo_surface_t *surface = (cairo_surface_t *)gsk_cairo_node_peek_surface (node);
-        cairo_surface_t *image_surface = NULL;
-        GdkTexture *texture;
-        guint32 texture_id;
-
-        if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE)
-          image_surface = cairo_surface_reference (surface);
-        else
-          {
-            cairo_t *cr;
-            image_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
-                                                        ceilf (node->bounds.size.width),
-                                                        ceilf (node->bounds.size.height));
-            cr = cairo_create (image_surface);
-            cairo_set_source_surface (cr, surface, 0, 0);
-            cairo_rectangle (cr, 0, 0, node->bounds.size.width, node->bounds.size.height);
-            cairo_fill (cr);
-            cairo_destroy (cr);
-          }
-
-        texture = gdk_texture_new_for_surface (image_surface);
-        g_ptr_array_add (node_textures, g_object_ref (texture)); /* Transfers ownership to node_textures */
-        texture_id = gdk_broadway_display_ensure_texture (display, texture);
-
-        add_uint32 (nodes, BROADWAY_NODE_TEXTURE);
-        add_rect (nodes, &node->bounds, offset_x, offset_y);
-        add_uint32 (nodes, texture_id);
-
-        cairo_surface_destroy (image_surface);
-      }
+      if (add_new_node (renderer, node, BROADWAY_NODE_TEXTURE))
+        {
+          cairo_surface_t *surface = (cairo_surface_t *)gsk_cairo_node_peek_surface (node);
+          cairo_surface_t *image_surface = NULL;
+          GdkTexture *texture;
+          guint32 texture_id;
+
+          if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE)
+            image_surface = cairo_surface_reference (surface);
+          else
+            {
+              cairo_t *cr;
+              image_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                                          ceilf (node->bounds.size.width),
+                                                          ceilf (node->bounds.size.height));
+              cr = cairo_create (image_surface);
+              cairo_set_source_surface (cr, surface, 0, 0);
+              cairo_rectangle (cr, 0, 0, node->bounds.size.width, node->bounds.size.height);
+              cairo_fill (cr);
+              cairo_destroy (cr);
+            }
+
+          texture = gdk_texture_new_for_surface (image_surface);
+          g_ptr_array_add (self->node_textures, g_object_ref (texture)); /* Transfers ownership to 
node_textures */
+          texture_id = gdk_broadway_display_ensure_texture (display, texture);
+
+          add_rect (nodes, &node->bounds, offset_x, offset_y);
+          add_uint32 (nodes, texture_id);
+
+          cairo_surface_destroy (image_surface);
+        }
       return;
 
     case GSK_COLOR_NODE:
-      {
-        add_uint32 (nodes, BROADWAY_NODE_COLOR);
-        add_rect (nodes, &node->bounds, offset_x, offset_y);
-        add_rgba (nodes, gsk_color_node_peek_color (node));
-      }
+      if (add_new_node (renderer, node, BROADWAY_NODE_COLOR))
+        {
+          add_rect (nodes, &node->bounds, offset_x, offset_y);
+          add_rgba (nodes, gsk_color_node_peek_color (node));
+        }
       return;
 
     case GSK_BORDER_NODE:
-      {
-        int i;
-        add_uint32 (nodes, BROADWAY_NODE_BORDER);
-        add_rounded_rect (nodes, gsk_border_node_peek_outline (node), offset_x, offset_y);
-        for (i = 0; i < 4; i++)
-          add_float (nodes, gsk_border_node_peek_widths (node)[i]);
-        for (i = 0; i < 4; i++)
-          add_rgba (nodes, &gsk_border_node_peek_colors (node)[i]);
-      }
+      if (add_new_node (renderer, node, BROADWAY_NODE_BORDER))
+        {
+          int i;
+          add_rounded_rect (nodes, gsk_border_node_peek_outline (node), offset_x, offset_y);
+          for (i = 0; i < 4; i++)
+            add_float (nodes, gsk_border_node_peek_widths (node)[i]);
+          for (i = 0; i < 4; i++)
+            add_rgba (nodes, &gsk_border_node_peek_colors (node)[i]);
+        }
       return;
 
     case GSK_OUTSET_SHADOW_NODE:
-      {
-        add_uint32 (nodes, BROADWAY_NODE_OUTSET_SHADOW);
-        add_rounded_rect (nodes, gsk_outset_shadow_node_peek_outline (node), offset_x, offset_y);
-        add_rgba (nodes, gsk_outset_shadow_node_peek_color (node));
-        add_float (nodes, gsk_outset_shadow_node_get_dx (node));
-        add_float (nodes, gsk_outset_shadow_node_get_dy (node));
-        add_float (nodes, gsk_outset_shadow_node_get_spread (node));
-        add_float (nodes, gsk_outset_shadow_node_get_blur_radius (node));
-      }
+      if (add_new_node (renderer, node, BROADWAY_NODE_OUTSET_SHADOW))
+        {
+          add_rounded_rect (nodes, gsk_outset_shadow_node_peek_outline (node), offset_x, offset_y);
+          add_rgba (nodes, gsk_outset_shadow_node_peek_color (node));
+          add_float (nodes, gsk_outset_shadow_node_get_dx (node));
+          add_float (nodes, gsk_outset_shadow_node_get_dy (node));
+          add_float (nodes, gsk_outset_shadow_node_get_spread (node));
+          add_float (nodes, gsk_outset_shadow_node_get_blur_radius (node));
+        }
       return;
 
     case GSK_INSET_SHADOW_NODE:
-      {
-        add_uint32 (nodes, BROADWAY_NODE_INSET_SHADOW);
-        add_rounded_rect (nodes, gsk_inset_shadow_node_peek_outline (node), offset_x, offset_y);
-        add_rgba (nodes, gsk_inset_shadow_node_peek_color (node));
-        add_float (nodes, gsk_inset_shadow_node_get_dx (node));
-        add_float (nodes, gsk_inset_shadow_node_get_dy (node));
-        add_float (nodes, gsk_inset_shadow_node_get_spread (node));
-        add_float (nodes, gsk_inset_shadow_node_get_blur_radius (node));
-      }
+      if (add_new_node (renderer, node, BROADWAY_NODE_INSET_SHADOW))
+        {
+          add_rounded_rect (nodes, gsk_inset_shadow_node_peek_outline (node), offset_x, offset_y);
+          add_rgba (nodes, gsk_inset_shadow_node_peek_color (node));
+          add_float (nodes, gsk_inset_shadow_node_get_dx (node));
+          add_float (nodes, gsk_inset_shadow_node_get_dy (node));
+          add_float (nodes, gsk_inset_shadow_node_get_spread (node));
+          add_float (nodes, gsk_inset_shadow_node_get_blur_radius (node));
+        }
       return;
 
     case GSK_LINEAR_GRADIENT_NODE:
-      {
-        guint i, n;
-
-        add_uint32 (nodes, BROADWAY_NODE_LINEAR_GRADIENT);
-        add_rect (nodes, &node->bounds, offset_x, offset_y);
-        add_point (nodes, gsk_linear_gradient_node_peek_start (node), offset_x, offset_y);
-        add_point (nodes, gsk_linear_gradient_node_peek_end (node), offset_x, offset_y);
-        n = gsk_linear_gradient_node_get_n_color_stops (node);
-        add_uint32 (nodes, n);
-        for (i = 0; i < n; i++)
-          add_color_stop (nodes, &gsk_linear_gradient_node_peek_color_stops (node)[i]);
-      }
+      if (add_new_node (renderer, node, BROADWAY_NODE_LINEAR_GRADIENT))
+        {
+          guint i, n;
+
+          add_rect (nodes, &node->bounds, offset_x, offset_y);
+          add_point (nodes, gsk_linear_gradient_node_peek_start (node), offset_x, offset_y);
+          add_point (nodes, gsk_linear_gradient_node_peek_end (node), offset_x, offset_y);
+          n = gsk_linear_gradient_node_get_n_color_stops (node);
+          add_uint32 (nodes, n);
+          for (i = 0; i < n; i++)
+            add_color_stop (nodes, &gsk_linear_gradient_node_peek_color_stops (node)[i]);
+        }
       return;
 
       /* Bin nodes */
 
     case GSK_SHADOW_NODE:
-      {
-        gsize i, n_shadows = gsk_shadow_node_get_n_shadows (node);
-        add_uint32 (nodes, BROADWAY_NODE_SHADOW);
-        add_uint32 (nodes, n_shadows);
-        for (i = 0; i < n_shadows; i++)
-          {
-            const GskShadow *shadow = gsk_shadow_node_peek_shadow (node, i);
-            add_rgba (nodes, &shadow->color);
-            add_float (nodes, shadow->dx);
-            add_float (nodes, shadow->dy);
-            add_float (nodes, shadow->radius);
-          }
-        gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
-                                        gsk_shadow_node_get_child (node),
-                                        offset_x, offset_y);
-      }
+      if (add_new_node (renderer, node, BROADWAY_NODE_SHADOW))
+        {
+          gsize i, n_shadows = gsk_shadow_node_get_n_shadows (node);
+
+          add_uint32 (nodes, n_shadows);
+          for (i = 0; i < n_shadows; i++)
+            {
+              const GskShadow *shadow = gsk_shadow_node_peek_shadow (node, i);
+              add_rgba (nodes, &shadow->color);
+              add_float (nodes, shadow->dx);
+              add_float (nodes, shadow->dy);
+              add_float (nodes, shadow->radius);
+            }
+          gsk_broadway_renderer_add_node (renderer,
+                                          gsk_shadow_node_get_child (node),
+                                          offset_x, offset_y);
+        }
       return;
 
     case GSK_OPACITY_NODE:
-      {
-        add_uint32 (nodes, BROADWAY_NODE_OPACITY);
-        add_float (nodes, gsk_opacity_node_get_opacity (node));
-        gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
-                                        gsk_opacity_node_get_child (node),
-                                        offset_x, offset_y);
-      }
+      if (add_new_node (renderer, node, BROADWAY_NODE_OPACITY))
+        {
+          add_float (nodes, gsk_opacity_node_get_opacity (node));
+          gsk_broadway_renderer_add_node (renderer,
+                                          gsk_opacity_node_get_child (node),
+                                          offset_x, offset_y);
+        }
       return;
 
     case GSK_ROUNDED_CLIP_NODE:
-      {
-        const GskRoundedRect *rclip = gsk_rounded_clip_node_peek_clip (node);
-        add_uint32 (nodes, BROADWAY_NODE_ROUNDED_CLIP);
-        add_rounded_rect (nodes, rclip, offset_x, offset_y);
-        gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
-                                        gsk_rounded_clip_node_get_child (node),
-                                        rclip->bounds.origin.x,
-                                        rclip->bounds.origin.y);
-      }
+      if (add_new_node (renderer, node, BROADWAY_NODE_ROUNDED_CLIP))
+        {
+          const GskRoundedRect *rclip = gsk_rounded_clip_node_peek_clip (node);
+
+          add_rounded_rect (nodes, rclip, offset_x, offset_y);
+          gsk_broadway_renderer_add_node (renderer,
+                                          gsk_rounded_clip_node_get_child (node),
+                                          rclip->bounds.origin.x,
+                                          rclip->bounds.origin.y);
+        }
       return;
 
     case GSK_CLIP_NODE:
-      {
-        const graphene_rect_t *clip = gsk_clip_node_peek_clip (node);
-        add_uint32 (nodes, BROADWAY_NODE_CLIP);
-        add_rect (nodes, clip, offset_x, offset_y);
-        gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
-                                        gsk_clip_node_get_child (node),
-                                        clip->origin.x,
-                                        clip->origin.y);
-      }
+      if (add_new_node (renderer, node, BROADWAY_NODE_CLIP))
+        {
+          const graphene_rect_t *clip = gsk_clip_node_peek_clip (node);
+
+          add_rect (nodes, clip, offset_x, offset_y);
+          gsk_broadway_renderer_add_node (renderer,
+                                          gsk_clip_node_get_child (node),
+                                          clip->origin.x,
+                                          clip->origin.y);
+        }
       return;
 
     case GSK_TRANSFORM_NODE:
       {
         GskTransform *transform = gsk_transform_node_get_transform (node);
         GskTransformCategory category = gsk_transform_get_category (transform);
-        float dx, dy;
 
         if (category >= GSK_TRANSFORM_CATEGORY_2D_TRANSLATE)
           {
-            gsk_transform_to_translate (transform, &dx, &dy);
-            add_uint32 (nodes, BROADWAY_NODE_TRANSLATE);
-            add_xy (nodes, dx, dy, offset_x, offset_y);
-            gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
-                                            gsk_transform_node_get_child (node),
-                                            0, 0);
+            if (add_new_node (renderer, node, BROADWAY_NODE_TRANSLATE)) {
+              float dx, dy;
+              gsk_transform_to_translate (transform, &dx, &dy);
+
+              add_xy (nodes, dx, dy, offset_x, offset_y);
+              gsk_broadway_renderer_add_node (renderer,
+                                              gsk_transform_node_get_child (node),
+                                              0, 0);
+            }
           }
         else
           {
             /* Fallback to texture for now */
             break;
           }
-
       }
       return;
 
     case GSK_DEBUG_NODE:
-      {
-        const char *message = gsk_debug_node_get_message (node);
-        add_uint32 (nodes, BROADWAY_NODE_DEBUG);
-        add_string (nodes, message);
-        gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
-                                        gsk_debug_node_get_child (node), offset_x, offset_y);
-      }
+      if (add_new_node (renderer, node, BROADWAY_NODE_DEBUG))
+        {
+          const char *message = gsk_debug_node_get_message (node);
+          add_string (nodes, message);
+          gsk_broadway_renderer_add_node (renderer,
+                                          gsk_debug_node_get_child (node), offset_x, offset_y);
+        }
       return;
 
       /* Generic nodes */
 
     case GSK_CONTAINER_NODE:
-      {
-        guint i;
-
-        add_uint32 (nodes, BROADWAY_NODE_CONTAINER);
-        add_uint32 (nodes, gsk_container_node_get_n_children (node));
+      if (add_new_node (renderer, node, BROADWAY_NODE_CONTAINER))
+        {
+          guint i;
 
-        for (i = 0; i < gsk_container_node_get_n_children (node); i++)
-          gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
-                                          gsk_container_node_get_child (node, i), offset_x, offset_y);
-      }
+          add_uint32 (nodes, gsk_container_node_get_n_children (node));
+          for (i = 0; i < gsk_container_node_get_n_children (node); i++)
+            gsk_broadway_renderer_add_node (renderer,
+                                            gsk_container_node_get_child (node, i), offset_x, offset_y);
+        }
       return;
 
     case GSK_COLOR_MATRIX_NODE:
@@ -466,58 +563,33 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
       break; /* Fallback */
     }
 
-  {
-    GdkTexture *texture;
-    guint32 texture_id;
-    FallbackCacheElement *hit;
-    float t_off_x = 0, t_off_y = 0;
-
-    hit = g_hash_table_lookup (self->fallback_cache, node);
-    if (hit)
-      {
-        texture = g_object_ref (hit->texture);
-        t_off_x = hit->off_x;
-        t_off_y = hit->off_y;
-        hit->used_in_frame = self->frame_nr;
-      }
-    else
-      {
-        FallbackCacheElement *element;
-
-        texture = node_texture_fallback (node, &t_off_x, &t_off_y);
-#if 0
-        g_print ("Fallback %p for %s\n", texture, node->node_class->type_name);
-#endif
-        element = g_new0 (FallbackCacheElement, 1);
-        element->texture = g_object_ref (texture);
-        element->node = gsk_render_node_ref (node);
-        element->off_x = t_off_x;
-        element->off_y = t_off_y;
-        element->used_in_frame = self->frame_nr;
-        g_hash_table_insert (self->fallback_cache, element->node, element);
-      }
-
-    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_float (nodes, node->bounds.origin.x + t_off_x - offset_x);
-    add_float (nodes, node->bounds.origin.y + t_off_y - offset_y);
-    add_float (nodes, gdk_texture_get_width (texture));
-    add_float (nodes, gdk_texture_get_height (texture));
-    add_uint32 (nodes, texture_id);
-  }
-}
-
-static gboolean
-clean_old_fallbacks (gpointer  key,
-                     gpointer  value,
-                     gpointer  user_data)
-{
-  GskBroadwayRenderer *self = GSK_BROADWAY_RENDERER (user_data);
-  FallbackCacheElement *element = value;
-
-  /* Remove cached fallbacks not used for 5 frames */
-  return self->frame_nr - element->used_in_frame > 5;
+  if (add_new_node (renderer, node, BROADWAY_NODE_TEXTURE))
+    {
+      GdkTexture *texture;
+      cairo_surface_t *surface;
+      cairo_t *cr;
+      guint32 texture_id;
+      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;
+
+      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);
+      g_ptr_array_add (self->node_textures, texture); /* Transfers ownership to node_textures */
+
+      texture_id = gdk_broadway_display_ensure_texture (display, texture);
+      add_float (nodes, x - offset_x);
+      add_float (nodes, y - offset_y);
+      add_float (nodes, gdk_texture_get_width (texture));
+      add_float (nodes, gdk_texture_get_height (texture));
+      add_uint32 (nodes, texture_id);
+    }
 }
 
 static void
@@ -527,13 +599,40 @@ gsk_broadway_renderer_render (GskRenderer          *renderer,
 {
   GskBroadwayRenderer *self = GSK_BROADWAY_RENDERER (renderer);
 
-  self->frame_nr++;
+  self->node_lookup = g_hash_table_new (g_direct_hash, g_direct_equal);
 
   gdk_draw_context_begin_frame (GDK_DRAW_CONTEXT (self->draw_context), update_area);
-  gsk_broadway_renderer_add_node (renderer, self->draw_context->nodes, self->draw_context->node_textures, 
root, 0, 0);
+
+  /* These are owned by the draw context between begin and end, but
+     cache them here for easier access during the render */
+  self->nodes = self->draw_context->nodes;
+  self->node_textures = self->draw_context->node_textures;
+
+  gsk_broadway_renderer_add_node (renderer, root, 0, 0);
+
+  self->nodes = NULL;
+  self->node_textures = NULL;
+
   gdk_draw_context_end_frame (GDK_DRAW_CONTEXT (self->draw_context));
 
-  g_hash_table_foreach_remove (self->fallback_cache, clean_old_fallbacks, renderer);
+  if (self->last_node_lookup)
+    g_hash_table_unref (self->last_node_lookup);
+  self->last_node_lookup = self->node_lookup;
+  self->node_lookup = NULL;
+
+  if (self->last_root)
+    gsk_render_node_unref (self->last_root);
+  self->last_root = gsk_render_node_ref (root);
+
+  if (self->next_node_id > G_MAXUINT32 / 2)
+    {
+      /* We're "near" a wrap of the ids, lets avoid reusing any of
+       * these nodes next frame, then we can reset the id counter
+       * without risk of any old nodes sticking around and conflicting. */
+
+      g_hash_table_remove_all (self->last_node_lookup);
+      self->next_node_id = 0;
+    }
 }
 
 static void
@@ -550,8 +649,4 @@ gsk_broadway_renderer_class_init (GskBroadwayRendererClass *klass)
 static void
 gsk_broadway_renderer_init (GskBroadwayRenderer *self)
 {
-  self->fallback_cache = g_hash_table_new_full ((GHashFunc)g_direct_hash,
-                                                (GEqualFunc)g_direct_equal,
-                                                NULL,
-                                                (GDestroyNotify)fallback_cache_element_free);
 }



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