Signatur
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 einemUint32Arraysamt 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
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);
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();