Start · Sprachen · JavaScript · Referenz · EXT_disjoint_timer_query extension

EXT_disjoint_timer_query extension

Schnittstelle

WebGL-1-Erweiterung, mit der sich die GPU-Laufzeit einer Reihe von GL-Befehlen asynchron messen lässt, ohne die Rendering-Pipeline anzuhalten.

seit JavaScript WebGL 1 Erweiterung (nur teilweise unterstützt, of Kategorie: webapi

Signatur

interface EXT_disjoint_timer_query

Beschreibung

Die Erweiterung EXT_disjoint_timer_query ergänzt einen WebGLRenderingContext um Timer-Queries. Damit misst du, wie lange die GPU tatsächlich für bestimmte Zeichenbefehle braucht. Mit performance.now() geht das nicht, denn es misst nur die CPU-Zeit für das Absetzen der Befehle. Du holst die Erweiterung über gl.getExtension("EXT_disjoint_timer_query"). Ist sie nicht verfügbar, bekommst du null.

Das Objekt stellt folgende Methoden bereit:

  • createQueryEXT(), deleteQueryEXT(query), isQueryEXT(query): Query-Objekte (WebGLTimerQueryEXT) anlegen, löschen und prüfen.
  • beginQueryEXT(ext.TIME_ELAPSED_EXT, query) / endQueryEXT(ext.TIME_ELAPSED_EXT): misst die Dauer aller Befehle zwischen den beiden Aufrufen.
  • queryCounterEXT(query, ext.TIMESTAMP_EXT): speichert einen GPU-Zeitstempel.
  • getQueryEXT(target, pname): Informationen zum Ziel, z. B. QUERY_COUNTER_BITS_EXT oder CURRENT_QUERY_EXT.
  • getQueryObjectEXT(query, pname): fragt mit QUERY_RESULT_AVAILABLE_EXT ab, ob das Ergebnis schon da ist, und liest es mit QUERY_RESULT_EXT in Nanosekunden aus.

Die Messung läuft asynchron: Das Ergebnis liegt meist erst ein oder mehrere Frames später vor. Frag es deshalb in einer späteren requestAnimationFrame-Runde ab. Prüfe außerdem jedes Mal gl.getParameter(ext.GPU_DISJOINT_EXT). Steht dieser Wert auf true, ist etwas passiert, das die Messung verfälscht (z. B. ein Wechsel der GPU-Taktrate oder ein Kontextwechsel). Das Ergebnis solltest du dann verwerfen.

Für WebGL 2 gibt es die eigene Erweiterung EXT_disjoint_timer_query_webgl2. Sie nutzt die eingebauten Query-Methoden des WebGL2RenderingContext (gl.createQuery(), gl.beginQuery() usw.).

Parameter

Name Typ Default Beschreibung
$name Pflicht string Argument für gl.getExtension(): genau "EXT_disjoint_timer_query".
$target Pflicht GLenum Für beginQueryEXT/endQueryEXT: ext.TIME_ELAPSED_EXT. Für queryCounterEXT: ext.TIMESTAMP_EXT.
$query Pflicht WebGLTimerQueryEXT Das mit createQueryEXT() erzeugte Query-Objekt.
$pname Pflicht GLenum Für getQueryObjectEXT: ext.QUERY_RESULT_AVAILABLE_EXT oder ext.QUERY_RESULT_EXT. Für getQueryEXT: ext.QUERY_COUNTER_BITS_EXT oder ext.CURRENT_QUERY_EXT.

Rückgabewert

Typ
EXT_disjoint_timer_query | null
Beschreibung
gl.getExtension() gibt das Erweiterungsobjekt zurück oder null, wenn die Erweiterung nicht unterstützt wird. getQueryObjectEXT(query, QUERY_RESULT_EXT) liefert die gemessene Zeit in Nanosekunden als number.

Beispiele

Erweiterung abfragen und Unterstützung prüfen

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 {
  // Genauigkeit des Zählers in Bit
  const bits = ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.QUERY_COUNTER_BITS_EXT);
  console.log(`TIME_ELAPSED-Zähler: ${bits} Bit`);
}
TIME_ELAPSED-Zähler: 64 Bit (oder: Timer-Queries nicht verfügbar)

GPU-Zeit eines Draw-Calls asynchron messen

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.clearColor(0.1, 0.2, 0.3, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// ... hier weitere Draw-Calls, z. B. gl.drawArrays(...)

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) – verworfen");
    ext.deleteQueryEXT(query);
  } else {
    requestAnimationFrame(poll); // Ergebnis noch nicht da
  }
};
requestAnimationFrame(poll);
GPU-Zeit: 0.042 ms

Zeitstempel-Queries (TIMESTAMP_EXT)

const gl = document.querySelector("canvas").getContext("webgl");
const ext = gl.getExtension("EXT_disjoint_timer_query");

// Zeitstempel werden nicht überall unterstützt: 0 Bit = nicht verfügbar
const tsBits = ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.QUERY_COUNTER_BITS_EXT);

if (tsBits > 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 check = () => {
    const ready = ext.getQueryObjectEXT(end, ext.QUERY_RESULT_AVAILABLE_EXT);
    if (!ready) return requestAnimationFrame(check);
    if (!gl.getParameter(ext.GPU_DISJOINT_EXT)) {
      const t0 = ext.getQueryObjectEXT(start, ext.QUERY_RESULT_EXT);
      const t1 = ext.getQueryObjectEXT(end, ext.QUERY_RESULT_EXT);
      console.log(`Dauer: ${(t1 - t0) / 1e6} ms`);
    }
    ext.deleteQueryEXT(start);
    ext.deleteQueryEXT(end);
  };
  requestAnimationFrame(check);
} else {
  console.log("TIMESTAMP_EXT nicht unterstützt");
}
Dauer: 0.035 ms (oder: TIMESTAMP_EXT nicht unterstützt)