Start · Sprachen · JavaScript · Referenz · GPUPipelineLayout

GPUPipelineLayout

Schnittstelle

Beschreibt, welche <code>GPUBindGroupLayout</code>s eine Render- oder Compute-Pipeline erwartet, und ordnet sie den Indizes <code>@group(n)</code> im Shader zu.

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

Signatur

interface GPUPipelineLayout

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);
Compute-Pipeline-Layout

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');
Fehler: … (die genaue Meldung hängt vom Browser ab)

// Wichtig · Fallstricke

  • Kein Konstruktor: new GPUPipelineLayout() wirft einen TypeError. Erzeuge Instanzen immer über device.createPipelineLayout().
  • „auto“ gegen explizit: Mit layout: "auto" gibt pipeline.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ägt createRenderPipeline() oder createComputePipeline() 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 Ereignis uncapturederror ab.
  • Grenzwerte: Beachte maxBindGroups, maxBindingsPerBindGroup sowie 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, dass requestAdapter() auch null liefern kann.