Start · Sprachen · JavaScript · Referenz · GPUDevice.createComputePipelineAsync()

GPUDevice.createComputePipelineAsync()

Methode

Erzeugt asynchron eine <code>GPUComputePipeline</code> und gibt ein <code>Promise</code> zurück, das erfüllt wird, sobald die Pipeline ohne Blockieren einsatzbereit ist.

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

Signatur

GPUDevice.prototype.createComputePipelineAsync(descriptor)

Beschreibung

createComputePipelineAsync() ist die asynchrone Variante von GPUDevice.createComputePipeline(). Beide erzeugen eine GPUComputePipeline, also die Kombination aus einem WGSL-Compute-Shader, dessen Einstiegspunkt und einem Pipeline-Layout. Diese Pipeline setzt du später in einem GPUComputePassEncoder mit setPipeline() und startest sie mit dispatchWorkgroups().

Der Vorteil der asynchronen Variante: Das Kompilieren von Shadern und das Erzeugen der Pipeline kann im Treiber dauern. Das Promise wird erst erfüllt, wenn die Pipeline ohne Verzögerung nutzbar ist. Bei der synchronen Variante kann dagegen die erste Benutzung der Pipeline hängen. Deshalb ist createComputePipelineAsync() die bessere Wahl, wenn du beim Start oder beim Nachladen viele Pipelines erzeugst und Ruckler vermeiden willst.

Ein weiterer Unterschied zeigt sich bei der Fehlerbehandlung: Ist der Deskriptor ungültig, zum Beispiel weil der Shader fehlerhaft ist, der Einstiegspunkt fehlt oder das Layout nicht passt, wird das Promise mit einer GPUPipelineError abgelehnt. Das kannst du direkt mit try/catch abfangen. Die synchrone Variante liefert in so einem Fall ein ungültiges Objekt und meldet den Fehler nur über Error-Scopes oder das Event uncapturederror.

Parameter

Name Typ Default Beschreibung
$descriptor Pflicht GPUComputePipelineDescriptor Konfigurationsobjekt der Pipeline mit folgenden Feldern:
  • layout (Pflichtfeld): ein GPUPipelineLayout oder der String "auto", damit das Layout automatisch aus dem Shader abgeleitet wird.
  • compute (Pflichtfeld): ein Objekt mit module (GPUShaderModule), optional entryPoint (String, darf fehlen, wenn das Modul genau einen @compute-Einstiegspunkt hat) und optional constants (Werte für die override-Konstanten im WGSL-Code).
  • label (optional): Name zur Fehlersuche.

Rückgabewert

Typ
Promise<GPUComputePipeline>
Beschreibung
Ein Promise, das mit der fertigen GPUComputePipeline erfüllt wird. Ist der Deskriptor ungültig, wird es mit einer GPUPipelineError abgelehnt. Deren reason ist dann "validation" oder "internal".

Beispiele

Compute-Pipeline asynchron erzeugen und Werte 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: /* wgsl */ `
    @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; }
    }`
});

const pipeline = await device.createComputePipelineAsync({
  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
});

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]

Shader-Fehler mit try/catch abfangen

const device = await (await navigator.gpu.requestAdapter()).requestDevice();
const module = device.createShaderModule({
  code: '@compute @workgroup_size(1) fn main() {}'
});

try {
  await device.createComputePipelineAsync({
    layout: 'auto',
    compute: { module, entryPoint: 'gibtEsNicht' }
  });
} catch (err) {
  if (err instanceof GPUPipelineError) {
    console.error(`Pipeline-Fehler (${err.reason}):`, err.message);
  } else {
    throw err;
  }
}
Pipeline-Fehler (validation): ...

Mehrere Pipelines parallel vorkompilieren, mit override-Konstanten

const device = await (await navigator.gpu.requestAdapter()).requestDevice();
const module = device.createShaderModule({
  code: /* wgsl */ `
    override FAKTOR: f32 = 1.0;
    @group(0) @binding(0) var<storage, read_write> d: array<f32>;
    @compute @workgroup_size(64)
    fn main(@builtin(global_invocation_id) id: vec3u) {
      if (id.x < arrayLength(&d)) { d[id.x] *= FAKTOR; }
    }`
});

const faktoren = [2, 3, 10];
const pipelines = await Promise.all(
  faktoren.map((f) => device.createComputePipelineAsync({
    label: `mal-${f}`,
    layout: 'auto',
    compute: { module, constants: { FAKTOR: f } }
  }))
);
console.log(pipelines.map((p) => p.label));
['mal-2', 'mal-3', 'mal-10']

// Wichtig · Fallstricke

  • Kompatibilität: WebGPU ist noch nicht überall verfügbar. Die API funktioniert nur in sicheren Kontexten (HTTPS) und teils nur auf bestimmten Plattformen. Prüfe deshalb immer navigator.gpu und ob requestAdapter() einen Adapter liefert, statt null.
  • Fehlerverhalten: Anders als bei der synchronen Variante wird ein ungültiger Deskriptor als abgelehntes Promise mit GPUPipelineError gemeldet, nicht über pushErrorScope(). Wenn du die Ablehnung nicht behandelst, erscheint sie als unhandledrejection.
  • Verlorenes Gerät: Geht das Gerät verloren (device.lost), wird das Promise nicht abgelehnt. Es wird mit einem unbrauchbaren Pipeline-Objekt erfüllt. Beobachte deshalb device.lost separat.
  • layout: "auto": Automatisch abgeleitete Bind-Group-Layouts sind nur mit genau dieser Pipeline kompatibel. Wenn mehrere Pipelines dieselben Bind Groups nutzen sollen, lege ein explizites GPUPipelineLayout an. Außerdem tauchen im Shader ungenutzte Bindings im automatischen Layout nicht auf.
  • Performance: Erzeuge Pipelines vorab, zum Beispiel beim Laden mit Promise.all, und nicht erst in der Render- oder Rechenschleife. Verwende sie danach immer wieder.
  • Vor der Pipeline lohnt sich ein Blick auf GPUShaderModule.getCompilationInfo(). Dort stehen genaue WGSL-Fehlermeldungen mit Zeilenangabe.