Signatur
Beschreibung
clientWaitSync() prüft den Zustand eines Fence-Sync-Objekts, das zuvor mit gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0) in den GPU-Befehlsstrom eingefügt wurde. Ist die GPU mit allen Befehlen vor dem Fence fertig, gilt das Sync-Objekt als signalisiert. So erfahren Sie, ob die GPU z. B. einen Rendervorgang oder das Befüllen eines Pixel-Buffers (PBO) abgeschlossen hat.
Der Haupteinsatz in WebGL ist das nicht blockierende Auslesen von GPU-Daten: Statt readPixels() synchron auf den CPU-Speicher warten zu lassen, liest man in einen PIXEL_PACK_BUFFER, setzt einen Fence und fragt pro Frame mit clientWaitSync(sync, 0, 0) ab, ob die Daten bereit sind. Erst dann holt man sie per getBufferSubData() ab – ohne die Render-Pipeline anzuhalten.
Im Unterschied zu waitSync(), das nur die GPU-Befehlswarteschlange warten lässt, blockiert clientWaitSync() den aufrufenden JavaScript-Thread. Da der Hauptthread nicht blockiert werden soll, erlauben Browser nur sehr kurze Timeouts: Der Wert darf gl.MAX_CLIENT_WAIT_TIMEOUT_WEBGL nicht überschreiten, der in vielen Implementierungen 0 ist. In der Praxis ruft man die Methode also fast immer mit Timeout 0 auf und fragt wiederholt ab (Polling).
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $sync Pflicht | WebGLSync | — | Das zu prüfende Sync-Objekt, erzeugt mit gl.fenceSync(). |
| $flags Pflicht | GLbitfield (number) | 0 | 0 oder gl.SYNC_FLUSH_COMMANDS_BIT. Mit dem Flag werden ausstehende Befehle vor dem Warten an die GPU übergeben (Flush), damit der Fence überhaupt erreicht werden kann. |
| $timeout Pflicht | GLint64 (number) | 0 | Maximale Wartezeit in Nanosekunden. Darf gl.getParameter(gl.MAX_CLIENT_WAIT_TIMEOUT_WEBGL) nicht überschreiten; 0 fragt den Zustand nur ab, ohne zu warten. |
Rückgabewert
gl.ALREADY_SIGNALED– das Sync-Objekt war beim Aufruf bereits signalisiert.gl.CONDITION_SATISFIED– es wurde während der Wartezeit signalisiert.gl.TIMEOUT_EXPIRED– der Timeout lief ab, die GPU ist noch nicht fertig.gl.WAIT_FAILED– ein Fehler trat auf (z. B. zu großer Timeout);gl.getError()liefert Details.
Beispiele
Einfache Statusabfrage eines Fence
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl2');
gl.clearColor(0, 0, 1, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// Fence nach den Render-Befehlen einfügen
const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
const status = gl.clientWaitSync(sync, gl.SYNC_FLUSH_COMMANDS_BIT, 0);
const names = {
[gl.ALREADY_SIGNALED]: 'ALREADY_SIGNALED',
[gl.CONDITION_SATISFIED]: 'CONDITION_SATISFIED',
[gl.TIMEOUT_EXPIRED]: 'TIMEOUT_EXPIRED',
[gl.WAIT_FAILED]: 'WAIT_FAILED'
};
console.log(names[status]);
gl.deleteSync(sync);
Fence als Promise verpacken (Polling pro Frame)
const waitForSync = (gl, sync) => new Promise((resolve, reject) => {
// Erster Aufruf mit Flush, danach reines Polling
let flags = gl.SYNC_FLUSH_COMMANDS_BIT;
const check = () => {
const res = gl.clientWaitSync(sync, flags, 0);
flags = 0;
if (res === gl.WAIT_FAILED) {
reject(new Error('clientWaitSync fehlgeschlagen'));
} else if (res === gl.TIMEOUT_EXPIRED) {
requestAnimationFrame(check);
} else {
resolve();
}
};
check();
});
const gl = document.createElement('canvas').getContext('webgl2');
const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
await waitForSync(gl, sync);
gl.deleteSync(sync);
console.log('GPU fertig');
Asynchrones readPixels mit Pixel-Buffer-Objekt
const readPixelsAsync = async (gl, x, y, w, h) => {
const pbo = gl.createBuffer();
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo);
gl.bufferData(gl.PIXEL_PACK_BUFFER, w * h * 4, gl.STREAM_READ);
// Mit gebundenem PBO schreibt readPixels in den Buffer statt in ein Array
gl.readPixels(x, y, w, h, gl.RGBA, gl.UNSIGNED_BYTE, 0);
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
gl.flush();
// Pro Frame abfragen, ohne den Hauptthread zu blockieren
while (gl.clientWaitSync(sync, 0, 0) === gl.TIMEOUT_EXPIRED) {
await new Promise(requestAnimationFrame);
}
gl.deleteSync(sync);
const data = new Uint8Array(w * h * 4);
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo);
gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, data);
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
gl.deleteBuffer(pbo);
return data;
};
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl2');
gl.clearColor(1, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
const pixel = await readPixelsAsync(gl, 0, 0, 1, 1);
console.log(Array.from(pixel));