Start · Sprachen · JavaScript · Referenz · WebGLQuery

WebGLQuery

Schnittstelle

Ein <code>WebGLQuery</code>-Objekt fragt GPU-Informationen asynchron ab, z. B. ob Pixel eine Verdeckungsprüfung bestanden haben oder wie viele Primitive per Transform Feedback geschrieben wurden.

seit JavaScript WebGL 2 – Baseline: alle modernen Browser (seit 20 Kategorie: webapi

Signatur

interface WebGLQuery extends WebGLObject

Beschreibung

WebGLQuery gehört zur WebGL-2-API (WebGL2RenderingContext) und steht für ein Abfrageobjekt auf der GPU. Die GPU arbeitet unabhängig von JavaScript. Mit einer Abfrage sammelt man während bestimmter Zeichenbefehle Informationen und liest das Ergebnis später aus, ohne dass die Render-Pipeline blockiert.

Die Schnittstelle hat weder eigene Eigenschaften noch eigene Methoden. Erzeugt wird ein Objekt mit gl.createQuery(), benutzt wird es über Methoden des Kontexts:

  • gl.beginQuery(target, query) / gl.endQuery(target) – legen fest, welche Befehle ausgewertet werden
  • gl.getQueryParameter(query, pname) – mit gl.QUERY_RESULT_AVAILABLE prüfen, ob das Ergebnis schon da ist, und dann mit gl.QUERY_RESULT auslesen
  • gl.getQuery(target, gl.CURRENT_QUERY) – liefert die aktive Abfrage für ein Ziel
  • gl.isQuery(query) und gl.deleteQuery(query) – prüfen bzw. geben frei

Mögliche Ziele (target) sind gl.ANY_SAMPLES_PASSED, gl.ANY_SAMPLES_PASSED_CONSERVATIVE (beide für die Verdeckungsprüfung, englisch Occlusion Culling) und gl.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN. Mit der Erweiterung EXT_disjoint_timer_query_webgl2 kommen GPU-Zeitmessungen hinzu (TIME_ELAPSED_EXT, TIMESTAMP_EXT).

Typische Einsatzfälle: Man zeichnet vorab eine einfache Hülle (Bounding Box), um verdeckte Objekte nicht aufwendig rendern zu müssen. Man misst die GPU-Zeit einzelner Render-Durchgänge. Oder man zählt die Ausgaben von Transform Feedback, etwa bei Partikelsystemen.

Rückgabewert

Typ
WebGLQuery
Beschreibung
Wird von gl.createQuery() zurückgegeben. Das Objekt ist undurchsichtig (opak), es wird nur als Argument an Kontext-Methoden weitergereicht.

Beispiele

Abfrage erzeugen und Verdeckung prüfen (Occlusion Query)

const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl2');
if (!gl) throw new Error('WebGL 2 wird nicht unterstützt');

const query = gl.createQuery();
console.log(query instanceof WebGLQuery); // true

// Alle Zeichenbefehle zwischen begin und end werden ausgewertet
gl.beginQuery(gl.ANY_SAMPLES_PASSED_CONSERVATIVE, query);
// z. B. eine vereinfachte Bounding Box zeichnen:
// gl.drawArrays(gl.TRIANGLES, 0, 36);
gl.endQuery(gl.ANY_SAMPLES_PASSED_CONSERVATIVE);

console.log(gl.isQuery(query));
true true

Ergebnis asynchron in späteren Frames abholen

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

gl.beginQuery(gl.ANY_SAMPLES_PASSED, query);
gl.clear(gl.COLOR_BUFFER_BIT); // hier stünden die Zeichenbefehle
gl.endQuery(gl.ANY_SAMPLES_PASSED);

const poll = () => {
  const available = gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE);
  if (!available) {
    requestAnimationFrame(poll); // nicht blockieren, im nächsten Frame erneut prüfen
    return;
  }
  const visible = gl.getQueryParameter(query, gl.QUERY_RESULT);
  console.log('Objekt sichtbar:', Boolean(visible));
  gl.deleteQuery(query);
};
requestAnimationFrame(poll);
Objekt sichtbar: false

GPU-Zeit messen mit EXT_disjoint_timer_query_webgl2

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

if (!ext) {
  console.log('Timer-Queries nicht verfügbar');
} else {
  const timer = gl.createQuery();
  gl.beginQuery(ext.TIME_ELAPSED_EXT, timer);
  gl.clear(gl.COLOR_BUFFER_BIT); // zu messender Render-Durchgang
  gl.endQuery(ext.TIME_ELAPSED_EXT);

  const check = () => {
    const available = gl.getQueryParameter(timer, gl.QUERY_RESULT_AVAILABLE);
    const disjoint = gl.getParameter(ext.GPU_DISJOINT_EXT);
    if (available && !disjoint) {
      const ns = gl.getQueryParameter(timer, gl.QUERY_RESULT);
      console.log(`GPU-Zeit: ${(ns / 1e6).toFixed(3)} ms`);
      gl.deleteQuery(timer);
    } else if (disjoint) {
      console.log('Messung ungültig (disjoint), bitte verwerfen');
      gl.deleteQuery(timer);
    } else {
      requestAnimationFrame(check);
    }
  };
  requestAnimationFrame(check);
}
GPU-Zeit: 0.012 ms

Geschriebene Primitive bei Transform Feedback zählen

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

gl.beginQuery(gl.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, countQuery);
// gl.beginTransformFeedback(gl.POINTS);
// gl.drawArrays(gl.POINTS, 0, particleCount);
// gl.endTransformFeedback();
gl.endQuery(gl.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);

setTimeout(() => {
  if (gl.getQueryParameter(countQuery, gl.QUERY_RESULT_AVAILABLE)) {
    console.log('Primitive:', gl.getQueryParameter(countQuery, gl.QUERY_RESULT));
  }
}, 100);
Primitive: 0