Start · Sprachen · JavaScript · Referenz · GPUCommandEncoder

GPUCommandEncoder

Schnittstelle

Der <code>GPUCommandEncoder</code> sammelt GPU-Befehle wie Render-Pässe, Compute-Pässe und Kopiervorgänge und fasst sie zu einem <code>GPUCommandBuffer</code> zusammen, der dann an die GPU übergeben wird.

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

Signatur

interface GPUCommandEncoder

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 einen GPURenderPassEncoder zurück.
  • beginComputePass(descriptor?) – startet einen Compute-Pass und gibt einen GPUComputePassEncoder zurü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 einen GPUCommandBuffer.

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

Typ
GPUCommandEncoder
Beschreibung
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);
Frame-Encoder

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

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);
Reset-Befehle

// 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 Zustand finish() 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 Ereignis uncapturederror. Mit label lassen sich die Meldungen leichter zuordnen.
  • Ausrichtung: Bei copyBufferToBuffer() und clearBuffer() müssen Offsets und Größen Vielfache von 4 sein. Bei Kopien zwischen Textur und Puffer muss bytesPerRow ein Vielfaches von 256 sein.
  • Buffer-Usage: Quellpuffer brauchen GPUBufferUsage.COPY_SRC, Zielpuffer COPY_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.gpu existiert, und einen Ausweichweg (z. B. WebGL) vorsehen. Die API ist auch in Web Workern verfügbar.