Start · Sprachen · JavaScript · Referenz · GPURenderPipeline

GPURenderPipeline

Schnittstelle

Eine unveränderliche WebGPU-Render-Pipeline, die Vertex- und Fragment-Shader samt festem Zustand bündelt und in <code>GPURenderPassEncoder</code> oder <code>GPURenderBundleEncoder</code> genutzt wird.

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

Signatur

interface GPURenderPipeline

Beschreibung

GPURenderPipeline beschreibt den kompletten Ablauf, mit dem die GPU Geometrie in Pixel umsetzt: den Vertex-Shader samt Layout der Vertex-Puffer, die Primitiv-Zusammensetzung (z. B. triangle-list), das Culling, den Tiefen- und Stencil-Test, das Multisampling, den Fragment-Shader und das Format der Farbziele einschließlich Blending. Diesen ganzen Zustand legt man einmal beim Erstellen fest. Danach lässt er sich nicht mehr ändern.

Ein Objekt erzeugt man nicht über einen Konstruktor, sondern mit GPUDevice.createRenderPipeline() (synchron) oder GPUDevice.createRenderPipelineAsync() (gibt ein Promise zurück). Beim Zeichnen bindet man die Pipeline mit passEncoder.setPipeline(pipeline) und ruft dann draw() oder drawIndexed() auf.

Die Schnittstelle selbst ist schlank:

  • label (string): frei wählbare Bezeichnung für Fehlermeldungen und Debugging-Werkzeuge. Man kann sie lesen und schreiben.
  • getBindGroupLayout(index): gibt das GPUBindGroupLayout für die Bind-Gruppe @group(index) zurück. Das ist vor allem bei layout: "auto" nötig, weil das Layout dann aus dem Shader-Code abgeleitet wird.

Für jede Kombination aus Shadern und festem Zustand braucht man eine eigene Pipeline, zum Beispiel für Blending an und aus oder für ein anderes Farbformat. Da das Erstellen teuer ist, sollte man Pipelines beim Start anlegen und wiederverwenden, statt sie in jedem Frame neu zu erzeugen.

Rückgabewert

Typ
GPURenderPipeline
Beschreibung
Wird von device.createRenderPipeline() direkt zurückgegeben, von createRenderPipelineAsync() als Promise<GPURenderPipeline>. getBindGroupLayout() liefert ein GPUBindGroupLayout.

Beispiele

Pipeline erstellen und ein Dreieck zeichnen

const adapter = await navigator.gpu?.requestAdapter();
if (!adapter) throw new Error('WebGPU wird nicht unterstützt');
const device = await adapter.requestDevice();

const canvas = document.querySelector('canvas');
const context = canvas.getContext('webgpu');
const format = navigator.gpu.getPreferredCanvasFormat();
context.configure({ device, format });

const module = device.createShaderModule({
  code: /* wgsl */ `
    @vertex fn vs(@builtin(vertex_index) i: u32) -> @builtin(position) vec4f {
      var pos = array<vec2f, 3>(vec2f(0, 0.5), vec2f(-0.5, -0.5), vec2f(0.5, -0.5));
      return vec4f(pos[i], 0, 1);
    }
    @fragment fn fs() -> @location(0) vec4f {
      return vec4f(1, 0.4, 0, 1);
    }
  `
});

const pipeline = device.createRenderPipeline({
  label: 'Dreieck-Pipeline',
  layout: 'auto',
  vertex: { module, entryPoint: 'vs' },
  fragment: { module, entryPoint: 'fs', targets: [{ format }] },
  primitive: { topology: 'triangle-list' }
});

const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass({
  colorAttachments: [{
    view: context.getCurrentTexture().createView(),
    clearValue: { r: 0.1, g: 0.1, b: 0.1, a: 1 },
    loadOp: 'clear',
    storeOp: 'store'
  }]
});
pass.setPipeline(pipeline);
pass.draw(3);
pass.end();
device.queue.submit([encoder.finish()]);
console.log(pipeline.label);
Dreieck-Pipeline

Uniform-Puffer über getBindGroupLayout() binden

// Voraussetzung: device, format wie oben
const module = device.createShaderModule({
  code: /* wgsl */ `
    @group(0) @binding(0) var<uniform> farbe: vec4f;
    @vertex fn vs(@builtin(vertex_index) i: u32) -> @builtin(position) vec4f {
      var p = array<vec2f, 3>(vec2f(0, 1), vec2f(-1, -1), vec2f(1, -1));
      return vec4f(p[i] * 0.5, 0, 1);
    }
    @fragment fn fs() -> @location(0) vec4f { return farbe; }
  `
});

const pipeline = device.createRenderPipeline({
  layout: 'auto',
  vertex: { module, entryPoint: 'vs' },
  fragment: { module, entryPoint: 'fs', targets: [{ format }] }
});

const uniformBuffer = device.createBuffer({
  size: 16,
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
});
device.queue.writeBuffer(uniformBuffer, 0, new Float32Array([0, 0.6, 1, 1]));

// Layout aus der Pipeline holen, weil layout: 'auto' verwendet wurde
const bindGroup = device.createBindGroup({
  layout: pipeline.getBindGroupLayout(0),
  entries: [{ binding: 0, resource: { buffer: uniformBuffer } }]
});

// Im Render-Pass:
// pass.setPipeline(pipeline);
// pass.setBindGroup(0, bindGroup);
// pass.draw(3);

Asynchron erstellen mit Vertex-Puffer, Tiefentest und Blending

const pipeline = await device.createRenderPipelineAsync({
  label: 'Mesh-Pipeline',
  layout: 'auto',
  vertex: {
    module: meshModule,
    entryPoint: 'vs',
    buffers: [{
      arrayStride: 24, // 3 × float32 Position + 3 × float32 Normale
      attributes: [
        { shaderLocation: 0, offset: 0, format: 'float32x3' },
        { shaderLocation: 1, offset: 12, format: 'float32x3' }
      ]
    }]
  },
  fragment: {
    module: meshModule,
    entryPoint: 'fs',
    targets: [{
      format,
      blend: {
        color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },
        alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' }
      }
    }]
  },
  primitive: { topology: 'triangle-list', cullMode: 'back' },
  depthStencil: { format: 'depth24plus', depthWriteEnabled: true, depthCompare: 'less' }
}).catch((err) => {
  // GPUPipelineError mit err.reason: 'validation' | 'internal'
  console.error('Pipeline fehlgeschlagen:', err.reason, err.message);
});