[gtk/wip/alexl/broadway5: 13/13] broadway: Load all textures before applying display ops, fixing flickers



commit 5c656da5ce732e08a9ea03711b4f87415ae42bf4
Author: Alexander Larsson <alexl redhat com>
Date:   Tue Mar 26 16:29:45 2019 +0100

    broadway: Load all textures before applying display ops, fixing flickers

 gdk/broadway/broadway.js | 128 ++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 103 insertions(+), 25 deletions(-)
---
diff --git a/gdk/broadway/broadway.js b/gdk/broadway/broadway.js
index a0636aadf2..00c341c393 100644
--- a/gdk/broadway/broadway.js
+++ b/gdk/broadway/broadway.js
@@ -62,6 +62,8 @@ const DISPLAY_OP_HIDE_SURFACE = 4;
 const DISPLAY_OP_DELETE_NODE = 5;
 const DISPLAY_OP_MOVE_NODE = 6;
 const DISPLAY_OP_RESIZE_NODE = 7;
+const DISPLAY_OP_RESTACK_SURFACES = 8;
+const DISPLAY_OP_DELETE_SURFACE = 9;
 
 // GdkCrossingMode
 const GDK_CROSSING_NORMAL = 0;
@@ -173,6 +175,7 @@ var surfaces = {};
 var textures = {};
 var stackingOrder = [];
 var outstandingCommands = new Array();
+var outstandingDisplayCommands = null;
 var inputSocket = null;
 var debugDecoding = false;
 var fakeInput = null;
@@ -199,6 +202,10 @@ function Texture(id, data) {
     this.url = window.URL.createObjectURL(blob);
     this.refcount = 1;
     this.id = id;
+
+    var image = new Image();
+    image.src = this.url;
+    this.image = image;
     textures[id] = this;
 }
 
@@ -278,14 +285,15 @@ function cmdRoundtrip(id, tag)
 function cmdRaiseSurface(id)
 {
     var surface = surfaces[id];
-
-    moveToHelper(surface);
+    if (surface)
+        moveToHelper(surface);
 }
 
 function cmdLowerSurface(id)
 {
     var surface = surfaces[id];
-    moveToHelper(surface, 0);
+    if (surface)
+        moveToHelper(surface, 0);
 }
 
 function TransformNodes(node_data, div, display_commands) {
@@ -491,9 +499,8 @@ TransformNodes.prototype.insertNode = function(parent, posInParent, oldNode)
             image.height = rect.height;
             image.style["position"] = "absolute";
             set_rect_style(image, rect);
-            var texture = textures[texture_id];
+            var texture = textures[texture_id].ref();
             image.src = texture.url;
-            texture.ref();
             // Unref blob url when loaded
             image.onload = function() { texture.unref(); };
             newNode = image;
@@ -833,25 +840,28 @@ function handleDisplayCommands(display_commands)
             div.style["height"] = cmd[3] + "px";
             break;
 
+        case DISPLAY_OP_RESTACK_SURFACES:
+            restackSurfaces();
+            break;
+        case DISPLAY_OP_DELETE_SURFACE:
+            var id = cmd[1];
+            delete surfaces[id];
+            break;
+
         default:
             alert("Unknown display op " + command);
         }
     }
 }
 
