[gtk+/wip/alexl/broadway4] broadway: Send diffs of node trees



commit b04adb905d124e24f5a62786f88312d7f2187c94
Author: Alexander Larsson <alexl redhat com>
Date:   Thu Nov 30 10:36:30 2017 +0100

    broadway: Send diffs of node trees
    
    Reusing pre-created nodes is a lot faster both in terms of
    dom modifications and of transfer sizes.

 gdk/broadway/broadway-output.c   |   52 +++++++++-
 gdk/broadway/broadway-protocol.h |    2 +
 gdk/broadway/broadway-server.c   |   39 ++++++--
 gdk/broadway/broadway-server.h   |    6 +-
 gdk/broadway/broadway.js         |  204 ++++++++++++++++++++++---------------
 gdk/broadway/broadwayd.c         |    7 +-
 gsk/gskbroadwayrenderer.c        |  130 +++++++++++++-----------
 7 files changed, 279 insertions(+), 161 deletions(-)
---
diff --git a/gdk/broadway/broadway-output.c b/gdk/broadway/broadway-output.c
index 9389de4..2265262 100644
--- a/gdk/broadway/broadway-output.c
+++ b/gdk/broadway/broadway-output.c
@@ -309,17 +309,61 @@ broadway_output_set_transient_for (BroadwayOutput *output,
   append_uint16 (output, parent_id);
 }
 
+/***********************************
+ * This outputs the tree to the client, while at the same time diffing
+ * against the old tree.  This allows us to avoid sending certain
+ * parts.
+ *
+ * Reusing existing dom nodes are problematic because doing so
+ * automatically inherits all their children.  There are two cases
+ * where we do this:
+ *
+ * If the entire sub tree is identical we emit a KEEP_ALL node which
+ * just reuses the entire old dom subtree.
+ *
+ * If a the node is unchanged (but some descendant may have changed),
+ * and all parents are also unchanged, then we can just avoid
+ * changing the dom node at all, and we emit a KEEP_THIS node.
+ *
+ ***********************************/
+
 static void
 append_node (BroadwayOutput *output,
-            BroadwayNode   *node)
+             BroadwayNode   *node,
+             BroadwayNode   *old_node,
+             gboolean        all_parents_are_kept)
 {
-  append_uint32 (output, node->type);
   guint32 i;
 
+  if (old_node != NULL && broadway_node_equal (node, old_node))
+    {
+      if (broadway_node_deep_equal (node, old_node))
+        {
+          append_uint32 (output, BROADWAY_NODE_KEEP_ALL);
+          return;
+        }
+
+      if (all_parents_are_kept)
+        {
+          append_uint32 (output, BROADWAY_NODE_KEEP_THIS);
+          append_uint32 (output, node->n_children);
+          for (i = 0; i < node->n_children; i++)
+            append_node (output, node->children[i],
+                         i < old_node->n_children ? old_node->children[i] : NULL,
+                         TRUE);
+          return;
+        }
+    }
+
+  append_uint32 (output, node->type);
+
   for (i = 0; i < node->n_data; i++)
     append_uint32 (output, node->data[i]);
   for (i = 0; i < node->n_children; i++)
-    append_node (output, node->children[i]);
+    append_node (output,
+                 node->children[i],
+                 (old_node != NULL && i < old_node->n_children) ? old_node->children[i] : NULL,
+                 FALSE);
 }
 
 void
@@ -337,7 +381,7 @@ broadway_output_window_set_nodes (BroadwayOutput *output,
   append_uint32 (output, 0);
 
   start = output->buf->len;
-  append_node (output, root);
+  append_node (output, root, old_root, TRUE);
   end = output->buf->len;
   patch_uint32 (output, (end - start) / 4, size_pos);
 }
