Start · Sprachen · JavaScript · Referenz · GPUBindGroup

GPUBindGroup

Schnittstelle

Fasst GPU-Ressourcen wie Puffer, Texturen und Sampler zu einer Gruppe zusammen, die nach einem <code>GPUBindGroupLayout</code> gemeinsam an eine Pipeline gebunden wird.

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

Signatur

interface GPUBindGroup

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

Typ
GPUBindGroup
Beschreibung
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);
true Verdoppeln-Daten

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

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);
Material: Holz

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);
Bind Group ungültig: ... (Meldung des Browsers, nennt das Label "Fehlerhafte Gruppe")