3.4 Using Web Workers

To enhance performance with WebGPU, you can execute it in a Web Worker. This approach allows the rendering workload to be offloaded to a worker, freeing up the main thread for other tasks. Here’s a step-by-step guide on how to achieve this.

Launch Playground - 3_04_worker

The worker script handles the rendering process. Below is a general structure of the worker script:

self.addEventListener('message', (ev) => {
    switch (ev.data.type) {
        case 'run': {
            try {
                run(ev.data.offscreenCanvas, ev.data.code);
            } catch (err) {
                self.postMessage({
                    type: 'log',
                    message: `Error while initializing WebGPU in worker process: ${err.message}`,
                });
            }
            break;
        }
    }
});
• • •
async function run(canvas, code) {
    let angle = 0.0;

    const adapter = await navigator.gpu.requestAdapter();

    let device = await adapter.requestDevice();

    let context = configContext(device, canvas);
• • •
async function render() {
• • •
        requestAnimationFrame(render);
    }

    requestAnimationFrame(render);

}

In this script, we add an event listener to handle messages from the main thread. This listener acts as the entry point for the rendering process. When a message is received, it includes three pieces of data: the message type, shader code, and the offscreen canvas—all part of the message payload.

Upon receiving the message, we check its type and pass both the offscreen canvas and shader code to the run function. This function performs rendering similarly to how it did before, but the specifics are not detailed here.

Next, let’s see how to configure the main thread to communicate with the worker and start the rendering process:

const worker = new Worker('./worker.js');
• • •
try {
    const offscreenCanvas = canvas.transferControlToOffscreen();
    const devicePixelRatio = window.devicePixelRatio || 1;
    offscreenCanvas.width = canvas.clientWidth * devicePixelRatio;
    offscreenCanvas.height = canvas.clientHeight * devicePixelRatio;
    let code = document.getElementById('teapot_shader').innerText;
    worker.postMessage({ type: 'run', offscreenCanvas, code }, [offscreenCanvas]);
} catch (err) {
    console.warn(err.message);
    worker.terminate();
}

Here’s what’s happening in this code:

In JavaScript, a transferable object has underlying resources unsafe for simultaneous access by multiple threads. Transferring the object's ownership to the worker thread is a safe way to handle this. You request this transfer by including the object in the transferable objects array (the second parameter of postMessage).

With these steps, you can effectively utilize workers for rendering tasks.

Leave a Comment on Github