Start · Sprachen · JavaScript · Referenz · GPUComputePassEncoder.dispatchWorkgroups()

GPUComputePassEncoder.dispatchWorkgroups()

Methode

Startet die aktuell gesetzte <code>GPUComputePipeline</code> mit einem dreidimensionalen Raster aus Arbeitsgruppen (Workgroups) auf der GPU.

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

Signatur

GPUComputePassEncoder.prototype.dispatchWorkgroups(workgroupCountX, workgroupCountY, workgroupCountZ)

Beschreibung

dispatchWorkgroups() ist der eigentliche „Startbefehl“ eines Compute-Durchlaufs in WebGPU. Die Methode zeichnet in den GPUComputePassEncoder einen Befehl auf, der den Compute-Shader der zuvor mit setPipeline() gesetzten Pipeline ausführt – und zwar für ein Raster aus X × Y × Z Arbeitsgruppen. Ausgeführt wird der Befehl erst, wenn der daraus erzeugte GPUCommandBuffer per device.queue.submit() an die GPU übergeben wird.

Wie viele Threads (Invocations) eine Arbeitsgruppe enthält, legt der WGSL-Shader mit dem Attribut @workgroup_size(x, y, z) fest. Die Gesamtzahl der Aufrufe ergibt sich also aus Anzahl Arbeitsgruppen × Arbeitsgruppengröße je Dimension. Im Shader erkennt jeder Aufruf seine Position über eingebaute Werte wie global_invocation_id, workgroup_id und local_invocation_id.

Typischerweise teilt man die Datenmenge durch die Arbeitsgruppengröße und rundet auf: Math.ceil(elementAnzahl / 64). Weil dabei am Ende mehr Threads entstehen können als Daten vorhanden sind, muss der Shader eine Bereichsprüfung enthalten. Bei 2D-Daten wie Bildern nutzt man X und Y, bei Volumendaten zusätzlich Z.

Vor dem Aufruf müssen Pipeline und alle benötigten Bind-Groups (setBindGroup()) gesetzt sein. Ist die Anzahl der Arbeitsgruppen erst auf der GPU bekannt, verwendet man stattdessen dispatchWorkgroupsIndirect().

Parameter

Name Typ Default Beschreibung
$workgroupCountX Pflicht number Anzahl der Arbeitsgruppen in X-Richtung. Ganze Zahl ≥ 0, höchstens device.limits.maxComputeWorkgroupsPerDimension (Standard 65535).
$workgroupCountY number 1 Anzahl der Arbeitsgruppen in Y-Richtung. Gleiche Obergrenze wie bei X.
$workgroupCountZ number 1 Anzahl der Arbeitsgruppen in Z-Richtung. Gleiche Obergrenze wie bei X.

Rückgabewert

Typ
undefined
Beschreibung
Kein Rückgabewert. Der Befehl wird nur aufgezeichnet; Ergebnisse stehen erst nach queue.submit() und z. B. dem Zurücklesen eines Buffers mit mapAsync() bereit.

Beispiele

Array-Werte auf der GPU verdoppeln (1D)

const adapter = await navigator.gpu?.requestAdapter();
if (!adapter) throw new Error('WebGPU wird nicht unterstützt');
const device = await adapter.requestDevice();

const WORKGROUP_SIZE = 64;
const module = device.createShaderModule({
  code: `
    @group(0) @binding(0) var<storage, read_write> data: array<f32>;

    @compute @workgroup_size(${WORKGROUP_SIZE})
    fn main(@builtin(global_invocation_id) id: vec3u) {
      if (id.x >= arrayLength(&data)) { return; } // Bereichsprüfung
      data[id.x] = data[id.x] * 2.0;
    }
  `,
});

const input = new Float32Array([1, 2, 3, 4, 5]);
const storage = device.createBuffer({
  size: input.byteLength,
  usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(storage, 0, input);

const readback = device.createBuffer({
  size: input.byteLength,
  usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
});

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 / WORKGROUP_SIZE));
pass.end();
encoder.copyBufferToBuffer(storage, 0, readback, 0, input.byteLength);
device.queue.submit([encoder.finish()]);

await readback.mapAsync(GPUMapMode.READ);
console.log(Array.from(new Float32Array(readback.getMappedRange())));
readback.unmap();
[2, 4, 6, 8, 10]

2D-Raster für die Bildverarbeitung berechnen

// Shader nutzt @workgroup_size(8, 8) – also 64 Threads pro Gruppe
const dispatchForImage = (pass, width, height, tile = 8) => {
  const x = Math.ceil(width / tile);
  const y = Math.ceil(height / tile);
  pass.dispatchWorkgroups(x, y); // Z ist standardmäßig 1
  return { x, y };
};

// Innerhalb eines bereits vorbereiteten Compute-Passes:
// pass.setPipeline(blurPipeline);
// pass.setBindGroup(0, imageBindGroup);
const fakePass = { dispatchWorkgroups: (...a) => console.log('dispatch', a) };
console.log(dispatchForImage(fakePass, 1920, 1080));
dispatch [240, 135] { x: 240, y: 135 }

Limits prüfen und große Datenmengen aufteilen

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

const maxPerDim = device.limits.maxComputeWorkgroupsPerDimension; // meist 65535
const WORKGROUP_SIZE = 64;
const elements = 10_000_000;
const groupsNeeded = Math.ceil(elements / WORKGROUP_SIZE); // 156250 > 65535

// Auf zwei Dimensionen verteilen; im Shader Index = id.x + id.y * (x * 64)
const x = Math.min(groupsNeeded, maxPerDim);
const y = Math.ceil(groupsNeeded / x);
console.log({ maxPerDim, x, y });
// pass.dispatchWorkgroups(x, y);
{ maxPerDim: 65535, x: 65535, y: 3 }

// Wichtig · Fallstricke

  • Arbeitsgruppen statt Threads: Die Argumente geben die Anzahl der Arbeitsgruppen an, nicht die der Threads. Ein häufiger Fehler ist dispatchWorkgroups(elementAnzahl) bei @workgroup_size(64) – dadurch laufen 64-mal zu viele Threads.
  • Bereichsprüfung im Shader: Durch das Aufrunden mit Math.ceil() entstehen überzählige Threads. Ohne if (id.x >= n) { return; } wird außerhalb des Buffers gelesen oder geschrieben. WebGPU verhindert Speicherzugriffe außerhalb der Grenzen zwar, die Ergebnisse sind dann aber undefiniert.
  • Limits: Jede Dimension darf maxComputeWorkgroupsPerDimension (Standard 65535) nicht überschreiten. Wird das Limit überschritten, wird der Befehlspuffer ungültig; der Fehler taucht als Validierungsfehler auf (device.pushErrorScope('validation') oder Ereignis uncapturederror), nicht als Ausnahme im JavaScript.
  • Ein Aufruf mit 0 in irgendeiner Dimension ist gültig, bewirkt aber nichts.
  • Ohne vorheriges setPipeline() bzw. mit fehlenden Bind-Groups ist der Pass ungültig.
  • Kompatibilität: WebGPU steht nur in sicheren Kontexten zur Verfügung und noch nicht in allen Browsern bzw. auf allen Plattformen. Vorher mit if (!navigator.gpu) prüfen. Hinweis: In frühen Entwürfen hieß die Methode dispatch(); diese Bezeichnung ist veraltet.