Commit graph

2652 commits

Author SHA1 Message Date
Erik
8c6563cada fix(render): pair cell-shell index segments with their reordered batches
The S1 review round ordered cell-shell batches by source surface index in
ObjectMeshManager.UploadGfxObjMeshData but left the index segments handed
to the arena in TextureBatches dictionary order. FirstIndex is positional
over the segments, so every cell shell's batch pointed at another batch's
index range: magenta placeholder walls, stretched textures, and missing
faces in every dungeon and house (G1 FAIL 2026-09-02). One upload order is
now computed once and used by the count, fill, and batch loops alike.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 20:31:59 +02:00
Erik
e2543d0ef0 fix(content): S1 review round - retail default sides shape, mask hoist, upload order
Campaign OVERHAUL S1 review fixes (two Opus lens reviews, findings verified
by the lead against the decomp):

- a raw sides_type outside 0/1/2 constructs retail's default single-side
  shape (ConstructMesh @0x0059DFA0 loop bounds default to 1) instead of
  dropping the polygon; still counted as a data anomaly (corpus has none);
- the positive-surface stippling mask OR runs once per polygon before the
  degenerate-fan guard, as retail's count loop does (pseudo-C 426859-426866);
- untextured slots keep their mask accounting but bake no texture and no
  vertices (contract §9 item 3); the dev pak shrinks by 114 KB;
- failed surface-override / Surface / texture-dependency lookups are
  attempted and logged once per slot, not once per candidate;
- cell-shell batches upload in ascending source surface index, retail's
  built-EnvCell subset draw order (ConstructMesh attribute-range scan,
  DrawMesh @0x0059D4A0); ordinary GfxObj meshes keep storage order;
- CellMesh.HasDrawableGeometry documented as the admission rule without
  texture-dependency resolution (a conservative superset of emission);
- the stippling/surface equivalence sweep's cell half is pinned at zero
  again; InAscendingSurfaceOrder is marked bake/upload/test-only;
- plan §5: reviewer findings are verified by the lead, one skeptic at most
  for a blocking finding, never more than five agents per step.

Three new Content tests pin the mask hoist, the vertex-free untextured
slot, and the single-side fallback. Content 213/213, Core Meshing and
Conformance green, App hermetic 6,757/6,757, Release build 0/0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 19:37:25 +02:00
Erik
b681717c30 feat(core): S1 one CellStruct interpretation, installed-DAT scan, retire AP-234
Campaign OVERHAUL S1 chunk B.

- CellMesh.Build's render role is deleted; CellMesh.HasDrawableGeometry
  is the exact predicate (any sides_type candidate whose resolved surface
  passes the built-EnvCell (Surface.Type & 6) != 0 test), and the
  streaming build job consumes it. Core and Content no longer carry two
  CellStruct interpretations.
- Installed-DAT scan over the complete Environment corpus (5,346
  landblocks, 729,888 cells, 8,601,560 polygons, 8,608,746 candidates):
  3,197 old-rule admissions were untextured surfaces the NoPos proxy let
  through; zero new-only; zero unexplained; zero unknown sides values;
  counts pinned as goldens against the contract's DAT hashes.
- Canonical pins: 0xF4180104 has eight ST_DOUBLE clip-map polygons and 44
  drawable side calls; the canonical NoPos surfaces are type 0x11 and are
  constructed but skipped; two independent extractions of all ten
  canonical cells hash identically.
- AP-234 retired from the divergence register (161 active rows) with
  ConstructMesh 0x0059DFA0 / DrawEnvCell 0x0059F170 / DrawMesh 0x0059D4A0.
- StipplingSurfaceEquivalenceTests no longer claims a build-time NoPos
  skip; the cell half is report-only, the building half stays pinned.
- Inventory and policy remarks updated; plan ledger records chunk A and
  the capture kit.

