Signatur
Beschreibung
Mit createRenderPipeline() legst du fest, wie die GPU Geometrie verarbeitet und Pixel erzeugt. Eine Render-Pipeline bündelt den Vertex-Shader (samt Layout der Vertex-Buffer), den optionalen Fragment-Shader (samt Farbzielen und Blending), die Primitiv-Topologie (Dreiecke, Linien, Punkte), den Tiefen-/Stencil-Zustand und die Multisampling-Einstellungen in einem unveränderlichen Objekt.
Die fertige Pipeline wird in einem GPURenderPassEncoder oder GPURenderBundleEncoder per setPipeline() gesetzt, bevor du mit draw() oder drawIndexed() zeichnest. Pipelines erstellst du normalerweise einmal beim Initialisieren und verwendest sie in jedem Frame wieder – das Erzeugen ist teuer, weil die Shader dabei für die konkrete Hardware kompiliert werden.
Die Methode arbeitet synchron und liefert sofort ein Pipeline-Objekt zurück. Validierungsfehler werfen keine Ausnahme, sondern erzeugen eine ungültige Pipeline und einen GPUValidationError, den du über pushErrorScope()/popErrorScope() oder das uncapturederror-Ereignis abfängst. Wer das Blockieren des Hauptthreads vermeiden will, nutzt die asynchrone Variante createRenderPipelineAsync().
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $descriptor Pflicht | GPURenderPipelineDescriptor | Konfigurationsobjekt der Pipeline mit den folgenden Eigenschaften. | |
| $descriptor.layout Pflicht | GPUPipelineLayout | "auto" | Das Pipeline-Layout mit den Bind-Group-Layouts. Mit "auto" wird das Layout aus den Shadern abgeleitet und ist über pipeline.getBindGroupLayout(index) abrufbar. |
|
| $descriptor.vertex Pflicht | GPUVertexState | Vertex-Stufe: module (GPUShaderModule), optional entryPoint, constants und buffers (Array aus GPUVertexBufferLayout mit arrayStride, stepMode und attributes). |
|
| $descriptor.fragment | GPUFragmentState | Fragment-Stufe: module, optional entryPoint, constants sowie targets (Array mit format, optional blend und writeMask). Weglassen ist z. B. für reine Tiefen-Passes (Shadow Maps) möglich. |
|
| $descriptor.primitive | GPUPrimitiveState | { topology: "triangle-list", frontFace: "ccw", cullMode: "none" } | Topologie ("point-list", "line-list", "line-strip", "triangle-list", "triangle-strip"), stripIndexFormat, frontFace, cullMode und unclippedDepth (erfordert Feature). |
| $descriptor.depthStencil | GPUDepthStencilState | Tiefen-/Stencil-Zustand: format (z. B. "depth24plus"), depthWriteEnabled, depthCompare, Stencil-Operationen und Depth-Bias. |
|
| $descriptor.multisample | GPUMultisampleState | { count: 1, mask: 0xFFFFFFFF, alphaToCoverageEnabled: false } | Multisampling-Einstellungen; count darf 1 oder 4 sein und muss zu den Render-Targets passen. |
| $descriptor.label | string | "" | Bezeichnung für Fehlermeldungen und Debugging-Werkzeuge. |
Rückgabewert
GPUValidationError ausgelöst.Beispiele
Minimale Pipeline für ein Dreieck
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 {
let pos = array(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() -> @location(0) vec4f {
return vec4f(1.0, 0.4, 0.0, 1.0);
}`
});
const pipeline = device.createRenderPipeline({
label: 'Dreieck',
layout: 'auto',
vertex: { module, entryPoint: 'vs' },
fragment: { module, entryPoint: 'fs', targets: [{ format }] }
});
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);
pass.draw(3);
pass.end();
device.queue.submit([encoder.finish()]);
Vertex-Buffer-Layout, Tiefentest und Backface-Culling
const pipeline = device.createRenderPipeline({
label: 'Mesh-Pipeline',
layout: 'auto',
vertex: {
module,
entryPoint: 'vs',
buffers: [{
arrayStride: 6 * 4, // 3 floats Position + 3 floats Normale
attributes: [
{ shaderLocation: 0, offset: 0, format: 'float32x3' },
{ shaderLocation: 1, offset: 12, format: 'float32x3' }
]
}]
},
fragment: {
module,
entryPoint: 'fs',
targets: [{ format: navigator.gpu.getPreferredCanvasFormat() }]
},
primitive: { topology: 'triangle-list', cullMode: 'back' },
depthStencil: {
format: 'depth24plus',
depthWriteEnabled: true,
depthCompare: 'less'
}
});
// Bind-Group-Layout aus dem automatischen Layout holen
const bindGroupLayout = pipeline.getBindGroupLayout(0);
console.log(bindGroupLayout.label);
Alpha-Blending und Fehlerbehandlung per Error-Scope
device.pushErrorScope('validation');
const pipeline = device.createRenderPipeline({
layout: 'auto',
vertex: { module, entryPoint: 'vs' },
fragment: {
module,
entryPoint: 'fs',
targets: [{
format: 'bgra8unorm',
blend: {
color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },
alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' }
}
}]
}
});
const error = await device.popErrorScope();
if (error) {
console.error('Pipeline ungültig:', error.message);
} else {
console.log('Pipeline erfolgreich erstellt');
}