3.4 웹 워커(Web Worker) 사용하기
WebGPU의 성능을 극대화하려면 Web Worker에서 실행할 수 있습니다. 이렇게 하면 렌더링 작업을 워커로 넘겨 메인 스레드의 부하를 줄이고, UI 반응성을 높일 수 있습니다. 아래는 이 과정을 단계별로 설명합니다.
플레이그라운드 실행 - 3_04_worker워커 스크립트는 실제 렌더링 작업을 담당합니다. 아래는 워커 스크립트의 기본 구조 예시입니다:
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: `워커에서 WebGPU 초기화 중 에러 발생: ${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);
}
이 스크립트에서는 메인 스레드로부터 메시지를 받아 처리하는 이벤트 리스너를 추가합니다. 이 리스너가 렌더링 작업의 진입점이 됩니다. 메시지에는 타입, 셰이더 코드, 오프스크린 캔버스 세 가지 정보가 포함되어 있습니다.
메시지를 받으면 타입을 확인한 뒤, 오프스크린 캔버스와 셰이더 코드를 run 함수에 전달합니다. run 함수는 기존과 비슷한 방식으로 렌더링을 수행합니다(세부 내용 생략).
다음은 메인 스레드에서 워커와 통신하며 렌더링을 시작하는 예제 코드입니다:
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();
}
코드의 동작 과정은 다음과 같습니다:
- 워커 스크립트를 먼저 불러옵니다.
transferControlToOffscreen을 통해 기존 캔버스를 오프스크린 캔버스로 변환하고, devicePixelRatio를 반영해 크기를 맞춥니다.- 셰이더 코드를 문자열로 가져옵니다.
- 마지막으로, run 타입 메시지와 함께 오프스크린 캔버스, 셰이더 코드를 워커로 보냅니다. 오프스크린 캔버스는 메시지 페이로드와 transferable 객체 배열(두 번째 인자) 모두에 포함되어야 합니다.
자바스크립트에서 transferable 객체는 여러 스레드가 동시에 접근하면 위험한 리소스를 담고 있습니다. 워커로 객체 소유권을 안전하게 넘기기 위해 transferable 배열에
추가하는 방식입니다(postMessage의 두 번째 인자).
위 과정을 통해 워커를 이용한 렌더링 분산 처리를 손쉽게 구현할 수 있습니다.