Signatur
Beschreibung
Über navigator.gpu kommt man innerhalb eines Web Workers (Dedicated Worker, in Chromium auch Shared und Service Worker) an das GPU-Objekt. Das ist dasselbe Objekt wie Navigator.gpu im Hauptthread. Damit fordert man mit requestAdapter() einen GPUAdapter an und erhält daraus ein GPUDevice. Mit diesem Gerät lassen sich Render- und Compute-Pipelines erstellen.
Besonders nützlich ist das bei rechenintensiven Aufgaben wie Bildverarbeitung, physikalischen Simulationen oder Machine-Learning-Inferenz. Die Vorbereitung der GPU-Befehle und das Zurücklesen der Ergebnisse laufen dann im Worker, sodass der Hauptthread nicht blockiert. Zusammen mit OffscreenCanvas kann ein Worker auch direkt auf eine Zeichenfläche rendern, die ihm per transferControlToOffscreen() übergeben wurde.
Die Eigenschaft steht nur in sicheren Kontexten (HTTPS oder localhost) zur Verfügung. Wenn der Browser WebGPU nicht unterstützt, ist sie undefined. Prüfe deshalb immer zuerst, ob sie vorhanden ist, bevor du sie nutzt.
Rückgabewert
GPU-Objekt des aktuellen Worker-Kontexts. undefined, wenn WebGPU nicht unterstützt wird oder der Kontext nicht sicher ist.Beispiele
Unterstützung prüfen und GPUDevice im Worker anfordern
// gpu-worker.js
const initGPU = async () => {
if (!navigator.gpu) {
throw new Error('WebGPU wird in diesem Worker nicht unterstützt.');
}
const adapter = await navigator.gpu.requestAdapter({ powerPreference: 'high-performance' });
if (!adapter) {
throw new Error('Kein passender GPU-Adapter gefunden.');
}
const device = await adapter.requestDevice();
device.lost.then((info) => console.warn('GPU-Gerät verloren:', info.message));
return device;
};
initGPU()
.then(() => postMessage({ status: 'bereit' }))
.catch((err) => postMessage({ status: 'fehler', message: err.message }));
Compute-Shader im Worker: Zahlen verdoppeln
// compute-worker.js
self.onmessage = async ({ data }) => {
const adapter = await navigator.gpu?.requestAdapter();
if (!adapter) return postMessage({ error: 'Kein WebGPU' });
const device = await adapter.requestDevice();
const input = new Float32Array(data);
const size = input.byteLength;
const storage = device.createBuffer({
size,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST
});
device.queue.writeBuffer(storage, 0, input);
const readBuffer = device.createBuffer({
size,
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
});
const module = device.createShaderModule({
code: `
@group(0) @binding(0) var<storage, read_write> werte: array<f32>;
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) id: vec3u) {
if (id.x < arrayLength(&werte)) { werte[id.x] = werte[id.x] * 2.0; }
}`
});
const pipeline = device.createComputePipeline({
layout: 'auto',
compute: { module, entryPoint: 'main' }
});
const bindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [{ binding: 0, resource: { buffer: storage } }]
});
const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(Math.ceil(input.length / 64));
pass.end();
encoder.copyBufferToBuffer(storage, 0, readBuffer, 0, size);
device.queue.submit([encoder.finish()]);
await readBuffer.mapAsync(GPUMapMode.READ);
const result = Array.from(new Float32Array(readBuffer.getMappedRange()));
readBuffer.unmap();
postMessage(result);
};
// main.js
// const worker = new Worker('compute-worker.js');
// worker.onmessage = (e) => console.log(e.data);
// worker.postMessage([1, 2, 3, 4]);
Rendern auf ein OffscreenCanvas im Worker
// main.js
const canvas = document.querySelector('canvas');
const offscreen = canvas.transferControlToOffscreen();
const worker = new Worker('render-worker.js');
worker.postMessage({ canvas: offscreen }, [offscreen]);
// render-worker.js
self.onmessage = async ({ data: { canvas } }) => {
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const context = canvas.getContext('webgpu');
const format = navigator.gpu.getPreferredCanvasFormat();
context.configure({ device, format, alphaMode: 'premultiplied' });
// Canvas einfarbig blau löschen
const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass({
colorAttachments: [{
view: context.getCurrentTexture().createView(),
clearValue: { r: 0.1, g: 0.3, b: 0.8, a: 1 },
loadOp: 'clear',
storeOp: 'store'
}]
});
pass.end();
device.queue.submit([encoder.finish()]);
console.log('Bevorzugtes Format:', format);
};
// Wichtig · Fallstricke
- Nur sichere Kontexte: Über HTTP (außer
localhost) istnavigator.gpuundefined. - Browser-Unterstützung: Chromium-Browser unterstützen WebGPU seit Version 113 (zuerst Windows, macOS und ChromeOS, Android ab 121). Safari unterstützt es ab Version 26, Firefox seit Version 141 zunächst nur unter Windows. Unterstützung in Workern ist in Dedicated Workern am verlässlichsten. Ob es auch in Shared oder Service Workern funktioniert, solltest du pro Browser prüfen.
requestAdapter()kannnullliefern, obwohlnavigator.gpuvorhanden ist, etwa wegen Treiber-Sperrlisten oder fehlender Hardware. Plane dafür eine Ausweichlösung ein, zum Beispiel WebGL oder die CPU.- Das
GPUDevicekann jederzeit verloren gehen, etwa durch einen Treiber-Absturz oder wenn der Tab im Hintergrund liegt. Beobachte deshalbdevice.lostund fordere das Gerät bei Bedarf neu an. - Ein
GPUDevicelässt sich nicht zwischen Threads übertragen. Hauptthread und Worker brauchen jeweils ein eigenes Gerät. Ressourcen wie Puffer lassen sich nicht direkt zwischen ihnen teilen. Tausche stattdessen die Daten perpostMessageaus, idealerweise als übertragbareArrayBuffer. - Die Eigenschaft ist schreibgeschützt, eine Zuweisung hat keine Wirkung.