Script Preparation code:
x
 
var blob = new Blob([
`
  let bitmap = undefined;
  let canvas = undefined;
  let ctx    = undefined;
  let buffer = undefined;
let postAMessage=self.postMessage;
function render()
{
        ctx.clearRect(0,0,640,480);
        ctx.drawImage(bitmap, 0,0);
        requestAnimationFrame(render);
}
self.onmessage = function(ev) {
  if(ev.data.msg === 'bitmap' && canvas != undefined) {
        bitmap = ev.data.imageB;
        console.log("Received an ImageBitmap next!");
        render();
  }
  if(ev.data.msg === 'init' && bitmap != undefined) {
    canvas = ev.data.canvas;
    ctx = canvas.getContext('2d');
    console.log("Received a canvas next!");
    render();
  }
  if(ev.data.msg === 'bitmap' && canvas == undefined) {
    console.log("Received a bitmap first!");
    
        bitmap = ev.data.imageB;
  }
  if(ev.data.msg === 'init' && bitmap == undefined) {
    console.log("Received a canvas first!");
    
    canvas = ev.data.canvas;
    ctx = canvas.getContext('2d');
  }
  if(ev.data.msg === 'buffer') {
    console.log("Clearing buffer " + ev.data.msgIndex + "from pool");
    self.postMessage({msg:'buffer',msgIndex:ev.data.msgIndex});
  }
}
`]);
var myWorker = new Worker(window.URL.createObjectURL(blob));
var buff = new ArrayBuffer(100);
var buffers = [];
//create a pool of buffers
for (let i = 0; i < 30; i++)
{
buffers.push(new ArrayBuffer(100));
buffers[i].isUsed = false;
}
var img = new Image();
function handleMessageFromWorker(msg) {
    console.log('the thread used buffer: ' + msg.data.msgIndex);
    if (msg.data.msg == "buffer")
    {
      buffers[parseInt(msg.data.msgIndex)] = new ArrayBuffer(100);
      buffers[parseInt(msg.data.msgIndex)].isUsed = false;
    }
}
myWorker.addEventListener('message', handleMessageFromWorker);
img.src = "https://www.google.com/s2/favicons?domain=www.google.com";
img.decode().then(() => {createImageBitmap(img).then(Ibitmap => { myWorker.postMessage({imageB: Ibitmap, msg:'bitmap'}, [Ibitmap]);  })});
var canvas =  document.createElement("canvas");
var c2 =  document.createElement("canvas");
c2.width = 640;
c2.height = 480;
c2 = c2.transferControlToOffscreen();
canvas.width = 640;
canvas.height = 480;
c2.width = 640;
c2.height = 480;
var ctx = canvas.getContext('2d');
myWorker.postMessage({msg: 'init', canvas: c2}, [c2]);
Tests:
  • drawImage Main Thread

     
    if (img.width > 0){
    ctx.drawImage(img, 0, 0);
    ctx.clearRect(0,0,640,480);
    }
  • drawImage Worker Thread, String

     
    if (img.width > 0) myWorker.postMessage("255,0,0");
  • drawImage Worker Thread, ArrayBuffer StructuredClone

     
    if (img.width > 0) myWorker.postMessage(buff);
  • drawImage Worker Thread, ArrayBuffer Transferable

     
    if (img.width > 0) { let buffersFull = true; for (let i = 0; i < buffers.length; i++) { if (!buffers[i].isUsed) {myWorker.postMessage({msg:'buffer', msgIndex:i, buffer:buffers[i]},[buffers[i]]); buffers[i].isUsed = true; buffersFull = false;} }if (buffersFull) console.log("All buffers in the pool are full")}
Rendered benchmark preparation results:

Suite status: <idle, ready to run>

Previous results

Experimental features:

  • Test case name Result
    drawImage Main Thread
    drawImage Worker Thread, String
    drawImage Worker Thread, ArrayBuffer StructuredClone
    drawImage Worker Thread, ArrayBuffer Transferable

    Fastest: N/A

    Slowest: N/A

Latest run results:
Run details: (Test run date: 5 years ago)
Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36
Chrome 73 on Windows 8.1
View result in a separate tab
Test name Executions per second
drawImage Main Thread 52419.6 Ops/sec
drawImage Worker Thread, String 122102.0 Ops/sec
drawImage Worker Thread, ArrayBuffer StructuredClone 82293.2 Ops/sec
drawImage Worker Thread, ArrayBuffer Transferable 9825.0 Ops/sec