Signatur
Beschreibung
getQueryObjectEXT() gehört zur WebGL-1-Erweiterung EXT_disjoint_timer_query. Damit misst man, wie lange die GPU für bestimmte Zeichenbefehle braucht. Mit performance.now() geht das nicht, weil WebGL-Aufrufe asynchron auf der GPU laufen. Ein Query-Objekt wird mit createQueryEXT() angelegt und mit beginQueryEXT()/endQueryEXT() um Zeichenbefehle gelegt. Alternativ schreibt queryCounterEXT() einen Zeitstempel hinein. Anschließend liest getQueryObjectEXT() das Ergebnis aus.
Über pname wählt man, was zurückkommt:
ext.QUERY_RESULT_AVAILABLE_EXT: einboolean, der angibt, ob die GPU die Messung schon abgeschlossen hat.ext.QUERY_RESULT_EXT: das Messergebnis alsnumberin Nanosekunden (beiTIME_ELAPSED_EXTdie verstrichene Zeit, beiTIMESTAMP_EXTein absoluter GPU-Zeitstempel).
Ergebnisse gibt es frühestens in einem späteren Frame. Der übliche Ablauf ist deshalb: in einer requestAnimationFrame-Schleife abfragen, bis QUERY_RESULT_AVAILABLE_EXT den Wert true liefert. Danach mit gl.getParameter(ext.GPU_DISJOINT_EXT) prüfen, ob die Messung gültig ist. Erst dann QUERY_RESULT_EXT auslesen. Ist GPU_DISJOINT_EXT true, ist etwas dazwischengekommen (z. B. ein Taktwechsel oder Energiesparmodus der GPU) und alle laufenden Messungen sind unbrauchbar.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $query Pflicht | WebGLTimerQueryEXT | Das Query-Objekt, das mit ext.createQueryEXT() erzeugt wurde. |
|
| $pname Pflicht | GLenum | Gewünschte Information: ext.QUERY_RESULT_EXT (Ergebnis in Nanosekunden) oder ext.QUERY_RESULT_AVAILABLE_EXT (liegt das Ergebnis vor?). |
Rückgabewert
QUERY_RESULT_EXT eine number (GLuint64, Nanosekunden). Bei QUERY_RESULT_AVAILABLE_EXT ein boolean. Bei ungültigen Argumenten null und ein WebGL-Fehler (INVALID_ENUM oder INVALID_OPERATION).Beispiele
GPU-Zeit eines Zeichenaufrufs messen (TIME_ELAPSED_EXT)
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
const ext = gl.getExtension('EXT_disjoint_timer_query');
if (!ext) {
console.warn('EXT_disjoint_timer_query nicht verfügbar');
} else {
const query = ext.createQueryEXT();
ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query);
gl.clearColor(0.2, 0.4, 0.6, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// ... hier gl.drawArrays(...) usw.
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.warn('Messung ungültig (GPU disjoint)');
ext.deleteQueryEXT(query);
} else {
requestAnimationFrame(poll);
}
};
requestAnimationFrame(poll);
}
Wiederverwendbarer Promise-Helfer
const measureGpu = (gl, ext, drawFn) => new Promise((resolve, reject) => {
const query = ext.createQueryEXT();
ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query);
drawFn();
ext.endQueryEXT(ext.TIME_ELAPSED_EXT);
const check = () => {
if (gl.getParameter(ext.GPU_DISJOINT_EXT)) {
ext.deleteQueryEXT(query);
reject(new Error('GPU disjoint – Messung verworfen'));
return;
}
if (ext.getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT)) {
const ns = ext.getQueryObjectEXT(query, ext.QUERY_RESULT_EXT);
ext.deleteQueryEXT(query);
resolve(ns / 1e6); // Millisekunden
} else {
requestAnimationFrame(check);
}
};
requestAnimationFrame(check);
});
// Nutzung
const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('EXT_disjoint_timer_query');
if (ext) {
const ms = await measureGpu(gl, ext, () => gl.clear(gl.COLOR_BUFFER_BIT));
console.log(`Clear dauerte ${ms.toFixed(3)} ms`);
}
Zeitstempel-Queries mit queryCounterEXT
const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('EXT_disjoint_timer_query');
// Nicht jede Implementierung unterstützt Zeitstempel
const bits = ext && ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.QUERY_COUNTER_BITS_EXT);
if (bits > 0) {
const start = ext.createQueryEXT();
const end = ext.createQueryEXT();
ext.queryCounterEXT(start, ext.TIMESTAMP_EXT);
gl.clear(gl.COLOR_BUFFER_BIT);
ext.queryCounterEXT(end, ext.TIMESTAMP_EXT);
const poll = () => {
const ready = ext.getQueryObjectEXT(end, ext.QUERY_RESULT_AVAILABLE_EXT);
if (!ready) return requestAnimationFrame(poll);
if (gl.getParameter(ext.GPU_DISJOINT_EXT)) return console.warn('disjoint');
const t0 = ext.getQueryObjectEXT(start, ext.QUERY_RESULT_EXT);
const t1 = ext.getQueryObjectEXT(end, ext.QUERY_RESULT_EXT);
console.log(`Differenz: ${(t1 - t0) / 1e6} ms`);
};
requestAnimationFrame(poll);
} else {
console.log('Zeitstempel-Queries nicht unterstützt');
}