Forward, Forward+, and Deferred — choosing the right path
Vulkan lets you build many kinds of pipelines. In practice, most real‑time engines gravitate toward one of three shading architectures: Forward, Forward+, or Deferred.
This page explains what each one is, why this sample chooses Forward+, and where the relevant pieces live in the code.
Forward rendering
Forward draws each object with its lighting in a single pass. It’s the most direct model: bind a material, bind lights (uniforms or textures/SSBOs), draw. It’s easy to reason about and integrates well with transparency and MSAA.
Pros:
-
Simple and predictable.
-
Good with transparent objects and MSAA.
-
Great for small light counts or baked lighting.
Cons:
-
Per‑pixel light loops can get expensive as the number of lights grows.
-
You evaluate lights even when most don’t affect the pixel.
Forward+ (what we use for dynamic lights)
Forward+ partitions the screen into tiles and assigns lights to those tiles with a compute pass. The main pass then shades with only the lights relevant to the pixel’s tile. In this sample we use a lightweight Forward+ that focuses on emissive/simplified lights to keep the code approachable.
Pros:
-
Scales to many local lights; you only evaluate lights that might affect the pixel.
-
Keeps forward’s strengths (transparency/MSAA friendliness).
Cons:
-
Requires a pre‑pass or depth info and a compute dispatch to build the tile lists.
-
More moving parts than plain forward.
Deferred shading (when to consider it)
Deferred writes material properties (G‑Buffer) in the first pass, then lights that buffer in a second pass. That turns lighting cost into “cost per lighted pixel” and tends to excel with many lights, but it makes transparency and MSAA trickier.
Pros:
-
Many dynamic lights at high performance.
-
Clear separation of material/write and light/evaluate.
Cons:
-
Transparent objects must be handled separately (often with a forward pass).
-
MSAA is more complex; memory bandwidth can be high.
What the sample uses (and why)
We use Forward+ for small, dynamic lights and a forward material path for everything else. That keeps the code compact while still letting you place many little lights around the scene. Transparency (glass) is shaded in a second forward pass so order and blending are correct.
If your project needs hundreds of shadowed lights and complex post‑lighting, explore a deferred path or a hybrid: deferred for opaque, forward for transparent.
Implementation highlights in this codebase
-
A small compute pass builds per‑tile light lists.
-
Per‑frame SSBOs hold tile headers/light indices; the main PBR pass reads those to loop only relevant lights.
-
Descriptor updates happen at the frame’s safe point so we don’t touch in‑use sets.
Where to look in the code
-
Forward/Forward+ render loop integration:
-
renderer_rendering.cpp
-
-
Pipeline + descriptor layout setup:
-
renderer_pipelines.cpp
-
-
Main PBR shader (reads per-tile light lists when Forward+ is enabled):
-
shaders/pbr.slang
-
The tile/cluster build shader is wired in renderer_pipelines.cpp. Start there and follow which compute pipeline is created for the Forward+ light assignment pass.
|
Choosing for your project
Use Forward if:
-
Light count is low, transparency/MSAA are priorities, and you want the simplest pipeline.
Use Forward+ if:
-
You want many local lights but still want forward’s strengths.
Use Deferred if:
-
You need to scale to many dynamic lights with complex lighting, and you’re ready to solve transparency/MSAA separately.
There’s no one answer; pick the simplest that meets your needs. You can always grow the pipeline later.
Future work ideas
If you want to expand the lighting system beyond “readable sample”:
-
Add clustered Forward+ (3D clusters using depth slices) instead of 2D tiles.
-
Add shadows (start with a single directional shadow map, then add point/spot shadows).
-
Add a small deferred path for opaque only (keep transparent as forward).
-
Add ray query helpers for selective effects (reflection rays, shadow rays, or AO probes) without building a full RT pipeline.