Core Meshing+Conformance 95/95, Content 208/208 (Lane!=Timing,
Lane!=PreparedPackage), App EnvCell/LandblockBuild/Streaming 432/432,
Release solution build 0 warnings / 0 errors.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 19:11:17 +02:00
Erik
acf172469e feat(content): S1 exact CellStruct surface-index construction, recipe 8
Campaign OVERHAUL S1 chunk A. Retail's D3DPolyRender::ConstructMesh
@0x0059DFA0 is ported as one pure Core descriptor plus the Content
extraction that consumes it:

- side candidates come only from sides_type (0/1/2); NoPos/NoNeg mean
  UV-array absence only and never suppress a side (CPolygon::UnPack
  @0x00538650);
- ST_DOUBLE's second copy is reversed with a negative normal; ST_BOTH's
  negative side has a negative normal and forward fan order (reverse is on
  the copy ordinal, not the side ordinal);
- an absent UV-index array is UV index 0 (ConstructMesh @0x0059E691
  xor ebx,ebx, arbitrated on the PDB-paired binary); copyVert @0x0059C080
  zeroes coordinates only for a negative or out-of-range index or a vertex
  without UVs, never by clamping to slot 0;
- the subset owner is the source surface-array index, emitted in ascending
  slot order with retail's per-slot mask (2 > 8 > 4 precedence, positive
  surface OR on signed stippling > 0);
- built-EnvCell admission is (Surface.Type & (BASE1_IMAGE|BASE1_CLIPMAP))
  != 0 after surface resolution (DrawEnvCell @0x0059F170 -> DrawMesh
  @0x0059D4A0 arg4=1); untextured slots are constructed but not emitted;
- cell batches carry SourceSurfaceIndex, RetailSurfaceMask, RawSurfaceType,
  IsCellShell, and fixed clockwise raster cull (RenderMeshSubset
  @0x0059CA10); authored sides_type is no longer stored as GPU cull.

Prepared-mesh serializer gains the four fields; bake recipe 7 -> 8 with a
FullRebuild migration; pak format stays 2 (pinned). Ordinary GfxObj
extraction is unchanged. AP-234's register row and CellMesh unification
land in chunk B.

Core: 32 descriptor tests. Content: 170/170. Bake: 18/18. Launcher.Core:
365/365 (Lane!=Linux). Solution Release build 0 warnings / 0 errors.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 18:40:33 +02:00
Erik
b3b7d922f1 checkpoint(render): preserve pre-overhaul investigation state 2026-09-01 18:04:24 +02:00
Erik
e880860291 fix(render): restore landscape objects and walk alpha order 2026-08-31 10:49:33 +02:00
Erik
4808d4d10b refactor(render): remove Campaign FW probes 2026-08-31 09:14:47 +02:00
Erik
b8befded8b checkpoint: preserve user-gated FW closeout fixes 2026-08-31 08:27:37 +02:00
Erik
a2f2eb7d78 refactor(render): remove stale portal products from frame exchange 2026-08-31 06:28:26 +02:00
Erik
4d0379744b fix(camera): complete retail in-head adjustment behavior 2026-08-31 06:18:37 +02:00
Erik
11e68aad82 fix(render): clip walk content to authored portal views 2026-08-31 06:15:12 +02:00
Erik
90eba0ec3c fix(camera): port retail offset adjustment laws 2026-08-31 05:42:23 +02:00
Erik
cb22691beb refactor(render): remove the production portal frame carrier 2026-08-31 05:34:46 +02:00
Erik
69e69408e8 refactor(render): delete legacy pview execution machinery 2026-08-31 05:27:25 +02:00
Erik
1be4889c1c refactor(render): require the production frame walk 2026-08-31 05:09:51 +02:00
Erik
cd08cd2ae8 fix(render): clip weather particles to walk views 2026-08-31 05:01:00 +02:00
Erik
837a33baff refactor(render): cull terrain by walk in-view cells 2026-08-31 04:54:03 +02:00
Erik
98d2d43fdc refactor(render): make the walk sole production visibility owner 2026-08-31 04:50:08 +02:00
Erik
2a3ec09a98 refactor(render): delete the dead cell visibility bfs 2026-08-31 04:46:20 +02:00
Erik
73ba1d3c32 refactor(render): delete the packed dispatcher 2026-08-31 04:44:22 +02:00
Erik
8d8aa715ee refactor(render): delete the packed pview product 2026-08-31 04:37:54 +02:00
Erik
78547718c7 fix(render): apply retail marker admission to the walk 2026-08-31 04:31:22 +02:00
Erik
a40a57b9b3 refactor(render): flush alpha at retail walk barriers 2026-08-31 04:25:34 +02:00
Erik
877e935ac4 refactor(render): publish the walk visible-cell set 2026-08-31 04:15:55 +02:00
Erik
c9fb7e7d4c refactor(render): detach packed pview product from production 2026-08-31 04:10:10 +02:00
Erik
ef12170a3c refactor(render): retire packed dynamic walk passes 2026-08-31 04:05:17 +02:00
Erik
2127c955f6 refactor(render): move outdoor dynamics onto landscape turns 2026-08-31 03:57:45 +02:00
Erik
474b05e7dc refactor(render): move root-cell dynamics onto walk turns 2026-08-31 03:44:32 +02:00
Erik
f9a80fbc44 refactor(render): move look-in dynamics onto walk turns 2026-08-31 03:39:03 +02:00
Erik
966836895f fix(render): draw look-in dynamics only at retail walk turns 2026-08-31 03:22:01 +02:00
Erik
572de1ec30 checkpoint: preserve cathedral look-in investigation state 2026-08-30 23:29:28 +02:00
Erik
fc2e6b79bc fix(render): DynamicLast admits only the walk''s own root flood
The [dyn-route] trace pinned the through-wall remote player (visible
from 0xF4180101 AND outdoors while parented at 0xF4180112 - a cell
with no retail sightline chain from either): the legacy visibility
builder invents cross-building cell views at the cathedral (REAL
3-5-plane cones for 0x112, not the zero-plane trapdoor), the viewcone
admits his sphere, and he rides the last dynamics pass - post-clear on
interior roots (walls'' depth wiped) and post-world outdoors.

