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

EXT_disjoint_timer_query.beginQueryEXT()

Methode

Startet eine GPU-Timerabfrage in WebGL 1, mit der sich messen lässt, wie lange die GPU für die folgenden Zeichenbefehle braucht.

seit JavaScript WebGL 1 Extension; eingeschränkte Browser-Unterstü Kategorie: webapi

Signatur

EXT_disjoint_timer_query.beginQueryEXT(target, query)

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

Typ
void
Beschreibung
Kein 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');
}
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);
GPU-Zeit: 0.042 ms

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);
Frame-GPU-Zeit: 1.37 ms

// Wichtig · Fallstricke

  • Nur eine aktive Abfrage: Pro Ziel darf immer nur eine TIME_ELAPSED_EXT-Abfrage laufen. Ein zweites beginQueryEXT() vor dem zugehörigen endQueryEXT() erzeugt INVALID_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 false für QUERY_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() auf null prüfen.
  • In WebGL 2 ist diese Erweiterung nicht verfügbar – dort EXT_disjoint_timer_query_webgl2 mit gl.beginQuery() verwenden.
  • Nicht mehr benötigte Abfrageobjekte mit deleteQueryEXT() freigeben, um GPU-Ressourcen nicht zu verschwenden.