Signatur
Beschreibung
getQueryParameter() ist das Gegenstück zu beginQuery() und endQuery(). Damit fragt man das Ergebnis einer asynchronen GPU-Abfrage ab. Bei einer Occlusion Query (ANY_SAMPLES_PASSED bzw. ANY_SAMPLES_PASSED_CONSERVATIVE) erfährt man, ob beim Zeichnen überhaupt Pixel sichtbar waren. Bei TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN erhält man die Zahl der geschriebenen Primitive. Mit der Erweiterung EXT_disjoint_timer_query_webgl2 misst man zusätzlich GPU-Laufzeiten in Nanosekunden.
GPU-Abfragen laufen asynchron. Deshalb prüft man zuerst mit gl.QUERY_RESULT_AVAILABLE, ob das Ergebnis schon vorliegt. Erst danach liest man den Wert mit gl.QUERY_RESULT. Laut WebGL-2-Spezifikation wird ein Ergebnis nie im selben Frame verfügbar, in dem die Abfrage beendet wurde. Frühestens nach Rückgabe an die Ereignisschleife (z. B. im nächsten requestAnimationFrame) kann das Ergebnis bereitstehen.
Typische Einsatzgebiete sind Occlusion Culling (verdeckte Objekte beim Zeichnen überspringen), Profiling einzelner Render-Durchläufe und die Kontrolle von Transform-Feedback-Ausgaben.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $query Pflicht | WebGLQuery | Das abzufragende Query-Objekt, erzeugt mit gl.createQuery() und zuvor mit beginQuery()/endQuery() verwendet. |
|
| $pname Pflicht | GLenum | Welche Information gelesen wird: gl.QUERY_RESULT_AVAILABLE (liegt ein Ergebnis vor?) oder gl.QUERY_RESULT (der Ergebniswert). |
Rückgabewert
QUERY_RESULT_AVAILABLE ein boolean. Bei QUERY_RESULT eine number (GLuint, bei Timer-Queries GLuint64 in Nanosekunden). Occlusion Queries liefern 0 oder 1. Bei einem Fehler wird null zurückgegeben, z. B. bei ungültigem pname oder einer noch aktiven Abfrage.Beispiele
Occlusion Query: Ist ein Objekt sichtbar?
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl2');
const query = gl.createQuery();
// Bounding-Box des Objekts testweise zeichnen
gl.beginQuery(gl.ANY_SAMPLES_PASSED, query);
// drawBoundingBox(); // eigener Zeichenaufruf
gl.endQuery(gl.ANY_SAMPLES_PASSED);
// Ergebnis frühestens im nächsten Frame abfragen
const poll = () => {
const available = gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE);
if (!available) {
requestAnimationFrame(poll);
return;
}
const visible = gl.getQueryParameter(query, gl.QUERY_RESULT);
console.log(visible ? 'Objekt sichtbar' : 'Objekt verdeckt');
};
requestAnimationFrame(poll);
GPU-Zeitmessung mit EXT_disjoint_timer_query_webgl2
const gl = document.querySelector('canvas').getContext('webgl2');
const ext = gl.getExtension('EXT_disjoint_timer_query_webgl2');
if (ext) {
const query = gl.createQuery();
gl.beginQuery(ext.TIME_ELAPSED_EXT, query);
// renderScene(); // eigener Render-Durchlauf
gl.endQuery(ext.TIME_ELAPSED_EXT);
const check = () => {
const available = gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE);
const disjoint = gl.getParameter(ext.GPU_DISJOINT_EXT);
if (available && !disjoint) {
const ns = gl.getQueryParameter(query, gl.QUERY_RESULT);
console.log(`GPU-Zeit: ${(ns / 1e6).toFixed(2)} ms`);
gl.deleteQuery(query);
} else if (!available) {
requestAnimationFrame(check);
}
};
requestAnimationFrame(check);
} else {
console.log('Timer-Queries nicht verfügbar');
}
Geschriebene Primitive bei Transform Feedback zählen
const gl = document.querySelector('canvas').getContext('webgl2');
const query = gl.createQuery();
gl.beginQuery(gl.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, query);
// gl.beginTransformFeedback(gl.POINTS);
// gl.drawArrays(gl.POINTS, 0, 1000);
// gl.endTransformFeedback();
gl.endQuery(gl.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
const waitForResult = () => new Promise((resolve) => {
const tick = () => {
if (gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE)) {
resolve(gl.getQueryParameter(query, gl.QUERY_RESULT));
} else {
requestAnimationFrame(tick);
}
};
requestAnimationFrame(tick);
});
const count = await waitForResult();
console.log(`Primitive geschrieben: ${count}`);
// Wichtig · Fallstricke
- Nicht im selben Frame abfragen:
QUERY_RESULT_AVAILABLEliefert vor Rückkehr zur Ereignisschleife immerfalse. Eine Warteschleife mitwhileblockiert endlos. Stattdessen mitrequestAnimationFrameodersetTimeouterneut prüfen. - Ein direkter Aufruf von
QUERY_RESULTohne vorherige Verfügbarkeitsprüfung kann die Pipeline anhalten und kostet viel Leistung. - Eine Abfrage, die gerade aktiv ist (zwischen
beginQueryundendQuery), erzeugtINVALID_OPERATION, und der Rückgabewert istnull. - Bei Timer-Queries muss
GPU_DISJOINT_EXTgeprüft werden. Ist der Werttrue, sind die Messwerte ungültig. Die Erweiterung ist in vielen Browsern aus Datenschutzgründen (Timing-Angriffe) deaktiviert oder in der Genauigkeit reduziert. - Die Methode gibt es nur in WebGL 2. In WebGL 1 steht nur die Erweiterung
EXT_disjoint_timer_querymitgetQueryObjectEXT()zur Verfügung. - Query-Objekte kann man wiederverwenden. Nicht mehr benötigte Objekte gibt man mit
gl.deleteQuery()frei.