Start · Sprachen · JavaScript · Referenz · GPURenderPassEncoder.setPipeline()

GPURenderPassEncoder.setPipeline()

Methode

Legt die <code>GPURenderPipeline</code> fest, die alle folgenden Zeichenbefehle (<code>draw()</code>, <code>drawIndexed()</code> …) im aktuellen Render-Pass verwenden.

seit JavaScript WebGPU – Chrome/Edge 113+, Firefox 141+ (Windows), Kategorie: webapi

Signatur

GPURenderPassEncoder.prototype.setPipeline(pipeline)

Beschreibung

Mit setPipeline() bindest du eine zuvor mit GPUDevice.createRenderPipeline() oder createRenderPipelineAsync() erstellte Render-Pipeline an den aktuellen Render-Pass. Die Pipeline legt fest, welche Vertex- und Fragment-Shader laufen, wie die Vertex-Puffer aufgebaut sind, welcher Primitivtyp gilt (z. B. triangle-list), wie überblendet wird und wie Tiefe und Stencil ausgewertet werden.

Der Aufruf wirkt auf alle folgenden Zeichenbefehle, bis im selben Pass erneut setPipeline() aufgerufen wird. Innerhalb eines Passes darfst du beliebig oft wechseln, etwa erst undurchsichtige Objekte mit einer Pipeline zeichnen und danach durchsichtige mit einer anderen Blend-Einstellung.

Gebundene Ressourcen wie Bind Groups (setBindGroup()) und Vertex-/Index-Puffer (setVertexBuffer(), setIndexBuffer()) bleiben beim Pipeline-Wechsel erhalten. Sie müssen aber zum Layout der neuen Pipeline passen. Die Pipeline muss außerdem mit den Anhängen des Passes kompatibel sein: Farbformate, Tiefen-/Stencil-Format und sampleCount müssen zum GPURenderPassDescriptor passen.

Parameter

Name Typ Default Beschreibung
$pipeline Pflicht GPURenderPipeline Die Render-Pipeline, die für alle folgenden Zeichenbefehle dieses Passes gelten soll. Sie muss vom selben GPUDevice stammen wie der Encoder.

Rückgabewert

Typ
undefined
Beschreibung
Kein Rückgabewert. Der Befehl wird nur in den Command-Buffer aufgezeichnet und erst nach GPUQueue.submit() ausgeführt.

Beispiele

Einfaches Dreieck zeichnen

const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const canvas = document.querySelector('canvas');
const context = canvas.getContext('webgpu');
const format = navigator.gpu.getPreferredCanvasFormat();
context.configure({ device, format });

const module = device.createShaderModule({
  code: `
    @vertex fn vs(@builtin(vertex_index) i: u32) -> @builtin(position) vec4f {
      var p = array<vec2f, 3>(vec2f(0.0, 0.5), vec2f(-0.5, -0.5), vec2f(0.5, -0.5));
      return vec4f(p[i], 0.0, 1.0);
    }
    @fragment fn fs() -> @location(0) vec4f {
      return vec4f(1.0, 0.4, 0.0, 1.0);
    }`
});

const pipeline = device.createRenderPipeline({
  layout: 'auto',
  vertex: { module, entryPoint: 'vs' },
  fragment: { module, entryPoint: 'fs', targets: [{ format }] },
  primitive: { topology: 'triangle-list' }
});

const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass({
  colorAttachments: [{
    view: context.getCurrentTexture().createView(),
    clearValue: { r: 0, g: 0, b: 0, a: 1 },
    loadOp: 'clear',
    storeOp: 'store'
  }]
});

pass.setPipeline(pipeline); // Pipeline vor draw() setzen
pass.draw(3);
pass.end();
device.queue.submit([encoder.finish()]);
Ein orangefarbenes Dreieck erscheint im Canvas.

Pipeline innerhalb eines Passes wechseln (undurchsichtig/transparent)

// opaquePipeline und blendPipeline wurden mit gleichem Layout erstellt
const renderScene = (pass, { opaquePipeline, blendPipeline, bindGroup, meshes }) => {
  pass.setBindGroup(0, bindGroup); // bleibt über den Pipeline-Wechsel erhalten

  pass.setPipeline(opaquePipeline);
  for (const mesh of meshes.filter((m) => !m.transparent)) {
    pass.setVertexBuffer(0, mesh.vertexBuffer);
    pass.draw(mesh.vertexCount);
  }

  pass.setPipeline(blendPipeline);
  for (const mesh of meshes.filter((m) => m.transparent)) {
    pass.setVertexBuffer(0, mesh.vertexBuffer);
    pass.draw(mesh.vertexCount);
  }
};

Validierungsfehler abfangen

device.pushErrorScope('validation');

const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass(passDescriptor);
pass.setPipeline(pipeline); // z. B. falsches Farbformat im Vergleich zum Anhang
pass.draw(3);
pass.end();
device.queue.submit([encoder.finish()]);

const error = await device.popErrorScope();
if (error) console.error('WebGPU-Fehler:', error.message);
WebGPU-Fehler: … (nur bei inkompatibler Pipeline)

// Wichtig · Fallstricke

  • Keine Exception: Ungültige Aufrufe werfen keinen Fehler. Stattdessen wird der Encoder ungültig, und der Fehler zeigt sich erst bei finish()/submit() als Validierungsfehler. Mit pushErrorScope() bzw. dem Event uncapturederror kannst du solche Fehler sichtbar machen.
  • Ohne vorheriges setPipeline() ist jeder draw()-Aufruf ungültig.
  • Die Pipeline muss zu den Anhängen des Passes passen: Farbformate, depthStencil.format und multisample.count. Ist bei depthReadOnly/stencilReadOnly der Tiefen- bzw. Stencil-Anhang schreibgeschützt, darf die Pipeline dort nicht schreiben.
  • Bei layout: 'auto' sind die Bind-Group-Layouts an genau diese Pipeline gebunden. Bind Groups lassen sich dann nicht zwischen Pipelines teilen. Für häufige Wechsel solltest du ein explizites GPUPipelineLayout verwenden.
  • Leistung: Ein Pipeline-Wechsel ist vergleichsweise teuer. Sortiere Zeichenaufrufe deshalb nach Pipeline, um unnötige Wechsel zu vermeiden.
  • Kompatibilität: WebGPU steht nur in sicheren Kontexten (HTTPS) zur Verfügung. Die Unterstützung wird noch ausgebaut: Chromium ab 113, Safari ab 26, Firefox zunächst unter Windows. Prüfe vorher, ob navigator.gpu vorhanden ist.
  • Die gleiche Methode gibt es auch bei GPURenderBundleEncoder. Bei Compute-Passes heißt das Gegenstück GPUComputePassEncoder.setPipeline() und erwartet eine GPUComputePipeline.