Signatur
Beschreibung
Ein GPUCommandEncoder ist in WebGPU der zentrale Baustein, um GPU-Arbeit aufzuzeichnen. Er wird über GPUDevice.createCommandEncoder() erzeugt und nimmt Befehle in der Reihenfolge auf, in der sie später ausgeführt werden sollen. Die Befehle werden dabei nicht sofort ausgeführt, sondern nur aufgezeichnet. Erst finish() erzeugt daraus einen GPUCommandBuffer, den man mit device.queue.submit([...]) an die GPU schickt.
Die wichtigsten Methoden sind:
beginRenderPass(descriptor)– startet einen Render-Pass und gibt einenGPURenderPassEncoderzurück.beginComputePass(descriptor?)– startet einen Compute-Pass und gibt einenGPUComputePassEncoderzurück.copyBufferToBuffer(),copyBufferToTexture(),copyTextureToBuffer(),copyTextureToTexture()– Kopiervorgänge zwischen Puffern und Texturen.clearBuffer(buffer, offset?, size?)– setzt einen Pufferbereich auf Null.resolveQuerySet()– schreibt Ergebnisse von Abfragen (z. B. Zeitstempeln) in einen Puffer.pushDebugGroup(),popDebugGroup(),insertDebugMarker()– Markierungen für Debugging-Werkzeuge.finish(descriptor?)– schließt die Aufzeichnung ab und liefert einenGPUCommandBuffer.
Außerdem gibt es die Eigenschaft label, einen frei wählbaren Namen, der in Fehlermeldungen und Debugging-Werkzeugen angezeigt wird. Ein Encoder ist für eine einzige Nutzung gedacht: Nach finish() ist er ungültig. Üblicherweise erzeugt man pro Frame bzw. pro Arbeitspaket einen neuen Encoder.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $descriptor | GPUCommandEncoderDescriptor | {} | Optionales Objekt, das an device.createCommandEncoder() übergeben wird. Unterstützt derzeit nur label (string), einen Namen fürs Debugging. |
Rückgabewert
device.createCommandEncoder() gibt eine neue GPUCommandEncoder-Instanz zurück. Aus ihr erzeugt finish() einen GPUCommandBuffer.Beispiele
Encoder erzeugen und Bildschirm löschen (Render-Pass)
const adapter = await navigator.gpu?.requestAdapter();
if (!adapter) throw new Error('WebGPU wird nicht unterstützt');
const device = await adapter.requestDevice();
const canvas = document.querySelector('canvas');
const context = canvas.getContext('webgpu');
const format = navigator.gpu.getPreferredCanvasFormat();
context.configure({ device, format });
// Encoder für diesen Frame anlegen
const encoder = device.createCommandEncoder({ label: 'Frame-Encoder' });
const pass = encoder.beginRenderPass({
colorAttachments: [{
view: context.getCurrentTexture().createView(),
clearValue: { r: 0.1, g: 0.2, b: 0.4, a: 1 },
loadOp: 'clear',
storeOp: 'store'
}]
});
pass.end(); // Pass muss vor finish() beendet werden
device.queue.submit([encoder.finish()]);
console.log(encoder.label);
Compute-Pass ausführen und Ergebnis zurücklesen
const adapter = await navigator.gpu.requestAdapter();
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]);
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 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();
// Ergebnis in einen lesbaren Puffer kopieren
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();
Debug-Gruppen und Puffer leeren
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const buffer = device.createBuffer({
size: 256,
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.STORAGE
});
const encoder = device.createCommandEncoder({ label: 'Reset-Encoder' });
encoder.pushDebugGroup('Puffer zurücksetzen');
encoder.clearBuffer(buffer); // gesamter Puffer auf 0
encoder.insertDebugMarker('Puffer geleert');
encoder.popDebugGroup();
const commandBuffer = encoder.finish({ label: 'Reset-Befehle' });
device.queue.submit([commandBuffer]);
console.log(commandBuffer.label);
// Wichtig · Fallstricke
- Einmalige Nutzung: Nach
finish()ist der Encoder ungültig. Weitere Aufrufe führen zu einem Validierungsfehler, und die Befehlspuffer sind dann unbrauchbar. Pro Frame einen neuen Encoder anlegen. - Offene Pässe: Solange ein Render- oder Compute-Pass nicht mit
pass.end()beendet ist, ist der Encoder gesperrt. Ruft man in diesem Zustandfinish()oder Kopiermethoden auf, entsteht ein Fehler. - Fehler werfen keine Ausnahmen: Validierungsfehler werden nicht als JavaScript-Ausnahme gemeldet. Sie erscheinen als Konsolenwarnung bzw. über
device.pushErrorScope()/popErrorScope()oder das Ereignisuncapturederror. Mitlabellassen sich die Meldungen leichter zuordnen. - Ausrichtung: Bei
copyBufferToBuffer()undclearBuffer()müssen Offsets und Größen Vielfache von 4 sein. Bei Kopien zwischen Textur und Puffer mussbytesPerRowein Vielfaches von 256 sein. - Buffer-Usage: Quellpuffer brauchen
GPUBufferUsage.COPY_SRC, ZielpufferCOPY_DST. Ein gemappter Puffer darf in übergebenen Befehlen nicht verwendet werden. - Kompatibilität: WebGPU steht nur in sicheren Kontexten (HTTPS) zur Verfügung und wird noch nicht überall unterstützt: Firefox bietet es bislang nur auf einzelnen Plattformen an, Safari erst in neuen Versionen. Deshalb immer zuerst prüfen, ob
navigator.gpuexistiert, und einen Ausweichweg (z. B. WebGL) vorsehen. Die API ist auch in Web Workern verfügbar.