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

This commit is contained in:
Alexander Larsson 2019-03-26 16:29:45 +01:00
parent 0481aa10e7
commit cf4226586a

View File

@ -62,6 +62,8 @@ const DISPLAY_OP_HIDE_SURFACE = 4;
const DISPLAY_OP_DELETE_NODE = 5; const DISPLAY_OP_DELETE_NODE = 5;
const DISPLAY_OP_MOVE_NODE = 6; const DISPLAY_OP_MOVE_NODE = 6;
const DISPLAY_OP_RESIZE_NODE = 7; const DISPLAY_OP_RESIZE_NODE = 7;
const DISPLAY_OP_RESTACK_SURFACES = 8;
const DISPLAY_OP_DELETE_SURFACE = 9;
// GdkCrossingMode // GdkCrossingMode
const GDK_CROSSING_NORMAL = 0; const GDK_CROSSING_NORMAL = 0;
@ -173,6 +175,7 @@ var surfaces = {};
var textures = {}; var textures = {};
var stackingOrder = []; var stackingOrder = [];
var outstandingCommands = new Array(); var outstandingCommands = new Array();
var outstandingDisplayCommands = null;
var inputSocket = null; var inputSocket = null;
var debugDecoding = false; var debugDecoding = false;
var fakeInput = null; var fakeInput = null;
@ -199,6 +202,10 @@ function Texture(id, data) {
this.url = window.URL.createObjectURL(blob); this.url = window.URL.createObjectURL(blob);
this.refcount = 1; this.refcount = 1;
this.id = id; this.id = id;
var image = new Image();
image.src = this.url;
this.image = image;
textures[id] = this; textures[id] = this;
} }
@ -278,13 +285,14 @@ function cmdRoundtrip(id, tag)
function cmdRaiseSurface(id) function cmdRaiseSurface(id)
{ {
var surface = surfaces[id]; var surface = surfaces[id];
if (surface)
moveToHelper(surface); moveToHelper(surface);
} }
function cmdLowerSurface(id) function cmdLowerSurface(id)
{ {
var surface = surfaces[id]; var surface = surfaces[id];
if (surface)
moveToHelper(surface, 0); moveToHelper(surface, 0);
} }
@ -491,9 +499,8 @@ TransformNodes.prototype.insertNode = function(parent, posInParent, oldNode)
image.height = rect.height; image.height = rect.height;
image.style["position"] = "absolute"; image.style["position"] = "absolute";
set_rect_style(image, rect); set_rect_style(image, rect);
var texture = textures[texture_id]; var texture = textures[texture_id].ref();
image.src = texture.url; image.src = texture.url;
texture.ref();
// Unref blob url when loaded // Unref blob url when loaded
image.onload = function() { texture.unref(); }; image.onload = function() { texture.unref(); };
newNode = image; newNode = image;
@ -833,25 +840,28 @@ function handleDisplayCommands(display_commands)
div.style["height"] = cmd[3] + "px"; div.style["height"] = cmd[3] + "px";
break; break;
case DISPLAY_OP_RESTACK_SURFACES:
restackSurfaces();
break;
case DISPLAY_OP_DELETE_SURFACE:
var id = cmd[1];
delete surfaces[id];
break;
default: default:
alert("Unknown display op " + command); alert("Unknown display op " + command);
} }
} }
} }
var active = false; function handleCommands(cmd, display_commands, new_textures, modified_trees)
function handleCommands(cmd)
{ {
if (!active) { var res = true;
start();
active = true;
}
var display_commands = new Array();
var need_restack = false; var need_restack = false;
while (cmd.pos < cmd.length) { while (res && cmd.pos < cmd.length) {
var id, x, y, w, h, q, surface; var id, x, y, w, h, q, surface;
var saved_pos = cmd.pos;
var command = cmd.get_char(); var command = cmd.get_char();
lastSerial = cmd.get_32(); lastSerial = cmd.get_32();
switch (command) { switch (command) {
@ -918,7 +928,8 @@ function handleCommands(cmd)
var div = surface.div; var div = surface.div;
display_commands.push([DISPLAY_OP_DELETE_NODE, 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; break;
case BROADWAY_OP_ROUNDTRIP: case BROADWAY_OP_ROUNDTRIP:
@ -962,7 +973,8 @@ function handleCommands(cmd)
case BROADWAY_OP_UPLOAD_TEXTURE: case BROADWAY_OP_UPLOAD_TEXTURE:
id = cmd.get_32(); id = cmd.get_32();
var data = cmd.get_data(); 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; break;
case BROADWAY_OP_RELEASE_TEXTURE: case BROADWAY_OP_RELEASE_TEXTURE:
@ -972,10 +984,18 @@ function handleCommands(cmd)
case BROADWAY_OP_SET_NODES: case BROADWAY_OP_SET_NODES:
id = cmd.get_16(); id = cmd.get_16();
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 (); var node_data = cmd.get_nodes ();
surface = surfaces[id]; surface = surfaces[id];
var transform_nodes = new TransformNodes (node_data, surface.div, display_commands); var transform_nodes = new TransformNodes (node_data, surface.div, display_commands);
transform_nodes.execute(); transform_nodes.execute();
}
break; break;
case BROADWAY_OP_GRAB_POINTER: case BROADWAY_OP_GRAB_POINTER:
@ -1000,22 +1020,74 @@ function handleCommands(cmd)
} }
if (need_restack) 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() function handleOutstanding()
{ {
var display_commands = new Array();
var new_textures = new Array();
var modified_trees = {};
if (outstandingDisplayCommands != null)
return;
while (outstandingCommands.length > 0) { while (outstandingCommands.length > 0) {
var cmd = outstandingCommands.shift(); var cmd = outstandingCommands.shift();
if (!handleCommands(cmd)) { if (!handleCommands(cmd, display_commands, new_textures, modified_trees)) {
outstandingCommands.unshift(cmd); 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) { function BinCommands(message) {
@ -1062,8 +1134,14 @@ BinCommands.prototype.get_data = function() {
return data; return data;
}; };
var active = false;
function handleMessage(message) function handleMessage(message)
{ {
if (!active) {
start();
active = true;
}
var cmd = new BinCommands(message); var cmd = new BinCommands(message);
outstandingCommands.push(cmd); outstandingCommands.push(cmd);
if (outstandingCommands.length == 1) { if (outstandingCommands.length == 1) {