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

GPURenderBundleEncoder.drawIndexed()

Methode

Nimmt einen indizierten Zeichenbefehl in ein Render-Bundle auf. Dieser nutzt die über <code>setVertexBuffer()</code> und <code>setIndexBuffer()</code> gesetzten Puffer.

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

Signatur

GPURenderBundleEncoder.prototype.drawIndexed(indexCount, instanceCount, firstIndex, baseVertex, firstInstance)

Beschreibung

drawIndexed() schreibt einen indizierten Zeichenbefehl in einen GPURenderBundleEncoder. Die Methode zeichnet noch nichts. Sie zeichnet den Befehl nur auf. Mit finish() wird daraus ein GPURenderBundle, das man mit GPURenderPassEncoder.executeBundles() in einem oder mehreren Render-Passes beliebig oft abspielen kann.

Beim indizierten Zeichnen liest die GPU Indizes aus dem Indexpuffer, der mit setIndexBuffer() gesetzt wurde. Über diese Indizes greift sie auf die Vertizes in den Vertexpuffern zu. So lassen sich gemeinsame Eckpunkte wiederverwenden. Ein Quadrat braucht dann zum Beispiel nur 4 Vertizes und 6 Indizes statt 6 Vertizes. Das spart Speicher und Arbeit im Vertex-Shader.

Render-Bundles eignen sich für statische Szenenteile, die jedes Frame gleich gezeichnet werden. Dazu gehören etwa Gelände, Gebäude oder UI-Geometrie. Der Aufwand für die Befehlsaufzeichnung in JavaScript fällt dann nur einmal an. Vor drawIndexed() müssen im Encoder eine Pipeline (setPipeline()), ein Indexpuffer, die benötigten Vertexpuffer und die Bind-Groups gesetzt sein.

Parameter

Name Typ Default Beschreibung
$indexCount Pflicht number Anzahl der Indizes, die aus dem Indexpuffer gelesen und gezeichnet werden.
$instanceCount number 1 Anzahl der Instanzen, die gezeichnet werden (Instancing).
$firstIndex number 0 Versatz in Indizes (nicht in Bytes) im Indexpuffer, ab dem gelesen wird.
$baseVertex number 0 Wert, der zu jedem Index addiert wird, bevor der Vertexpuffer gelesen wird. Er darf negativ sein.
$firstInstance number 0 Erste Instanz-ID (@builtin(instance_index)). Ein Wert ungleich 0 erfordert das Feature "indirect-first-instance" nur bei indirekten Aufrufen, hier ist er immer erlaubt.

Rückgabewert

Typ
undefined
Beschreibung
Kein Rückgabewert. Der Befehl wird nur im Encoder aufgezeichnet.

Beispiele

Quadrat aus zwei Dreiecken in ein Render-Bundle aufnehmen

// Voraussetzung: device, pipeline, vertexBuffer (4 Vertizes) und bindGroup existieren bereits
const indices = new Uint16Array([0, 1, 2, 2, 1, 3]);
const indexBuffer = device.createBuffer({
  size: indices.byteLength,
  usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(indexBuffer, 0, indices);

const bundleEncoder = device.createRenderBundleEncoder({
  colorFormats: [navigator.gpu.getPreferredCanvasFormat()],
});
bundleEncoder.setPipeline(pipeline);
bundleEncoder.setBindGroup(0, bindGroup);
bundleEncoder.setVertexBuffer(0, vertexBuffer);
bundleEncoder.setIndexBuffer(indexBuffer, 'uint16');
bundleEncoder.drawIndexed(indices.length); // 6 Indizes, 1 Instanz

const quadBundle = bundleEncoder.finish();
console.log(quadBundle instanceof GPURenderBundle);
true

Bundle in jedem Frame wiederverwenden

const frame = () => {
  const commandEncoder = device.createCommandEncoder();
  const pass = commandEncoder.beginRenderPass({
    colorAttachments: [{
      view: context.getCurrentTexture().createView(),
      loadOp: 'clear',
      clearValue: { r: 0.1, g: 0.1, b: 0.1, a: 1 },
      storeOp: 'store',
    }],
  });
  // Aufgezeichnete drawIndexed()-Befehle abspielen
  pass.executeBundles([quadBundle]);
  pass.end();
  device.queue.submit([commandEncoder.finish()]);
  requestAnimationFrame(frame);
};
requestAnimationFrame(frame);

Mehrere Meshes aus einem gemeinsamen Puffer mit Instancing

// Ein gemeinsamer Vertex-/Indexpuffer enthält zwei Meshes:
// Mesh A: 36 Indizes ab Index 0, Vertizes ab 0
// Mesh B: 24 Indizes ab Index 36, Vertizes ab 24
bundleEncoder.setPipeline(pipeline);
bundleEncoder.setBindGroup(0, sceneBindGroup);
bundleEncoder.setVertexBuffer(0, sharedVertexBuffer);
bundleEncoder.setVertexBuffer(1, instanceBuffer); // stepMode: 'instance'
bundleEncoder.setIndexBuffer(sharedIndexBuffer, 'uint32');

// 100 Würfel (Mesh A)
bundleEncoder.drawIndexed(36, 100, 0, 0, 0);
// 50 Instanzen von Mesh B, Instanzdaten ab Eintrag 100
bundleEncoder.drawIndexed(24, 50, 36, 24, 100);

const sceneBundle = bundleEncoder.finish();

// Wichtig · Fallstricke

  • Validierung ist verzögert: Fehlt ein Index- oder Vertexpuffer, ist keine Pipeline gesetzt oder liegt firstIndex + indexCount außerhalb des Indexpuffers, wirft drawIndexed() keine Exception. Der Encoder wird ungültig, und der Fehler erscheint erst bei finish() als Validierungsfehler. Man fängt ihn mit device.pushErrorScope('validation') oder über das Ereignis uncapturederror ab.
  • firstIndex wird in Indizes gezählt, nicht in Bytes. Der Byte-Versatz aus setIndexBuffer(buffer, format, offset) kommt noch dazu.
  • Das Indexformat ('uint16' oder 'uint32') muss zu den Daten passen. Bei Topologien mit Strips ('triangle-strip', 'line-strip') muss primitive.stripIndexFormat der Pipeline mit dem Format des Indexpuffers übereinstimmen.
  • Die Formate im Deskriptor von createRenderBundleEncoder() (colorFormats, depthStencilFormat, sampleCount) müssen zum Render-Pass passen, in dem das Bundle ausgeführt wird.
  • Nach executeBundles() ist der Zustand des Render-Passes (Pipeline, Puffer, Bind-Groups) zurückgesetzt und muss für weitere direkte Zeichenbefehle neu gesetzt werden.
  • Kompatibilität: WebGPU gibt es nur in sicheren Kontexten und nicht in allen Browsern bzw. auf allen Plattformen. Prüfe vorher mit if (!navigator.gpu) und halte eine Ausweichlösung bereit, etwa WebGL.