The stage-set split (synthesis plan step 4): an interior-parented
dynamic may ride DynamicLast ONLY when its parent cell is in THE
WALK''S OWN ROOT FLOOD (oracle-trace-conformant; retail draws look-in
occupants inside the landscape stage through the composed portal
chain, and unreachable cells'' occupants not at all). The frame
product''s build input gains RootFloodCells (the driver''s
InteriorFloodCells as a per-frame set); non-walk/diagnostic frames and
the comparison wrapper keep the legacy drawableCells meaning. The
[dyn-route] probe logs the new rootflood-excluded state.

Hermetic 6,762/0; InstalledDat walk conformance 40/1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 22:13:48 +02:00
Erik
4686a1ac74 probe(render): [dyn-route] DynamicLast admission trace + rain PASS note
The owner''s through-wall remote-player report (standing in 0xF4180101,
a player parented at 0xF4180112 - the south hall, geometrically
unseeable through the cathedral - renders through opaque walls; from
0xF4180104 he is correctly hidden). The DynamicLast admission chain
has three gates (indoor / old-apparatus look-in exclusion /
SphereVisibleInCell against old-assembly cell planes), and gate 3
carries a known trapdoor: a zero-plane scissor-fallback slice is
PASS-ALL. This trace logs, per entity ON CHANGE: parent cell, look-in
exclusion hits, the viewcone verdict, and the cell''s lifted-plane
shape, so one 0104<->0101 crossing with the remote player in place
pins the admitting gate. Rain gate 75eab42f is owner-PASSED.

Hermetic 6,762/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 21:55:24 +02:00
Erik
75eab42f3c fix(render): rain draws only while the player stands outside
The owner''s seam report (rain in the 0xF4180104/0xF4180101 crossing
strip where retail has none) led straight to retail''s actual rain
confinement, which is neither depth nor view clipping:
GameSky::Draw @0x00506ff0 gates its WEATHER pass (arg2==1) on
SmartBox::is_player_outside @0x00451e80 - Ghidra-arbitrated (BN''s
body was flag-idiom mush): (player objcell_id & 0xFFFF) < 0x100. The
sky pass always draws; rain draws ONLY while the PLAYER is in an
outdoor cell. One boolean - rain on the ledges, none the instant the
player crosses into an interior cell. Retail also runs the sky/weather
draw at DEPTHTEST_ALWAYS write-off (pc 0x00507063), confirming our
depth-disabled weather port; the missing piece was only this gate,
now applied at the executor''s weather site from the frame''s
PlayerCellId.

