Start · Sprachen · JavaScript · Referenz · GPUComputePipeline

GPUComputePipeline

Schnittstelle

Repräsentiert eine kompilierte Compute-Pipeline der WebGPU-API, die einen Compute-Shader samt Bind-Group-Layout kapselt und in einem <code>GPUComputePassEncoder</code> ausgeführt wird.

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

Signatur

interface GPUComputePipeline

Beschreibung

GPUComputePipeline fasst alles zusammen, was die GPU für allgemeine Berechnungen (GPGPU) braucht: ein GPUShaderModule mit WGSL-Code, den Namen des Einstiegspunkts (@compute-Funktion), optionale Override-Konstanten und das Pipeline-Layout, das festlegt, welche Ressourcen (Buffer, Texturen, Sampler) der Shader über Bind Groups sieht.

Instanzen werden nicht per Konstruktor erzeugt, sondern über GPUDevice.createComputePipeline() (synchron) oder GPUDevice.createComputePipelineAsync() (liefert ein Promise und blockiert den Hauptthread nicht während der Shader-Kompilierung). Anschließend setzt man die Pipeline mit passEncoder.setPipeline(pipeline) und startet die Berechnung mit dispatchWorkgroups().

Die Schnittstelle bietet:

  • label (Eigenschaft) – frei wählbarer Name, der in Fehlermeldungen und Debugging-Tools erscheint.
  • getBindGroupLayout(index) (Methode) – liefert das GPUBindGroupLayout an Position index. Besonders nützlich mit layout: "auto", weil das Layout dann vom Shader abgeleitet wird.

Typische Einsatzgebiete sind Bildverarbeitung, Partikelsimulationen, Physik, Matrixrechnungen und Inferenz von neuronalen Netzen direkt im Browser.

Parameter

Name Typ Default Beschreibung
$descriptor.compute.module Pflicht GPUShaderModule Das per device.createShaderModule() erzeugte Shader-Modul mit dem WGSL-Code (Teil des Deskriptors für createComputePipeline()).
$descriptor.compute.entryPoint string einzige @compute-Funktion im Modul Name der @compute-Funktion. Pflicht, wenn das Modul mehrere Compute-Einstiegspunkte enthält.
$descriptor.compute.constants Record<string, number> Werte für override-Konstanten im WGSL-Code, z. B. die Workgroup-Größe.
$descriptor.layout Pflicht GPUPipelineLayout | "auto" Explizites Pipeline-Layout oder "auto", um das Layout aus dem Shader abzuleiten.
$descriptor.label string "" Debug-Name der Pipeline; wird in pipeline.label gespeichert.

Beispiele

Compute-Pipeline erzeugen und Daten auf der GPU verdoppeln

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> data: array<f32>;

    @compute @workgroup_size(64)
    fn main(@builtin(global_invocation_id) id: vec3u) {
      if (id.x < arrayLength(&data)) {
        data[id.x] = data[id.x] * 2.0;
      }
    }
  `
});

// Pipeline mit automatisch abgeleitetem Layout
const pipeline = device.createComputePipeline({
  label: 'Verdoppeln',
  layout: 'auto',
  compute: { module, entryPoint: 'main' }
});

const input = new Float32Array([1, 2, 3, 4]);
const storage = device.createBuffer({
  size: input.byteLength,
  usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST
});
device.queue.writeBuffer(storage, 0, input);

const readback = device.createBuffer({
  size: input.byteLength,
  usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
});

// Bind Group über das Layout der Pipeline erzeugen
const bindGroup = device.createBindGroup({
  layout: pipeline.getBindGroupLayout(0),
  entries: [{ binding: 0, resource: { buffer: storage } }]
});

const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(Math.ceil(input.length / 64));
pass.end();
encoder.copyBufferToBuffer(storage, 0, readback, 0, input.byteLength);
device.queue.submit([encoder.finish()]);

await readback.mapAsync(GPUMapMode.READ);
console.log(Array.from(new Float32Array(readback.getMappedRange())));
readback.unmap();
[2, 4, 6, 8]

Asynchron kompilieren mit Override-Konstante und Fehlerbehandlung

const device = await (await navigator.gpu.requestAdapter()).requestDevice();

const module = device.createShaderModule({
  code: `
    override WG_SIZE: u32 = 64;
    @group(0) @binding(0) var<storage, read_write> out: array<u32>;

    @compute @workgroup_size(WG_SIZE)
    fn fill(@builtin(global_invocation_id) id: vec3u) {
      out[id.x] = id.x;
    }
  `
});

try {
  // blockiert den Hauptthread nicht während der Kompilierung
  const pipeline = await device.createComputePipelineAsync({
    label: 'Füllen',
    layout: 'auto',
    compute: { module, entryPoint: 'fill', constants: { WG_SIZE: 128 } }
  });
  console.log(`Pipeline "${pipeline.label}" bereit`);
} catch (err) {
  // GPUPipelineError bei ungültigem Shader/Deskriptor
  console.error(err.name, err.reason, err.message);
}
Pipeline "Füllen" bereit

Explizites Pipeline-Layout für mehrere Pipelines teilen

const device = await (await navigator.gpu.requestAdapter()).requestDevice();

const bindGroupLayout = device.createBindGroupLayout({
  entries: [
    { binding: 0, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'read-only-storage' } },
    { binding: 1, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'storage' } }
  ]
});
const layout = device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout] });

const module = device.createShaderModule({
  code: `
    @group(0) @binding(0) var<storage, read> src: array<f32>;
    @group(0) @binding(1) var<storage, read_write> dst: array<f32>;
    @compute @workgroup_size(64) fn copy(@builtin(global_invocation_id) id: vec3u) { dst[id.x] = src[id.x]; }
    @compute @workgroup_size(64) fn negate(@builtin(global_invocation_id) id: vec3u) { dst[id.x] = -src[id.x]; }
  `
});

// Beide Pipelines nutzen dasselbe Layout -> dieselbe Bind Group ist austauschbar
const copyPipeline = device.createComputePipeline({ layout, compute: { module, entryPoint: 'copy' } });
const negatePipeline = device.createComputePipeline({ layout, compute: { module, entryPoint: 'negate' } });

console.log(copyPipeline.getBindGroupLayout(0) instanceof GPUBindGroupLayout);
true