Signatur
Beschreibung
createBindGroupLayout() beschreibt die Schnittstelle zwischen JavaScript-Ressourcen und einem WGSL-Shader. Ein Layout legt fest, welche Ressource unter welcher @binding(n)-Nummer liegt, welcher Art sie ist (Uniform-Buffer, Storage-Buffer, Sampler, Textur, Storage-Textur oder externe Textur) und in welchen Shader-Stufen (Vertex, Fragment, Compute) sie sichtbar ist.
Das Layout selbst enthält keine Daten. Es dient als Vorlage für GPUDevice.createBindGroup(), mit dem die konkreten Ressourcen gebunden werden, und für GPUDevice.createPipelineLayout(), das mehrere Bind-Group-Layouts (eines je @group(n)) zu einem Pipeline-Layout zusammenfasst.
Ein explizites Layout lohnt sich, wenn mehrere Pipelines dieselben Bind Groups nutzen sollen, zum Beispiel Kamera- oder Szenen-Uniforms, die in allen Render-Pipelines unter @group(0) liegen. Mit layout: "auto" erzeugte Pipelines haben dagegen eigene, nicht austauschbare Layouts.
Jeder Eintrag in entries braucht eine eindeutige binding-Nummer, eine visibility-Bitmaske aus GPUShaderStage und genau eines der Felder buffer, sampler, texture, storageTexture oder externalTexture.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $descriptor Pflicht | GPUBindGroupLayoutDescriptor | Objekt, das das Layout beschreibt. | |
| $descriptor.entries Pflicht | Array<GPUBindGroupLayoutEntry> | Liste der Einträge. Jeder Eintrag enthält binding (number, entspricht @binding(n) in WGSL), visibility (Bitmaske aus GPUShaderStage.VERTEX, FRAGMENT und COMPUTE) und genau einen Ressourcentyp: buffer, sampler, texture, storageTexture oder externalTexture. |
|
| $entries[].buffer | { type?: 'uniform'|'storage'|'read-only-storage', hasDynamicOffset?: boolean, minBindingSize?: number } | { type: 'uniform', hasDynamicOffset: false, minBindingSize: 0 } | Bindet einen Buffer. Mit hasDynamicOffset gibt man den Offset erst bei setBindGroup() an. |
| $entries[].sampler | { type?: 'filtering'|'non-filtering'|'comparison' } | { type: 'filtering' } | Bindet einen GPUSampler. |
| $entries[].texture | { sampleType?: 'float'|'unfilterable-float'|'depth'|'sint'|'uint', viewDimension?: string, multisampled?: boolean } | { sampleType: 'float', viewDimension: '2d', multisampled: false } | Bindet eine Textur-Ansicht, aus der der Shader liest. |
| $entries[].storageTexture | { access?: 'write-only'|'read-only'|'read-write', format: GPUTextureFormat, viewDimension?: string } | { access: 'write-only', viewDimension: '2d' } | Bindet eine Storage-Textur. format muss angegeben werden. |
| $entries[].externalTexture | {} | Bindet eine GPUExternalTexture, etwa ein Videobild. |
|
| $descriptor.label | string | "" | Name zur Fehlersuche. Er erscheint in Fehlermeldungen und in Debug-Werkzeugen. |
Rückgabewert
Beispiele
Uniform-Buffer, Sampler und Textur für eine Render-Pipeline
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
// Passt zu WGSL:
// @group(0) @binding(0) var<uniform> camera: Camera;
// @group(0) @binding(1) var mySampler: sampler;
// @group(0) @binding(2) var myTexture: texture_2d<f32>;
const bindGroupLayout = 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' } }
]
});
console.log(bindGroupLayout.label);
Compute-Shader mit Storage-Buffern: Layout, Pipeline und Bind Group
const device = await (await navigator.gpu.requestAdapter()).requestDevice();
const layout = device.createBindGroupLayout({
entries: [
{ binding: 0, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'read-only-storage' } },
{ binding: 1, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'storage' } }
]
});
const module = device.createShaderModule({ code: `
@group(0) @binding(0) var<storage, read> input: array<f32>;
@group(0) @binding(1) var<storage, read_write> output: array<f32>;
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) id: vec3u) {
if (id.x < arrayLength(&input)) { output[id.x] = input[id.x] * 2.0; }
}` });
const pipeline = device.createComputePipeline({
layout: device.createPipelineLayout({ bindGroupLayouts: [layout] }),
compute: { module, entryPoint: 'main' }
});
const size = 1024 * 4;
const inputBuf = device.createBuffer({ size, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });
const outputBuf = device.createBuffer({ size, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC });
const bindGroup = device.createBindGroup({
layout,
entries: [
{ binding: 0, resource: { buffer: inputBuf } },
{ binding: 1, resource: { buffer: outputBuf } }
]
});
console.log('Bind Group bereit:', bindGroup instanceof GPUBindGroup);
Validierungsfehler gezielt abfangen
const device = await (await navigator.gpu.requestAdapter()).requestDevice();
device.pushErrorScope('validation');
device.createBindGroupLayout({
entries: [
// Fehler: binding 0 ist doppelt vergeben
{ binding: 0, visibility: GPUShaderStage.FRAGMENT, sampler: {} },
{ binding: 0, visibility: GPUShaderStage.FRAGMENT, texture: {} }
]
});
const error = await device.popErrorScope();
console.log(error ? `Fehler: ${error.message}` : 'Layout gültig');