Signatur
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');
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`);
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();
// 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 teuresflush()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ätzlichesfinish()davor ist überflüssig. - Die Methode ist in allen Browsern mit WebGL-Unterstützung vorhanden, auch in
WebGL2RenderingContext.