From: root Date: Thu, 24 Mar 2022 14:27:41 +0000 (+0000) Subject: added buffered instructions for canvas; seems to be performant X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=14311ee3a9ee1436321895c9f1605a99abd4266f;p=onyx-live.git added buffered instructions for canvas; seems to be performant --- diff --git a/static/src/canvas.js b/static/src/canvas.js index a880bfb..58c74b1 100644 --- a/static/src/canvas.js +++ b/static/src/canvas.js @@ -1,5 +1,6 @@ let canvas_element = ""; let ctx = null; +let canvas_op_buffer = []; function update_canvas_data() { let canvas_data = new Int32Array(canvas_shared_buffer); @@ -14,8 +15,26 @@ function update_canvas_data() { Atomics.notify(canvas_data, 2); } +function canvas_buffer_flush() { + canvas_op_buffer.sort((a, b) => a[0] - b[0]); + + for (let op of canvas_op_buffer) { + canvas_process(op); + } + + canvas_op_buffer = []; +} + function canvas_handler(instr) { - switch (instr[0]) { + if (instr[1] == 'sync' || instr[1] == 'init') { + canvas_process(instr); + } else { + canvas_op_buffer.push(instr); + } +} + +function canvas_process(instr) { + switch (instr[1]) { case 'init': { canvas_element = document.getElementById("render-target"); ctx = canvas_element.getContext('2d'); @@ -25,6 +44,7 @@ function canvas_handler(instr) { case 'sync': { window.requestAnimationFrame(() => { + canvas_buffer_flush(); let canvas_data = new Int32Array(canvas_shared_buffer); Atomics.store(canvas_data, 2, 1); Atomics.notify(canvas_data, 2); @@ -32,37 +52,37 @@ function canvas_handler(instr) { break; } - case 'fill_style': ctx.fillStyle = instr[1]; break; - case 'font': ctx.font = instr[1]; break; - case 'image_smoothing_enabled': ctx.imageSmoothingEnabled = instr[1]; break; - case 'line_join': ctx.lineJoin = instr[1]; break; - case 'line_width': ctx.lineWidth = instr[1]; break; - case 'stroke_style': ctx.strokeStyle = instr[1]; break; - case 'text_align': ctx.textAlign = instr[1]; break; + case 'fill_style': ctx.fillStyle = instr[2]; break; + case 'font': ctx.font = instr[2]; break; + case 'image_smoothing_enabled': ctx.imageSmoothingEnabled = instr[2]; break; + case 'line_join': ctx.lineJoin = instr[2]; break; + case 'line_width': ctx.lineWidth = instr[2]; break; + case 'stroke_style': ctx.strokeStyle = instr[2]; break; + case 'text_align': ctx.textAlign = instr[2]; break; - case 'arc': ctx.arc(instr[1], instr[2], instr[3], instr[4], instr[5], instr[6]); break; - case 'arc_to': ctx.arcTo(instr[1], instr[2], instr[3], instr[4], instr[5]); break; + case 'arc': ctx.arc(instr[2], instr[3], instr[4], instr[5], instr[6], instr[7]); break; + case 'arc_to': ctx.arcTo(instr[2], instr[3], instr[4], instr[5], instr[6]); break; case 'begin_path': ctx.beginPath(); break; case 'clear': ctx.clearRect(0, 0, canvas_element.width, canvas_element.height); break; - case 'clear_rect': ctx.clearRect(instr[1], instr[2], instr[3], instr[4]); break; - case 'clip': ctx.clip(instr[1]); break; + case 'clear_rect': ctx.clearRect(instr[2], instr[3], instr[4], instr[5]); break; + case 'clip': ctx.clip(instr[2]); break; case 'close_path': ctx.closePath(); break; - case 'ellipse': ctx.ellipse(instr[1], instr[2], instr[3], instr[4], instr[5], instr[6], instr[7], instr[8]); break; - case 'fill': ctx.fill(instr[1]); break; - case 'fill_rect': ctx.fillRect(instr[1], instr[2], instr[3], instr[4]); break; - case 'fill_text': ctx.fillText(instr[1], instr[2], instr[3], instr[4]); break; - case 'line_to': ctx.lineTo(instr[1], instr[2]); break; - case 'move_to': ctx.moveTo(instr[1], instr[2]); break; - case 'quadratic_curve_to': ctx.quadraticCurveTo(instr[1], instr[2], instr[3], instr[4]); break; - case 'rect': ctx.rect(instr[1], instr[2], instr[3], instr[4]); break; - case 'rotate': ctx.rotate(instr[1]); break; + case 'ellipse': ctx.ellipse(instr[2], instr[3], instr[4], instr[5], instr[6], instr[7], instr[8], instr[9]); break; + case 'fill': ctx.fill(instr[2]); break; + case 'fill_rect': ctx.fillRect(instr[2], instr[3], instr[4], instr[5]); break; + case 'fill_text': ctx.fillText(instr[2], instr[3], instr[4], instr[5]); break; + case 'line_to': ctx.lineTo(instr[2], instr[3]); break; + case 'move_to': ctx.moveTo(instr[2], instr[3]); break; + case 'quadratic_curve_to': ctx.quadraticCurveTo(instr[2], instr[3], instr[4], instr[5]); break; + case 'rect': ctx.rect(instr[2], instr[3], instr[4], instr[5]); break; + case 'rotate': ctx.rotate(instr[2]); break; case 'save': ctx.save(); break; - case 'scale': ctx.scale(instr[1], instr[2]); break; - case 'set_transform': ctx.setTransform(instr[1], instr[2], instr[3], instr[4], instr[5], instr[6]); break; + case 'scale': ctx.scale(instr[2], instr[3]); break; + case 'set_transform': ctx.setTransform(instr[2], instr[3], instr[4], instr[5], instr[6], instr[7]); break; case 'stroke': ctx.stroke(); break; - case 'stroke_rect': ctx.strokeRect(instr[1], instr[2], instr[3], instr[4]); break; - case 'stroke_text': ctx.strokeText(instr[1], instr[2], instr[3], instr[4]); break; - case 'transform': ctx.transform(instr[1], instr[2], instr[3], instr[4], instr[5], instr[6]); break; - case 'translate': ctx.translate(instr[1], instr[2]); break; + case 'stroke_rect': ctx.strokeRect(instr[2], instr[3], instr[4], instr[5]); break; + case 'stroke_text': ctx.strokeText(instr[2], instr[3], instr[4], instr[5]); break; + case 'transform': ctx.transform(instr[2], instr[3], instr[4], instr[5], instr[6], instr[7]); break; + case 'translate': ctx.translate(instr[2], instr[3]); break; } } diff --git a/static/src/worker.js b/static/src/worker.js index b2a1df8..1090777 100644 --- a/static/src/worker.js +++ b/static/src/worker.js @@ -7,6 +7,8 @@ function S(strptr, strlen) { return new TextDecoder().decode(strdata); } +let canvas_order_id = 0; + let import_obj = { host: { print_str(strptr, strlen) { @@ -48,7 +50,8 @@ let import_obj = { canvas: { init() { - self.postMessage({ type: 'canvas', data: [ 'init' ] }); + self.postMessage({ type: 'canvas', data: [ 0, 'init' ] }); + canvas_order_id = 0; // Issue a synchronization because the data needs to be correct // before control should be handed back to the running program. @@ -58,7 +61,7 @@ let import_obj = { }, sync() { - self.postMessage({ type: 'canvas', data: [ 'sync'] }); + self.postMessage({ type: 'canvas', data: [ 0, 'sync'] }); let canvas_data = new Int32Array(canvas_data_buffer); Atomics.wait(canvas_data, 2, 0); @@ -74,9 +77,9 @@ let import_obj = { }, // Settings - fill_style(styleptr, stylelen) { self.postMessage({ type: 'canvas', data: [ 'fill_style', S(styleptr, stylelen) ] }); }, - font(p, l) { self.postMessage({ type: 'canvas', data: [ 'font', S(p, l) ] }); }, - image_smoothing_enabled(e) { self.postMessage({ type: 'canvas', data: [ 'image_smoothing_enabled', e != 0 ] }); }, + fill_style(styleptr, stylelen) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'fill_style', S(styleptr, stylelen) ] }); }, + font(p, l) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'font', S(p, l) ] }); }, + image_smoothing_enabled(e) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'image_smoothing_enabled', e != 0 ] }); }, line_join(join) { let method = ""; switch (join) { @@ -86,11 +89,11 @@ let import_obj = { } if (method != "") { - self.postMessage({ type: 'canvas', data: [ 'line_join' , method ] }); + self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'line_join' , method ] }); } }, - line_width(width) { self.postMessage({ type: 'canvas', data: [ 'line_width', width ] }); }, - stroke_style(s, l) { self.postMessage({ type: 'canvas', data: [ 'stroke_style', S(s, l) ] }); }, + line_width(width) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'line_width', width ] }); }, + stroke_style(s, l) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'stroke_style', S(s, l) ] }); }, text_align(align) { let method = ""; switch (align) { @@ -102,16 +105,16 @@ let import_obj = { } if (method != "") { - self.postMessage({ type: 'canvas', data: [ 'text_align' , method ] }); + self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'text_align' , method ] }); } }, // Drawing - arc(x, y, r, sa, ea, cc) { self.postMessage({ type: 'canvas', data: [ 'arc', x, y, r, sa, ea, cc != 0 ] }); }, - arc_to(x1, y1, x2, y2, r) { self.postMessage({ type: 'canvas', data: [ 'arc_to', x1, y1, x2, y2, r ] }); }, - begin_path() { self.postMessage({ type: 'canvas', data: [ 'begin_path' ] }); }, - clear() { self.postMessage({ type: 'canvas', data: [ 'clear' ] }); }, - clear_rect(x, y, w, h) { self.postMessage({ type: 'canvas', data: [ 'clear_rect', x, y, w, h ] }); }, + arc(x, y, r, sa, ea, cc) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'arc', x, y, r, sa, ea, cc != 0 ] }); }, + arc_to(x1, y1, x2, y2, r) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'arc_to', x1, y1, x2, y2, r ] }); }, + begin_path() { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'begin_path' ] }); }, + clear() { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'clear' ] }); }, + clear_rect(x, y, w, h) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'clear_rect', x, y, w, h ] }); }, clip(cliprule) { let method = ""; switch (cliprule) { @@ -120,11 +123,11 @@ let import_obj = { } if (method != "") { - self.postMessage({ type: 'canvas', data: [ 'clip', method ] }); + self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'clip', method ] }); } }, - close_path() { self.postMessage({ type: 'canvas', data: [ 'close_path' ] }); }, - ellipse(x, y, rx, ry, r, sa, ea, cc) { self.postMessage({ type: 'canvas', data: [ 'ellipse', x, y, rx, ry, r, sa, ea, cc != 0 ] }); }, + close_path() { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'close_path' ] }); }, + ellipse(x, y, rx, ry, r, sa, ea, cc) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'ellipse', x, y, rx, ry, r, sa, ea, cc != 0 ] }); }, fill(fillrule) { let method = ""; switch (fillrule) { @@ -133,27 +136,27 @@ let import_obj = { } if (method != "") { - self.postMessage({ type: 'canvas', data: [ 'fill', method ] }); + self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'fill', method ] }); } }, - fill_rect(x, y, w, h) { self.postMessage({ type: 'canvas', data: [ 'fill_rect', x, y, w, h ] }); }, + fill_rect(x, y, w, h) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'fill_rect', x, y, w, h ] }); }, fill_text(textptr, textlen, x, y, max_width) { - self.postMessage({ type: 'canvas', data: [ 'fill_text', S(textptr, textlen), x, y, max_width > 0 ? max_width : null ] }); + self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'fill_text', S(textptr, textlen), x, y, max_width > 0 ? max_width : null ] }); }, - line_to(x, y) { self.postMessage({ type: 'canvas', data: [ 'line_to', x, y ] }); }, - move_to(x, y) { self.postMessage({ type: 'canvas', data: [ 'move_to', x, y ] }); }, - quadratic_curve_to(cpx, cpy, x, y) { self.postMessage({ type: 'canvas', data: [ 'quadratic_curve_to', cpx, cpy, x, y ] }); }, - rect(x, y, w, h) { self.postMessage({ type: 'canvas', data: [ 'rect', x, y, w, h ] }); }, - restore() { self.postMessage({ type: 'canvas', data: [ 'restore' ] }); }, - rotate(angle) { self.postMessage({ type: 'canvas', data: [ 'rotate', angle ] }); }, - save() { self.postMessage({ type: 'canvas', data: [ 'save' ] }); }, - scale(x, y) { self.postMessage({ type: 'canvas', data: [ 'scale', x, y ] }); }, - set_transform(a, b, c, d, e, f) { self.postMessage({ type: 'canvas', data: [ 'set_transform', a, b, c, d, e, f ] }); }, - stroke() { self.postMessage({ type: 'canvas', data: [ 'stroke' ] }); }, - stroke_rect(x, y, w, h) { self.postMessage({ type: 'canvas', data: [ 'stroke_rect', x, y, w, h ] }); }, - stroke_text(textptr, textlen, x, y, max_width) { self.postMessage({ type: 'canvas', data: [ 'stroke_text', S(textptr, textlen), x, y, max_width > 0 ? max_width : null ] }); }, - transform(a, b, c, d, e, f) { self.postMessage({ type: 'canvas', data: [ 'transform', a, b, c, d, e, f ] }); }, - translate(x, y) { self.postMessage({ type: 'canvas', data: [ 'translate', x, y ] }); }, + line_to(x, y) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'line_to', x, y ] }); }, + move_to(x, y) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'move_to', x, y ] }); }, + quadratic_curve_to(cpx, cpy, x, y) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'quadratic_curve_to', cpx, cpy, x, y ] }); }, + rect(x, y, w, h) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'rect', x, y, w, h ] }); }, + restore() { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'restore' ] }); }, + rotate(angle) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'rotate', angle ] }); }, + save() { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'save' ] }); }, + scale(x, y) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'scale', x, y ] }); }, + set_transform(a, b, c, d, e, f) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'set_transform', a, b, c, d, e, f ] }); }, + stroke() { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'stroke' ] }); }, + stroke_rect(x, y, w, h) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'stroke_rect', x, y, w, h ] }); }, + stroke_text(textptr, textlen, x, y, max_width) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'stroke_text', S(textptr, textlen), x, y, max_width > 0 ? max_width : null ] }); }, + transform(a, b, c, d, e, f) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'transform', a, b, c, d, e, f ] }); }, + translate(x, y) { self.postMessage({ type: 'canvas', data: [ canvas_order_id++, 'translate', x, y ] }); }, } }; @@ -176,6 +179,7 @@ onmessage = function(m) { self.postMessage({ type: 'errored', data: e.toString() }); } + self.postMessage({ type: 'canvas', data: [ 0, 'sync' ] }); self.postMessage({ type: 'terminated' }); self.close(); }); @@ -184,6 +188,7 @@ onmessage = function(m) { } case 'stop': + self.postMessage({ type: 'canvas', data: [ 0, 'sync' ] }); self.postMessage({ type: 'terminated' }); self.close(); break;