Start · Sprachen · JavaScript · Referenz · WorkerNavigator.gpu

WorkerNavigator.gpu

Eigenschaft

Schreibgeschützte Eigenschaft, die im Worker das <code>GPU</code>-Objekt liefert – den Einstiegspunkt der WebGPU-API.

seit JavaScript WebGPU; Chrome/Edge 113+, Safari 26+, Firefox 141+ Kategorie: webapi

Signatur

WorkerNavigator.gpu

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

Typ
GPU | undefined
Beschreibung
Das 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 }));
{ status: 'bereit' } (an den Hauptthread gesendet)

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]);
[2, 4, 6, 8]

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);
};
Bevorzugtes Format: bgra8unorm

// Wichtig · Fallstricke

  • Nur sichere Kontexte: Über HTTP (außer localhost) ist navigator.gpu undefined.
  • 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() kann null liefern, obwohl navigator.gpu vorhanden ist, etwa wegen Treiber-Sperrlisten oder fehlender Hardware. Plane dafür eine Ausweichlösung ein, zum Beispiel WebGL oder die CPU.
  • Das GPUDevice kann jederzeit verloren gehen, etwa durch einen Treiber-Absturz oder wenn der Tab im Hintergrund liegt. Beobachte deshalb device.lost und fordere das Gerät bei Bedarf neu an.
  • Ein GPUDevice lä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 per postMessage aus, idealerweise als übertragbare ArrayBuffer.
  • Die Eigenschaft ist schreibgeschützt, eine Zuweisung hat keine Wirkung.