Signatur
Beschreibung
createRenderPipelineAsync() ist die asynchrone Variante von GPUDevice.createRenderPipeline(). Beide nehmen denselben Deskriptor entgegen: Vertex- und Fragment-Shader-Stufen, Vertex-Puffer-Layouts, Primitiv-Topologie, Tiefen-/Stencil-Zustand, Multisampling und das Pipeline-Layout. Der Unterschied: Das Promise wird erst erfüllt, wenn die Pipeline tatsächlich einsatzbereit ist. Der Aufrufer muss also nicht warten, während der Treiber die Shader kompiliert.
Das Kompilieren von Shadern und Pipelines kann je nach Treiber Dutzende bis Hunderte Millisekunden dauern. Die synchrone Variante liefert zwar sofort ein Objekt zurück. Die erste Nutzung in einem Render-Pass kann dann aber zu einem Ruckeln führen, weil die Kompilierung erst dort erzwungen wird. Mit createRenderPipelineAsync() kann man Pipelines im Hintergrund vorbereiten, etwa beim Laden eines Levels oder beim Anzeigen eines Ladebildschirms, und sie erst verwenden, wenn sie fertig sind.
Auch die Fehlerbehandlung unterscheidet sich. Bei einem ungültigen Deskriptor erzeugt die synchrone Methode eine ungültige Pipeline und meldet einen Validierungsfehler über den Fehler-Scope des Geräts. Die asynchrone Methode lehnt ihr Promise dagegen mit einer GPUPipelineError ab. Deren Eigenschaft reason ist "validation" oder "internal". Deshalb lässt sich der Fehler direkt mit try/catch abfangen.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $descriptor Pflicht | GPURenderPipelineDescriptor | Konfigurationsobjekt der Pipeline mit folgenden Eigenschaften:
|
Rückgabewert
GPURenderPipeline erfüllt wird. Bei ungültigem Deskriptor oder internem Fehler wird es mit einer GPUPipelineError abgelehnt.Beispiele
Einfache Dreieck-Pipeline asynchron erzeugen
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const format = navigator.gpu.getPreferredCanvasFormat();
const shaderModule = device.createShaderModule({
code: `
@vertex
fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4f {
var pos = array<vec2f, 3>(vec2f(0.0, 0.5), vec2f(-0.5, -0.5), vec2f(0.5, -0.5));
return vec4f(pos[i], 0.0, 1.0);
}
@fragment
fn fs_main() -> @location(0) vec4f {
return vec4f(1.0, 0.4, 0.0, 1.0);
}
`
});
const pipeline = await device.createRenderPipelineAsync({
label: 'Dreieck',
layout: 'auto',
vertex: { module: shaderModule, entryPoint: 'vs_main' },
fragment: {
module: shaderModule,
entryPoint: 'fs_main',
targets: [{ format }]
},
primitive: { topology: 'triangle-list' }
});
console.log(pipeline.label);
Validierungsfehler mit try/catch abfangen
try {
const pipeline = await device.createRenderPipelineAsync({
layout: 'auto',
vertex: { module: shaderModule, entryPoint: 'gibt_es_nicht' },
fragment: { module: shaderModule, entryPoint: 'fs_main', targets: [{ format }] }
});
} catch (err) {
if (err instanceof GPUPipelineError) {
console.error(`Pipeline-Fehler (${err.reason}):`, err.message);
} else {
throw err;
}
}
Mehrere Pipelines parallel beim Laden vorkompilieren
const makeDescriptor = (label, cullMode) => ({
label,
layout: 'auto',
vertex: {
module: shaderModule,
entryPoint: 'vs_main',
buffers: [{
arrayStride: 12,
attributes: [{ shaderLocation: 0, offset: 0, format: 'float32x3' }]
}]
},
fragment: { module: shaderModule, entryPoint: 'fs_main', targets: [{ format }] },
primitive: { topology: 'triangle-list', cullMode },
depthStencil: { format: 'depth24plus', depthWriteEnabled: true, depthCompare: 'less' }
});
// Kompilierung läuft im Hintergrund, der Ladebildschirm bleibt flüssig
const [opaque, doubleSided] = await Promise.all([
device.createRenderPipelineAsync(makeDescriptor('opak', 'back')),
device.createRenderPipelineAsync(makeDescriptor('beidseitig', 'none'))
]);
console.log(opaque.label, doubleSided.label);
// Wichtig · Fallstricke
- Browser-Unterstützung: WebGPU ist noch nicht überall verfügbar. Die Methode funktioniert nur in sicheren Kontexten (HTTPS oder
localhost). Prüfe vorher, obnavigator.gpuvorhanden ist, und halte bei Bedarf einen WebGL-Ersatz bereit. - Fehlerverhalten: Anders als bei
createRenderPipeline()landen Fehler nicht im Fehler-Scope (pushErrorScope/popErrorScope). Stattdessen wird das Promise mit einerGPUPipelineErrorabgelehnt. Wird das Promise nicht behandelt, entsteht eineunhandledrejection. - Verlorenes Gerät: Geht das Gerät verloren (
device.lost), kann das Promise trotzdem erfüllt werden. Die Pipeline ist dann aber unbrauchbar. - Layout
"auto": Die daraus abgeleiteten Bind-Group-Layouts gehören genau zu dieser einen Pipeline. Sie lassen sich nicht mit anderen Pipelines teilen. Wenn Bind-Groups für mehrere Pipelines gelten sollen, lege ein explizitesGPUPipelineLayoutan. - Leistung: Erzeuge Pipelines einmal und speichere sie zwischen, statt sie in jedem Frame neu anzulegen. Die asynchrone Variante verhindert Ruckler, beschleunigt die Kompilierung aber nicht.