Signatur
Beschreibung
GPUComputePassEncoder ist Teil der WebGPU-API und steht für einen einzelnen Compute-Pass innerhalb eines Befehlspuffers. Man erhält eine Instanz nicht über einen Konstruktor, sondern über GPUCommandEncoder.beginComputePass(). Innerhalb des Passes legt man fest, welche GPUComputePipeline (also welcher Compute-Shader) ausgeführt wird, welche Ressourcen (Buffer, Texturen, Sampler) über Bind Groups verfügbar sind und wie viele Arbeitsgruppen gestartet werden.
Wichtige Methoden und Eigenschaften:
setPipeline(pipeline)– legt die zu verwendendeGPUComputePipelinefest.setBindGroup(index, bindGroup, dynamicOffsets?)– bindet eineGPUBindGroupan den Index@group(n)im WGSL-Shader.dispatchWorkgroups(x, y?, z?)– startet ein Raster von Arbeitsgruppen (Standard füryundz:1).dispatchWorkgroupsIndirect(indirectBuffer, indirectOffset)– wie oben, die Anzahl der Gruppen wird aber aus einem GPU-Buffer gelesen.pushDebugGroup(label),popDebugGroup(),insertDebugMarker(label)– Beschriftungen für Debugging-Werkzeuge.end()– schließt den Pass ab; danach ist der Encoder nicht mehr nutzbar.label– frei wählbarer Name, der in Fehlermeldungen erscheint.
Typische Einsatzgebiete sind GPGPU-Aufgaben direkt im Browser: Bildfilter, Partikelsimulationen, Physik, Matrixmultiplikation oder Inferenz neuronaler Netze. Der Ablauf ist immer gleich: Command Encoder anlegen → Compute-Pass beginnen → Pipeline und Bind Groups setzen → dispatchen → end() → commandEncoder.finish() → an device.queue.submit() übergeben.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $descriptor (von beginComputePass) | GPUComputePassDescriptor | {} | Optionales Objekt für GPUCommandEncoder.beginComputePass() mit label (string) und timestampWrites (für Zeitmessung, benötigt das Feature "timestamp-query"). |
| $workgroupCountX Pflicht | number | Parameter von dispatchWorkgroups(): Anzahl der Arbeitsgruppen in X-Richtung. |
|
| $workgroupCountY | number | 1 | Anzahl der Arbeitsgruppen in Y-Richtung. |
| $workgroupCountZ | number | 1 | Anzahl der Arbeitsgruppen in Z-Richtung. |
Rückgabewert
GPUCommandEncoder.beginComputePass() liefert eine neue Instanz. Die Methoden des Encoders selbst geben undefined zurück; Fehler werden asynchron als Validierungsfehler des GPUDevice gemeldet.Beispiele
Compute-Pass anlegen und Werte in einem Buffer verdoppeln
const adapter = await navigator.gpu?.requestAdapter();
if (!adapter) throw new Error('WebGPU wird nicht unterstützt');
const device = await adapter.requestDevice();
const module = device.createShaderModule({
code: `
@group(0) @binding(0) var<storage, read_write> data: array<f32>;
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) id: vec3u) {
if (id.x < arrayLength(&data)) {
data[id.x] = data[id.x] * 2.0;
}
}
`
});
const pipeline = device.createComputePipeline({
layout: 'auto',
compute: { module, entryPoint: 'main' }
});
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 bindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [{ binding: 0, resource: { buffer: storage } }]
});
const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass({ label: 'Verdoppeln' });
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(Math.ceil(input.length / 64));
pass.end();
// Ergebnis in einen lesbaren Buffer kopieren
const readback = device.createBuffer({
size: input.byteLength,
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
});
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();
2D-Dispatch für die Bildverarbeitung mit Debug-Gruppen
// Annahme: device, pipeline und bindGroup existieren bereits (Shader mit @workgroup_size(8, 8))
const width = 1920;
const height = 1080;
const encoder = device.createCommandEncoder({ label: 'Bildfilter' });
const pass = encoder.beginComputePass({ label: 'Graustufen-Pass' });
pass.pushDebugGroup('Graustufen');
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.insertDebugMarker('Dispatch startet');
pass.dispatchWorkgroups(Math.ceil(width / 8), Math.ceil(height / 8));
pass.popDebugGroup();
pass.end();
device.queue.submit([encoder.finish()]);
console.log(`${Math.ceil(width / 8) * Math.ceil(height / 8)} Arbeitsgruppen gestartet`);
Indirekter Dispatch – Gruppenanzahl aus einem Buffer
// Drei u32-Werte: x, y, z – können auch von einem vorherigen Shader geschrieben werden
const indirect = device.createBuffer({
size: 12,
usage: GPUBufferUsage.INDIRECT | GPUBufferUsage.COPY_DST
});
device.queue.writeBuffer(indirect, 0, new Uint32Array([16, 1, 1]));
const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroupsIndirect(indirect, 0);
pass.end();
device.queue.submit([encoder.finish()]);
Validierungsfehler abfangen
device.pushErrorScope('validation');
const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass();
// Fehler: dispatch ohne vorher gesetzte Pipeline
pass.dispatchWorkgroups(1);
pass.end();
device.queue.submit([encoder.finish()]);
const error = await device.popErrorScope();
if (error) console.warn('Validierungsfehler:', error.message);