Start · Sprachen · JavaScript · Referenz · GPURenderBundleEncoder.drawIndexedIndirect()

GPURenderBundleEncoder.drawIndexedIndirect()

Methode

Nimmt einen indizierten Zeichenbefehl in ein Render-Bundle auf, dessen Parameter (Indexanzahl, Instanzen, Offsets) zur Laufzeit aus einem <code>GPUBuffer</code> gelesen werden.

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

Signatur

GPURenderBundleEncoder.prototype.drawIndexedIndirect(indirectBuffer, indirectOffset)

Beschreibung

drawIndexedIndirect() zeichnet indizierte Primitive, wobei die Zeichenparameter nicht als JavaScript-Argumente übergeben werden, sondern erst bei der Ausführung auf der GPU aus einem Puffer gelesen werden. Ab indirectOffset müssen dort fünf 32-Bit-Werte liegen: indexCount, instanceCount, firstIndex, baseVertex (vorzeichenbehaftet, Int32) und firstInstance – zusammen 20 Byte.

Die Methode ist dann nützlich, wenn die GPU selbst entscheidet, wie viel gezeichnet wird, z. B. nach Frustum- oder Occlusion-Culling in einem Compute-Shader, bei GPU-gesteuerter LOD-Auswahl oder bei Partikelsystemen. So bleiben die Daten vollständig auf der GPU, und es ist kein Rücklesen zur CPU nötig.

Auf einem GPURenderBundleEncoder wird der Befehl nur aufgezeichnet. Das mit finish() erzeugte GPURenderBundle kann danach in beliebig vielen Render-Passes mit GPURenderPassEncoder.executeBundles() wiederverwendet werden. Da der Puffer erst beim Ausführen gelesen wird, übernimmt jede Ausführung des Bundles automatisch die aktuellen Pufferwerte. Das passt gut zu Render-Bundles, deren Befehle ja unveränderlich sind.

Vorher müssen eine Render-Pipeline (setPipeline()), ein Indexpuffer (setIndexBuffer()) sowie die benötigten Vertex-Puffer und Bind-Groups gesetzt sein.

Parameter

Name Typ Default Beschreibung
$indirectBuffer Pflicht GPUBuffer Puffer mit den Zeichenparametern. Er muss mit dem Nutzungsflag GPUBufferUsage.INDIRECT erstellt worden sein.
$indirectOffset Pflicht number Byte-Offset im Puffer, an dem die fünf 32-Bit-Werte beginnen. Muss ein Vielfaches von 4 sein, und indirectOffset + 20 darf die Puffergröße nicht überschreiten.

Rückgabewert

Typ
undefined
Beschreibung
Kein Rückgabewert. Der Befehl wird in das Bundle aufgenommen; Validierungsfehler zeigen sich erst bei finish() als ungültiges Bundle bzw. als Fehler auf dem Gerät.

Beispiele

Indirekten Puffer anlegen und Bundle aufzeichnen

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

// Parameter: indexCount, instanceCount, firstIndex, baseVertex, firstInstance
const indirectBuffer = device.createBuffer({
  size: 20,
  usage: GPUBufferUsage.INDIRECT | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(indirectBuffer, 0, new Uint32Array([6, 1, 0, 0, 0]));

// pipeline, vertexBuffer und indexBuffer wurden vorher erstellt
const bundleEncoder = device.createRenderBundleEncoder({
  colorFormats: [format],
});
bundleEncoder.setPipeline(pipeline);
bundleEncoder.setVertexBuffer(0, vertexBuffer);
bundleEncoder.setIndexBuffer(indexBuffer, 'uint16');
bundleEncoder.drawIndexedIndirect(indirectBuffer, 0);
const bundle = bundleEncoder.finish();

Bundle in jedem Frame ausführen

const frame = () => {
  const encoder = device.createCommandEncoder();
  const pass = encoder.beginRenderPass({
    colorAttachments: [{
      view: context.getCurrentTexture().createView(),
      loadOp: 'clear',
      clearValue: { r: 0, g: 0, b: 0, a: 1 },
      storeOp: 'store',
    }],
  });
  pass.executeBundles([bundle]); // liest indirectBuffer bei jeder Ausführung neu
  pass.end();
  device.queue.submit([encoder.finish()]);
  requestAnimationFrame(frame);
};
requestAnimationFrame(frame);

Instanzanzahl per Compute-Shader bestimmen (GPU-Culling)

// Der Puffer darf zusätzlich als Storage-Puffer beschrieben werden
const drawArgs = device.createBuffer({
  size: 20,
  usage: GPUBufferUsage.INDIRECT | GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
});
// indexCount = 36 (Würfel), instanceCount wird vom Compute-Shader gesetzt
device.queue.writeBuffer(drawArgs, 0, new Uint32Array([36, 0, 0, 0, 0]));

// WGSL-Ausschnitt im Culling-Shader:
// @group(0) @binding(0) var<storage, read_write> args: array<atomic<u32>, 5>;
// if (visible) { atomicAdd(&args[1], 1u); }

const enc = device.createRenderBundleEncoder({ colorFormats: [format] });
enc.setPipeline(pipeline);
enc.setVertexBuffer(0, cubeVertices);
enc.setIndexBuffer(cubeIndices, 'uint16');
enc.drawIndexedIndirect(drawArgs, 0);
const cullBundle = enc.finish();
// Pro Frame: erst Compute-Pass (Culling), dann executeBundles([cullBundle])

// Wichtig · Fallstricke

  • Ohne GPUBufferUsage.INDIRECT ist der Aufruf ungültig. Dann wird das gesamte Bundle ungültig, und es gibt einen Validierungsfehler auf dem GPUDevice (abfangbar mit pushErrorScope('validation')).
  • indirectOffset muss durch 4 teilbar sein. Wenn du mehrere Zeichenbefehle in einem Puffer ablegst, nutze einen Abstand von 20 Byte pro Eintrag.
  • baseVertex ist vorzeichenbehaftet (i32). Negative Werte schreibst du am besten über eine Int32Array-Sicht auf denselben ArrayBuffer.
  • firstInstance ungleich 0 erfordert das Feature "indirect-first-instance" beim Anfordern des Geräts. Ohne dieses Feature wird der Zeichenbefehl stillschweigend verworfen.
  • Liegen die gelesenen Indizes außerhalb des gebundenen Indexpuffers, wird dieser Zeichenbefehl nicht ausgeführt. Es gibt dafür keinen JavaScript-Fehler.
  • Ein Bundle übernimmt keinen Zustand aus dem Render-Pass. Pipeline, Puffer und Bind-Groups müssen deshalb im Bundle selbst gesetzt werden.
  • Browser-Unterstützung: WebGPU gibt es nur in sicheren Kontexten. In Firefox und Safari ist die Unterstützung noch neu bzw. plattformabhängig. Prüfe vorher, ob navigator.gpu vorhanden ist.