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

GPURenderBundleEncoder.setPipeline()

Methode

Legt die <code>GPURenderPipeline</code> fest, die alle nachfolgenden Zeichenbefehle im Render-Bündel verwenden.

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

Signatur

GPURenderBundleEncoder.prototype.setPipeline(pipeline)

Beschreibung

Mit setPipeline() legst du fest, welche GPURenderPipeline (Shader, Vertex-Layout, Primitiv-Topologie, Blending, Tiefen-/Stencil-Zustand) für die folgenden draw()-, drawIndexed()-, drawIndirect()- und drawIndexedIndirect()-Aufrufe innerhalb eines Render-Bündels gilt. Ohne gesetzte Pipeline sind Zeichenbefehle ungültig.

Ein GPURenderBundleEncoder zeichnet eine Befehlsfolge einmal auf. Mit finish() wird daraus ein GPURenderBundle, das du in beliebig vielen Render-Passes über GPURenderPassEncoder.executeBundles() erneut abspielen kannst. Das spart CPU-Aufwand, wenn sich statische Szenenteile wie Gelände, Umgebung oder UI-Hintergrund Frame für Frame nicht ändern.

Du kannst setPipeline() innerhalb eines Bündels mehrmals aufrufen, um zwischen Pipelines zu wechseln. Bind-Groups und Vertex-Puffer bleiben dabei gesetzt, müssen aber zum Layout der neuen Pipeline passen. Die Pipeline muss mit dem Bündel kompatibel sein. Die colorFormats, das depthStencilFormat und der sampleCount aus createRenderBundleEncoder() müssen also zu den fragment.targets, zum depthStencil.format und zu multisample.count der Pipeline passen.

Parameter

Name Typ Default Beschreibung
$pipeline Pflicht GPURenderPipeline Die Render-Pipeline für die nachfolgenden Zeichenbefehle. Erzeugt mit GPUDevice.createRenderPipeline() oder createRenderPipelineAsync(), und zwar auf demselben GPUDevice wie der Encoder.

Rückgabewert

Typ
undefined
Beschreibung
Kein Rückgabewert. Der Befehl wird im Encoder aufgezeichnet.

Beispiele

Einfaches Render-Bündel mit einem Dreieck

const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const format = navigator.gpu.getPreferredCanvasFormat();

const module = device.createShaderModule({
  code: `
    @vertex fn vs(@builtin(vertex_index) i: u32) -> @builtin(position) vec4f {
      var p = array(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 }] },
});

// colorFormats muss zu fragment.targets passen
const bundleEncoder = device.createRenderBundleEncoder({ colorFormats: [format] });
bundleEncoder.setPipeline(pipeline);
bundleEncoder.draw(3);
const bundle = bundleEncoder.finish();
console.log(bundle instanceof GPURenderBundle);
true

Bündel in jedem Frame wiederverwenden

// Voraussetzung: device, format, bundle aus dem vorigen Beispiel
const canvas = document.querySelector('canvas');
const context = canvas.getContext('webgpu');
context.configure({ device, format });

const frame = () => {
  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.executeBundles([bundle]); // Pipeline + draw kommen aus dem Bündel
  pass.end();
  device.queue.submit([encoder.finish()]);
  requestAnimationFrame(frame);
};
requestAnimationFrame(frame);

Mehrere Pipelines in einem Bündel

// opaquePipeline und transparentPipeline: gleiche colorFormats/depthStencil-Formate
const be = device.createRenderBundleEncoder({
  colorFormats: [format],
  depthStencilFormat: 'depth24plus',
});

be.setPipeline(opaquePipeline);
be.setBindGroup(0, sceneBindGroup);
be.setVertexBuffer(0, terrainVertices);
be.draw(terrainVertexCount);

be.setPipeline(transparentPipeline); // Wechsel innerhalb des Bündels
be.setVertexBuffer(0, waterVertices);
be.draw(waterVertexCount);

const staticScene = be.finish({ label: 'statische Szene' });

// Wichtig · Fallstricke

  • Validierungsfehler statt Exception: Eine inkompatible Pipeline (falsches Farbformat, abweichendes depthStencilFormat oder anderer sampleCount) wirft keinen JavaScript-Fehler. Stattdessen macht sie den Encoder ungültig. Der Fehler zeigt sich erst bei finish() bzw. über device.pushErrorScope('validation') oder das Ereignis uncapturederror.
  • Hat die Pipeline ein depthStencil mit depthWriteEnabled: true, darf das Bündel nicht mit depthReadOnly: true erzeugt worden sein. Dasselbe gilt für Stencil-Schreibzugriffe und stencilReadOnly.
  • Render-Bündel erben keinen Zustand aus dem Render-Pass. Pipeline, Bind-Groups und Puffer müssen im Bündel selbst gesetzt werden. Umgekehrt ist der Zustand des Passes nach executeBundles() zurückgesetzt, und du musst setPipeline() dort erneut aufrufen.
  • Dynamische Zustände wie setViewport(), setScissorRect() oder setBlendConstant() gibt es im Bündel-Encoder nicht. Sie werden am Render-Pass gesetzt.
  • Nach finish() ist der Encoder verbraucht. Weitere Aufrufe von setPipeline() erzeugen einen Validierungsfehler.
  • Kompatibilität: WebGPU läuft nur in sicheren Kontexten und ist noch nicht überall verfügbar. Prüfe vorher mit if (!navigator.gpu) und sieh eine Ausweichlösung vor, etwa WebGL.