Hermetic 6,762/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 21:39:28 +02:00
Erik
280c054943 fix(render): FW4 - static-owner particles submit at their walk turns
Ports the user-verified #132 invariant (e102fb36 on the old pipeline)
walk-natively, on the owner''s direction: retail''s falls containment
is POSITIONAL - an outdoor emitter''s polys join the ONE alpha list
during its owner cell''s DrawObjCell in the far-to-near landscape walk
(DrawSortCell @0x005a17c0), so every nearer building''s pre-punch
barrier (DrawBuilding @0x0059f2a0''s FlushAlphaList @0x0059f30b)
drains the already-queued FARTHER content against still-true depth
BEFORE its punch stamps far-Z into the aperture. The walk path had
kept the per-building AlphaBarrier events but batched ALL landscape
static-owner submission into one lump at the pre-clear closure - after
every punch had run: the barriers fired over an empty queue and the
falls drained against punched-far aperture pixels (phase=pre in every
probe line, which is why six rounds of phase-staging repairs could
never see it - the phase was right, the position within the phase was
wrong).

New WalkFrameEventKind.StaticParticles: each landscape cell''s and
each building shell''s emitter owners now submit AT THAT TURN during
Replay (marked so the owner''s meshes flush first - retail''s
per-object order); the batched SubmitWalkLandscapeStaticParticles and
its closure/post-replay call sites are deleted for both root kinds.
Two driver sequence pins adjudicated to the new turn order.

Hermetic 6,762/0; InstalledDat walk conformance 40/1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 21:26:40 +02:00
Erik
f4522297cc probe(render): cathedral Phase I instrumentation
Implements the synthesis plan''s Phase I (instrument-first, no rendering
edits): the [falls] watch printing EVERY particle submission carrying a
real falls owner (the outdoor landblock emitters 0xCF418000-13 the six
fix rounds never traced) with route + phase; [falls-adm] admission
verdicts for F418 emitters at the retail ShouldDrawParticles port
(owner, cell, distance, in-set, eligibility); the continuous
[walk-root] line now separating the TRUE root flood
(InteriorFloodCells) from the visited union (root + look-ins - the
conflation the review corpus indicted) and printing the walk''s own
exit-view count; [walk-cam] replayable camera dumps; and the restored
[walk-portal] per-portal side/clip decisions. All under the existing
ACDREAM_PROBE_WALK_ROOT flag; the depth-checkpoint readback is
deferred until these answers narrow the question (mid-pass Vulkan
depth reads need render-pass splitting).

Hermetic 6,762/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 20:58:12 +02:00
Erik
6d990d38b3 docs: the cathedral seven-report synthesis + adjudicated FW4 plan
Files the independent review corpus (fable/opus/external + audit,
DAT-format, F418 DAT-geometry, retail decomp reconstruction) and the
synthesis that adjudicates their disagreements: retail roots INTERIOR
at the posed terrace defect pose with a ONE-CELL flood and ov=1 (the
free captures rooted outdoor - both were real); the falls are outdoor
landblock emitters 0xCF418000-13; the "thin seam bands" claim is
DAT-refuted (full-size exits; the seam is a coincident double
exit-to-outside plane at y=48); clear+seals live inside retail''s ov>0
block with a one-frame seal latch. Carries the instrument-first plan
(falls-owner trace, depth checkpoints, pose conformance) and the
decomp-pinned repair sequence toward FW4''s single-owner goal.

Also sweeps in the probe scaffolding slice 5''s path-limited add had
missed (RenderingDiagnostics field + the launch-options row wording).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 20:52:14 +02:00
Erik
7f500b97a3 revert(render): FW4 slices 4+5 - unanimous tri-review verdict
Reverts 0d6cd5c0 (slice 5) and f3a03efc (slice 4). All three
independent cathedral reviews (fable, opus, external) plus the DAT
geometry ground truth condemned both:

- Slice 5 misidentified its targets: owners 0x4F418012-15 are cell
  0xF4180106''s TORCHES + a prop + a plant (InteriorEntityIdAllocator
  namespace), not the falls - the real waterfall emitters are outdoor
  landblock objects in the 0xC namespace (0xCF418000-13). Moving torch
  flames pre-clear is the owner-reported in-cathedral particle
  regression.
- Slice 4''s straddle rule is invented (retail stages by actual
  shadow-cell membership), and its "emit once" contradicts retail''s
  deliberate mid-frame m_nFrameStamp re-arm @0x005a4886 (a part
  overlapping both scopes legitimately draws twice).

The synthesis of all seven review reports and the adjudicated fix plan
live in docs/research/2026-08-30-cathedral-synthesis.md (next commit).

Hermetic 6,762/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 20:50:52 +02:00
Erik
bb9212c70d fix(render): FW4 slice 6 - interior roots draw landscape unclipped
Five ordering repairs later the cathedral falls still bled, and the
final probe round proved why no ordering fix could ever land it: the
falls now submit pre-clear with depth test LESS - and STILL pass,
because the depth they test against is EMPTY. DrawWalkTerrainSlice
scissored + hardware-clipped the terrain to the walk''s exit-view
polygons, and at the #456 cathedral seam those authored portal polys
are thin BANDS far narrower than the real opening: most of the vista
never received terrain color or depth that frame (the visible scene
there was stale color), so unclipped alpha painted straight across.

Retail''s rule, per the PV campaign''s proven model and LScape::draw:
landscape content is view-CULLED, never view-CLIPPED - terrain blocks
draw whole, once per landscape turn (the views feed only CheckBlocks''
block visibility), and aperture exactness comes from the depth clear +
exit seals + interior repaint that follow in the walk''s own order.
This slice ports that: the driver fans exactly ONE terrain turn
(correcting slice 1''s per-view fan), and an interior root''s terrain
and sky draw unclipped/unscissored; the outdoor root keeps its single
full-screen slice (equivalent by construction). The walk''s views
still own the punch fans and dynamics apertures.

Hermetic 6,762/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 20:06:23 +02:00
Erik
0d6cd5c06e fix(render): FW4 slice 5 - straddling cell statics'' particles emit pre-clear
The cathedral falls'' true identity, pinned by the emitter/owner dumps
and the owner''s retail-vs-ACE oracle (retail renders them perfectly):
they are EnvCell STAB-LIST statics (interior-cell dat objects, ids
minted by InteriorEntityIdAllocator) whose geometry hangs out over the
lake. Our cell-statics route submitted every interior flood cell''s
static particle owners POST-clear ("walls own depth") - true for
content that stays inside, but a straddler''s outside half meets
seal/cleared depth and splats across the vista.