-var active = false;
-function handleCommands(cmd)
+function handleCommands(cmd, display_commands, new_textures, modified_trees)
 {
-    if (!active) {
-        start();
-        active = true;
-    }
-
-    var display_commands = new Array();
+    var res = true;
     var need_restack = false;
 
-    while (cmd.pos < cmd.length) {
+    while (res && cmd.pos < cmd.length) {
         var id, x, y, w, h, q, surface;
+        var saved_pos = cmd.pos;
         var command = cmd.get_char();
         lastSerial = cmd.get_32();
         switch (command) {
@@ -918,7 +928,8 @@ function handleCommands(cmd)
             var div = surface.div;
 
             display_commands.push([DISPLAY_OP_DELETE_NODE, div]);
-            delete surfaces[id];
+            // We need to delay this until its really deleted because we can still get events to it
+            display_commands.push([DISPLAY_OP_DELETE_SURFACE, id]);
             break;
 
         case BROADWAY_OP_ROUNDTRIP:
@@ -962,7 +973,8 @@ function handleCommands(cmd)
         case BROADWAY_OP_UPLOAD_TEXTURE:
             id = cmd.get_32();
             var data = cmd.get_data();
-            var texure = new Texture (id, data); // Stores a ref in textures
+            var texture = new Texture (id, data); // Stores a ref in global textures array
+            new_textures.push(texture);
             break;
 
         case BROADWAY_OP_RELEASE_TEXTURE:
@@ -972,10 +984,18 @@ function handleCommands(cmd)
 
         case BROADWAY_OP_SET_NODES:
             id = cmd.get_16();
-            var node_data = cmd.get_nodes ();
-            surface = surfaces[id];
-            var transform_nodes = new TransformNodes (node_data, surface.div, display_commands);
-            transform_nodes.execute();
+            if (id in modified_trees) {
+                // Can't modify the same dom tree in the same loop, bail out and do the first one
+                cmd.pos = saved_pos;
+                res = false;
+            } else {
+                modified_trees[id] = true;
+
+                var node_data = cmd.get_nodes ();
+                surface = surfaces[id];
+                var transform_nodes = new TransformNodes (node_data, surface.div, display_commands);
+                transform_nodes.execute();
+            }
             break;
 
         case BROADWAY_OP_GRAB_POINTER:
@@ -1000,22 +1020,74 @@ function handleCommands(cmd)
     }
 
     if (need_restack)
-        restackSurfaces();
+        display_commands.push([DISPLAY_OP_RESTACK_SURFACES]);
 
-    handleDisplayCommands(display_commands);
+    return res;
+}
 
-    return true;
+function handleOutstandingDisplayCommands()
+{
+    if (outstandingDisplayCommands) {
+        window.requestAnimationFrame(
+            function () {
+                handleDisplayCommands(outstandingDisplayCommands);
+                outstandingDisplayCommands = null;
+
+                if (outstandingCommands.length > 0)
+                    setTimeout(handleOutstanding);
+            });
+    } else {
+        if (outstandingCommands.length > 0)
+            handleOutstanding ();
+    }
 }
 
+/* Mode of operation.
+ * We run all outstandingCommands, until either we run out of things
+ * to process, or we update the dom nodes of the same surface twice.
+ * Then we wait for all textures to load, and then we request am
+ * animation frame and apply the display changes. Then we loop back and
+ * handle outstanding commands again.
+ *
+ * The reason for stopping if we update the same tree twice is that
+ * the delta operations generally assume that the previous dom tree
+ * is in pristine condition.
+ */
 function handleOutstanding()
 {
+    var display_commands = new Array();
+    var new_textures = new Array();
+    var modified_trees = {};
+
+    if (outstandingDisplayCommands != null)
+        return;
+
     while (outstandingCommands.length > 0) {
         var cmd = outstandingCommands.shift();
-        if (!handleCommands(cmd)) {
+        if (!handleCommands(cmd, display_commands, new_textures, modified_trees)) {
             outstandingCommands.unshift(cmd);
-            return;
+            break;
+        }
+    }
+
+    if (display_commands.length > 0)
+        outstandingDisplayCommands = display_commands;
+
+    if (new_textures.length > 0) {
+        var n_textures = new_textures.length;
+        for (var i = 0; i < new_textures.length; i++) {
+            var t = new_textures[i];
+            t.image.onload = function() {
+                n_textures -= 1;
+                if (n_textures == 0) {
+                    handleOutstandingDisplayCommands();
+                }
+            };
         }
+    } else {
+        handleOutstandingDisplayCommands();
     }
+
 }
 
 function BinCommands(message) {
@@ -1062,8 +1134,14 @@ BinCommands.prototype.get_data = function() {
     return data;
 };
 
+var active = false;
 function handleMessage(message)
 {
+    if (!active) {
+        start();
+        active = true;
+    }
+
     var cmd = new BinCommands(message);
     outstandingCommands.push(cmd);
     if (outstandingCommands.length == 1) {


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