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

EXT_disjoint_timer_query.queryCounterEXT()

Methode

Schreibt einen GPU-Zeitstempel in das angegebene Abfrageobjekt, sobald alle vorher abgesetzten GL-Befehle auf der GPU fertig sind.

seit JavaScript WebGL 1 Extension; eingeschränkte Verfügbarkeit (v Kategorie: webapi

Signatur

EXT_disjoint_timer_query.queryCounterEXT(query, target)

Beschreibung

queryCounterEXT() gehört zur WebGL-Erweiterung EXT_disjoint_timer_query. Die Methode schreibt einen GPU-Zeitstempel in ein WebGLTimerQueryEXT-Objekt. Einen Zeitraum wie bei beginQueryEXT()/endQueryEXT() misst sie nicht. Aufgezeichnet wird der Zeitpunkt, an dem die GPU alle vorher abgesetzten Befehle vollständig abgearbeitet hat. Der Wert wird in Nanosekunden angegeben.

Wenn du zwei Zeitstempel vor und nach einer Befehlsfolge setzt und die Differenz bildest, erhältst du die tatsächliche GPU-Dauer dieses Abschnitts. Anders als bei beginQueryEXT() dürfen Zeitstempel-Abfragen verschachtelt oder überlappend verwendet werden. Deshalb eignen sie sich gut für Profiler, die mehrere Render-Durchläufe eines Frames getrennt messen.

Das Ergebnis liegt asynchron vor. Frühestens in einem späteren Frame prüfst du mit getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT), ob es bereit ist, und liest es dann mit ext.QUERY_RESULT_EXT aus. Prüfe außerdem immer gl.getParameter(ext.GPU_DISJOINT_EXT). Ist der Wert true, ist in der Zwischenzeit etwas passiert, das die Zeitmessung ungültig macht, zum Beispiel ein Taktwechsel der GPU. Die Messwerte musst du dann verwerfen.

In WebGL 2 gibt es dafür die eigene Erweiterung EXT_disjoint_timer_query_webgl2. Sie stellt queryCounterEXT() mit denselben Parametern bereit, verwendet für die Abfrageobjekte aber die normalen Methoden des WebGL2-Kontexts (gl.createQuery(), gl.getQueryParameter()).

Parameter

Name Typ Default Beschreibung
$query Pflicht WebGLTimerQueryEXT Das Abfrageobjekt, das den Zeitstempel aufnimmt. In WebGL 1 wird es mit ext.createQueryEXT() erzeugt, in WebGL 2 mit gl.createQuery().
$target Pflicht GLenum Muss ext.TIMESTAMP_EXT sein. Andere Werte führen zu gl.INVALID_ENUM.

Rückgabewert

Typ
void
Beschreibung
Kein Rückgabewert. Das Ergebnis wird später asynchron über getQueryObjectEXT() bzw. gl.getQueryParameter() gelesen.

Beispiele

GPU-Dauer eines Zeichenaufrufs mit zwei Zeitstempeln messen (WebGL 1)

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 {
  const start = ext.createQueryEXT();
  const end = ext.createQueryEXT();

  ext.queryCounterEXT(start, ext.TIMESTAMP_EXT);
  gl.clearColor(0.2, 0.4, 0.6, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
  // ... weitere Zeichenbefehle ...
  ext.queryCounterEXT(end, ext.TIMESTAMP_EXT);

  const poll = () => {
    const available = ext.getQueryObjectEXT(end, ext.QUERY_RESULT_AVAILABLE_EXT);
    const disjoint = gl.getParameter(ext.GPU_DISJOINT_EXT);

    if (available && !disjoint) {
      const t0 = ext.getQueryObjectEXT(start, ext.QUERY_RESULT_EXT);
      const t1 = ext.getQueryObjectEXT(end, ext.QUERY_RESULT_EXT);
      console.log(`GPU-Zeit: ${((t1 - t0) / 1e6).toFixed(3)} ms`);
      ext.deleteQueryEXT(start);
      ext.deleteQueryEXT(end);
    } else if (disjoint) {
      console.log('Messung ungültig (disjoint)');
    } else {
      requestAnimationFrame(poll);
    }
  };
  requestAnimationFrame(poll);
}
GPU-Zeit: 0.042 ms

Zeitstempel in WebGL 2 mit EXT_disjoint_timer_query_webgl2

const gl = document.createElement('canvas').getContext('webgl2');
const ext = gl.getExtension('EXT_disjoint_timer_query_webgl2');

if (ext) {
  const q0 = gl.createQuery();
  const q1 = gl.createQuery();

  ext.queryCounterEXT(q0, ext.TIMESTAMP_EXT);
  gl.clear(gl.COLOR_BUFFER_BIT);
  ext.queryCounterEXT(q1, ext.TIMESTAMP_EXT);

  const check = () => {
    const ready = gl.getQueryParameter(q1, gl.QUERY_RESULT_AVAILABLE);
    if (!ready) return requestAnimationFrame(check);
    if (gl.getParameter(ext.GPU_DISJOINT_EXT)) return console.log('verworfen');
    const ns = gl.getQueryParameter(q1, gl.QUERY_RESULT) - gl.getQueryParameter(q0, gl.QUERY_RESULT);
    console.log(`Clear dauerte ${ns} ns`);
  };
  requestAnimationFrame(check);
}
Clear dauerte 18432 ns

Unterstützung für Zeitstempel prüfen

const gl = document.createElement('canvas').getContext('webgl');
const ext = gl.getExtension('EXT_disjoint_timer_query');

// Manche Implementierungen haben einen 0-Bit-Zähler für Zeitstempel
const bits = ext ? ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.QUERY_COUNTER_BITS_EXT) : 0;
console.log(bits > 0 ? `Zeitstempel mit ${bits} Bit` : 'queryCounterEXT nicht nutzbar');
Zeitstempel mit 64 Bit

// Wichtig · Fallstricke

  • Verfügbarkeit: Wegen der Timing-Angriffe Spectre und GLitch wurde die Erweiterung in vielen Browsern zeitweise abgeschaltet. Firefox und Safari bieten sie in der Regel nicht an. Verlass dich deshalb nie darauf, dass sie vorhanden ist, und prüfe immer vorher mit getExtension().
  • Auch wenn die Erweiterung vorhanden ist, kann QUERY_COUNTER_BITS_EXT für TIMESTAMP_EXT den Wert 0 haben. Zeitstempel werden dann nicht unterstützt, auch wenn beginQueryEXT()/endQueryEXT() funktionieren.
  • Wenn du das Ergebnis im selben Frame liest, ist es nie verfügbar. Frag es frühestens im nächsten Frame ab, sonst blockiert oder verzögert sich die Pipeline.
  • Wird GPU_DISJOINT_EXT gemeldet, müssen alle offenen Messwerte verworfen werden.
  • Ist query gerade in einer aktiven beginQueryEXT()-Messung in Gebrauch, löst der Aufruf INVALID_OPERATION aus. Dasselbe gilt, wenn das Objekt zuvor schon mit einem anderen Ziel verwendet wurde.
  • Die Werte sind 64-Bit-Nanosekunden. Als JavaScript-number kann dabei Genauigkeit verloren gehen, für Differenzen im Frame-Bereich reicht sie aber aus.