Signatur
Beschreibung
Die Methode beginQueryEXT() der WebGL-Erweiterung EXT_disjoint_timer_query startet eine Zeitmessung auf der GPU. Alle WebGL-Befehle, die zwischen beginQueryEXT() und dem zugehörigen endQueryEXT() abgesetzt werden, fließen in die gemessene Zeit ein. So lässt sich die tatsächliche GPU-Ausführungszeit einzelner Render-Passes ermitteln – etwas, das mit performance.now() auf der CPU nicht möglich ist, weil WebGL-Aufrufe asynchron ausgeführt werden.
Als Ziel wird ext.TIME_ELAPSED_EXT übergeben, als zweites Argument ein zuvor mit ext.createQueryEXT() angelegtes WebGLTimerQueryEXT-Objekt. Das Ergebnis steht nicht sofort bereit: Man fragt es in späteren Frames mit getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT) ab und liest dann den Wert in Nanosekunden mit ext.QUERY_RESULT_EXT aus.
Wichtig ist die Prüfung auf GPU_DISJOINT_EXT: Tritt während der Messung ein „disjunktes" Ereignis auf (z. B. Taktänderung, Kontextwechsel), ist das Ergebnis ungültig und muss verworfen werden. Diese Erweiterung gilt nur für WebGL 1; in WebGL 2 heißt das Pendant EXT_disjoint_timer_query_webgl2 und verwendet gl.beginQuery().
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $target Pflicht | GLenum | Art der Abfrage. Für diese Methode muss ext.TIME_ELAPSED_EXT übergeben werden. |
|
| $query Pflicht | WebGLTimerQueryEXT | Das mit ext.createQueryEXT() erzeugte Abfrageobjekt, in dem das Messergebnis gespeichert wird. |
Rückgabewert
undefined). Das Ergebnis wird später über getQueryObjectEXT() abgerufen.Beispiele
Einfache GPU-Zeitmessung eines Draw-Calls
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');
const ext = gl.getExtension('EXT_disjoint_timer_query');
if (!ext) {
console.log('Timer-Queries werden nicht unterstützt');
} 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 gl.drawArrays() / gl.drawElements()
ext.endQueryEXT(ext.TIME_ELAPSED_EXT);
console.log('Messung gestartet und beendet');
}
Ergebnis asynchron in späteren Frames abfragen
const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('EXT_disjoint_timer_query');
const query = ext.createQueryEXT();
ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query);
gl.clear(gl.COLOR_BUFFER_BIT);
ext.endQueryEXT(ext.TIME_ELAPSED_EXT);
const pollResult = () => {
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) – verworfen');
ext.deleteQueryEXT(query);
} else {
requestAnimationFrame(pollResult);
}
};
requestAnimationFrame(pollResult);
Pro Frame messen mit Query-Pool
const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('EXT_disjoint_timer_query');
const pending = [];
const renderFrame = () => {
const query = ext.createQueryEXT();
ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query);
gl.clear(gl.COLOR_BUFFER_BIT);
// Szene zeichnen ...
ext.endQueryEXT(ext.TIME_ELAPSED_EXT);
pending.push(query);
// Fertige Abfragen auswerten (älteste zuerst)
const disjoint = gl.getParameter(ext.GPU_DISJOINT_EXT);
while (pending.length &&
ext.getQueryObjectEXT(pending[0], ext.QUERY_RESULT_AVAILABLE_EXT)) {
const q = pending.shift();
if (!disjoint) {
const ms = ext.getQueryObjectEXT(q, ext.QUERY_RESULT_EXT) / 1e6;
console.log(`Frame-GPU-Zeit: ${ms.toFixed(2)} ms`);
}
ext.deleteQueryEXT(q);
}
requestAnimationFrame(renderFrame);
};
requestAnimationFrame(renderFrame);
// Wichtig · Fallstricke
- Nur eine aktive Abfrage: Pro Ziel darf immer nur eine
TIME_ELAPSED_EXT-Abfrage laufen. Ein zweitesbeginQueryEXT()vor dem zugehörigenendQueryEXT()erzeugtINVALID_OPERATION. Verschachtelte Messungen sind nicht möglich. - Ergebnis nie im selben Frame: Browser geben das Ergebnis frühestens nach Rückkehr in die Ereignisschleife frei. Wer im gleichen Frame abfragt, bekommt
falsefürQUERY_RESULT_AVAILABLE_EXT. - Disjoint prüfen: Ist
gl.getParameter(ext.GPU_DISJOINT_EXT)true, sind alle laufenden Messungen unbrauchbar. - Sicherheit/Kompatibilität: Wegen Timing-Angriffen (z. B. GLitch, Spectre) wurde die Erweiterung in vielen Browsern zeitweise deaktiviert oder die Präzision reduziert. Firefox und Safari bieten sie standardmäßig nicht an; immer mit
gl.getExtension()aufnullprüfen. - In WebGL 2 ist diese Erweiterung nicht verfügbar – dort
EXT_disjoint_timer_query_webgl2mitgl.beginQuery()verwenden. - Nicht mehr benötigte Abfrageobjekte mit
deleteQueryEXT()freigeben, um GPU-Ressourcen nicht zu verschwenden.