Start · Sprachen · JavaScript · Referenz · GPUDevice.createCommandEncoder()

GPUDevice.createCommandEncoder()

Methode

Erstellt einen <code>GPUCommandEncoder</code>, mit dem Render-, Compute- und Kopierbefehle aufgezeichnet und anschließend als <code>GPUCommandBuffer</code> an die GPU übergeben werden.

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

Signatur

GPUDevice.prototype.createCommandEncoder(descriptor)

Beschreibung

createCommandEncoder() ist der Einstiegspunkt für jede Arbeit, die in WebGPU auf der GPU ausgeführt werden soll. Der zurückgegebene GPUCommandEncoder zeichnet Befehle auf, führt sie aber noch nicht aus: Mit beginRenderPass() und beginComputePass() startest du Render- bzw. Compute-Durchläufe, mit copyBufferToBuffer(), copyBufferToTexture() und ähnlichen Methoden kodierst du Kopiervorgänge.

Sind alle Befehle aufgezeichnet, rufst du encoder.finish() auf. Das liefert einen GPUCommandBuffer, den du mit device.queue.submit([commandBuffer]) an die GPU übergibst. Ein Encoder kann nach finish() nicht wiederverwendet werden. Üblich ist deshalb, pro Frame bzw. pro Arbeitspaket einen neuen Encoder zu erzeugen. Das ist billig und so auch vorgesehen.

Über den optionalen Deskriptor kannst du ein label vergeben. Dieses Label taucht in Fehlermeldungen, Validierungswarnungen und Debug-Werkzeugen auf. Gerade in größeren Anwendungen mit vielen Encodern ist das sehr hilfreich.

Parameter

Name Typ Default Beschreibung
$descriptor GPUCommandEncoderDescriptor {} Optionales Konfigurationsobjekt. Enthält derzeit nur die Eigenschaft label.
$descriptor.label string "" Bezeichnung zur Identifikation des Encoders in Fehlermeldungen und Debug-Werkzeugen.

Rückgabewert

Typ
GPUCommandEncoder
Beschreibung
Ein neuer GPUCommandEncoder, mit dem Befehle aufgezeichnet werden. Mit finish() wird daraus ein GPUCommandBuffer.

Beispiele

Canvas mit einer Farbe 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');
context.configure({ device, format: navigator.gpu.getPreferredCanvasFormat() });

// Encoder für diesen Frame erzeugen
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();

// Aufzeichnung abschließen und an die GPU übergeben
device.queue.submit([encoder.finish()]);

Buffer kopieren und Ergebnis zurücklesen

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

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

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

const encoder = device.createCommandEncoder({ label: 'Kopier-Encoder' });
encoder.copyBufferToBuffer(src, 0, readback, 0, data.byteLength);
device.queue.submit([encoder.finish()]);

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

Compute-Shader ausführen

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

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 buffer = device.createBuffer({
  size: 256 * 4,
  usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST
});
const bindGroup = device.createBindGroup({
  layout: pipeline.getBindGroupLayout(0),
  entries: [{ binding: 0, resource: { buffer } }]
});

const encoder = device.createCommandEncoder({ label: 'Compute-Encoder' });
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(Math.ceil(256 / 64));
pass.end();
device.queue.submit([encoder.finish()]);

// Wichtig · Fallstricke

  • Nur einmal verwendbar: Nach finish() ist der Encoder ungültig. Weitere Aufrufe darauf führen zu einem Validierungsfehler. Erzeuge pro Frame einen neuen Encoder.
  • Passes schließen: Jeder mit beginRenderPass() oder beginComputePass() geöffnete Pass muss mit end() beendet werden, bevor du finish() aufrufst. Solange ein Pass offen ist, ist der Encoder gesperrt.
  • Keine Ausführung ohne Übergabe: Aufgezeichnete Befehle laufen erst, wenn der Befehlspuffer per device.queue.submit() übergeben wird.
  • Fehler kommen verzögert: Validierungsfehler werfen keine Ausnahme. Sie landen im Fehlerbereich des Geräts. Fange sie mit device.pushErrorScope()/popErrorScope() oder über das Ereignis uncapturederror ab.
  • Kompatibilität: WebGPU gibt es nur in sicheren Kontexten (HTTPS) und noch nicht auf allen Plattformen. Prüfe vorab mit navigator.gpu, ob die API vorhanden ist, und sieh eine Ausweichlösung vor, zum Beispiel WebGL. Seit Chrome 113 ist WebGPU auch in Web Workern verfügbar.