Retail''s mechanism is the outdoor shadow-cell draw: an object
overlapping outdoor landcells draws during LScape::draw (DrawBlock
pc:430056-430064, once per overlapped shadow cell), so its particles
submit in the landscape scope and drain at the pre-clear boundary
flush over true terrain depth; the cell''s own turn skips them
(drawn-once). This slice ports that rule for particles: after Collect,
the interior flood cells'' static records are classified by the shared
exit-plane straddle test (extracted as SphereStraddlesExitPlane from
the #118 dynamic split); straddlers'' owners submit in the pre-clear
closure and the post-replay cell-owners union subtracts them, so every
owner emits exactly once. Also adds the [walk-emit] probe dump that
falsified the unattached-route theory.

Hermetic 6,762/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 19:58:04 +02:00
Erik
f3a03efc81 fix(render): FW4 slice 4 - straddler particles emit pre-clear, once
The cathedral falls survived slices 2 and 3; the [walk-dyn] dump then
pinned the real gate: the falls emitters are interior-parented
exit-plane STRADDLERS that pass BOTH the outside-stage predicate and
the viewcone (outside=1 cone=1) - they were in the pre-clear union all
along, and the deliberate ExceptRoute(DynamicLast) stripped them so
they emitted in the post-clear last pass instead ("straddlers emit
HERE"). That placement protects an indoor flame from interior repaint,
but a straddling emitter whose particles hang OUTSIDE the exit portal
has no depth left to occlude them after the clear - they splat across
terrain and water.

Retail submits an emitter''s polys at its object''s FIRST draw - the
landscape stage for anything overlapping outdoor shadow cells - and
drains them at the pre-clear boundary flush over true landscape depth.
Under an interior root the late-union now keeps the outside-stage
owners (straddlers included), records them, and BOTH last-pass arms
subtract the recorded set so every owner emits exactly once. Outdoor
roots keep the old except-at-union placement: both stages drain at the
same final flush there, so the last-pass emission is already correct
and a pre-emission would double-composite.

Hermetic 6,762/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 19:37:18 +02:00
Erik
5f7ccdeab5 fix(render): FW4 slice 3 - the outside-stage predicate reads the walk flood
The cathedral falls persisted through the slice-2 seal fix; the probe
already held the tell: the same two emitter entities (f4243/f4285) draw
phase=pre on good frames and phase=post on bad ones. They are
interior-parented, so DynamicDrawsInOutsideStage takes the indoor arm,
whose first gate is flood membership - and that still read the OLD
apparatus flood, which drops cells at the #456 seam band. On those
frames the falls fell to the post-clear last pass and splatted across
the cleared depth (terrain and water alike).

Both predicate call sites (the frame product's BuildOutsideDynamicRoutes
via BuildAndBorrow's input, and the renderer's own outside-stage
classification) now receive the walk's VisitedCells on walk frames -
root flood plus look-in floods, all of whose objects retail draws inside
LScape::draw, pre-clear. drawableCells keeps its other roles. Also adds
the probe-gated [walk-dyn] per-entity classification dump (parent cell,
outside/cone verdicts) so a surviving repro pins the failing gate
directly.

Hermetic 6,762/0, Walk lane 213/1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 19:28:57 +02:00
Erik
c40aecfc8c fix(render): FW4 slice 2 - exit seals stamp the walk''s own flood
The cathedral falls shine-through (owner report at the #456 terrace
seam, toggling with the camera''s side of the seam): the probe''s new
phase tags pinned the falls emitters to the cell-owners route
submitting POST-depth-clear on interior roots - retail''s own timing,
safe only because seals own the aperture depth by the end-of-frame
alpha drain. Our seal SET still came from the old apparatus''s flood,
which misses exit portals at the #456 seam band (its never-drawn panel
family), so the drain z-passed across the unsealed opening.

DrawWalkExitPortalMasks now stamps the walk''s own ordered interior
flood (WalkFrameDriver.InteriorFloodCells, captured at
OnInteriorFloodDrawTurn - retail PView::DrawCells pc:432785-432786),
keeping the old assembly''s per-cell slice clips where present and
sealing unclipped where the old apparatus had no entry (benign: the
exact dat aperture polygon, z-tested). The legacy path keeps the old
seal draw. Also: ACDREAM_PROBE_WALK_ROOT gains [walk-part]/[walk-alpha]
phase-tagged submission/drain lines (the apparatus that pinned this).

Hermetic 6,762/0, Walk lane 213/1, InstalledDat walk conformance 40/1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 19:22:56 +02:00
Erik
37febd1fe6 fix(render): FW4 slice 1 - interior outside-view slices come from the walk
The FW3 visual gate's stairwell/grass transition flash (grass briefly
covering floor openings at doorway crossings - the #119 family) was the
FW3 dual path leaking: the walk decided WHETHER terrain draws while the
old PortalVisibilityBuilder assembly decided WHERE (slice planes, count,
scissor), and punch fans indexed the old slice array with walk view
indices. The new ACDREAM_PROBE_WALK_ROOT apparatus pinned the boundary
frames: fat/degenerate old-apparatus exit views splash terrain over
interior pixels, the interior depth-clear preserves color, and cells
absent from the walk's flood never repaint. Retail has ONE visibility
structure and cannot produce this.

ClipFrameAssembler.ReassembleOutsideViewFromWalk now materializes the
walk's own outside_view (pixel screen points -> standard NDC -> the
existing ClipPlaneSet machinery) into the assembly's outside-view block
after Collect, ahead of the single PrepareClipFrame publication (moved
below the walk block). The Landscape event carries the walk's active
view count on the record's existing OutsideViewCount field (trace
mapping compares kind only - zero oracle-fixture churn) and the driver
fans exactly that many terrain slices; activeTerrainSliceCount is
deleted end to end. Outdoor roots keep the assembler's single
full-screen slice, asserted ==1.

