Signatur
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_EXTfindet man heraus, ob der Treiber fürTIME_ELAPSED_EXToderTIMESTAMP_EXTüberhaupt einen brauchbaren Zähler hat. Ist der Wert0, gibt es für dieses Ziel keine Zeitmessung. - Beim Fehlersuchen und in Profiling-Werkzeugen: Mit
CURRENT_QUERY_EXTerfährt man, ob gerade eineTIME_ELAPSED_EXT-Abfrage zwischenbeginQueryEXT()undendQueryEXT()läuft. So vermeidet man den FehlerINVALID_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
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'}`);
}
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));
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);
}
// 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()aufnull. - Nur WebGL 1: In einem WebGL-2-Kontext gibt es diese Erweiterung nicht. Dort verwendet man
EXT_disjoint_timer_query_webgl2zusammen mitgl.getQuery(),gl.beginQuery()undgl.getQueryParameter(). - Mit
TIMESTAMP_EXTliefertCURRENT_QUERY_EXTimmernull. Zeitstempel erzeugt man mitqueryCounterEXT(), nicht mit begin/end. - Andere Werte für
targetoderpnamelösenINVALID_ENUMaus (abfragbar übergl.getError()); die Methode gibt dannnullzurück. - Werte einer Messung nach einem
GPU_DISJOINT_EXT-Ereignis sind unzuverlässig und sollten verworfen werden.