Signatur
Beschreibung
GPUPipelineLayout ist ein Objekt der WebGPU-API. Es legt fest, welche Ressourcen-Schnittstellen eine Pipeline hat. Es besteht aus einer geordneten Liste von GPUBindGroupLayout-Objekten: Der Eintrag an Index n gehört zu @group(n) im WGSL-Shader. Der Konstruktor ist nicht direkt aufrufbar. Du erzeugst ein Pipeline-Layout über GPUDevice.createPipelineLayout(descriptor) und übergibst es dann als layout an createRenderPipeline() oder createComputePipeline().
Die Alternative ist layout: "auto". Dann leitet WebGPU das Layout aus dem Shader-Code ab. Das ist bequem, hat aber einen Nachteil: Automatisch erzeugte Bind-Group-Layouts gelten nur für genau die Pipeline, aus der sie stammen. Ein explizites Pipeline-Layout brauchst du, wenn mehrere Pipelines dieselben Bind Groups nutzen sollen, etwa eine gemeinsame Kamera- oder Szenen-Bind-Group an @group(0). Sonst müsstest du beim Wechsel der Pipeline alle Bind Groups neu anlegen.
Das Interface hat selbst nur die Eigenschaft label (aus dem Mixin GPUObjectBase). Sie hilft bei der Fehlersuche, weil Validierungsfehler und Entwicklerwerkzeuge das Objekt unter diesem Namen anzeigen. Das Layout ist unveränderlich. Brauchst du eine andere Zusammenstellung von Bind-Group-Layouts, legst du ein neues Pipeline-Layout an.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $descriptor.bindGroupLayouts Pflicht | Array<GPUBindGroupLayout | null> | Geordnete Liste der Bind-Group-Layouts. Index n entspricht @group(n) im Shader. Die Länge darf device.limits.maxBindGroups nicht überschreiten (mindestens 4 garantiert). Laut aktueller Spezifikation darf ein Eintrag auch null sein, wenn eine Gruppe leer bleibt. Das unterstützen noch nicht alle Browser. |
|
| $descriptor.label | string | "" | Optionaler Name des Objekts. Er erscheint in Fehlermeldungen und Entwicklerwerkzeugen. |
Beispiele
Pipeline-Layout erzeugen und für eine Compute-Pipeline nutzen
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
// Layout für @group(0): ein Storage-Buffer an @binding(0)
const bindGroupLayout = device.createBindGroupLayout({
label: 'Daten-Layout',
entries: [{
binding: 0,
visibility: GPUShaderStage.COMPUTE,
buffer: { type: 'storage' }
}]
});
const pipelineLayout = device.createPipelineLayout({
label: 'Compute-Pipeline-Layout',
bindGroupLayouts: [bindGroupLayout]
});
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: pipelineLayout,
compute: { module, entryPoint: 'main' }
});
console.log(pipelineLayout.label);
Gemeinsame Kamera-Bind-Group für mehrere Render-Pipelines
// Wie oben: device und format liegen bereits vor
const format = navigator.gpu.getPreferredCanvasFormat();
const kameraLayout = device.createBindGroupLayout({
entries: [{ binding: 0, visibility: GPUShaderStage.VERTEX, buffer: {} }]
});
const materialLayout = device.createBindGroupLayout({
entries: [{ binding: 0, visibility: GPUShaderStage.FRAGMENT, buffer: {} }]
});
// Beide Pipelines teilen dasselbe Layout -> Bind Groups sind austauschbar
const gemeinsamesLayout = device.createPipelineLayout({
label: 'Szene',
bindGroupLayouts: [kameraLayout, materialLayout] // @group(0), @group(1)
});
const shader = device.createShaderModule({ code: `
struct Kamera { viewProj: mat4x4f };
@group(0) @binding(0) var<uniform> kamera: Kamera;
@group(1) @binding(0) var<uniform> farbe: vec4f;
@vertex fn vs(@location(0) p: vec3f) -> @builtin(position) vec4f {
return kamera.viewProj * vec4f(p, 1.0);
}
@fragment fn fsVoll() -> @location(0) vec4f { return farbe; }
@fragment fn fsHalb() -> @location(0) vec4f { return farbe * 0.5; }`
});
const vertex = {
module: shader, entryPoint: 'vs',
buffers: [{ arrayStride: 12, attributes: [{ shaderLocation: 0, offset: 0, format: 'float32x3' }] }]
};
const pipelineVoll = device.createRenderPipeline({
layout: gemeinsamesLayout, vertex,
fragment: { module: shader, entryPoint: 'fsVoll', targets: [{ format }] }
});
const pipelineHalb = device.createRenderPipeline({
layout: gemeinsamesLayout, vertex,
fragment: { module: shader, entryPoint: 'fsHalb', targets: [{ format }] }
});
// Eine Kamera-Bind-Group funktioniert jetzt mit beiden Pipelines
const kameraBuffer = device.createBuffer({ size: 64, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
const kameraGroup = device.createBindGroup({
layout: kameraLayout,
entries: [{ binding: 0, resource: { buffer: kameraBuffer } }]
});
Validierungsfehler abfangen
device.pushErrorScope('validation');
const layout = device.createPipelineLayout({
label: 'Zu viele Gruppen',
// Überschreitet absichtlich maxBindGroups
bindGroupLayouts: Array.from(
{ length: device.limits.maxBindGroups + 1 },
() => device.createBindGroupLayout({ entries: [] })
)
});
const fehler = await device.popErrorScope();
console.log(fehler ? `Fehler: ${fehler.message}` : 'OK');
// Wichtig · Fallstricke
- Kein Konstruktor:
new GPUPipelineLayout()wirft einenTypeError. Erzeuge Instanzen immer überdevice.createPipelineLayout(). - „auto“ gegen explizit: Mit
layout: "auto"gibtpipeline.getBindGroupLayout(n)Layouts zurück, die nur mit genau dieser Pipeline kompatibel sind. Sollen mehrere Pipelines Bind Groups teilen, brauchst du ein explizites Pipeline-Layout. - Passung zum Shader: Jede Ressource, die der Shader verwendet, muss im passenden Bind-Group-Layout stehen, mit korrektem Typ und passender
visibility. Sonst schlägtcreateRenderPipeline()odercreateComputePipeline()mit einem Validierungsfehler fehl. - Fehler kommen asynchron: WebGPU wirft bei Validierungsfehlern keine Ausnahme. Es entsteht ein ungültiges Objekt. Fange Fehler mit
pushErrorScope()/popErrorScope()oder dem Ereignisuncapturederrorab. - Grenzwerte: Beachte
maxBindGroups,maxBindingsPerBindGroupsowie die Grenzen für dynamische Offsets (maxDynamicUniformBuffersPerPipelineLayout,maxDynamicStorageBuffersPerPipelineLayout). Diese gelten für das gesamte Pipeline-Layout. - Kompatibilität: WebGPU steht nur in sicheren Kontexten zur Verfügung. Die Browser-Unterstützung wächst noch. Prüfe vorher auf
navigator.gpu, und rechne damit, dassrequestAdapter()auchnullliefern kann.