Hermetic 6,762/0 (4 new materializer tests pin the y-flip and
plane-sign conventions), Walk lane 209/1, InstalledDat walk conformance
40/1. Seals/cell slices/look-in seeding stay on the old per-cell views
for the rest of FW4 (identical dat polygons; only the visible set can
differ).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 18:21:08 +02:00
Erik
f3cb4b6cc9 fix(render) Campaign FW3 visual gate: sweep-fail frames root on the PLAYER cell
Owner report: floor openings (stairs down) briefly covered by outside
terrain at camera in/out crossings - the historic #119 family
transition arm. Root cause: at a doorway graze the camera collision
sweep can transiently fail; retail total-failure fallback
set_viewer(&player->m_position, 1) (update_viewer @0x00453fad, and the
cell==0 bail @0x00453d1d) copies the WHOLE player Position - objcell_id
included - so retail roots those frames on the player interior cell.
Our fallback left ViewerCellId = 0, which the walk reads as an OUTDOOR
root while the eye is inside: full-screen terrain with the look-in
gates correctly refusing from the interior side - grass over the
stairwell for exactly the failing frames. The fallback now inherits
the pivot/player cell, matching retail byte-for-byte.

Suites: full Release build 0 warnings; hermetic 6,758/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 17:45:18 +02:00
Erik
77fd40771b fix(render) Campaign FW3 visual gate: candle flames pre-clear too
Second owner report at the same gate: candle-flame particles still
invisible looking out. Flame emitters attach to STATIC owners (lamp
and candle statics), and the walk path submitted the landscape-stage
static-owner particle union AFTER the driver replay - post-clear, so
the pre-clear drain never carried them and a later flush z-failed
them at the sealed aperture. For an interior root the submission now
runs at the walk pre-clear boundary (retail inserts each emitter
during its cell landscape walk turn, before the clear+seals);
outdoor roots keep the post-replay frame-end placement (#132).
Interior cells own emitters stay in the final world scope where the
walls already own depth.

Suites: full Release build 0 warnings; hermetic 6,758/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 17:34:14 +02:00
Erik
f8fc21d467 fix(render) Campaign FW3 visual gate: interior dynamics draw PRE-clear
Owner gate report (Holtburg, looking out from a house): no doors on
buildings, no candle particles, the character vanishing for a frame
when crossing out. One family: the cutover moved the interior depth
clear + exit seals into the walk turn, but the dynamics phase
(outside dynamics, look-in dynamics, outdoor particles, weather)
still ran AFTER the whole walk - everything beyond the door plane
z-failed against the seals stamp, the exact #118 house-exit
clip+vanish class the old code documented.

Retail draws the outside world's objects INSIDE LScape::draw,
strictly before the clear+seals. The driver's clearInteriorDepth
closure now invokes the dynamics phase at that pre-clear boundary for
interior roots (_walkPreClearDynamics, assigned around the Replay,
cleared in finally); outdoor roots keep the post-driver order (no
clear exists there). Packed route consumption order is unchanged
(LookInObject -> LandscapeOutsideDynamic -> DynamicLast).

Suites: full Release build 0 warnings; hermetic 6,758/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 17:27:11 +02:00
Erik
1da178d9ba fix(render) Campaign FW3.4a: rebind sections per DrawOrderedRange - the device-lost fix
The dense-Arwic re-measure crashed with VK_ERROR_DEVICE_LOST: between
ordered ranges the walk leaf draws (terrain, shells, sky, punch fans)
and RetailAlphaQueue flushes rebind the SAME set-0 storage slots to
their own sections, so the bind-once latch made the next range draw
against foreign buffers - out-of-bounds instance reads and a GPU
fault. Sections now re-bind on every DrawOrderedRange call, exactly
like the proven DrawPreparedAlphaBatchRhi; the once-per-frame ring
WRITES in PrepareOrderedStream (the actual measured cost) are
unchanged. The bind-once referee test flips to assert per-range
rebinds with unchanged draw coverage.

Suites: full Release build 0 warnings; hermetic 6,758/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 17:07:29 +02:00
Erik
212f5a12e5 perf(render) Campaign FW3.4a: one walk pass; prepare-once/draw-ranges; arena records
The FW3.4 dense-Arwic pair triggered the +/-20% stop rule (+33.5% CPU
p50, 14x frame allocation). This slice removes the three measured
costs without changing GPU command order (the referee suites assert
identical recorded call sequences):

- WalkFrameDriver: Collect (ONE walk per frame - no GPU work; leaf
  calls and flush points become a recorded event list; the driver
  absorbed the renderer collection pass and exposes the visited sets)
  + Replay (prepare the whole stream once, then replay events,
  interleaving DrawOrderedRange with leaf calls in the exact recorded
  order). RunFrame = Collect+Replay for existing callers.
- WbDrawDispatcher: SubmitOrderedStream split into PrepareOrderedStream
  (all sections + commands + merge runs uploaded once per frame) and
  DrawOrderedRange (bind-once latch; per-run pipeline + DrawIdOffset +
  DrawIndirectRangeRhi). Load-bearing correctness catch from the
  implementation round: merge runs take FORCED BREAKS at the recorded
  event marks - whole-stream merging must not fuse two segments that
  retail separates with a leaf GPU call (shell, punch); the straddle
  assert stays as a dead-code safety net.
- WalkProductionWorldData: WalkFrameStaticRecords carries an
  ArraySegment into a per-frame grow-only arena; the per-cell
  fresh-array copies (the 1.9 MB/frame alloc p50) are gone - zero
  steady-state allocation after warmup.

Suites (lead-verified): full Release build 0 warnings; hermetic
6,758/0; Walk lane 209/1; InstalledDat Walk conformance 40/1
untouched. Next: the dense-Arwic re-measure against the same-session
baseline.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 17:01:26 +02:00
Erik
05240d2cab feat(render) Campaign FW3.3: ShellDrawLiftZ is RETIRED - cells draw at the dat origin
Retail draws cell geometry at the dat EnvCell origin verbatim; the
0.02 m lift was our z-fight stand-in (register row AP-32, deleted in
this commit). With the walk owning retail draw ORDER under
WorldDepthContract Less (first-drawn-wins: DrawBlock terrain-then-
objects per cell, DrawCells shells-then-contents), the coplanar
tie-breaks the lift compensated for are now resolved the way retail
resolves them.

Deleted at every site: the PortalVisibilityBuilder const + the
drawLiftZ Build parameter and its lifted exit-portal projection branch
(gate and drawn geometry now share ONE space); the seal/punch fan
lifts (DrawPortalDepthWrite + the walk's DrawWalkPunchFan); the
LandblockBuildFactory drawn-cell-transform lift (render and physics
share the one verbatim transform).

The #130 proof flipped exactly as its own doc predicted:
UnliftedGate_LeavesTheStripAtTheDrawnTopEdge is deleted (its premise -
gate space != drawn space - no longer exists), and the renamed
ExitDoorTopEdge_GateCoversTheDrawnApertureWithinPixelTolerance sweep
(147 eye/gaze combos at the Holtburg corner door) passes with both in
the same unlifted space (worst plane gap <= 1.2 px, scissor <= 0.15 px
- unchanged tolerances). Ten more replay-test call sites swept to the
new Build signature.

Suites: full Release build 0 warnings; hermetic 6,750/0; the 21
affected InstalledDat replay tests green; Walk conformance 40/1
untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 16:08:52 +02:00