diff --git a/gdk/broadway/broadway-protocol.h b/gdk/broadway/broadway-protocol.h
index 41861ef..3457d2c 100644
--- a/gdk/broadway/broadway-protocol.h
+++ b/gdk/broadway/broadway-protocol.h
@@ -20,6 +20,8 @@ typedef enum { /* Sync changes with broadway.js */
   BROADWAY_NODE_SHADOW = 8,
   BROADWAY_NODE_OPACITY = 9,
   BROADWAY_NODE_CLIP = 10,
+  BROADWAY_NODE_KEEP_ALL = 11,
+  BROADWAY_NODE_KEEP_THIS = 12,
 } BroadwayNodeType;
 
 typedef enum {
diff --git a/gdk/broadway/broadway-server.c b/gdk/broadway/broadway-server.c
index bec36e3..18e37d6 100644
--- a/gdk/broadway/broadway-server.c
+++ b/gdk/broadway/broadway-server.c
@@ -147,33 +147,51 @@ broadway_node_free (BroadwayNode *node)
 
 gboolean
 broadway_node_equal (BroadwayNode     *a,
-                    BroadwayNode     *b)
+                     BroadwayNode     *b)
 {
   int i;
 
-  if (a->hash != b->hash)
-    return FALSE;
-
   if (a->type != b->type)
     return FALSE;
 
   if (a->n_data != b->n_data)
     return FALSE;
 
-  if (a->n_children != b->n_children)
+  /* Don't check data for containers, that is just n_children, which
+     we don't want to compare for a shallow equal */
+  if (a->type != BROADWAY_NODE_CONTAINER)
+    {
+      for (i = 0; i < a->n_data; i++)
+        if (a->data[i] != b->data[i])
+          return FALSE;
+    }
+
+  return TRUE;
+}
+
+gboolean
+broadway_node_deep_equal (BroadwayNode     *a,
+                          BroadwayNode     *b)
+{
+  int i;
+
+  if (a->hash != b->hash)
     return FALSE;
 
-  for (i = 0; i < a->n_data; i++)
-    if (a->data[i] != b->data[i])
-      return FALSE;
+  if (!broadway_node_equal (a,b))
+    return FALSE;
+
+  if (a->n_children != b->n_children)
+    return FALSE;
 
   for (i = 0; i < a->n_children; i++)
-    if (!broadway_node_equal (a->children[i], b->children[i]))
+    if (!broadway_node_deep_equal (a->children[i], b->children[i]))
       return FALSE;
 
   return TRUE;
 }
 
