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

GPUComputePassEncoder.end()

Methode

Beendet die Aufzeichnung der Befehle eines Compute-Passes und gibt den übergeordneten <code>GPUCommandEncoder</code> für weitere Befehle frei.

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

Signatur

GPUComputePassEncoder.prototype.end()

Beschreibung

end() schließt einen Compute-Pass ab, den du vorher mit GPUCommandEncoder.beginComputePass() geöffnet hast. Zwischen Öffnen und Beenden zeichnest du Befehle wie setPipeline(), setBindGroup() und dispatchWorkgroups() auf. Erst mit end() ist der Pass abgeschlossen. Danach kannst du auf dem GPUCommandEncoder weitere Befehle aufzeichnen, zum Beispiel copyBufferToBuffer() oder einen weiteren Pass, oder den Encoder mit finish() abschließen.

Solange ein Pass offen ist, ist der übergeordnete Command-Encoder gesperrt. Rufst du finish() auf, ohne vorher end() aufzurufen, entsteht ein Validierungsfehler und der erzeugte GPUCommandBuffer ist ungültig.

Nach end() ist der Pass-Encoder verbraucht: Weitere Aufrufe von Methoden auf demselben GPUComputePassEncoder führen zu Validierungsfehlern. Für einen neuen Pass rufst du erneut beginComputePass() auf.

Rückgabewert

Typ
undefined
Beschreibung
Kein Rückgabewert. Fehler werden nicht als Ausnahme geworfen, sondern über die WebGPU-Fehlerbehandlung gemeldet (pushErrorScope()/popErrorScope() bzw. das Ereignis uncapturederror).

Beispiele

Einfacher Compute-Pass: 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]);
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();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(Math.ceil(input.length / 64));
pass.end(); // Pass abschließen – Encoder ist wieder frei

// Ergebnis in einen lesbaren Buffer kopieren
const readback = device.createBuffer({
  size: input.byteLength,
  usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
});
encoder.copyBufferToBuffer(storage, 0, readback, 0, input.byteLength);
device.queue.submit([encoder.finish()]);

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

Mehrere Passes nacheinander in einem Command-Encoder

// Annahme: device, pipelineA, pipelineB, bindGroupA, bindGroupB existieren
const encoder = device.createCommandEncoder({ label: 'Simulation' });

const passA = encoder.beginComputePass({ label: 'Kräfte berechnen' });
passA.setPipeline(pipelineA);
passA.setBindGroup(0, bindGroupA);
passA.dispatchWorkgroups(128);
passA.end(); // muss vor dem nächsten Pass stehen

const passB = encoder.beginComputePass({ label: 'Positionen integrieren' });
passB.setPipeline(pipelineB);
passB.setBindGroup(0, bindGroupB);
passB.dispatchWorkgroups(128);
passB.end();

device.queue.submit([encoder.finish()]);

Fehlendes end() mit Error Scopes erkennen

device.pushErrorScope('validation');

const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.dispatchWorkgroups(1);
// pass.end(); fehlt absichtlich
const commandBuffer = encoder.finish();
device.queue.submit([commandBuffer]);

const error = await device.popErrorScope();
if (error) console.warn('Validierungsfehler:', error.message);
Validierungsfehler: ... (Meldung je nach Browser, z. B. Hinweis auf einen noch offenen Pass)

// Wichtig · Fallstricke

  • Pflichtaufruf: Ohne end() bleibt der GPUCommandEncoder gesperrt. finish() liefert dann einen ungültigen Command-Buffer, und submit() erzeugt einen Validierungsfehler.
  • Nur einmal aufrufen: Ein zweiter Aufruf von end() oder weitere Befehle auf demselben Pass-Encoder führen zu Validierungsfehlern. Diese werden nicht als JavaScript-Ausnahme geworfen, sondern still über Error Scopes bzw. uncapturederror gemeldet. Das macht das Debuggen schwerer, deshalb helfen label-Angaben.
  • Gleichgewicht bei Debug-Gruppen: Jeder Aufruf von pushDebugGroup() braucht vor end() ein passendes popDebugGroup(), sonst ist der Pass ungültig.
  • Keine Ausführung: end() führt nichts auf der GPU aus. Die Befehle laufen erst nach device.queue.submit().
  • Veraltet: In frühen Entwürfen der Spezifikation hieß die Methode endPass(). Sie wurde in end() umbenannt und sollte nicht mehr verwendet werden.
  • Kompatibilität: WebGPU steht nur in sicheren Kontexten (HTTPS) zur Verfügung. Die Unterstützung ist je nach Browser und Plattform noch uneinheitlich. Prüfe deshalb vorher mit navigator.gpu und requestAdapter(), ob WebGPU verfügbar ist.