Signatur
Beschreibung
setBindGroup() legt fest, welche Ressourcen (Puffer, Texturen, Sampler) ein Compute-Shader beim nächsten dispatchWorkgroups() bzw. dispatchWorkgroupsIndirect() sieht. Der Parameter index entspricht dem @group(n)-Attribut im WGSL-Shader, die Einträge der Bind Group entsprechen den @binding(m)-Deklarationen.
Die Bindung bleibt für alle folgenden Dispatch-Befehle innerhalb desselben Compute-Passes gültig, bis sie durch einen weiteren setBindGroup()-Aufruf am selben Index ersetzt wird. So lassen sich z. B. selten wechselnde Daten (Gruppe 0) und häufig wechselnde Daten (Gruppe 1) getrennt verwalten, um den Aufwand beim Umschalten gering zu halten.
Enthält das zugehörige GPUBindGroupLayout Puffer-Einträge mit hasDynamicOffset: true, müssen über dynamicOffsets passende Byte-Offsets übergeben werden. Damit kann ein einziger großer Puffer in mehrere Abschnitte aufgeteilt und pro Dispatch ein anderer Abschnitt adressiert werden, ohne neue Bind Groups zu erzeugen. Alternativ gibt es eine Überladung mit Uint32Array plus Start und Länge, die Speicherallokationen im Hot-Path vermeidet.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $index Pflicht | number | Gruppenindex, entspricht @group(index) im WGSL-Shader. Muss kleiner als device.limits.maxBindGroups sein (mindestens 4). |
|
| $bindGroup Pflicht | GPUBindGroup | null | Die zu bindende Bind Group. null entfernt eine zuvor gesetzte Bindung (neuere Implementierungen). |
|
| $dynamicOffsets | Array<number> | Iterable<number> | [] | Byte-Offsets für jeden Eintrag mit hasDynamicOffset: true, in der Reihenfolge der binding-Nummern. Jeder Offset muss ein Vielfaches von minUniformBufferOffsetAlignment bzw. minStorageBufferOffsetAlignment (meist 256) sein. |
| $dynamicOffsetsData | Uint32Array | Alternative Überladung: typisiertes Array mit den Offsets. | |
| $dynamicOffsetsDataStart | number | Nur bei Überladung mit Uint32Array: Startindex (in Elementen) innerhalb von dynamicOffsetsData. |
|
| $dynamicOffsetsDataLength | number | Nur bei Überladung mit Uint32Array: Anzahl der zu lesenden Offsets. |
Beispiele
Einfacher Compute-Pass: Array-Werte verdoppeln
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const shader = device.createShaderModule({
code: `
@group(0) @binding(0) var<storage, read_write> data: array<f32>;
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) id: vec3u) {
if (id.x < arrayLength(&data)) { data[id.x] = data[id.x] * 2.0; }
}`
});
const pipeline = device.createComputePipeline({
layout: 'auto',
compute: { module: shader, entryPoint: 'main' }
});
const input = new Float32Array([1, 2, 3, 4]);
const storage = device.createBuffer({
size: input.byteLength,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST
});
device.queue.writeBuffer(storage, 0, input);
const readback = device.createBuffer({
size: input.byteLength,
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
});
const bindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [{ binding: 0, resource: { buffer: storage } }]
});
const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup); // entspricht @group(0)
pass.dispatchWorkgroups(Math.ceil(input.length / 64));
pass.end();
encoder.copyBufferToBuffer(storage, 0, readback, 0, input.byteLength);
device.queue.submit([encoder.finish()]);
await readback.mapAsync(GPUMapMode.READ);
console.log(new Float32Array(readback.getMappedRange()));
readback.unmap();
Mehrere Gruppen: feste Parameter und wechselnde Daten
// Shader: @group(0) = Parameter (Uniform), @group(1) = Datenpuffer
// Annahme: device, pipeline, paramsGroup und datasets (Array von GPUBindGroup) existieren
const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, paramsGroup); // bleibt für alle Dispatches gesetzt
for (const dataGroup of datasets) {
pass.setBindGroup(1, dataGroup); // nur Gruppe 1 wechselt
pass.dispatchWorkgroups(16);
}
pass.end();
device.queue.submit([encoder.finish()]);
Dynamische Offsets: ein Uniform-Puffer für viele Dispatches
const ALIGN = device.limits.minUniformBufferOffsetAlignment; // meist 256
const jobs = 4;
const uniformBuffer = device.createBuffer({
size: ALIGN * jobs,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
});
for (let i = 0; i < jobs; i++) {
device.queue.writeBuffer(uniformBuffer, i * ALIGN, new Float32Array([i * 0.5, 0, 0, 0]));
}
const layout = device.createBindGroupLayout({
entries: [{
binding: 0,
visibility: GPUShaderStage.COMPUTE,
buffer: { type: 'uniform', hasDynamicOffset: true }
}]
});
const bindGroup = device.createBindGroup({
layout,
entries: [{ binding: 0, resource: { buffer: uniformBuffer, size: 16 } }]
});
// pipeline wurde mit device.createPipelineLayout({ bindGroupLayouts: [layout] }) erstellt
const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
const offsets = new Uint32Array(jobs).map((_, i) => i * ALIGN);
for (let i = 0; i < jobs; i++) {
// Überladung mit Uint32Array, Start und Länge – keine neue Array-Allokation
pass.setBindGroup(0, bindGroup, offsets, i, 1);
pass.dispatchWorkgroups(8);
}
pass.end();
device.queue.submit([encoder.finish()]);