Signatur
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 dasGPUBindGroupLayoutan Positionindex. Besonders nützlich mitlayout: "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();
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);
}
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);