Signatur
Beschreibung
endQueryEXT() gehört zur WebGL-Erweiterung EXT_disjoint_timer_query. Damit lässt sich messen, wie lange die GPU für eine Folge von Zeichenbefehlen braucht. Eine Messung startet man mit beginQueryEXT(ext.TIME_ELAPSED_EXT, query), danach folgen die zu messenden Aufrufe wie drawArrays() oder drawElements(), und endQueryEXT(ext.TIME_ELAPSED_EXT) schließt die Messung ab.
Die Methode bekommt nur das Ziel (target) übergeben, nicht das Query-Objekt selbst. Pro Ziel kann immer nur eine Abfrage aktiv sein, deshalb weiß WebGL, welche Abfrage beendet werden soll.
Das Ergebnis liegt asynchron vor. Nach endQueryEXT() fragt man in späteren Frames mit getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT) ab, ob der Wert schon bereitsteht. Erst dann liest man mit ext.QUERY_RESULT_EXT die verstrichene Zeit in Nanosekunden aus. Vorher prüft man gl.getParameter(ext.GPU_DISJOINT_EXT): Ist der Wert true, ist die Messung ungültig, etwa weil der Takt gewechselt hat oder die GPU zurückgesetzt wurde.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $target Pflicht | GLenum | Ziel der Abfrage, die beendet werden soll. Möglich ist nur ext.TIME_ELAPSED_EXT. Zeitstempel (ext.TIMESTAMP_EXT) setzt man dagegen mit queryCounterEXT(), nicht mit begin/end. |
Rückgabewert
undefined). Das Messergebnis holt man später mit getQueryObjectEXT().Beispiele
Zeichenaufruf messen und Ergebnis auslesen
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');
const ext = gl.getExtension('EXT_disjoint_timer_query');
if (!ext) {
console.log('Timer-Queries nicht verfügbar');
} else {
const query = ext.createQueryEXT();
ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query);
gl.clearColor(0.2, 0.3, 0.4, 1);
gl.clear(gl.COLOR_BUFFER_BIT); // hier stünden normalerweise drawArrays()-Aufrufe
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) {
console.log('Messung ungültig (disjoint)');
ext.deleteQueryEXT(query);
} else {
requestAnimationFrame(poll);
}
};
requestAnimationFrame(poll);
}
Wiederverwendbare Messfunktion für die Render-Schleife
const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('EXT_disjoint_timer_query');
const pending = [];
const measure = (label, drawFn) => {
if (!ext) return drawFn();
const query = ext.createQueryEXT();
ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query);
drawFn();
ext.endQueryEXT(ext.TIME_ELAPSED_EXT); // nur das Ziel, nicht die Query übergeben
pending.push({ label, query });
};
const collectResults = () => {
if (!ext) return;
const disjoint = gl.getParameter(ext.GPU_DISJOINT_EXT);
for (let i = pending.length - 1; i >= 0; i--) {
const { label, query } = pending[i];
if (disjoint || ext.getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT)) {
if (!disjoint) {
const ns = ext.getQueryObjectEXT(query, ext.QUERY_RESULT_EXT);
console.log(`${label}: ${(ns / 1e6).toFixed(2)} ms`);
}
ext.deleteQueryEXT(query);
pending.splice(i, 1);
}
}
};
const frame = () => {
measure('Szene', () => {
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
});
collectResults();
requestAnimationFrame(frame);
};
requestAnimationFrame(frame);
Fehler: endQueryEXT ohne aktive Abfrage
const gl = document.createElement('canvas').getContext('webgl');
const ext = gl.getExtension('EXT_disjoint_timer_query');
if (ext) {
ext.endQueryEXT(ext.TIME_ELAPSED_EXT); // keine Abfrage aktiv
console.log(gl.getError() === gl.INVALID_OPERATION);
}
// Wichtig · Fallstricke
- Verfügbarkeit: Die Erweiterung ist wegen Timing-Angriffen (z. B. GLitch/Spectre) in vielen Browsern abgeschaltet oder nur mit gröberer Zeitauflösung verfügbar. Prüfe
gl.getExtension()immer aufnull. Sie funktioniert nur in WebGL1-Kontexten. In WebGL2 nimmst duEXT_disjoint_timer_query_webgl2mitgl.endQuery(). - Wenn für das Ziel keine Abfrage aktiv ist, erzeugt der Aufruf
INVALID_OPERATION. Ein falsches Ziel erzeugtINVALID_ENUM. - Das Ergebnis ist nie im selben Frame verfügbar. Blockierendes Warten in einer Schleife bringt nichts, weil der Browser Ergebnisse erst nach Rückkehr in die Ereignisschleife aktualisiert. Frage die Ergebnisse deshalb in späteren Frames ab.
- Verwirf die Messwerte, sobald
GPU_DISJOINT_EXTden Werttrueliefert. - Timer-Abfragen können nicht verschachtelt werden: Solange eine
TIME_ELAPSED_EXT-Abfrage läuft, kann keine zweite starten. - Das Ergebnis ist in Nanosekunden angegeben und kann Werte über 253 erreichen. In der Praxis ist das bei Laufzeiten aber unkritisch.