muerwre.github.io/content/Frontend/WebGL/Rendering without blocking in a Worker.md
2022-11-27 19:44:07 +06:00

68 lines
No EOL
2 KiB
Markdown

Rendering items on #canvas in main loop ==might cause interface freezes==, preventing render process from executing properly by flooding execution stack with operations.
To handle it properly, we can start separate #worker thread, that will handle rendering in its own event loop, so that won't affect the source tab's event loop.
Workers can have access to [Transferrable Objects](https://developer.mozilla.org/en-US/docs/Glossary/Transferable_objects) from main thread by receiving memory address. ==That's a lot faster than cloning==. In this case [ImageBitmap](https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap) is transferrable.
Code for the main thread component:
```typescript
// main.ts
const instance = new Worker('./render-worker.ts');
const canvas = document.getElementById('view');
// attaching event listener to worker
instance.onmessage = (event: MessageEvent) => {
const ctx = canvas?.getContext("2d");
if (!ctx) {
throw new Error(`Can't get 2D context`);
}
ctx.drawImage(event.data as ImageBitmap, 0, 0);
}
// sending canvas contents to worker
const renderInCanvas = () => {
if (!canvas.current) {
return;
}
createImageBitmap(canvas.current).then(image => {
instance.postMessage(image, { transfer: [image] })
});
};
```
Worker code:
```typescript
// render-worker.ts
export default () => {
self.onmessage = (message: MessageEvent) => {
const data = message.data;
if (!(data instanceof ImageBitmap)) {
throw new Error('Received unknown data')
}
// OffscreenCanvas can be set up inside workers
const canvas = new OffscreenCanvas(data.width, data.height);
const ctx = canvas.getContext("2d");
if (!ctx) {
throw new Error(`Can't get 2D context`);
}
ctx.drawImage(data, 0, 0);
// That will block execution inside worker for
// a couple of seconds
doHardRenderingStuffHere(ctx, data.width, data.height);
// Sending resulting image back to main thread
createImageBitmap(canvas).then((image) => {
self.postMessage(image, { transfer: [image] });
});
};
};
```