Start · Sprachen · JavaScript · Referenz · GPUQueue.onSubmittedWorkDone()

GPUQueue.onSubmittedWorkDone()

Methode

Gibt ein Promise zurück, das erfüllt wird, sobald die GPU alle Arbeiten abgeschlossen hat, die bis zum Aufruf über diese <code>GPUQueue</code> übermittelt wurden.

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

Signatur

GPUQueue.prototype.onSubmittedWorkDone()

Beschreibung

onSubmittedWorkDone() ist eine Methode der GPUQueue-Schnittstelle der WebGPU-API. Sie liefert ein Promise, das erfüllt wird, sobald die GPU alle Arbeiten fertig bearbeitet hat, die bis zum Aufruf über diese Warteschlange eingereicht wurden. Dazu gehören Command-Buffer aus queue.submit() sowie Operationen wie queue.writeBuffer(), queue.writeTexture() und queue.copyExternalImageToTexture().

WebGPU arbeitet asynchron: submit() kehrt sofort zurück, ohne auf die GPU zu warten. Mit onSubmittedWorkDone() kann JavaScript an einem festen Punkt auf die GPU warten, ohne dass ein Buffer zurückgelesen werden muss. Das ist nützlich für grobe Zeitmessungen (Benchmarks), für die Drosselung, damit nicht zu viele Frames oder Compute-Jobs auf einmal in der Warteschlange stehen, und für das Freigeben oder Wiederverwenden von Ressourcen, etwa mit buffer.destroy(), sobald die GPU sie sicher nicht mehr braucht.

Arbeiten, die nach dem Aufruf eingereicht werden, beeinflussen das Promise nicht. Wer Ergebnisse auf der CPU lesen will, nutzt dagegen GPUBuffer.mapAsync(), das ohnehin auf die zugehörigen GPU-Arbeiten wartet.

Rückgabewert

Typ
Promise<undefined>
Beschreibung
Ein Promise, das mit undefined erfüllt wird, sobald alle bis zum Aufruf übermittelten Arbeiten der Warteschlange abgeschlossen sind.

Beispiele

Auf das Ende eines Compute-Durchlaufs warten

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 count = 1024;
const buffer = device.createBuffer({
  size: count * 4,
  usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
});
device.queue.writeBuffer(buffer, 0, new Float32Array(count).fill(1));

const bindGroup = device.createBindGroup({
  layout: pipeline.getBindGroupLayout(0),
  entries: [{ binding: 0, resource: { buffer } }]
});

const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(Math.ceil(count / 64));
pass.end();
device.queue.submit([encoder.finish()]);

await device.queue.onSubmittedWorkDone();
console.log('GPU-Arbeit abgeschlossen');
buffer.destroy(); // jetzt gefahrlos freigeben
GPU-Arbeit abgeschlossen

Grobe Zeitmessung der GPU-Arbeit

const measureGpu = async (device, commandBuffers) => {
  // Vorherige Arbeit abwarten, damit sie nicht mitgemessen wird
  await device.queue.onSubmittedWorkDone();
  const start = performance.now();
  device.queue.submit(commandBuffers);
  await device.queue.onSubmittedWorkDone();
  return performance.now() - start;
};

// Aufruf, z. B. mit einem fertig kodierten Command-Buffer:
// const ms = await measureGpu(device, [encoder.finish()]);
// console.log(`GPU-Zeit ca. ${ms.toFixed(2)} ms`);
GPU-Zeit ca. 3.42 ms

Frames drosseln: höchstens zwei Frames in der Warteschlange

const MAX_FRAMES_IN_FLIGHT = 2;
const inFlight = [];

const renderFrame = async (device, encodeFrame) => {
  if (inFlight.length >= MAX_FRAMES_IN_FLIGHT) {
    await inFlight.shift(); // ältesten Frame abwarten
  }
  const encoder = device.createCommandEncoder();
  encodeFrame(encoder);
  device.queue.submit([encoder.finish()]);
  inFlight.push(device.queue.onSubmittedWorkDone());
};

// Wichtig · Fallstricke

  • Keine genaue Zeitmessung: Das Promise wird erst in einem späteren Durchlauf der Ereignisschleife erfüllt und enthält den Verwaltungsaufwand von Browser und Treiber. Für exakte GPU-Zeiten nutzt man Timestamp-Queries (Feature "timestamp-query", GPUQuerySet). Browser runden diese Werte aus Sicherheitsgründen.
  • Nicht für jeden Frame nötig: Wer nach jedem submit() wartet, lässt CPU und GPU nicht mehr parallel arbeiten und verliert dadurch Leistung. Die Methode sollte gezielt eingesetzt werden.
  • Zum Zurücklesen ungeeignet: Um Daten auszulesen, ist GPUBuffer.mapAsync() der richtige Weg. Ein vorheriges onSubmittedWorkDone() ist dafür überflüssig.
  • Geräteverlust: Geht das Gerät verloren (device.lost), wird das Promise laut Spezifikation trotzdem erfüllt. Deshalb sollte man device.lost separat überwachen.
  • Kompatibilität: WebGPU ist nur in sicheren Kontexten (HTTPS) und nicht in allen Browsern bzw. auf allen Plattformen verfügbar. Vorher auf navigator.gpu prüfen. In Workern steht WebGPU ebenfalls zur Verfügung.