Start · Sprachen · JavaScript · Referenz · EXT_disjoint_timer_query.getQueryEXT()

EXT_disjoint_timer_query.getQueryEXT()

Methode

Liefert Informationen zu einem Abfrageziel der WebGL-Timer-Erweiterung, z. B. die gerade aktive Timer-Abfrage oder die Bitbreite des GPU-Zeitzählers.

seit JavaScript WebGL 1 Erweiterung; eingeschränkte Browser-Unters Kategorie: webapi

Signatur

EXT_disjoint_timer_query.prototype.getQueryEXT(target, pname)

Beschreibung

Die Methode getQueryEXT() gehört zur WebGL-1-Erweiterung EXT_disjoint_timer_query. Damit misst man, wie lange die GPU für eine Folge von Zeichenbefehlen braucht. Die Methode startet oder beendet selbst keine Messung. Sie fragt nur den Zustand eines Abfrageziels ab: welche Abfrage gerade läuft und wie viele Bits der GPU-Zeitzähler hat.

Typische Einsatzfälle:

  • Vor der Messung prüfen: Mit QUERY_COUNTER_BITS_EXT findet man heraus, ob der Treiber für TIME_ELAPSED_EXT oder TIMESTAMP_EXT überhaupt einen brauchbaren Zähler hat. Ist der Wert 0, gibt es für dieses Ziel keine Zeitmessung.
  • Beim Fehlersuchen und in Profiling-Werkzeugen: Mit CURRENT_QUERY_EXT erfährt man, ob gerade eine TIME_ELAPSED_EXT-Abfrage zwischen beginQueryEXT() und endQueryEXT() läuft. So vermeidet man den Fehler INVALID_OPERATION, der bei verschachtelten Abfragen entsteht.

Das Ergebnis einer Messung liest man nicht mit dieser Methode aus, sondern mit getQueryObjectEXT(). Zusätzlich prüft man den Parameter GPU_DISJOINT_EXT über gl.getParameter(), weil Messwerte nach einem „disjoint“-Ereignis ungültig sind (z. B. nach einer Taktänderung oder einem Kontextwechsel der GPU).

Parameter

Name Typ Default Beschreibung
$target Pflicht GLenum Das Abfrageziel: ext.TIME_ELAPSED_EXT (gemessene Dauer) oder ext.TIMESTAMP_EXT (GPU-Zeitstempel).
$pname Pflicht GLenum Die gewünschte Information: ext.CURRENT_QUERY_EXT liefert die gerade aktive Abfrage, ext.QUERY_COUNTER_BITS_EXT die Bitbreite des Zeitzählers.

Rückgabewert

Typ
WebGLTimerQueryEXT | number | null
Beschreibung
Bei CURRENT_QUERY_EXT das gerade aktive WebGLTimerQueryEXT-Objekt oder null. Für TIMESTAMP_EXT ist das Ergebnis immer null, weil Zeitstempel nicht über begin/end laufen. Bei QUERY_COUNTER_BITS_EXT eine Ganzzahl (GLint) mit der Bitbreite des Zählers; 0 heißt, dass das Ziel nicht unterstützt wird.

Beispiele

Unterstützung und Zählergenauigkeit prüfen

const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');
const ext = gl?.getExtension('EXT_disjoint_timer_query');

if (!ext) {
  console.log('EXT_disjoint_timer_query nicht verfügbar');
} else {
  const elapsedBits = ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.QUERY_COUNTER_BITS_EXT);
  const timestampBits = ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.QUERY_COUNTER_BITS_EXT);
  console.log(`TIME_ELAPSED: ${elapsedBits} Bit`);
  console.log(`TIMESTAMP: ${timestampBits > 0 ? timestampBits + ' Bit' : 'nicht unterstützt'}`);
}
TIME_ELAPSED: 64 Bit TIMESTAMP: 64 Bit

Aktive Abfrage prüfen, um verschachtelte Messungen zu vermeiden

const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('EXT_disjoint_timer_query');

const beginSafe = (query) => {
  const active = ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT);
  if (active !== null) {
    console.warn('Es läuft bereits eine Messung – Start übersprungen');
    return false;
  }
  ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query);
  return true;
};

const q = ext.createQueryEXT();
console.log(beginSafe(q)); // true
console.log(beginSafe(q)); // false + Warnung
ext.endQueryEXT(ext.TIME_ELAPSED_EXT);
console.log(ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT));
true Es läuft bereits eine Messung – Start übersprungen false null

Komplette GPU-Zeitmessung eines Frames

const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('EXT_disjoint_timer_query');

if (ext && ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.QUERY_COUNTER_BITS_EXT) > 0) {
  const query = ext.createQueryEXT();
  ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query);
  gl.clearColor(0.1, 0.2, 0.3, 1);
  gl.clear(gl.COLOR_BUFFER_BIT); // hier stünden die eigentlichen Zeichenbefehle
  ext.endQueryEXT(ext.TIME_ELAPSED_EXT);

  const poll = () => {
    const available = ext.getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT);
    const disjoint = gl.getParameter(ext.GPU_DISJOINT_EXT);
    if (available && !disjoint) {
      const ns = ext.getQueryObjectEXT(query, ext.QUERY_RESULT_EXT);
      console.log(`GPU-Zeit: ${(ns / 1e6).toFixed(3)} ms`);
      ext.deleteQueryEXT(query);
    } else if (!disjoint) {
      requestAnimationFrame(poll);
    } else {
      ext.deleteQueryEXT(query); // Messwert ungültig
    }
  };
  requestAnimationFrame(poll);
}
GPU-Zeit: 0.042 ms

// Wichtig · Fallstricke

  • Browser-Unterstützung: Wegen timingbasierter Seitenkanalangriffe (Spectre, „GLitch“) haben viele Browser die Erweiterung zeitweise abgeschaltet oder machen sie absichtlich ungenauer. Chromium bietet sie auf Desktop-Systemen an, in Firefox ist sie meist nur über eine Einstellung erreichbar, in Safari fehlt sie. Prüfe deshalb immer den Rückgabewert von getExtension() auf null.
  • Nur WebGL 1: In einem WebGL-2-Kontext gibt es diese Erweiterung nicht. Dort verwendet man EXT_disjoint_timer_query_webgl2 zusammen mit gl.getQuery(), gl.beginQuery() und gl.getQueryParameter().
  • Mit TIMESTAMP_EXT liefert CURRENT_QUERY_EXT immer null. Zeitstempel erzeugt man mit queryCounterEXT(), nicht mit begin/end.
  • Andere Werte für target oder pname lösen INVALID_ENUM aus (abfragbar über gl.getError()); die Methode gibt dann null zurück.
  • Werte einer Messung nach einem GPU_DISJOINT_EXT-Ereignis sind unzuverlässig und sollten verworfen werden.