fix #176: light pool tracked the camera via flood scoping - collect residents, anchor at player

The seam-floor purple flicker was NOT a draw z-fight. The in-engine
[seam-*] probe (ACDREAM_PROBE_SEAMDRAW - built because RenderDoc cannot
capture this pipeline: it hides GL_ARB_bindless_texture and the
mandatory-modern startup gate throws; AMD GPU rules out Nsight) killed
every double-draw suspect: ONE shell instance per seam cell at the
lifted z, no floor-coincident entity (portal entities sit at z=-12.05),
zero portal depth fans in the sealed Hub. What it caught instead: the
corridor floor's applied light set flipping wholesale with the flood.

Root cause: c500912b scoped BuildPointLightSnapshot by the per-frame
portal flood, on the research doc's gloss of CEnvCell::visible_cell_table
as "the portal-flood visible set". The named decomp refutes the gloss:
add_visible_cell (0x0052de40) DBObj-LOADS absent cells and inserts them;
a cell activation adds itself + its whole dat visible-cell list
(0x0052e228/0x0052e24a); entries leave only via the flush machinery.
It is the RESIDENT-cell registry - gaze can never remove a cell.
add_dynamic_lights (0x0052d410) walks the WHOLE table per frame
(caller 0x00452d30), and insert_light (0x0054d1b0) caps the pool by
distance to Render::player_pos (0x0054d1dd). Retail's pool is a function
of player position only. Ours followed the camera: turning changed the
flood (probe: 8..41 cells across one turn), the six intensity-100
under-room portal purples entered/left the pool, and the wedge blinked.

Fix: BuildPointLightSnapshot(playerWorldPos) collects ALL registered
(=resident) lit lights; over cap keeps dynamics FIRST (retail's separate
7-slot dynamic pool never competes with statics) then nearest-the-player;
the RebuildScopedLights callback is deleted. Live-verified with the probe:
full-circle turn, flood churning 8..41, the floor set held the same 8
identities on every post-spawn frame. The purple wedge SHAPE stays - it
is cdb-proven retail-faithful.

Residual deviation (AP-85 rewritten): single 128 pool vs retail's
7-dynamic/40-static degrade-scaled dual pools - the Hub now shows
7 purples + viewer where retail's cdb showed 4 + viewer + fixture slots;
if the gate reads the wedge as too purple, the A7 dual-pool cap is the
faithful trim.

Pins: PointSnapshot_HubScaleLightCount_ObjectSelectionIsCameraInvariant
(rewritten to the corrected model),
PointSnapshot_OverCap_DynamicsNeverEvictedByNearerStatics,
PointSnapshot_OverCap_KeepsNearestThePlayer,
PointSnapshot_ResidentCollection_CellTagDoesNotFilter.
Suites: Core 2599+2skip / App 726+2skip / UI 425 / Net 385.

The [seam-*] probes stay until the visual gate passes, then strip.
Correction banner added to 2026-07-06-a7-per-cell-lighting-pseudocode.md;
outcome banner on the z-fight handoff; ISSUES #176 updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-06 15:12:31 +02:00
parent 8cb3176daa
commit d8984e877f
12 changed files with 525 additions and 194 deletions

View file

@ -1,5 +1,26 @@
# Pickup handoff — #176 seam-floor lighting flicker = a RUNTIME draw z-fight (RenderDoc next)
> ## ✅ OUTCOME (2026-07-06, the pickup session) — z-fight REFUTED; it was the light POOL tracking the camera; FIX SHIPPED
>
> RenderDoc was infeasible (it does not support `GL_ARB_bindless_texture` and hides
> it → our mandatory-modern startup gate throws `NotSupportedException`; the AMD RX
> 9070 XT rules out Nsight). The equivalent evidence came from the in-engine
> `ACDREAM_PROBE_SEAMDRAW` probe (`[seam-blk]/[seam-cell]/[seam-snap]/[seam-ent]/
> [seam-mask]`): **one** shell instance per seam cell at the lifted z (suspect 1
> dead), **no** floor-coincident entity (suspect-class dead), **zero** portal depth
> fans (suspect 3 dead) — but the corridor floor's applied LIGHT SET flipped
> wholesale with flood composition. Root cause: the `c500912b` visible-cell scoping
> was built on a wrong gloss — retail's `CEnvCell::visible_cell_table` is the
> **RESIDENT-cell registry** (`add_visible_cell` 0x0052de40 dat-loads absent cells),
> not the frame flood, and `insert_light` (0x0054d1b0) anchors the pool at
> **`Render::player_pos`**. Fix: `BuildPointLightSnapshot(playerWorldPos)` — resident
> collection, dynamics-first player-nearest cap; `RebuildScopedLights` deleted.
> Live-verified: flood churned 8→41 cells over a full-circle turn while the floor's
> set held the same 8 identities. Details: ISSUES #176, register AP-85, the
> correction banner in `2026-07-06-a7-per-cell-lighting-pseudocode.md`. The document
> below is kept as the historical elimination record — its DO-NOT-RETRY table
> remains valid (all those channels stay dead).
**Paste the companion prompt below into a fresh session.** Read this file, then
`claude-memory/project_render_pipeline_digest.md` (top banner), then **ISSUES #176**.