Start · Sprachen · JavaScript · Referenz · EXT_disjoint_timer_query.getQueryObjectEXT()

EXT_disjoint_timer_query.getQueryObjectEXT()

Methode

Fragt den Status oder das Ergebnis eines GPU-Timer-Query-Objekts ab – ob das Ergebnis schon vorliegt oder wie viele Nanosekunden gemessen wurden.

seit JavaScript WebGL 1 Extension; nur eingeschränkt verfügbar (Ch Kategorie: webapi

Signatur

EXT_disjoint_timer_query.getQueryObjectEXT(query, pname)

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: ein boolean, der angibt, ob die GPU die Messung schon abgeschlossen hat.
  • ext.QUERY_RESULT_EXT: das Messergebnis als number in Nanosekunden (bei TIME_ELAPSED_EXT die verstrichene Zeit, bei TIMESTAMP_EXT ein 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

Typ
number | boolean | null
Beschreibung
Bei 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);
}
GPU-Zeit: 0.042 ms

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`);
}
Clear dauerte 0.015 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');
}
Differenz: 0.02 ms