Start · Sprachen · JavaScript · Referenz · WebGL2RenderingContext.fenceSync()

WebGL2RenderingContext.fenceSync()

Methode

Erstellt ein neues <code>WebGLSync</code>-Objekt (eine „Fence“) und fügt es in den GL-Befehlsstrom ein, um den Abschluss vorheriger GPU-Befehle abfragen zu können.

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

Signatur

WebGL2RenderingContext.prototype.fenceSync(condition, flags)

Beschreibung

fenceSync() setzt eine Synchronisationsmarke (Fence) in die Befehlswarteschlange der GPU. Die Fence wird signalisiert, sobald alle vor ihr abgesetzten GL-Befehle vollständig von der GPU ausgeführt wurden. So lässt sich feststellen, wann asynchrone Arbeit wie Rendern, Transform Feedback oder ein readPixels() in einen Pixel-Buffer-Objekt (PBO) wirklich fertig ist.

Der typische Anwendungsfall ist das nicht blockierende Zurücklesen von GPU-Daten: Man startet readPixels() in einen PIXEL_PACK_BUFFER, setzt direkt danach eine Fence und fragt in späteren Frames mit clientWaitSync() (Timeout 0) oder getSyncParameter() ab, ob die Fence signalisiert ist. Erst dann ruft man getBufferSubData() auf – ohne die Render-Pipeline anzuhalten.

In WebGL 2 ist als condition ausschließlich gl.SYNC_GPU_COMMANDS_COMPLETE und als flags ausschließlich 0 zulässig. Das zurückgegebene Objekt sollte nach Gebrauch mit deleteSync() freigegeben werden.

Parameter

Name Typ Default Beschreibung
$condition Pflicht GLenum Bedingung, bei der die Fence signalisiert wird. Einziger gültiger Wert: gl.SYNC_GPU_COMMANDS_COMPLETE.
$flags Pflicht GLbitfield 0 Bitmaske für Verhaltensoptionen. Muss in WebGL 2 immer 0 sein.

Rückgabewert

Typ
WebGLSync | null
Beschreibung
Ein neues WebGLSync-Objekt, oder null, wenn ein Fehler auftritt (z. B. ungültige Argumente oder verlorener Kontext).

Beispiele

Fence setzen und Status abfragen

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 Befehlen einfügen
const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
gl.flush(); // sicherstellen, dass die Fence an die GPU übergeben wird

const status = gl.getSyncParameter(sync, gl.SYNC_STATUS);
console.log(status === gl.SIGNALED ? 'fertig' : 'noch in Arbeit');
noch in Arbeit (oder: fertig)

Nicht blockierendes readPixels über PBO

const readPixelsAsync = (gl, x, y, w, h) => {
  const buf = gl.createBuffer();
  gl.bindBuffer(gl.PIXEL_PACK_BUFFER, buf);
  gl.bufferData(gl.PIXEL_PACK_BUFFER, w * h * 4, gl.STREAM_READ);
  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();

  return new Promise((resolve, reject) => {
    const poll = () => {
      const res = gl.clientWaitSync(sync, 0, 0);
      if (res === gl.WAIT_FAILED) {
        gl.deleteSync(sync);
        reject(new Error('clientWaitSync fehlgeschlagen'));
      } else if (res === gl.TIMEOUT_EXPIRED) {
        requestAnimationFrame(poll); // später erneut prüfen
      } else {
        gl.deleteSync(sync);
        const data = new Uint8Array(w * h * 4);
        gl.bindBuffer(gl.PIXEL_PACK_BUFFER, buf);
        gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, data);
        gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
        gl.deleteBuffer(buf);
        resolve(data);
      }
    };
    poll();
  });
};

const gl = document.createElement('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(pixel);
Uint8Array(4) [255, 0, 0, 255]

Fence auf der GPU-Seite abwarten (waitSync)

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

const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
// Die GPU wartet, bevor nachfolgende Befehle ausgeführt werden – blockiert JS nicht
gl.waitSync(sync, 0, gl.TIMEOUT_IGNORED);
gl.deleteSync(sync);
console.log(gl.getError() === gl.NO_ERROR);
true

// Wichtig · Fallstricke

  • Nur WebGL 2: In einem WebGL-1-Kontext gibt es fenceSync() nicht. Wird von allen aktuellen Browsern mit WebGL-2-Unterstützung angeboten (Chrome, Edge, Firefox, Safari 15+).
  • Ungültige Argumente: Andere Werte als SYNC_GPU_COMMANDS_COMPLETE bzw. 0 erzeugen INVALID_ENUM bzw. INVALID_VALUE, und es wird null zurückgegeben.
  • Flush nicht vergessen: Ohne gl.flush() (oder das Flag SYNC_FLUSH_COMMANDS_BIT bei clientWaitSync()) wird die Fence unter Umständen nie signalisiert.
  • Status erst im nächsten Task: Browser aktualisieren den Sync-Status laut Spezifikation frühestens, nachdem die aktuelle Aufgabe an die Event-Loop zurückgegeben wurde. Aktives Warten in einer Schleife funktioniert daher nicht – stattdessen per requestAnimationFrame oder setTimeout erneut prüfen.
  • Timeouts: clientWaitSync() erlaubt nur Timeouts bis MAX_CLIENT_WAIT_TIMEOUT_WEBGL (oft 0); blockierendes Warten im Hauptthread ist praktisch nicht möglich.
  • Ressourcen: Nicht mehr benötigte Fences mit deleteSync() freigeben, um Speicherlecks zu vermeiden.