Render Pipeline
A render pipeline describes how the GPU turns vertices into pixels: which shaders run, how primitives are assembled, and which texture format receives the color. It is created before drawing. A draw call only binds it and calls draw().
The triangle example already runs one pipeline. This chapter separates the steps.
Shader module
Section titled “Shader module”WGSL source is compiled into a GPUShaderModule. createShaderModule() does not throw on compilation errors. Read getCompilationInfo():
const shaderModule = device.createShaderModule({ label: "Triangle shader", code: wgslSource,});
const info = await shaderModule.getCompilationInfo();const errors = info.messages.filter((message) => message.type === "error");if (errors.length > 0) { throw new Error(errors.map((message) => message.message).join("\n"));}The vertex and fragment entry points may share one module or live in two modules.
Creating the pipeline
Section titled “Creating the pipeline”const pipeline = device.createRenderPipeline({ label: "Triangle pipeline", layout: "auto", vertex: { module: shaderModule, entryPoint: "vs_main", }, fragment: { module: shaderModule, entryPoint: "fs_main", targets: [{ format: canvasFormat }], }, primitive: { topology: "triangle-list", },});layout: "auto"builds a pipeline layout from the@groupand@bindingattributes in the shader. It is the short path when the shader has no external resources. An explicit layout is shown with bind groups.vertex.entryPointmust name an@vertexfunction.fragment.targets[0].formatmust match the color attachment. For a canvas, use the result ofnavigator.gpu.getPreferredCanvasFormat(), which is"bgra8unorm"or"rgba8unorm".primitive.topologycontrols how vertices become primitives. The default is"triangle-list": every three vertices form one triangle.
Common topologies:
| topology | Meaning |
|---|---|
"triangle-list" | Independent triangle for every 3 vertices |
"triangle-strip" | Adjacent triangles share an edge |
"line-list" | Independent line segment for every 2 vertices |
"line-strip" | A polyline through successive vertices |
"point-list" | One point per vertex |
frontFace defaults to "ccw" and cullMode defaults to "none".
Render pass
Section titled “Render pass”The pipeline does not draw by itself. Each frame records commands:
- Call
context.getCurrentTexture(), thencreateView(). Do not reuse that view on the next frame. beginRenderPass()names the color attachment.loadOp: "clear"fills it withclearValue.storeOp: "store"keeps the result.- Call
setPipeline(), thensetVertexBuffer()andsetBindGroup()when needed, thendraw(vertexCount). - Call
pass.end(),encoder.finish(), anddevice.queue.submit().
const encoder = device.createCommandEncoder();const pass = encoder.beginRenderPass({ colorAttachments: [ { view: context.getCurrentTexture().createView(), clearValue: { r: 0.3, g: 0.3, b: 0.3, a: 1 }, loadOp: "clear", storeOp: "store", }, ],});pass.setPipeline(pipeline);pass.draw(3);pass.end();device.queue.submit([encoder.finish()]);The argument to draw(3) is a vertex count, not a triangle count. Under "triangle-list", three vertices make one triangle. The positions can still live in a WGSL array, as they do in the triangle example. The next chapter reads them from a buffer instead.
The same triangle is filled with "triangle-list" and outlined with "line-strip". The outline repeats the first vertex, so it draws four vertices.
triangle-list
line-strip
Move the coordinates into a buffer so mesh data can be uploaded from JavaScript. The pipeline fields are listed under GPURenderPipeline.