The render pipeline

01-render-pipeline.md · updated 2026-10-15

Everything a GPU draws takes the same conveyor belt: vertices in one end, lit pixels out the other, each station doing one job and handing the result on. Knowing the order is how you work out where a bug or a stall lives — a wrong shape is upstream, a wrong colour is downstream.

The belt

Vertex data → vertex shader → primitive assembly → clipping → rasterization → fragment shader → depth/stencil → blending → framebuffer.

Three vertices, a million fragments

Per-vertex cost scales with model complexity; per-fragment cost scales with screen area — and area is the one that bites.

One triangle filling a 1080p screen runs the vertex shader 3 times and the fragment shader two million. So a heavy vertex shader rarely matters and a heavy fragment shader always does, resolution is the most expensive setting you own, and overdraw — shading a fragment a later one covers up — is work you paid for in full and threw away. That asymmetry explains most graphics performance advice.

Draw calls and state

The GPU is fast; the conversation with it is slow.

A draw call is "with this shader, these buffers and this state, draw N primitives". Each costs CPU time to validate and submit, and changing state between calls — shader, blend mode, render target — can force the driver to rebuild a pipeline and drain work in flight. Batching merges objects that share state into one call; sorting draws by state is worth as much as cutting their number.

The modern variant

Compute shaders run arbitrary parallel work with no triangles involved; mesh shaders replace the vertex stage with a program that emits small batches of primitives directly. Both are escapes from the fixed belt, not replacements for understanding it — rasterization, depth and blending are unchanged downstream.

Demo

One triangle stepping through every stage — auto-advance through the stages, then drag the pixel-size slider and watch the fragment counter against the stubbornly constant "3 vertices".

Gotchas

Symptom Cause
Frame rate collapses when the camera moves closerFill-bound: the same geometry now covers far more fragments
Halving the resolution nearly doubles the frame rateConfirms you are fragment-bound, not vertex- or CPU-bound
10k objects crawl despite trivial geometryDraw-call bound on the CPU — batch, instance, or merge
Big win from drawing opaque objects front-to-backYou were paying for overdraw; early-z can now reject most of it
Stall right after a shader or blend-state changePipeline rebuild mid-frame; sort draws by state
Geometry disappears at the screen edge instead of being cutClipping is fine, your near plane or guard-band assumption is not

Open the demo →

← all notes & demos