Start · Sprachen · JavaScript · Referenz · GPUCommandEncoder.beginComputePass()

GPUCommandEncoder.beginComputePass()

Methode

Startet die Aufzeichnung eines Compute-Passes und gibt einen <code>GPUComputePassEncoder</code> zurück, über den Compute-Pipelines gebunden und Dispatches aufgezeichnet werden.

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

Signatur

GPUCommandEncoder.prototype.beginComputePass(descriptor)

Beschreibung

Mit beginComputePass() öffnest du innerhalb eines GPUCommandEncoder einen Compute-Pass. Das ist ein Abschnitt im Befehlspuffer, in dem Compute-Shader (@compute in WGSL) ausgeführt werden. Der zurückgegebene GPUComputePassEncoder bietet unter anderem setPipeline(), setBindGroup(), dispatchWorkgroups() und dispatchWorkgroupsIndirect().

Compute-Passes nutzt du für allgemeine Berechnungen auf der GPU (GPGPU), etwa Bildfilter, Partikelsimulationen, Matrixrechnungen, Vorverarbeitung für Machine Learning oder Culling vor einem Render-Pass. Anders als bei beginRenderPass() gibt es keine Farb- oder Tiefen-Anhänge. Die Daten laufen ausschließlich über Bind-Groups (Storage-Buffer, Storage-Texturen, Uniforms).

Ablauf: device.createCommandEncoder() → beginComputePass() → Befehle aufzeichnen → pass.end() → encoder.finish() → device.queue.submit([...]). Solange ein Pass offen ist, ist der GPUCommandEncoder gesperrt. Weitere Encoder-Befehle wie copyBufferToBuffer() oder ein neuer Pass sind erst nach end() erlaubt.

Über das optionale timestampWrites kannst du GPU-Zeitstempel am Anfang und Ende des Passes in ein GPUQuerySet schreiben lassen. So misst du die Laufzeit auf der GPU. Voraussetzung dafür ist das Feature "timestamp-query".

Parameter

Name Typ Default Beschreibung
$descriptor GPUComputePassDescriptor {} Optionales Konfigurationsobjekt für den Pass.
$descriptor.label string "" Name zur Fehlersuche. Er erscheint in Fehlermeldungen und in GPU-Debug-Werkzeugen.
$descriptor.timestampWrites GPUComputePassTimestampWrites Objekt mit querySet (ein GPUQuerySet vom Typ "timestamp") sowie optional beginningOfPassWriteIndex und endOfPassWriteIndex. Setzt das Feature "timestamp-query" voraus.

Rückgabewert

Typ
GPUComputePassEncoder
Beschreibung
Ein Encoder, mit dem die Befehle des Compute-Passes aufgezeichnet werden. Er muss mit end() abgeschlossen werden.

Beispiele

Array auf der GPU verdoppeln

const adapter = await navigator.gpu?.requestAdapter();
if (!adapter) throw new Error('WebGPU nicht verfügbar');
const device = await adapter.requestDevice();

const input = new Float32Array([1, 2, 3, 4]);

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 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({ label: 'Verdoppeln' });
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(Math.ceil(input.length / 64));
pass.end(); // Pflicht, bevor der Encoder weiterverwendet wird

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]

Laufzeit des Passes mit timestampWrites messen

const adapter = await navigator.gpu.requestAdapter();
if (!adapter.features.has('timestamp-query')) {
  throw new Error('timestamp-query wird nicht unterstützt');
}
const device = await adapter.requestDevice({ requiredFeatures: ['timestamp-query'] });

const querySet = device.createQuerySet({ type: 'timestamp', count: 2 });
const resolveBuf = device.createBuffer({
  size: 16,
  usage: GPUBufferUsage.QUERY_RESOLVE | GPUBufferUsage.COPY_SRC
});
const resultBuf = device.createBuffer({
  size: 16,
  usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
});

const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass({
  label: 'Messung',
  timestampWrites: { querySet, beginningOfPassWriteIndex: 0, endOfPassWriteIndex: 1 }
});
// ... hier setPipeline / setBindGroup / dispatchWorkgroups aufrufen
pass.end();

encoder.resolveQuerySet(querySet, 0, 2, resolveBuf, 0);
encoder.copyBufferToBuffer(resolveBuf, 0, resultBuf, 0, 16);
device.queue.submit([encoder.finish()]);

await resultBuf.mapAsync(GPUMapMode.READ);
const [start, end] = new BigUint64Array(resultBuf.getMappedRange());
console.log(`GPU-Zeit: ${Number(end - start) / 1e6} ms`);
resultBuf.unmap();
GPU-Zeit: 0.012 ms (Beispielwert)

Mehrere Passes nacheinander in einem Encoder

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

const physics = encoder.beginComputePass({ label: 'Physik' });
physics.setPipeline(pipelineA);
physics.setBindGroup(0, bindGroupA);
physics.dispatchWorkgroups(128);
physics.end();

// Der zweite Pass sieht die Ergebnisse des ersten Passes
const collide = encoder.beginComputePass({ label: 'Kollision' });
collide.setPipeline(pipelineB);
collide.setBindGroup(0, bindGroupB);
collide.dispatchWorkgroups(128);
collide.end();

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