Builtin Library
readme.md
renderDiagnostics
Three diagnostic camera views that answer questions a lit frame cannot.
capture { pass = "overdraw" } -- how many fragments each pixel cost
capture { pass = "zfighting" } -- where two opaque surfaces own the same depth
capture { pass = "shadowPages" } -- which shadow-map level each pixel samples
All three are registered for the whole session. A capture view's passes are gated to its own debug channel, so nothing draws for a camera that is not a capture selecting the view, and the drawing feature is built the first time one is selected.
overdraw
Counts every fragment the rasterizer produced for a pixel, with the depth test switched off — so the count includes the fragments a depth test later discards, which is the shading work the stack costs whether or not it ends up visible.
The result is read by naming a colour:
| Colour | Layers |
|---|---|
| black | 0 — nothing drawn |
| blue | 1 |
| cyan | 2 |
| green | 3 |
| yellow | 4 |
| orange | 5-6 |
| red | 7-9 |
| white | 10+ |
Foliage, particles, glass, decals and UI-in-world stack layers by design, so warm bands over them are expected. Warm bands over a plain wall or floor are geometry drawn on top of geometry.
zfighting
Counts how many opaque surfaces come within a hair of the winning depth at each
pixel, by re-running the depth test the scene already resolved (lessEqual,
writing no depth) over a band a few depth-buffer steps deep. Normally exactly
one surface passes. Two surfaces the depth buffer cannot keep apart both pass —
and those are the pixels that flicker when either the camera or the surface
moves.
The band is what makes a still answer a question about motion. An exact depth test separates surfaces down to the last step the buffer holds, far finer than a frame holds onto: two surfaces one step apart are ordered in this frame and swap in the next. Counting the band asks how many surfaces are close enough for the order to be up for grabs.
| Colour | Meaning |
|---|---|
| near-black | no opaque surface |
| dark grey | one surface owns the pixel — healthy |
| red | two surfaces the buffer cannot separate — will flicker |
| white | three or more |
This is the view worth taking on every scene, because z-fighting is invisible in the frame you would otherwise look at: a still shows whichever surface won that frame, fully shaded and correct. The shimmer only exists across frames.
Common causes of red: a floor and a rug/road/platform placed at the same height, a wall and a poster at the same depth, two copies of the same mesh at the same transform, a ground plane that a terrain or building slab sits exactly flush with. The fix is separation (a few millimetres is enough at close range and scales with distance), a decal, or deleting the duplicate.
shadowPages
Colours each pixel by the level of the sun's virtual shadow map the lit path samples it from. Every receiver asks for the page of the level its screen footprint selects, so the levels form rings around the camera, and a page that is not rendered yet is read from the next coarser level that is.
The level and the page come from zshadow_directional_level and
zshadow_page_lookup, the functions the lit path calls, so what is drawn is
what the shading read rather than a second copy of the rule.
| Colour | Meaning |
|---|---|
| a hue per level | the level the pixel asked for and reads |
| darker hue | the page asked for is not rendered; a coarser level is read |
| yellow stripes | the page read is dirty: something casting into it changed, and it is drawn again on the next run |
| cyan | no page at any level holds the pixel |
| grey | past the last level: nothing shadows the pixel |
| magenta everywhere | the sun casts nothing this frame |
Brightness carries the surface normal so the scene stays legible.
What to look for. Dark patches that persist on a still camera are pages the pool could not hold or the page budget has not reached; stripes that persist on a still scene are pages that keep being invalidated, a caster whose shadow inputs change every frame.
Structure
renderDiagnostics.module— registers all three views;register()/unregister().overdraw.renderFeature— the counting + banding passes foroverdraw.zfighting.renderFeature— the fill + counting + verdict passes forzfighting.shadowPages.renderFeature— the level-coloured geometry pass forshadowPages.shaders/diagnosticStep.shader— one rasterized fragment's contribution to a counting pass: a flatstep_color, summed by an additive geometry pass.shaders/shadowPagesMap.shader— a surface's shadow-map level and page state as a colour.
A view of your own follows the same shape — register a name, gate a feature's
passes to the channel it returns. topics/rendering-capture-passes covers the contract.