From 96347b7a9aa3931faddba11ee623ef7ba3c5e0b5 Mon Sep 17 00:00:00 2001 From: totaam Date: Tue, 17 Aug 2021 14:24:58 +0700 Subject: [PATCH] preserve paint order when using the decode worker (#20) --- html5/js/Client.js | 125 ++++++++++++++++++++------------------- html5/js/DecodeWorker.js | 69 +++++++++++++++++++-- html5/js/Window.js | 7 +++ 3 files changed, 134 insertions(+), 67 deletions(-) diff --git a/html5/js/Client.js b/html5/js/Client.js index 1a4a8572..077bf42c 100644 --- a/html5/js/Client.js +++ b/html5/js/Client.js @@ -380,72 +380,75 @@ XpraClient.prototype.connect = function() { return; } } + this.initialize_workers(); +} + +XpraClient.prototype.initialize_workers = function() { // detect websocket in webworker support and degrade gracefully - if(window.Worker) { - this.clog("we have webworker support"); - // spawn worker that checks for a websocket - const me = this; - const worker = new Worker('js/lib/wsworker_check.js'); - worker.addEventListener('message', function(e) { - const data = e.data; - switch (data['result']) { - case true: - // yey, we can use websocket in worker! - me.clog("we can use websocket in webworker"); - me._do_connect(true); - break; - case false: - me.clog("we can't use websocket in webworker, won't use webworkers"); - me._do_connect(false); - break; - default: - me.clog("client got unknown message from worker"); - me._do_connect(false); - } - }, false); - // ask the worker to check for websocket support, when we receive a reply - // through the eventlistener above, _do_connect() will finish the job - worker.postMessage({'cmd': 'check'}); - - const decode_worker = new Worker('js/DecodeWorker.js'); - decode_worker.addEventListener('message', function(e) { - const data = e.data; - if (data['draw']) { - me.do_process_draw(data['draw']); - return; - } - if (data['error']) { - const msg = data['error'], - packet = data['packet'], - wid = packet[1], - width = packet[2], - height = packet[3], - coding = packet[6], - packet_sequence = packet[8]; - me.clog("decode error on ", coding, "packet sequence", packet_sequence, ":", msg); - me.do_send_damage_sequence(packet_sequence, wid, width, height, -1, msg); - return; - } - switch (data['result']) { - case true: - me.clog("we can decode using a worker"); - me.decode_worker = decode_worker; - break; - case false: - me.clog("we can't decode using a worker"); - break; - default: - me.clog("client got unknown message from the decode worker"); - } - }, false); - // ask the worker to check for websocket support, when we receive a reply - // through the eventlistener above, _do_connect() will finish the job - decode_worker.postMessage({'cmd': 'check'}); - } else { + if (!window.Worker) { // no webworker support this.clog("no webworker support at all."); this._do_connect(false); } + this.clog("we have webworker support"); + // spawn worker that checks for a websocket + const me = this; + const worker = new Worker('js/lib/wsworker_check.js'); + worker.addEventListener('message', function(e) { + const data = e.data; + switch (data['result']) { + case true: + // yey, we can use websocket in worker! + me.clog("we can use websocket in webworker"); + me._do_connect(true); + break; + case false: + me.clog("we can't use websocket in webworker, won't use webworkers"); + me._do_connect(false); + break; + default: + me.clog("client got unknown message from worker"); + me._do_connect(false); + } + }, false); + // ask the worker to check for websocket support, when we receive a reply + // through the eventlistener above, _do_connect() will finish the job + worker.postMessage({'cmd': 'check'}); + + const decode_worker = new Worker('js/DecodeWorker.js'); + decode_worker.addEventListener('message', function(e) { + const data = e.data; + if (data['draw']) { + me.do_process_draw(data['draw']); + return; + } + if (data['error']) { + const msg = data['error'], + packet = data['packet'], + wid = packet[1], + width = packet[2], + height = packet[3], + coding = packet[6], + packet_sequence = packet[8]; + me.clog("decode error on ", coding, "packet sequence", packet_sequence, ":", msg); + me.do_send_damage_sequence(packet_sequence, wid, width, height, -1, msg); + return; + } + switch (data['result']) { + case true: + me.clog("we can decode using a worker"); + me.decode_worker = decode_worker; + break; + case false: + me.clog("we can't decode using a worker"); + break; + default: + me.clog("client got unknown message from the decode worker"); + } + }, false); + // ask the worker to check for websocket support, when we receive a reply + // through the eventlistener above, _do_connect() will finish the job + decode_worker.postMessage({'cmd': 'check'}); }; XpraClient.prototype._do_connect = function(with_worker) { diff --git a/html5/js/DecodeWorker.js b/html5/js/DecodeWorker.js index f359becd..78ffd534 100644 --- a/html5/js/DecodeWorker.js +++ b/html5/js/DecodeWorker.js @@ -101,6 +101,8 @@ function close_broadway(wid) { } } +const on_hold = new Map(); + onmessage = function(e) { var data = e.data; switch (data.cmd) { @@ -108,21 +110,46 @@ onmessage = function(e) { self.postMessage({'result': true}); break; case 'eos': - const wid = data.wid; - close_broadway(wid); + close_broadway(data.wid); + if (data.wid in on_hold) { + on_hold.remove(data.wid); + } //console.log("decode worker eos for wid", wid); break; case 'decode': - const packet = data.packet; + const packet = data.packet, + wid = packet[1], + coding = packet[6], + packet_sequence = packet[8]; + let wid_hold = on_hold.get(wid); //console.log("packet to decode:", data.packet); function send_back(raw_buffers) { + //console.log("send_back: wid_hold=", wid_hold); + if (wid_hold) { + //find the highest sequence number which is still lower than this packet + let seq_holding = 0; + for (let seq of wid_hold.keys()) { + if (seq>seq_holding && seq10) options = packet[10]; diff --git a/html5/js/Window.js b/html5/js/Window.js index 6d754bb2..ba287c6b 100644 --- a/html5/js/Window.js +++ b/html5/js/Window.js @@ -1303,6 +1303,13 @@ XpraWindow.prototype._non_video_paint = function(coding) { * The image is painted into off-screen canvas. */ XpraWindow.prototype.paint = function paint() { + if (this.client.decode_worker) { + //no need to synchronize paint packets here + //the decode worker ensures that we get the packets + //in the correct order, ready to update the canvas + XpraWindow.prototype.do_paint.apply(this, arguments); + return; + } //process all paint request in order using the paint_queue: const item = Array.prototype.slice.call(arguments); this.paint_queue.push(item);