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

WebGL2RenderingContext.beginQuery()

Methode

Startet eine asynchrone GPU-Abfrage (z. B. Occlusion- oder Transform-Feedback-Abfrage) für das angegebene Ziel mit einem <code>WebGLQuery</code>-Objekt.

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

Signatur

WebGL2RenderingContext.prototype.beginQuery(target, query)

Beschreibung

Mit beginQuery() beginnt man eine asynchrone Abfrage auf der GPU. Alle Zeichenbefehle zwischen beginQuery() und dem passenden endQuery() werden für die Abfrage erfasst. Das Ergebnis liegt nicht sofort vor, sondern wird später mit getQueryParameter() ausgelesen, sobald gl.QUERY_RESULT_AVAILABLE den Wert true liefert.

Typische Einsatzfälle:

  • Occlusion Culling: Mit gl.ANY_SAMPLES_PASSED prüft man, ob ein Objekt (oder dessen Bounding-Box) überhaupt sichtbar ist, und spart sich in späteren Frames aufwendiges Zeichnen verdeckter Objekte.
  • Transform Feedback: Mit gl.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN zählt man, wie viele Primitive in Transform-Feedback-Puffer geschrieben wurden.
  • GPU-Zeitmessung: Mit der Erweiterung EXT_disjoint_timer_query_webgl2 und dem Ziel ext.TIME_ELAPSED_EXT misst man die Ausführungszeit auf der GPU.

Pro Ziel darf immer nur eine Abfrage gleichzeitig aktiv sein. Das Ergebnis sollte man frühestens im nächsten Frame (z. B. im nächsten requestAnimationFrame-Callback) abfragen – WebGL gibt Ergebnisse ohnehin erst nach Rückkehr zur Ereignisschleife frei, um Pipeline-Stalls zu vermeiden.

Parameter

Name Typ Default Beschreibung
$target Pflicht GLenum Art der Abfrage: gl.ANY_SAMPLES_PASSED (mind. ein Sample hat den Tiefentest bestanden), gl.ANY_SAMPLES_PASSED_CONSERVATIVE (schnellere, ungenauere Variante, darf falsch-positiv sein) oder gl.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN. Mit Erweiterung zusätzlich ext.TIME_ELAPSED_EXT.
$query Pflicht WebGLQuery Ein mit gl.createQuery() erzeugtes Abfrageobjekt, das die Ergebnisse aufnimmt.

Beispiele

Occlusion-Abfrage: Ist ein Objekt sichtbar?

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

const query = gl.createQuery();

// Bounding-Box des Objekts testweise zeichnen
gl.colorMask(false, false, false, false);
gl.depthMask(false);
gl.beginQuery(gl.ANY_SAMPLES_PASSED, query);
gl.drawArrays(gl.TRIANGLES, 0, 36); // Würfel-Box
gl.endQuery(gl.ANY_SAMPLES_PASSED);
gl.colorMask(true, true, true, true);
gl.depthMask(true);

// Ergebnis frühestens im nächsten Frame abholen
const pollResult = () => {
  const available = gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE);
  if (!available) {
    requestAnimationFrame(pollResult);
    return;
  }
  const visible = gl.getQueryParameter(query, gl.QUERY_RESULT);
  console.log('Objekt sichtbar:', visible);
};
requestAnimationFrame(pollResult);
Objekt sichtbar: true

Geschriebene Primitive bei Transform Feedback zählen

const gl = document.createElement('canvas').getContext('webgl2');
// Annahme: program mit Transform-Feedback-Varyings ist gelinkt,
// transformFeedback und Zielpuffer sind gebunden.
const query = gl.createQuery();

gl.enable(gl.RASTERIZER_DISCARD);
gl.beginQuery(gl.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, query);
gl.beginTransformFeedback(gl.POINTS);
gl.drawArrays(gl.POINTS, 0, 1000);
gl.endTransformFeedback();
gl.endQuery(gl.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
gl.disable(gl.RASTERIZER_DISCARD);

setTimeout(() => {
  if (gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE)) {
    const count = gl.getQueryParameter(query, gl.QUERY_RESULT);
    console.log(`Geschriebene Primitive: ${count}`);
  }
  gl.deleteQuery(query);
}, 100);
Geschriebene Primitive: 1000

GPU-Zeitmessung mit EXT_disjoint_timer_query_webgl2

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

if (ext) {
  const query = gl.createQuery();
  gl.beginQuery(ext.TIME_ELAPSED_EXT, query);
  gl.drawArrays(gl.TRIANGLES, 0, 3);
  gl.endQuery(ext.TIME_ELAPSED_EXT);

  const check = () => {
    const available = gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE);
    const disjoint = gl.getParameter(ext.GPU_DISJOINT_EXT);
    if (available && !disjoint) {
      const ns = gl.getQueryParameter(query, gl.QUERY_RESULT);
      console.log(`GPU-Zeit: ${(ns / 1e6).toFixed(3)} ms`);
      gl.deleteQuery(query);
    } else if (!available) {
      requestAnimationFrame(check);
    }
  };
  requestAnimationFrame(check);
} else {
  console.log('Timer-Abfragen nicht verfügbar');
}
GPU-Zeit: 0.042 ms

// Wichtig · Fallstricke

  • Nur eine aktive Abfrage pro Ziel: Ein zweites beginQuery() für dasselbe Ziel ohne vorheriges endQuery() erzeugt gl.INVALID_OPERATION. Dasselbe gilt, wenn das Query-Objekt bereits für ein anderes Ziel verwendet wurde.
  • ANY_SAMPLES_PASSED und ANY_SAMPLES_PASSED_CONSERVATIVE teilen sich einen Slot – sie dürfen nicht gleichzeitig aktiv sein.
  • Ergebnis nicht im selben Frame abfragen: WebGL meldet QUERY_RESULT_AVAILABLE frühestens nach Rückkehr zur Ereignisschleife als true. Aktives Warten in einer Schleife blockiert den Tab dauerhaft.
  • Eine ungültige target-Konstante führt zu gl.INVALID_ENUM; ein gelöschtes Query-Objekt zu gl.INVALID_OPERATION.
  • Timer-Abfragen (EXT_disjoint_timer_query_webgl2) sind aus Sicherheitsgründen (Timing-Angriffe) in vielen Browsern deaktiviert oder in der Auflösung reduziert – immer auf null prüfen.
  • Kompatibilität: Nur in WebGL 2 (getContext('webgl2')) verfügbar; in WebGL 1 gibt es Occlusion-Queries nicht, Timer nur über EXT_disjoint_timer_query.