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

WebGL2RenderingContext.getQuery()

Methode

Gibt die aktuell aktive <code>WebGLQuery</code> für das angegebene Query-Ziel zurück oder <code>null</code>, wenn für dieses Ziel keine Abfrage läuft.

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

Signatur

WebGL2RenderingContext.prototype.getQuery(target, pname)

Beschreibung

Mit getQuery() fragst du ab, welches WebGLQuery-Objekt gerade für ein bestimmtes Ziel aktiv ist, also zwischen beginQuery() und endQuery() steht. Für jedes Ziel kann zur gleichen Zeit höchstens eine Abfrage aktiv sein. Mit getQuery() kannst du das vor einem weiteren beginQuery() prüfen und so den Fehler INVALID_OPERATION vermeiden.

Typische Ziele sind Occlusion Queries (gl.ANY_SAMPLES_PASSED, gl.ANY_SAMPLES_PASSED_CONSERVATIVE). Sie zeigen an, ob beim Zeichnen überhaupt ein Fragment den Tiefentest bestanden hat. Außerdem gibt es Transform-Feedback-Queries (gl.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN). Mit der Erweiterung EXT_disjoint_timer_query_webgl2 kommen noch GPU-Zeitmessungen hinzu (ext.TIME_ELAPSED_EXT).

Wichtig: getQuery() liefert nur das Query-Objekt, nicht sein Ergebnis. Das Ergebnis liest du nach endQuery() mit getQueryParameter() aus. Das geht asynchron, meist erst in einem der nächsten Frames.

Parameter

Name Typ Default Beschreibung
$target Pflicht GLenum Das Query-Ziel: gl.ANY_SAMPLES_PASSED, gl.ANY_SAMPLES_PASSED_CONSERVATIVE, gl.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN. Mit EXT_disjoint_timer_query_webgl2 zusätzlich ext.TIME_ELAPSED_EXT sowie ext.TIMESTAMP_EXT (nur zusammen mit QUERY_COUNTER_BITS_EXT).
$pname Pflicht GLenum Muss gl.CURRENT_QUERY sein. Mit der Timer-Erweiterung ist auch ext.QUERY_COUNTER_BITS_EXT erlaubt: Dann bekommst du statt eines Objekts die Bit-Breite des Zeitzählers als Zahl.

Rückgabewert

Typ
WebGLQuery | number | null
Beschreibung
Das aktive WebGLQuery-Objekt oder null, wenn für target keine Abfrage läuft. Bei pname = ext.QUERY_COUNTER_BITS_EXT ist es ein GLint (Anzahl der Bits).

Beispiele

Aktive Occlusion Query prüfen

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

const query = gl.createQuery();
console.log(gl.getQuery(gl.ANY_SAMPLES_PASSED, gl.CURRENT_QUERY)); // vorher

gl.beginQuery(gl.ANY_SAMPLES_PASSED, query);
const active = gl.getQuery(gl.ANY_SAMPLES_PASSED, gl.CURRENT_QUERY);
console.log(active === query);

gl.endQuery(gl.ANY_SAMPLES_PASSED);
console.log(gl.getQuery(gl.ANY_SAMPLES_PASSED, gl.CURRENT_QUERY)); // nachher
null true null

Doppeltes beginQuery() verhindern

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

const safeBeginQuery = (target, q) => {
  if (gl.getQuery(target, gl.CURRENT_QUERY) !== null) {
    console.warn('Für dieses Ziel läuft bereits eine Abfrage.');
    return false;
  }
  gl.beginQuery(target, q);
  return true;
};

safeBeginQuery(gl.ANY_SAMPLES_PASSED_CONSERVATIVE, query); // true
safeBeginQuery(gl.ANY_SAMPLES_PASSED_CONSERVATIVE, query); // Warnung, false
gl.endQuery(gl.ANY_SAMPLES_PASSED_CONSERVATIVE);
Für dieses Ziel läuft bereits eine Abfrage.

GPU-Timer: Zählerbreite und aktive Zeitmessung

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

if (ext) {
  const bits = gl.getQuery(ext.TIME_ELAPSED_EXT, ext.QUERY_COUNTER_BITS_EXT);
  console.log(`Zählerbreite: ${bits} Bit`);

  const timer = gl.createQuery();
  gl.beginQuery(ext.TIME_ELAPSED_EXT, timer);
  // ... Zeichenbefehle ...
  console.log(gl.getQuery(ext.TIME_ELAPSED_EXT, gl.CURRENT_QUERY) === timer);
  gl.endQuery(ext.TIME_ELAPSED_EXT);

  // Ergebnis frühestens im nächsten Frame abholen
  const poll = () => {
    if (gl.getQueryParameter(timer, gl.QUERY_RESULT_AVAILABLE)) {
      const ns = gl.getQueryParameter(timer, gl.QUERY_RESULT);
      console.log(`GPU-Zeit: ${(ns / 1e6).toFixed(3)} ms`);
    } else {
      requestAnimationFrame(poll);
    }
  };
  requestAnimationFrame(poll);
} else {
  console.log('Timer-Queries werden nicht unterstützt.');
}
Zählerbreite: 64 Bit true GPU-Zeit: 0.012 ms

// Wichtig · Fallstricke

  • getQuery() gibt nicht das Ergebnis der Abfrage zurück. Das Ergebnis liest du mit getQueryParameter(query, gl.QUERY_RESULT) aus, und erst nachdem QUERY_RESULT_AVAILABLE den Wert true liefert.
  • Ein ungültiges target oder pname löst gl.INVALID_ENUM aus. Der Rückgabewert ist dann null. Prüfe das bei der Fehlersuche mit gl.getError().
  • Nur in WebGL 2 verfügbar. In WebGL 1 gibt es die Methode nicht. Dort gibt es nur die Erweiterung EXT_disjoint_timer_query mit getQueryEXT().
  • Die Erweiterung EXT_disjoint_timer_query_webgl2 ist aus Sicherheitsgründen (Timing-Angriffe) in vielen Browsern nicht verfügbar oder nur mit ungenauer Auflösung. Prüfe immer, ob getExtension() ein Objekt zurückgibt.
  • Rufst du getQuery() in jedem Frame auf, erzwingt das zwar keine GPU-Synchronisation, du solltest es aber trotzdem nur zur Absicherung und Fehlersuche einsetzen.