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

GPUDevice.createRenderPipelineAsync()

Methode

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

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

Signatur

GPUDevice.prototype.createRenderPipelineAsync(descriptor)

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:
  • layout (Pflicht): ein GPUPipelineLayout oder "auto", um das Layout aus den Shadern ableiten zu lassen.
  • vertex (Pflicht): { module, entryPoint?, constants?, buffers? }. Die Vertex-Stufe mit GPUShaderModule und den Layouts der Vertex-Puffer.
  • fragment (optional): { module, entryPoint?, constants?, targets }. targets legt Format, Blending und writeMask für jedes Farbziel fest.
  • primitive (optional): topology (Standard "triangle-list"), stripIndexFormat, frontFace, cullMode, unclippedDepth.
  • depthStencil (optional): format, depthWriteEnabled, depthCompare, Stencil- und Bias-Einstellungen.
  • multisample (optional): count (1 oder 4), mask, alphaToCoverageEnabled.
  • label (optional): Name zur Fehlersuche.

Rückgabewert

Typ
Promise<GPURenderPipeline>
Beschreibung
Ein Promise, das mit der fertig kompilierten 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);
Dreieck

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;
  }
}
Pipeline-Fehler (validation): ...

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);
opak beidseitig

// 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, ob navigator.gpu vorhanden 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 einer GPUPipelineError abgelehnt. Wird das Promise nicht behandelt, entsteht eine unhandledrejection.
  • 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 explizites GPUPipelineLayout an.
  • 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.