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

GPUComputePassEncoder.setBindGroup()

Methode

Setzt die <code>GPUBindGroup</code>, die nachfolgende <code>dispatchWorkgroups()</code>-Aufrufe in einem Compute-Pass an einem bestimmten Gruppenindex verwenden.

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

Signatur

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

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();
Float32Array(4) [2, 4, 6, 8]

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()]);