+
 static void
 broadway_server_init (BroadwayServer *server)
 {
@@ -1665,7 +1683,8 @@ broadway_server_window_set_nodes (BroadwayServer   *server,
 
   if (server->output != NULL)
     broadway_output_window_set_nodes (server->output, window->id,
-                                      root, window->nodes);
+                                      root,
+                                      window->nodes);
 
   if (window->nodes)
     broadway_node_free (window->nodes);
diff --git a/gdk/broadway/broadway-server.h b/gdk/broadway/broadway-server.h
index dba1628..3a40f72 100644
--- a/gdk/broadway/broadway-server.h
+++ b/gdk/broadway/broadway-server.h
@@ -22,7 +22,7 @@ typedef struct _BroadwayNode BroadwayNode;
 
 struct _BroadwayNode {
   guint32 type;
-  guint32 hash;
+  guint32 hash; /* deep hash */
   guint32 n_children;
   BroadwayNode **children;
   guint32 n_data;
@@ -30,7 +30,9 @@ struct _BroadwayNode {
 };
 
 gboolean            broadway_node_equal                      (BroadwayNode     *a,
-                                                             BroadwayNode     *b);
+                                                              BroadwayNode     *b);
+gboolean            broadway_node_deep_equal                 (BroadwayNode     *a,
+                                                              BroadwayNode     *b);
 
 BroadwayServer     *broadway_server_new                      (char             *address,
                                                              int               port,
diff --git a/gdk/broadway/broadway.js b/gdk/broadway/broadway.js
index 0f85c7a..5474960 100644
--- a/gdk/broadway/broadway.js
+++ b/gdk/broadway/broadway.js
@@ -301,31 +301,9 @@ function SwapNodes(node_data, div) {
     this.node_data_signed = new Int32Array(node_data);
     this.data_pos = 0;
     this.div = div;
-    this.div2 = document.createElement('div');
     this.outstanding = 1;
 }
 
-SwapNodes.prototype.did_one = function(image) {
-    this.outstanding--;
-    if (this.outstanding == 0) {
-        var oldDiv2 = null;
-        if (this.div.hasChildNodes())
-            oldDiv2 = this.div.lastChild;
-
-        this.div.appendChild(this.div2);
-        if (oldDiv2)
-            this.div.removeChild(oldDiv2);
-    }
-}
-
-SwapNodes.prototype.add_image = function(image) {
-    this.outstanding++;
-    var v = this;
-    image.addEventListener('load', function() {
-        v.did_one ();
-    }, false);
-};
-
 SwapNodes.prototype.decode_uint32 = function() {
     return this.node_data[this.data_pos++];
 }
@@ -421,26 +399,37 @@ function px(x) {
     return x + "px";
 }
 
-function set_rect_style (div, rect, offset_x, offset_y) {
-    div.style["left"] = px(rect.x - offset_x);
-    div.style["top"] = px(rect.y - offset_y);
+function set_rect_style (div, rect) {
+    div.style["left"] = px(rect.x);
+    div.style["top"] = px(rect.y);
     div.style["width"] = px(rect.width);
     div.style["height"] = px(rect.height);
 }
 
-function set_rrect_style (div, rrect, offset_x, offset_y) {
-    set_rect_style(div, rrect.bounds, offset_x, offset_y);
+function set_rrect_style (div, rrect) {
+    set_rect_style(div, rrect.bounds);
     div.style["border-top-left-radius"] = args(px(rrect.sizes[0].width), px(rrect.sizes[0].height));
     div.style["border-top-right-radius"] = args(px(rrect.sizes[1].width), px(rrect.sizes[1].height));
     div.style["border-bottom-right-radius"] = args(px(rrect.sizes[2].width), px(rrect.sizes[2].height));
     div.style["border-bottom-left-radius"] = args(px(rrect.sizes[3].width), px(rrect.sizes[3].height));
 }
 
-SwapNodes.prototype.handle_node = function(parent, offset_x, offset_y)
+SwapNodes.prototype.insertNode = function(parent, posInParent, oldNode)
 {
     var type = this.decode_uint32();
+    var newNode = null;
+
+    // We need to dup this because as we reuse children the original order is lost
+    var oldChildren = [];
+    if (oldNode) {
+        for (var i = 0; i < oldNode.children.length; i++)
+            oldChildren[i] = oldNode.children[i];
+    }
+
     switch (type)
     {
+        /* Leaf nodes */
+
         case 0:  // TEXTURE
         {
             var rect = this.decode_rect();
@@ -449,22 +438,10 @@ SwapNodes.prototype.handle_node = function(parent, offset_x, offset_y)
             image.width = rect.width;
             image.height = rect.height;
             image.style["position"] = "absolute";
-            set_rect_style(image, rect, offset_x, offset_y);
+            set_rect_style(image, rect);
             var texture_url = textures[texture_id];
-            this.add_image(image);
             image.src = texture_url;
-            parent.appendChild(image);
-        }
-        break;
-
-    case 1: // CONTAINER
-        {
-            var div = document.createElement('div');
-            var len = this.decode_uint32();
-            for (var i = 0; i < len; i++) {
-                this.handle_node(div, offset_x, offset_y);
-            }
-            parent.appendChild(div);
+            newNode = image;
         }
         break;
 
@@ -474,9 +451,9 @@ SwapNodes.prototype.handle_node = function(parent, offset_x, offset_y)
             var c = this.decode_color ();
             var div = document.createElement('div');
             div.style["position"] = "absolute";
-            set_rect_style(div, rect, offset_x, offset_y);
+            set_rect_style(div, rect);
             div.style["background-color"] = c;
-            parent.appendChild(div);
+            newNode = div;
         }
         break;
 
@@ -494,7 +471,7 @@ SwapNodes.prototype.handle_node = function(parent, offset_x, offset_y)
             div.style["position"] = "absolute";
             rrect.bounds.width -= border_widths[1] + border_widths[3];
             rrect.bounds.height -= border_widths[0] + border_widths[2];
-            set_rrect_style(div, rrect, offset_x, offset_y);
+            set_rrect_style(div, rrect);
             div.style["border-style"] = "solid";
             div.style["border-top-color"] = border_colors[0];
             div.style["border-top-width"] = px(border_widths[0]);
@@ -504,7 +481,7 @@ SwapNodes.prototype.handle_node = function(parent, offset_x, offset_y)
             div.style["border-bottom-width"] = px(border_widths[2]);
             div.style["border-left-color"] = border_colors[3];
             div.style["border-left-width"] = px(border_widths[3]);
-            parent.appendChild(div);
+            newNode = div;
         }
         break;
 
@@ -519,9 +496,9 @@ SwapNodes.prototype.handle_node = function(parent, offset_x, offset_y)
 
             var div = document.createElement('div');
             div.style["position"] = "absolute";
-            set_rrect_style(div, rrect, offset_x, offset_y);
+            set_rrect_style(div, rrect);
             div.style["box-shadow"] = args(px(dx), px(dy), px(blur), px(spread), color);
-            parent.appendChild(div);
+            newNode = div;
         }
         break;
 
@@ -536,23 +513,12 @@ SwapNodes.prototype.handle_node = function(parent, offset_x, offset_y)
 
             var div = document.createElement('div');
             div.style["position"] = "absolute";
-            set_rrect_style(div, rrect, offset_x, offset_y);
+            set_rrect_style(div, rrect);
             div.style["box-shadow"] = args("inset", px(dx), px(dy), px(blur), px(spread), color);
-            parent.appendChild(div);
+            newNode = div;
         }
         break;
 
-    case 6:  // ROUNDED_CLIP
-        {
-            var rrect = this.decode_rounded_rect();
-            var div = document.createElement('div');
-            div.style["position"] = "absolute";
-            set_rrect_style(div, rrect, offset_x, offset_y);
-            div.style["overflow"] = "hidden";
-            parent.appendChild(div);
-            this.handle_node(div, rrect.bounds.x, rrect.bounds.y);
-        }
-        break;
 
     case 7:  // LINEAR_GRADIENT
         {
@@ -562,7 +528,7 @@ SwapNodes.prototype.handle_node = function(parent, offset_x, offset_y)
             var stops = this.decode_color_stops ();
             var div = document.createElement('div');
             div.style["position"] = "absolute";
-            set_rect_style(div, rect, offset_x, offset_y);
+            set_rect_style(div, rect);
 
             // direction:
             var dx = end.x - start.x;
@@ -595,7 +561,48 @@ SwapNodes.prototype.handle_node = function(parent, offset_x, offset_y)
             gradient = gradient + ")";
 
             div.style["background-image"] = gradient;
-            parent.appendChild(div);
+            newNode = div;
+        }
+        break;
+
+
+    /* Bin nodes */
+
+    case 10:  // CLIP
+        {
+            var rect = this.decode_rect();
+            var div = document.createElement('div');
+            div.style["position"] = "absolute";
+            set_rect_style(div, rect);
+            div.style["overflow"] = "hidden";
+            this.insertNode(div, -1, oldChildren[0]);
+            newNode = div;
+        }
+        break;
+
+    case 6:  // ROUNDED_CLIP
+        {
+            var rrect = this.decode_rounded_rect();
+            var div = document.createElement('div');
+            div.style["position"] = "absolute";
+            set_rrect_style(div, rrect);
+            div.style["overflow"] = "hidden";
+            this.insertNode(div, -1, oldChildren[0]);
+            newNode = div;
+        }
+        break;
+
+    case 9:  // OPACITY
+        {
+            var opacity = this.decode_float();
+            var div = document.createElement('div');
+            div.style["position"] = "absolute";
+            div.style["left"] = px(0);
+            div.style["top"] = px(0);
+            div.style["opacity"] = opacity;
+
+            this.insertNode(div, -1, oldChildren[0]);
+            newNode = div;
         }
         break;
 
@@ -616,40 +623,72 @@ SwapNodes.prototype.handle_node = function(parent, offset_x, offset_y)
             div.style["top"] = px(0);
             div.style["filter"] = filters;
 
-            parent.appendChild(div);
-            this.handle_node(div, offset_x, offset_y);
+            this.insertNode(div, -1, oldChildren[0]);
+            newNode = div;
         }
         break;
 
-    case 9:  // OPACITY
+   /* Generic nodes */
+
+    case 1: // CONTAINER
         {
-            var opacity = this.decode_float();
             var div = document.createElement('div');
-            div.style["position"] = "absolute";
-            div.style["left"] = px(0);
-            div.style["top"] = px(0);
-            div.style["opacity"] = opacity;
+            var len = this.decode_uint32();
+            for (var i = 0; i < len; i++) {
+                this.insertNode(div, -1, oldChildren[i]);
+            }
+            newNode = div;
+        }
+        break;
+
+    case 11:  // KEEP_ALL
+        {
+            if (!oldNode)
+                alert("KEEP_ALL with no oldNode");
 
-            parent.appendChild(div);
-            this.handle_node(div, offset_x, offset_y);
+            if (oldNode.parentNode != parent)
+                newNode = oldNode;
+            else
+                newNode = null;
         }
         break;
 
-    case 10:  // CLIP
+    case 12:  // KEEP_THIS
         {
-            var rect = this.decode_rect();
-            var div = document.createElement('div');
-            div.style["position"] = "absolute";
-            set_rect_style(div, rect, offset_x, offset_y);
-            div.style["overflow"] = "hidden";
-            parent.appendChild(div);
-            this.handle_node(div, rect.x, rect.y);
+            if (!oldNode)
+                alert("KEEP_THIS with no oldNode ");
+
+            /* We only get keep-this if all parents were kept, check this */
+            if (oldNode.parentNode != parent)
+                alert("Got KEEP_THIS for non-kept parent");
+
+            var len = this.decode_uint32();
+            var i;
+
+            for (i = 0; i < len; i++) {
+                this.insertNode(oldNode, i,
+                                oldChildren[i]);
+            }
+
+            /* Remove children that are after the new length */
+            for (i = oldChildren.length - 1; i > len - 1; i--)
+                oldNode.removeChild(oldChildren[i]);
+
+            /* NOTE: No need to modify the parent, we're keeping this node as is */
+            newNode = null;
         }
         break;
 
     default:
         alert("Unexpected node type " + type);
     }
