makepad/libs/render/CLUSTERED.md
Admin d2130e6550 libs: model, mesh_edit, csg, scene, sim, render, xr
Squash of 25 work commits (Sep 1–12):
  237da90  render: the sprite lane hands the screen draw back the way it found it
  895b9a7  particles: an emitter can ride a body's own frame
  914471f  ai-hub: a feed session whose last socket left ends on its idle timeout; skin: parent, skinned centroid and a nodes-only rig for retargets
  3fcccf3  sim: the whole world is implicitly editable, and one seam says where the ground is
  5692fab  sim: the landform world proves itself — walker through the tunnel included
  f4af6df  sim: terrain knows who changed it — a plan layer over player history
  adbb078  render: a water volume can be physics without a picture
  c17480f  render: a non-rigid body may carry its own orientation
  acad401  sim: an agent with no route holds and retries instead of walking into the wall
  22b2bf9  sim + chat: the composed world surface takes a map floor; the chat gets plan tools
  faf8112  web path: the tessellator's lap timer, the trace span and the fusion cycle timer have no clock on the web
  d3472eb  tsdf: the clock-taking XR helpers are native-only — the browser has no depth camera and no Instant
  4946c9e  sim + render: a repaint is not a world edit — colour and glow restyles never rebake the lightmap
  4317f58  sim + render + chat: walk decks as a surface, the filmed body is no obstruction, the brief never asks
  9791279  render: support rigged models and custom materials across viewers
  3ce2792  sim: use deck geometry for collision and sensing
  c8b79ff  Add portable PBR, rig and soft-body authoring support
  108d423  Add transactional polygon modeling and editable asset documents
  38f4d3b  Refine editable modeling and firm yarn character behavior
  08a2637  sim: add entity-owned lights and vehicle headlights
  00d96a7  render: add clustered lights, local shadows and incremental GI
  cf916af  render: import glTF asset extensions and wire clustered GI
  c6d2cca  model: cut transaction memory and raise capacity limits
  41af47a  raytrace: add a CPU probe-ray BVH budget example
  c963d0a  libs: the game sim splits into makepad-scene and makepad-soft-body; render, model, fab and the asset importer retarget

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:40:31 +02:00

101 lines
5.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Clustered forward lighting
`Renderer` defaults to clustered local lights. Geometry submission, static slabs,
instancing, and GPU skinning are unchanged: one forward material pass, with
separate sun-shadow passes. No G-buffer, geometry prepass, compute shader, depth
readback, or per-object light selection is required by clustering.
This follows the CPU-assigned clustered-forward approach described in
[Filament's rendering documentation](https://google.github.io/filament/main/filament.html).
It is a first lighting increment, not Filament feature or visual parity.
## Lighting and shadow policy
| Local lighting | Sun mode | World lighting work |
| --- | --- | --- |
| Clustered (default) | Realtime | CSM; no world atlas or CPU probe bake |
| Clustered | OnChange | Sun-only atlas and legacy CPU probes |
| Legacy (`MAKEPAD_CLUSTERED=off`) | Either | Original eight-light selection and lamp atlas |
Baked per-model AO is retained in every mode. Clustered lights reach primitives,
terrain, diffuse/PBR/custom models, and skinned characters per fragment. PBR
materials evaluate local GGX specular as well as diffuse. Explicitly prelit
materials retain their existing unlit/baked-color semantics.
Local lights can request budgeted realtime shadows; see [Entity lights](ENTITY_LIGHTS.md)
for Splash headlights, point/spot fixtures and shadow budgets. Generic lights
default to unshadowed and can shine through walls. The sun retains its own tier.
Engine-authored lights use `(1 - distance / radius)^2`; imported glTF punctual
lights use their separate photometric mode. Full linear HDR, exposure/tone
mapping, IBL and unified PBR materials remain follow-ups. Clustering does not
itself add MSAA.
## Budgets and stereo
`Renderer::set_cluster_config(ClusterConfig { ... })` controls XY tiles, depth
slices, lights per cluster, and total uploaded lights. Defaults are 16 × 9 × 24,
32 lights/cluster, and 1024 total. Hard limits are 32 × 24 × 32, 64 lights/cluster,
and 4096 total. `set_clustered_lighting(bool)` switches the complete legacy/new
lighting policy and invalidates old baked state. Policy survives realm changes.
Flat views use frustum tiles with logarithmic depth. The backend currently
late-latches XR eye matrices without exposing them to this host. XR therefore
uses a conservative world-space 3D grid over light influence, shared by both
eyes—not clusters derived from an unrelated flat camera. Large, dispersed worlds
make that fallback less selective. Exact stereo/per-eye frustum clustering needs
an eye-view snapshot API before it can replace this fallback.
Smaller `lights_per_cluster` bounds fragment cost but may visibly omit lights;
finer grids cost CPU/memory but can reduce false-positive light references. Start
device tuning with the counters, not a claimed universal Quest preset. This path
has not yet been benchmarked on a headset.
`RenderStats::clustered` / `Renderer::cluster_stats()` report build/upload-queue
CPU microseconds, uploaded bytes, occupancy, references, rejected lights, and
overflow. They do **not** measure GPU time. Per-cluster overflow retains lights
with the strongest conservative contribution estimate; equal scores retain the
earlier input. The global cap keeps the first valid input lights. Counts are
explicit because neither cap promises all lights survive. Overflow logs are
throttled; `MAKEPAD_CLUSTER_STATS=1` also logs frame counters periodically.
## Texture ABI
One nearest-sampled RGBA32F texture contains cluster headers, packed light
indices (four scalar indices per texel), then three texels per light:
position/radius, RGB/angular falloff, and normalized emission direction/mode. Headers
store scalar index offset/count. CPU list storage and texture upload vectors are
reused. Integer-valued floats are exactly representable within the hard caps.
The shader mixin and CPU packing live together in `src/clustered.rs`.
Local shadows use a separate nearest-sampled metadata texture (one light header,
four camera rows per face) and a depth atlas, preserving the light-data stride.
Inherited texture bindings are resolved by name for derived PBR materials.
## Reproduce
Build the sandbox in its own workspace, then launch from the checkout root:
```sh
# In apps/sandbox:
cargo build --release -p makepad-sandbox
# From the checkout root:
SANDBOX_WORLD=clustered MAKEPAD_CLUSTER_STATS=1 ./apps/sandbox/target/release/makepad-sandbox --remote
```
The fixture supplies 256 moving lights and a single large floor, independent of
downloaded models. `MAKEPAD_CLUSTER_DEMO_LIGHTS=N` changes the count;
`MAKEPAD_CLUSTER_DEMO_FREEZE=1` fixes light motion for A/B images. Add
`MAKEPAD_CLUSTERED=off` for the old path. That comparison deliberately shows
missing lights in the old eight-slot path; it is **not** equal-quality GPU work.
F8 switches sun modes. Finish owned test instances via the printed remote port's
`/gq` endpoint.
```sh
cargo test --release -p makepad-render
cargo test --release -p makepad-render clustered::tests::benchmark_build_and_pack -- --ignored --nocapture
```
Coverage includes more than eight overlapping lights, depth/tile boundaries,
rotated and orthographic cameras, XR/world-grid lookup, empty/invalid inputs,
bounded overflow, texture packing, material shader compilation/binding order,
and mode/realm lifecycle. The ignored benchmark measures CPU build+pack only.