Signatur
Beschreibung
GPUBindGroup ist ein Interface der WebGPU API. Ein Objekt dieses Typs bündelt konkrete Ressourcen, zum Beispiel GPUBuffer-Bereiche, GPUTextureViews, GPUSampler oder GPUExternalTexture, und ordnet sie den Binding-Nummern zu, die in einem GPUBindGroupLayout festgelegt sind. Die Shader greifen über @group(n) @binding(m) in WGSL auf diese Ressourcen zu.
Einen eigenen Konstruktor gibt es nicht. Instanzen entstehen immer über GPUDevice.createBindGroup(descriptor). Der Deskriptor enthält das Layout (layout), eine Liste von Einträgen (entries) mit binding und resource sowie optional ein label. Das Layout kann man selbst mit createBindGroupLayout() anlegen, oder man liest es mit pipeline.getBindGroupLayout(index) aus einer Pipeline mit layout: "auto" aus.
Beim Kodieren der Befehle bindet man die Gruppe mit passEncoder.setBindGroup(index, bindGroup) an einen Render- oder Compute-Pass. Es ist üblich, Ressourcen nach Änderungshäufigkeit auf mehrere Gruppen zu verteilen: Gruppe 0 für Daten pro Frame (Kamera), Gruppe 1 pro Material, Gruppe 2 pro Objekt. So muss man bei einem Wechsel nur die Gruppen neu binden, die sich tatsächlich geändert haben.
Das Interface hat nur eine eigene Eigenschaft: label, einen lesbaren Namen für Fehlermeldungen und Debugging-Werkzeuge. Eine Bind Group ist unveränderlich. Wenn eine andere Ressource gebunden werden soll, muss man eine neue Gruppe erzeugen. Der Inhalt der gebundenen Puffer lässt sich dagegen jederzeit mit queue.writeBuffer() ändern.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $descriptor.layout Pflicht | GPUBindGroupLayout | Das Layout, zu dem die Einträge passen müssen (Art der Ressource, Binding-Nummer, Sichtbarkeit). | |
| $descriptor.entries Pflicht | Array<GPUBindGroupEntry> | Liste von Objekten { binding, resource }. resource ist ein GPUBufferBinding ({ buffer, offset?, size? }), ein GPUSampler, eine GPUTextureView oder eine GPUExternalTexture. In neueren Implementierungen sind auch ein GPUBuffer oder eine GPUTexture direkt erlaubt. |
|
| $descriptor.label | string | "" | Optionaler Name, der in Fehlermeldungen und Debugging-Werkzeugen erscheint. Später über bindGroup.label lesbar und änderbar. |
Rückgabewert
GPUDevice.createBindGroup() gibt synchron ein neues GPUBindGroup-Objekt zurück. Ist der Deskriptor ungültig, erhält man trotzdem ein Objekt. Es ist dann aber intern ungültig, und der Fehler wird asynchron über die Fehlerbereiche (Error Scopes) oder das Ereignis uncapturederror gemeldet.Beispiele
Bind Group für einen Compute-Shader erzeugen
const adapter = await navigator.gpu?.requestAdapter();
if (!adapter) throw new Error('WebGPU nicht verfügbar');
const device = await adapter.requestDevice();
const module = device.createShaderModule({
code: `
@group(0) @binding(0) var<storage, read_write> daten: array<f32>;
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) id: vec3u) {
if (id.x < arrayLength(&daten)) {
daten[id.x] = daten[id.x] * 2.0;
}
}`
});
const pipeline = device.createComputePipeline({
layout: 'auto',
compute: { module, entryPoint: 'main' }
});
const eingabe = new Float32Array([1, 2, 3, 4]);
const buffer = device.createBuffer({
size: eingabe.byteLength,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST
});
device.queue.writeBuffer(buffer, 0, eingabe);
// Bind Group auf Basis des automatisch erzeugten Layouts
const bindGroup = device.createBindGroup({
label: 'Verdoppeln-Daten',
layout: pipeline.getBindGroupLayout(0),
entries: [{ binding: 0, resource: { buffer } }]
});
console.log(bindGroup instanceof GPUBindGroup, bindGroup.label);
Bind Group im Compute-Pass binden und ausführen
// Setzt device, pipeline, bindGroup und buffer aus Beispiel 1 voraus
const leseBuffer = device.createBuffer({
size: buffer.size,
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
});
const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup); // Index = @group(0)
pass.dispatchWorkgroups(1);
pass.end();
encoder.copyBufferToBuffer(buffer, 0, leseBuffer, 0, buffer.size);
device.queue.submit([encoder.finish()]);
await leseBuffer.mapAsync(GPUMapMode.READ);
console.log(Array.from(new Float32Array(leseBuffer.getMappedRange())));
leseBuffer.unmap();
Explizites Layout mit Uniform-Puffer, Sampler und Textur
// device ist ein bereits angefordertes GPUDevice
const layout = device.createBindGroupLayout({
label: 'Material-Layout',
entries: [
{ binding: 0, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: 'uniform' } },
{ binding: 1, visibility: GPUShaderStage.FRAGMENT, sampler: { type: 'filtering' } },
{ binding: 2, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'float' } }
]
});
const uniformBuffer = device.createBuffer({
size: 64, // z. B. eine mat4x4f
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
});
const sampler = device.createSampler({ magFilter: 'linear', minFilter: 'linear' });
const texture = device.createTexture({
size: [256, 256],
format: 'rgba8unorm',
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST
});
const materialGroup = device.createBindGroup({
label: 'Material: Holz',
layout,
entries: [
{ binding: 0, resource: { buffer: uniformBuffer } },
{ binding: 1, resource: sampler },
{ binding: 2, resource: texture.createView() }
]
});
// Später im Render-Pass: renderPass.setBindGroup(1, materialGroup);
console.log(materialGroup.label);
Validierungsfehler beim Erzeugen abfangen
device.pushErrorScope('validation');
const kaputt = device.createBindGroup({
label: 'Fehlerhafte Gruppe',
layout, // aus Beispiel 3, erwartet drei Einträge
entries: [{ binding: 0, resource: { buffer: uniformBuffer } }]
});
const fehler = await device.popErrorScope();
if (fehler) console.warn('Bind Group ungültig:', fehler.message);