+
+    if (newNode) {
+        if (posInParent >= 0 && parent.children[posInParent])
+            parent.replaceChild(newNode, parent.children[posInParent]);
+        else
+            parent.appendChild(newNode);
+    }
 }
 
 function cmdWindowSetNodes(id, node_data)
@@ -662,10 +701,9 @@ function cmdWindowSetNodes(id, node_data)
     /* We use a secondary div so that we can remove all previous children in one go */
 
     var swap = new SwapNodes (node_data, div);
-    swap.handle_node(swap.div2, 0, 0);
+    swap.insertNode(div, 0, div.firstChild);
     if (swap.data_pos != node_data.length)
         alert ("Did not consume entire array (len " + node_data.length + " end " + end + ")");
-    swap.did_one ();
 }
 
 function cmdUploadTexture(id, data)
diff --git a/gdk/broadway/broadwayd.c b/gdk/broadway/broadwayd.c
index a885a5e..9a0f420 100644
--- a/gdk/broadway/broadwayd.c
+++ b/gdk/broadway/broadwayd.c
@@ -234,7 +234,7 @@ rotl (guint32 value, int shift)
 
 static BroadwayNode *
 decode_nodes (BroadwayClient *client,
-             int len, guint32 data[], int *pos)
+              int len, guint32 data[], int *pos)
 {
   BroadwayNode *node;
   guint32 type;
@@ -305,9 +305,8 @@ decode_nodes (BroadwayClient *client,
     {
       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])));
