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

GPURenderBundleEncoder.setBindGroup()

Methode

Legt die <code>GPUBindGroup</code> fest, die für nachfolgende Zeichenbefehle im Render-Bundle an einem bestimmten Gruppen-Index verwendet wird.

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

Signatur

GPURenderBundleEncoder.prototype.setBindGroup(index, bindGroup, dynamicOffsets)

Beschreibung

Mit setBindGroup() ordnest du beim Aufzeichnen eines Render-Bundles einer Bindungs-Gruppe (@group(n) im WGSL-Shader) eine konkrete GPUBindGroup zu. Diese enthält die Ressourcen wie Uniform-Buffer, Storage-Buffer, Texturen und Sampler, auf die der Shader bei den folgenden draw()- bzw. drawIndexed()-Aufrufen zugreift.

Render-Bundles (GPURenderBundle) sind vorab aufgezeichnete Befehlsfolgen, die du mit GPURenderPassEncoder.executeBundles() in beliebig vielen Render-Passes wiederverwenden kannst. Das lohnt sich vor allem bei statischen Szenen mit vielen Zeichenaufrufen, weil der JavaScript-Overhead pro Frame stark sinkt. Die mit setBindGroup() gesetzten Bindungen werden dabei mit ins Bundle aufgenommen.

Die Methode gibt es in zwei Überladungen:

  • setBindGroup(index, bindGroup, dynamicOffsets) – mit einem gewöhnlichen Array (oder Iterable) dynamischer Offsets.
  • setBindGroup(index, bindGroup, dynamicOffsetsData, dynamicOffsetsDataStart, dynamicOffsetsDataLength) – mit einem Uint32Array samt Start und Länge. So vermeidest du beim Aufzeichnen vieler Befehle zusätzliche Speicherzuweisungen.

Wichtig: Beim Ausführen eines Bundles wird der Zustand des Render-Passes vorher und nachher zurückgesetzt. Bind-Gruppen aus dem umgebenden Pass gelten nicht im Bundle, und die Bindungen aus dem Bundle gelten nicht mehr danach. Deshalb musst du im Bundle-Encoder alle benötigten Gruppen selbst setzen.

Parameter

Name Typ Default Beschreibung
$index Pflicht number Gruppen-Index, der @group(index) im Shader bzw. dem Index in GPUPipelineLayout.bindGroupLayouts entspricht. Muss kleiner als device.limits.maxBindGroups sein (Standardwert 4).
$bindGroup Pflicht GPUBindGroup | null Die zu setzende Bind-Gruppe. Ihr Layout muss zum Layout der Pipeline an diesem Index passen. Mit null wird die Bindung an diesem Index aufgehoben (neuere Implementierungen).
$dynamicOffsets Iterable<number> [] Byte-Offsets für Einträge mit hasDynamicOffset: true, in der Reihenfolge ihrer binding-Nummern. Jeder Offset muss ein Vielfaches von minUniformBufferOffsetAlignment bzw. minStorageBufferOffsetAlignment (meist 256) sein.
$dynamicOffsetsData Uint32Array Alternative Überladung: Typed Array, das die dynamischen Offsets enthält.
$dynamicOffsetsDataStart number Index des ersten Elements in dynamicOffsetsData, das verwendet wird (nur zusammen mit dynamicOffsetsData).
$dynamicOffsetsDataLength number Anzahl der Offsets, die ab dynamicOffsetsDataStart gelesen werden.

Rückgabewert

Typ
undefined
Beschreibung
Kein Rückgabewert. Der Befehl wird in das Render-Bundle aufgenommen.

Beispiele

Statisches Render-Bundle mit einer Bind-Gruppe aufzeichnen

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

const module = device.createShaderModule({
  code: `
    @group(0) @binding(0) var<uniform> farbe : vec4f;
    @vertex fn vs(@builtin(vertex_index) i : u32) -> @builtin(position) vec4f {
      var p = array(vec2f(0, 0.5), vec2f(-0.5, -0.5), vec2f(0.5, -0.5));
      return vec4f(p[i], 0, 1);
    }
    @fragment fn fs() -> @location(0) vec4f { return farbe; }
  `
});

const pipeline = device.createRenderPipeline({
  layout: 'auto',
  vertex: { module, entryPoint: 'vs' },
  fragment: { module, entryPoint: 'fs', targets: [{ format }] }
});

const uniformBuffer = device.createBuffer({
  size: 16,
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
});
device.queue.writeBuffer(uniformBuffer, 0, new Float32Array([1, 0.4, 0, 1]));

const bindGroup = device.createBindGroup({
  layout: pipeline.getBindGroupLayout(0),
  entries: [{ binding: 0, resource: { buffer: uniformBuffer } }]
});

// Bundle einmalig aufzeichnen
const bundleEncoder = device.createRenderBundleEncoder({ colorFormats: [format] });
bundleEncoder.setPipeline(pipeline);
bundleEncoder.setBindGroup(0, bindGroup);
bundleEncoder.draw(3);
const bundle = bundleEncoder.finish();

// Später in jedem Frame: pass.executeBundles([bundle]);
console.log(bundle instanceof GPURenderBundle);
true

Viele Objekte mit dynamischen Offsets in einem Bundle

// Annahme: device, pipeline und format existieren; die Pipeline verwendet ein
// explizites Layout mit hasDynamicOffset: true für @group(0) @binding(0).
const OBJEKTE = 100;
const STRIDE = 256; // minUniformBufferOffsetAlignment

const objektBuffer = device.createBuffer({
  size: OBJEKTE * STRIDE,
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
});

const bindGroup = device.createBindGroup({
  layout: bindGroupLayout, // mit hasDynamicOffset: true erstellt
  entries: [{ binding: 0, resource: { buffer: objektBuffer, size: 64 } }]
});

const encoder = device.createRenderBundleEncoder({ colorFormats: [format] });
encoder.setPipeline(pipeline);
for (let i = 0; i < OBJEKTE; i++) {
  // Gleiche Bind-Gruppe, anderer Ausschnitt des Buffers pro Objekt
  encoder.setBindGroup(0, bindGroup, [i * STRIDE]);
  encoder.draw(3);
}
const bundle = encoder.finish({ label: 'szene' });

Überladung mit Uint32Array ohne zusätzliche Zuweisungen

// Offsets für alle Objekte einmalig vorbereiten
const offsets = new Uint32Array(OBJEKTE);
offsets.forEach((_, i) => { offsets[i] = i * 256; });

const encoder = device.createRenderBundleEncoder({ colorFormats: [format] });
encoder.setPipeline(pipeline);
for (let i = 0; i < OBJEKTE; i++) {
  // index, bindGroup, data, start, länge
  encoder.setBindGroup(0, bindGroup, offsets, i, 1);
  encoder.draw(3);
}
const bundle = encoder.finish();