Signatur
Beschreibung
GPURenderPassEncoder ist das Objekt, über das du in WebGPU die Befehle für einen Render-Durchlauf (Render Pass) aufzeichnest. Du erhältst es nie über einen Konstruktor, sondern immer über GPUCommandEncoder.beginRenderPass(descriptor). Im Deskriptor legst du fest, in welche Farb- und Tiefen-/Stencil-Anhänge (meist Texturen des Canvas-Kontexts) gezeichnet wird und ob sie vorher geleert (loadOp: "clear") oder erhalten werden.
Innerhalb des Durchlaufs setzt du den Zustand und löst Zeichenaufrufe aus. Die wichtigsten Methoden sind:
setPipeline(pipeline)– aktiveGPURenderPipelinefestlegen.setBindGroup(index, bindGroup, dynamicOffsets?)– Ressourcen (Uniform-Puffer, Texturen, Sampler) binden.setVertexBuffer(slot, buffer, offset?, size?)undsetIndexBuffer(buffer, format, offset?, size?)– Geometriedaten binden.draw(vertexCount, instanceCount?, firstVertex?, firstInstance?),drawIndexed(...),drawIndirect(...),drawIndexedIndirect(...)– Zeichenaufrufe.setViewport(),setScissorRect(),setBlendConstant(),setStencilReference()– dynamischer Rasterzustand.beginOcclusionQuery()/endOcclusionQuery(),executeBundles(), Debug-Marker (pushDebugGroup(),popDebugGroup(),insertDebugMarker()) sowie die Eigenschaftlabel.end()– schließt den Durchlauf ab; erst danach kann der Command-Encoder weiterverwendet bzw. mitfinish()abgeschlossen werden.
Die Befehle werden nicht sofort ausgeführt, sondern nur aufgezeichnet. Erst wenn der mit commandEncoder.finish() erzeugte GPUCommandBuffer über device.queue.submit() übergeben wird, arbeitet die GPU sie ab. Pro Frame erzeugst du typischerweise einen neuen Command-Encoder und einen neuen Render-Pass-Encoder – beide sind Einweg-Objekte.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $descriptor Pflicht | GPURenderPassDescriptor | Wird an GPUCommandEncoder.beginRenderPass() übergeben. Enthält colorAttachments (Array mit view, loadOp, storeOp, clearValue), optional depthStencilAttachment, occlusionQuerySet, timestampWrites, maxDrawCount und label. |
Rückgabewert
beginRenderPass() gelieferte Encoder, über den die Render-Befehle bis zum Aufruf von end() aufgezeichnet werden.Beispiele
Render-Pass erzeugen und Canvas leeren
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 commandEncoder = device.createCommandEncoder();
// Render-Pass-Encoder erzeugen – kein Konstruktor!
const pass = commandEncoder.beginRenderPass({
label: 'Clear-Pass',
colorAttachments: [{
view: context.getCurrentTexture().createView(),
clearValue: { r: 0.1, g: 0.2, b: 0.3, a: 1 },
loadOp: 'clear',
storeOp: 'store'
}]
});
console.log(pass instanceof GPURenderPassEncoder, pass.label);
pass.end();
device.queue.submit([commandEncoder.finish()]);
Dreieck zeichnen mit setPipeline() und draw()
const shader = device.createShaderModule({
code: `
@vertex fn vs(@builtin(vertex_index) i: u32) -> @builtin(position) vec4f {
var pos = array<vec2f, 3>(vec2f(0, 0.5), vec2f(-0.5, -0.5), vec2f(0.5, -0.5));
return vec4f(pos[i], 0, 1);
}
@fragment fn fs() -> @location(0) vec4f {
return vec4f(1, 0.4, 0, 1);
}`
});
const pipeline = device.createRenderPipeline({
layout: 'auto',
vertex: { module: shader, entryPoint: 'vs' },
fragment: { module: shader, entryPoint: 'fs', targets: [{ format }] }
});
const render = () => {
const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass({
colorAttachments: [{
view: context.getCurrentTexture().createView(),
clearValue: [0, 0, 0, 1],
loadOp: 'clear',
storeOp: 'store'
}]
});
pass.setPipeline(pipeline);
pass.draw(3); // 3 Vertices, 1 Instanz
pass.end();
device.queue.submit([encoder.finish()]);
requestAnimationFrame(render);
};
requestAnimationFrame(render);
Indiziertes Zeichnen mit Vertex-/Index-Puffer, Bind-Group und Viewport
// Annahme: pipeline, vertexBuffer, indexBuffer, uniformBindGroup existieren
const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass({
colorAttachments: [{
view: context.getCurrentTexture().createView(),
loadOp: 'clear',
storeOp: 'store',
clearValue: [1, 1, 1, 1]
}],
depthStencilAttachment: {
view: depthTexture.createView(),
depthClearValue: 1.0,
depthLoadOp: 'clear',
depthStoreOp: 'store'
}
});
pass.pushDebugGroup('Mesh zeichnen');
pass.setPipeline(pipeline);
pass.setViewport(0, 0, canvas.width, canvas.height, 0, 1);
pass.setBindGroup(0, uniformBindGroup);
pass.setVertexBuffer(0, vertexBuffer);
pass.setIndexBuffer(indexBuffer, 'uint16');
pass.drawIndexed(36, 10); // 36 Indizes, 10 Instanzen
pass.popDebugGroup();
pass.end();
device.queue.submit([encoder.finish()]);