-
+        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++)
diff --git a/gsk/gskbroadwayrenderer.c b/gsk/gskbroadwayrenderer.c
index e388c31..887befd 100644
--- a/gsk/gskbroadwayrenderer.c
+++ b/gsk/gskbroadwayrenderer.c
@@ -116,10 +116,10 @@ add_float (GArray *nodes, float f)
 }
 
 static void
-add_point (GArray *nodes, const graphene_point_t *point)
+add_point (GArray *nodes, const graphene_point_t *point, float offset_x, float offset_y)
 {
-  add_float (nodes, point->x);
-  add_float (nodes, point->y);
+  add_float (nodes, point->x - offset_x);
+  add_float (nodes, point->y - offset_y);
 }
 
 static void
@@ -130,17 +130,17 @@ add_size (GArray *nodes, const graphene_size_t *size)
 }
 
 static void
-add_rect (GArray *nodes, const graphene_rect_t *rect)
+add_rect (GArray *nodes, const graphene_rect_t *rect, float offset_x, float offset_y)
 {
-  add_point (nodes, &rect->origin);
+  add_point (nodes, &rect->origin, offset_x, offset_y);
   add_size (nodes, &rect->size);
 }
 
 static void
-add_rounded_rect (GArray *nodes, const GskRoundedRect *rrect)
+add_rounded_rect (GArray *nodes, const GskRoundedRect *rrect, float offset_x, float offset_y)
 {
   int i;
-  add_rect (nodes, &rrect->bounds);
+  add_rect (nodes, &rrect->bounds, offset_x, offset_y);
   for (i = 0; i < 4; i++)
     add_size (nodes, &rrect->corner[i]);
 }
