Start · Sprachen · JavaScript · Referenz · GPUDevice.createQuerySet()

GPUDevice.createQuerySet()

Methode

Erstellt ein <code>GPUQuerySet</code>, in dem Render- und Compute-Passes Ergebnisse von Okklusions- oder Zeitstempelabfragen ablegen können.

seit JavaScript WebGPU – Chrome/Edge 113+, Safari 26+, Firefox 141 Kategorie: webapi

Signatur

GPUDevice.prototype.createQuerySet(descriptor)

Beschreibung

Mit createQuerySet() legst du auf einem GPUDevice einen Satz von Abfrage-Slots an. Jeder Slot speichert ein 64-Bit-Ergebnis. Es gibt zwei Abfragetypen:

  • "occlusion": zählt, ob bzw. wie viele Fragmente eines Zeichenaufrufs den Tiefen- und Stencil-Test bestanden haben. So kannst du prüfen, ob ein Objekt sichtbar ist (Occlusion Culling).
  • "timestamp": speichert GPU-Zeitstempel in Nanosekunden am Anfang und Ende eines Passes. Damit misst du die Laufzeit auf der GPU. Dafür muss das Device mit dem Feature "timestamp-query" angefordert worden sein.

Die Ergebnisse lassen sich nicht direkt aus JavaScript lesen. Der übliche Ablauf: Du ordnest das QuerySet einem Pass zu (occlusionQuerySet bzw. timestampWrites im Pass-Deskriptor). Dann kopierst du die Werte mit GPUCommandEncoder.resolveQuerySet() in einen Puffer mit dem Flag QUERY_RESOLVE. Von dort kopierst du sie in einen mappbaren Puffer (MAP_READ) und liest sie als BigUint64Array aus.

Brauchst du ein QuerySet nicht mehr, gib seine GPU-Ressourcen mit querySet.destroy() frei.

Parameter

Name Typ Default Beschreibung
$descriptor Pflicht GPUQuerySetDescriptor Konfigurationsobjekt mit folgenden Eigenschaften.
$descriptor.type Pflicht "occlusion" | "timestamp" Art der Abfragen. "timestamp" setzt das Device-Feature "timestamp-query" voraus.
$descriptor.count Pflicht number Anzahl der Abfrage-Slots. Höchstens 4096.
$descriptor.label string "" Optionaler Name für Fehlermeldungen und Debugging-Werkzeuge.

Rückgabewert

Typ
GPUQuerySet
Beschreibung
Ein neues GPUQuerySet-Objekt. Es hat die schreibgeschützten Eigenschaften type, count und label sowie die Methode destroy().

Beispiele

Okklusions-QuerySet anlegen

const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();

// Ein Slot pro Objekt, dessen Sichtbarkeit geprüft werden soll
const occlusionSet = device.createQuerySet({
  label: 'Sichtbarkeitstest',
  type: 'occlusion',
  count: 32,
});

console.log(occlusionSet.type, occlusionSet.count);
occlusion 32

GPU-Laufzeit eines Compute-Passes messen

const adapter = await navigator.gpu.requestAdapter();
if (!adapter.features.has('timestamp-query')) {
  throw new Error('timestamp-query wird nicht unterstützt');
}
const device = await adapter.requestDevice({
  requiredFeatures: ['timestamp-query'],
});

const querySet = device.createQuerySet({ type: 'timestamp', count: 2 });

// Zielpuffer für resolveQuerySet: 2 × 8 Byte
const resolveBuffer = device.createBuffer({
  size: 16,
  usage: GPUBufferUsage.QUERY_RESOLVE | GPUBufferUsage.COPY_SRC,
});
const readBuffer = device.createBuffer({
  size: 16,
  usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
});

const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass({
  timestampWrites: {
    querySet,
    beginningOfPassWriteIndex: 0,
    endOfPassWriteIndex: 1,
  },
});
// ... pass.setPipeline(...); pass.dispatchWorkgroups(...);
pass.end();

encoder.resolveQuerySet(querySet, 0, 2, resolveBuffer, 0);
encoder.copyBufferToBuffer(resolveBuffer, 0, readBuffer, 0, 16);
device.queue.submit([encoder.finish()]);

await readBuffer.mapAsync(GPUMapMode.READ);
const [start, end] = new BigUint64Array(readBuffer.getMappedRange());
console.log(`GPU-Zeit: ${Number(end - start) / 1e6} ms`);
readBuffer.unmap();
querySet.destroy();
GPU-Zeit: 0.042 ms

Okklusionsabfrage in einem Render-Pass

// Voraussetzung: device, occlusionSet (count ≥ 1), colorView, depthView, pipeline
const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass({
  colorAttachments: [{ view: colorView, loadOp: 'clear', storeOp: 'store' }],
  depthStencilAttachment: {
    view: depthView,
    depthClearValue: 1,
    depthLoadOp: 'clear',
    depthStoreOp: 'store',
  },
  occlusionQuerySet: occlusionSet,
});
pass.setPipeline(pipeline);
pass.beginOcclusionQuery(0);
pass.draw(36); // Bounding-Box des Objekts
pass.endOcclusionQuery();
pass.end();
// Anschließend mit resolveQuerySet() auslesen: Wert 0 bedeutet "verdeckt"

// Wichtig · Fallstricke

  • Feature nötig: type: \"timestamp\" ohne das Feature \"timestamp-query\" führt zu einem Validierungsfehler. Ebenso ein count über 4096. Wie bei WebGPU üblich wird dabei keine Exception geworfen: Du erhältst ein ungültiges Objekt, und der Fehler wird über pushErrorScope()/popErrorScope() oder das uncapturederror-Event gemeldet.
  • Ungenaue Zeitstempel: Zum Schutz vor Timing-Angriffen runden Browser die Zeitstempel oft auf etwa 100 µs. Die Werte können auch nicht monoton sein, also end < start. Prüfe darauf, bevor du Differenzen bildest.
  • Bei resolveQuerySet() muss der Ziel-Offset ein Vielfaches von 256 sein. Der Puffer braucht das Flag GPUBufferUsage.QUERY_RESOLVE.
  • Die Ergebnisse sind 64-Bit-Ganzzahlen. Lies sie mit BigUint64Array und nicht mit Float32Array.
  • Das alte API writeTimestamp() auf Encodern wurde entfernt. Verwende stattdessen timestampWrites im Pass-Deskriptor.
  • WebGPU gibt es nur in sicheren Kontexten und noch nicht in allen Browsern. Prüfe vorher, ob navigator.gpu vorhanden ist.