Start · Sprachen · JavaScript · Referenz · WebGLRenderingContext.finish()

WebGLRenderingContext.finish()

Methode

Blockiert den aufrufenden Thread, bis die GPU alle zuvor abgesetzten WebGL-Befehle vollständig abgearbeitet hat.

seit JavaScript WebGL 1.0 – Baseline: alle modernen Browser Kategorie: webapi

Signatur

WebGLRenderingContext.prototype.finish()

Beschreibung

finish() ist das WebGL-Gegenstück zu glFinish() aus OpenGL ES 2.0. Die Methode kehrt erst zurück, wenn alle bisher an den Kontext übergebenen Befehle vollständig ausgeführt sind. Das gilt auch für Zeichenbefehle und Änderungen an Framebuffern. Anders als flush(), das die Befehle nur an die GPU weiterreicht, wartet finish() synchron auf das Ergebnis.

Im normalen Rendering-Ablauf braucht man finish() praktisch nie. Der Browser schickt die Befehle am Ende jedes Frames von selbst ab und stellt das Bild dar. Nützlich ist die Methode vor allem bei der Fehlersuche und für grobe Zeitmessungen, wenn man sehen will, wie lange die GPU tatsächlich für eine Reihe von Befehlen braucht.

Weil der Haupt-Thread wartet, bis die GPU fertig ist, bremst finish() die Parallelität zwischen CPU und GPU aus und kann Frames kosten. Viele Browser setzen den Aufruf außerdem intern anders um, etwa weil die GPU-Befehle in einem eigenen Prozess laufen. Deshalb ist nicht garantiert, dass wirklich synchron gewartet wird.

Beispiele

Einfacher Aufruf nach dem Zeichnen

const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');

gl.clearColor(0.2, 0.4, 0.8, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);

// Warten, bis das Löschen tatsächlich ausgeführt wurde
gl.finish();
console.log('Alle Befehle abgeschlossen');
Alle Befehle abgeschlossen

Grobe GPU-Zeitmessung eines Zeichenaufrufs

const measureDraw = (gl, drawFn) => {
  gl.finish(); // vorherige Arbeit abschließen
  const start = performance.now();
  drawFn();
  gl.finish(); // auf das Ende der Zeichenarbeit warten
  return performance.now() - start;
};

const gl = document.createElement('canvas').getContext('webgl');
const ms = measureDraw(gl, () => {
  gl.clearColor(1, 0, 0, 1);
  for (let i = 0; i < 100; i++) gl.clear(gl.COLOR_BUFFER_BIT);
});
console.log(`Dauer: ${ms.toFixed(2)} ms`);
Dauer: 0.85 ms (abhängig von Gerät und Browser)

Moderne Alternative in WebGL 2: Fence-Sync statt finish()

const gl = document.createElement('canvas').getContext('webgl2');

gl.clearColor(0, 1, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);

// Nicht blockierend auf die GPU warten
const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
gl.flush();

const poll = () => {
  const status = gl.clientWaitSync(sync, 0, 0);
  if (status === gl.TIMEOUT_EXPIRED) {
    requestAnimationFrame(poll);
  } else {
    gl.deleteSync(sync);
    console.log('GPU fertig, ohne den Haupt-Thread zu blockieren');
  }
};
poll();
GPU fertig, ohne den Haupt-Thread zu blockieren

// Wichtig · Fallstricke

  • Leistung: Wer finish() in jedem Frame aufruft, blockiert die Render-Schleife und senkt die Bildrate spürbar. Im Produktivcode sollte der Aufruf nicht vorkommen.
  • Keine Garantie: Browser wie Chrome führen WebGL-Befehle in einem eigenen GPU-Prozess aus. finish() verhält sich dort oft wie ein teures flush() mit einer Synchronisation zwischen den Prozessen. Zeitmessungen damit sind nur Näherungswerte.
  • Präzise GPU-Zeiten: Genauer misst man mit der Erweiterung EXT_disjoint_timer_query (WebGL 1) bzw. EXT_disjoint_timer_query_webgl2. Die Verfügbarkeit ist allerdings eingeschränkt, weil die Erweiterung wegen Timing-Angriffen teilweise deaktiviert ist.
  • readPixels: readPixels() synchronisiert von sich aus. Ein zusätzliches finish() davor ist überflüssig.
  • Die Methode ist in allen Browsern mit WebGL-Unterstützung vorhanden, auch in WebGL2RenderingContext.