@@ -461,11 +461,17 @@ node_texture_fallback (GskRenderNode *node,
   return texture;
 }
 
+/* Note: This tracks the offset so that we can convert
+   the absolute coordinates of the GskRenderNodes to
+   parent-relative which is what the dom uses, and
+   which is good for re-using subtrees. */
 static void
 gsk_broadway_renderer_add_node (GskRenderer *self,
                                 GArray *nodes,
                                 GPtrArray *node_textures,
-                                GskRenderNode *node)
+                                GskRenderNode *node,
+                                float offset_x,
+                                float offset_y)
 {
   GdkDisplay *display = gsk_renderer_get_display (self);
 
@@ -475,6 +481,8 @@ gsk_broadway_renderer_add_node (GskRenderer *self,
       g_assert_not_reached ();
       return;
 
+    /* Leaf nodes */
+
     case GSK_TEXTURE_NODE:
       {
         GdkTexture *texture = gsk_texture_node_get_texture (node);
@@ -484,10 +492,7 @@ gsk_broadway_renderer_add_node (GskRenderer *self,
         texture_id = gdk_broadway_display_ensure_texture (display, texture);
 
         add_uint32 (nodes, BROADWAY_NODE_TEXTURE);
-        add_float (nodes, node->bounds.origin.x);
-        add_float (nodes, node->bounds.origin.y);
-        add_float (nodes, gdk_texture_get_width (texture));
-        add_float (nodes, gdk_texture_get_height (texture));
+        add_rect (nodes, &node->bounds, offset_x, offset_y);
         add_uint32 (nodes, texture_id);
       }
       return;
@@ -495,39 +500,23 @@ gsk_broadway_renderer_add_node (GskRenderer *self,
     case GSK_CAIRO_NODE:
       {
         const cairo_surface_t *surface = gsk_cairo_node_peek_surface (node);
-       GdkTexture *texture;
+        GdkTexture *texture;
         guint32 texture_id;
 
-       texture = gdk_texture_new_for_surface ((cairo_surface_t *)surface);
+        texture = gdk_texture_new_for_surface ((cairo_surface_t *)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_float (nodes, node->bounds.origin.x);
-        add_float (nodes, node->bounds.origin.y);
-        add_float (nodes, node->bounds.size.width);
-        add_float (nodes, node->bounds.size.height);
+        add_rect (nodes, &node->bounds, offset_x, offset_y);
         add_uint32 (nodes, texture_id);
       }
       return;
 
-    case GSK_CONTAINER_NODE:
-      {
-        guint i;
-
-        add_uint32 (nodes, BROADWAY_NODE_CONTAINER);
-        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 (self, nodes, node_textures,
-                                          gsk_container_node_get_child (node, i));
-      }
-      return;
-
     case GSK_COLOR_NODE:
       {
         add_uint32 (nodes, BROADWAY_NODE_COLOR);
-        add_rect (nodes, &node->bounds);
+        add_rect (nodes, &node->bounds, offset_x, offset_y);
         add_rgba (nodes, gsk_color_node_peek_color (node));
       }
       return;
@@ -536,7 +525,7 @@ gsk_broadway_renderer_add_node (GskRenderer *self,
       {
         int i;
         add_uint32 (nodes, BROADWAY_NODE_BORDER);
-        add_rounded_rect (nodes, gsk_border_node_peek_outline (node));
+        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++)
@@ -547,7 +536,7 @@ gsk_broadway_renderer_add_node (GskRenderer *self,
     case GSK_OUTSET_SHADOW_NODE:
       {
         add_uint32 (nodes, BROADWAY_NODE_OUTSET_SHADOW);
-        add_rounded_rect (nodes, gsk_outset_shadow_node_peek_outline (node));
+        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));
@@ -559,7 +548,7 @@ gsk_broadway_renderer_add_node (GskRenderer *self,
     case GSK_INSET_SHADOW_NODE:
       {
         add_uint32 (nodes, BROADWAY_NODE_INSET_SHADOW);
-        add_rounded_rect (nodes, gsk_inset_shadow_node_peek_outline (node));
+        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));
@@ -568,23 +557,14 @@ gsk_broadway_renderer_add_node (GskRenderer *self,
       }
       return;
 
-    case GSK_ROUNDED_CLIP_NODE:
-      {
-        add_uint32 (nodes, BROADWAY_NODE_ROUNDED_CLIP);
-        add_rounded_rect (nodes, gsk_rounded_clip_node_peek_clip (node));
-        gsk_broadway_renderer_add_node (self, nodes, node_textures,
-                                        gsk_rounded_clip_node_get_child (node));
-      }
-      return;
-
     case GSK_LINEAR_GRADIENT_NODE:
       {
         guint i, n;
 
         add_uint32 (nodes, BROADWAY_NODE_LINEAR_GRADIENT);
-        add_rect (nodes, &node->bounds);
-        add_point (nodes, gsk_linear_gradient_node_peek_start (node));
-        add_point (nodes, gsk_linear_gradient_node_peek_end (node));
+        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++)
@@ -592,6 +572,8 @@ gsk_broadway_renderer_add_node (GskRenderer *self,
       }
       return;
 
+      /* Bin nodes */
+
     case GSK_SHADOW_NODE:
       {
         gsize i, n_shadows = gsk_shadow_node_get_n_shadows (node);
@@ -606,7 +588,8 @@ gsk_broadway_renderer_add_node (GskRenderer *self,
             add_float (nodes, shadow->radius);
           }
         gsk_broadway_renderer_add_node (self, nodes, node_textures,
-                                        gsk_shadow_node_get_child (node));
+                                        gsk_shadow_node_get_child (node),
+                                        offset_x, offset_y);
       }
       return;
 
@@ -615,16 +598,47 @@ gsk_broadway_renderer_add_node (GskRenderer *self,
         add_uint32 (nodes, BROADWAY_NODE_OPACITY);
         add_float (nodes, gsk_opacity_node_get_opacity (node));
         gsk_broadway_renderer_add_node (self, nodes, node_textures,
-                                        gsk_opacity_node_get_child (node));
+                                        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 (self, nodes, node_textures,
+                                        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, gsk_clip_node_peek_clip (node));
+        add_rect (nodes, clip, offset_x, offset_y);
         gsk_broadway_renderer_add_node (self, nodes, node_textures,
-                                        gsk_clip_node_get_child (node));
+                                        gsk_clip_node_get_child (node),
+                                        clip->origin.x,
+                                        clip->origin.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));
+
+        for (i = 0; i < gsk_container_node_get_n_children (node); i++)
+          gsk_broadway_renderer_add_node (self, nodes, node_textures,
+                                          gsk_container_node_get_child (node, i), offset_x, offset_y);
       }
       return;
 
@@ -643,25 +657,25 @@ gsk_broadway_renderer_add_node (GskRenderer *self,
   {
     GdkTexture *texture;
     guint32 texture_id;
-    float off_x = 0, off_y = 0;
+    float t_off_x = 0, t_off_y = 0;
 
-    texture = node_cache_lookup (node, &off_x, &off_y);
+    texture = node_cache_lookup (node, &t_off_x, &t_off_y);
 
     if (!texture)
       {
-        texture = node_texture_fallback (node, &off_x, &off_y);
+        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
 
-        node_cache_store (node, texture, off_x, off_y);
+        node_cache_store (node, texture, t_off_x, t_off_y);
       }
 
     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 + off_x);
-    add_float (nodes, node->bounds.origin.y + off_y);
+    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);
@@ -676,7 +690,7 @@ gsk_broadway_renderer_render (GskRenderer   *self,
   GArray *nodes = g_array_new (FALSE, FALSE, sizeof(guint32));
   GPtrArray *node_textures = g_ptr_array_new_with_free_func (g_object_unref);
 
-  gsk_broadway_renderer_add_node (self, nodes, node_textures, root);
+  gsk_broadway_renderer_add_node (self, nodes, node_textures, root, 0, 0);
   gdk_broadway_window_set_nodes (window, nodes, node_textures);
   g_array_unref (nodes);
   g_ptr_array_unref (node_textures);


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