Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

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:

ColourLayers
black0 — nothing drawn
blue1
cyan2
green3
yellow4
orange5-6
red7-9
white10+

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.

ColourMeaning
near-blackno opaque surface
dark greyone surface owns the pixel — healthy
redtwo surfaces the buffer cannot separate — will flicker
whitethree 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.

ColourMeaning
a hue per levelthe level the pixel asked for and reads
darker huethe page asked for is not rendered; a coarser level is read
yellow stripesthe page read is dirty: something casting into it changed, and it is drawn again on the next run
cyanno page at any level holds the pixel
greypast the last level: nothing shadows the pixel
magenta everywherethe 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 for overdraw.
  • zfighting.renderFeature — the fill + counting + verdict passes for zfighting.
  • shadowPages.renderFeature — the level-coloured geometry pass for shadowPages.
  • shaders/diagnosticStep.shader — one rasterized fragment's contribution to a counting pass: a flat step_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.