Commit graph

2006 commits

Author SHA1 Message Date
Erik
015b660d0f test(render): pin cell-shell batch-to-index-segment pairing under reorder
Uploads a cell-shell mesh whose storage order is the reverse of its surface
order and asserts each uploaded batch's FirstIndex reads back its own
indices from the arena. Guards the G1 regression fixed at 8c6563ca.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 21:10:16 +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
0840d5fb77 docs(render): document the checkpoint's probe flags and re-pin portal_depth
The b3b7d922 investigation checkpoint committed two probe read sites
(ACDREAM_PROBE_CATHEDRAL_SKIP_PUNCH, ACDREAM_PROBE_FACILITY_STAIRS) without
their launch-options rows, and removed the #117/#129 bias from
portal_depth.vert without re-pinning its SPIR-V oracle hash. Both left the
hermetic App lane red at the OVERHAUL v2 base. Rows added (both default-off,
scheduled for deletion in S5); the shader pin now records the binary on the
branch, with S4 named as the owner of its acceptance.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 19:14:57 +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
b8befded8b checkpoint: preserve user-gated FW closeout fixes 2026-08-31 08:27:37 +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
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
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
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
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
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
8f8b0b9c57 test(render) FW3 gate: the fallback camera test pins the corrected retail cell
The total-fallback assertion pinned ViewerCellId == 0 - the pre-fix
adaptation. Retail set_viewer copies the whole player Position,
objcell_id included, so the fallback inherits the player cell; the
re-extend-from-the-player property the test exists for is unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 17:46:17 +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
Erik
4918677b45 feat(render) Campaign FW3.2b-2: THE STATIC CUTOVER - the walk drives production statics
The retail frame walk now drives every production static draw. In
RetailPViewRenderer.DrawInside, when the concrete executor + the
packed product + the FW3.1 walk registries are all wired (all
production compositions - anything less throws):

- A pre-walk events-only collection pass (the shadow sink generalized
  to WalkVisitedSetCollector) gathers the frame's visited cells,
  buildings, and landscape-cell turns; the visited cells union into
  prepareCells so EnvCellRenderer prepares every shell the driver
  draws.
- DrawWalkDrivenStatics runs the WalkFrameDriver over the production
  world data (WalkProductionWorldData over RenderSceneQuery + the
  building registry): sky, terrain slices, outdoor statics at their
  landscape-cell turns, buildings (alpha barrier -> punch/look-in
  passes -> shell) in retail order, interior clear+seals as leaf
  closures (the old tail block's drain reasoning moves with them),
  flood cells shell-then-contents. Landscape/cell-stage particle
  owners re-source from the walk's visited sets - retail gates
  particles per cell turn (ShouldDrawParticles @0x0050FE60), which
  this is; the old sphere filter was the approximation.
- DrawLandscapeDynamicsPhase + DrawBuildingLookInDynamics carry the
  dynamics-only remainder (LookInObject now dynamic-classified,
  late outside-dynamics + weather, particle unions); DrawDynamicsLast
  and the outdoor flush are unchanged.
- The product builder stops emitting LandscapeOutdoorStatic /
  LandscapeBuildingShell / CellStatic (methods deleted, dead index
  tracking removed); LookInObject loads cells with
  includeStatics: false.

The old static path survives ONLY behind !walkActive for the
standalone/diagnostic executor-fake path that keeps 15 retail-ordering
regression tests exercising the barrier/punch/seal machinery; no
production composition can reach it. Its deletion is FW4 scope (the
plan's "deleting the patch apparatus") - recorded in the plan.

Transitional risks recorded in code/report: the two-pass walk cost
(FW3.4 measures), the interior slice-count reconciliation between the
old clip assembly and the walk's own exit-view survival, and the
outdoor merged-flood punch coverage now riding the walk's own
building-BSP punches (retail-faithful per FW1; the owner visual gate
verifies).

Suites (lead-verified): full Release build 0 warnings; hermetic
6,750/0 (baseline minus the three deleted route tests); Walk lane
201/1; InstalledDat Walk conformance 40/1 untouched. The two
IL-branch tests the implementation round reported failing pass in
every lead run - the recurring parallel-load flake pair.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 15:43:22 +02:00
Erik
1b59eae428 feat(render) Campaign FW3.2b-2 step 1: the production leaf adapter
Additive, nothing invokes it yet (the cutover flip is step 2):

- WalkProductionLeafRenderer + RetailPViewPassExecutor.WalkLeaf: the
  walk driver's leaf turns over the SAME executor renderers the packed
  path uses today - DrawWalkSky (the per-slice sky block looped under
  one driver turn), DrawWalkTerrainSlice (the terrain block of
  DrawLandscapeSlice), per-cell shells via EnvCellRenderer,
  DrawWalkPunchFan (PortalDepthMaskRenderer far-Z, +ShellDrawLiftZ
  matching today's DrawPortalDepthWrite until FW3.3 retires it), the
  alpha barrier via FlushLandscapeAlphaFartherThan, and caller-supplied
  clear/seal actions (the renderer owns the pass scope). The cutover
  changes ORDER, never leaf mechanics.
- Punch fans now carry the ACTIVE VIEW INDEX end to end (retail pins
  building_view = Render::portal_view_num @0x0059f3bf for the whole
  two-pass walk; the fan clips by that view's slice planes):
  PortalPassSink.ActiveViewIndex -> IWalkEventSink.OnPunchGeometry ->
  IWalkFrameLeafRenderer.DrawPunchFan.
- The FW3.2b-2 rooting design is recorded in the plan (dual-compute
  split, LookInObject route filtered to dynamics, consumer
  re-pointing, gate list).

Suites: full Release build 0 warnings; Walk lane green; hermetic green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 14:40:24 +02:00
Erik
035d7b0148 fix(render) Campaign FW3.2b-1: interior draw order - landscape before cells
The oracle trace order DI|DC|LS is breakpoint-ENTRY order; retail's
actual DRAW order inside PView::DrawCells @0x005a4840 for an interior
root is LScape::draw FIRST (pc:432719, only when exit views survive),
then the depth clear (pc:432731-432732), the exit-portal seals
(pc:432785-432786), THEN the flood's own cells far-to-near. The
driver drew flood cells before the landscape - inverted.

RetailFrameWalk.DrawInside gains the additive
OnInteriorFloodDrawTurn(cells) hook firing after the conditional
landscape turn; the DC EVENT stays at its original point (conformance
untouched - 40/1 InstalledDat green). WalkFrameDriver records the
interior flood at the DC turn and draws it at the new turn:
flush -> ClearInteriorDepth -> flush -> DrawExitSeals -> per-cell
shell-then-contents. Building look-in floods still draw immediately at
their building turn (retail's reentrant DrawCells with no clear/seal).
Two new leaf members map to IWorldPassScope.ClearInteriorDepth and the
seal-fan machinery at FW3.2b-2. Reconciliation note recorded: the
driver clears unconditionally for interior roots while production
stages the clear on OutsideViewSlices>0 - observably equivalent at
ov=0, awaiting a firmer decomp read of the clear's gate.

Suites: full Release build 0 warnings; Walk lane 201/1 skip;
hermetic 6,753/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 14:34:45 +02:00
Erik
03f63686cc feat(render) Campaign FW3.2b-1: the walk frame driver
WalkFrameDriver executes one full static-content frame from the walk's
turns so GPU command-buffer order equals retail's walk order. One rule
does the interleaving: the accumulated OrderedDrawStream flushes
through SubmitOrderedStream immediately before EVERY non-stream draw
(sky, terrain slice, cell shell, punch fan, alpha barrier).

Turn script, all decomp-cited and two of them corrected in review:
- Interior flood: per cell IN FLOOD ORDER, shell first then contents
  (PView::DrawCells @0x005a4840: DrawEnvCell @0x005a4abe precedes
  DrawObjCellForDummies @0x005a4b0d).
- Landscape: sky once, terrain per active slice, then blocks
  far-to-near; per cell the building turn precedes the cell's outdoor
  statics (DrawSortCell @0x0059f140).
- Building (DrawBuilding @0x0059f2a0): the BLD probe event stays at
  entry, but the ENTIRE body - alpha barrier, portal passes, shell -
  sits inside retail's gfxobj[deg_level]!=0 gate @0x0059f2d3, and the
  order is FlushAlphaList @0x0059f30b -> the two-pass punch/look-in
  walk -> THEN the shell draw @0x0059f345. The driver review caught
  both the missing gate and a shell-before-punch inversion; fixed
  with the addresses cited.

Walk seam: three additive default-implemented IWalkEventSink hooks
(OnLandscapeCellTurn / OnBuildingTurn / OnBuildingShellTurn /
OnPunchGeometry) - every existing sink and all FW1 conformance
fixtures unchanged. WalkLandBlock gains LandblockId for the cell-id
encoding. Leaf draws go through IWalkFrameLeafRenderer so FW3.2b-2
wires the real renderers and the referee suite runs on fakes +
RecordingGpuDevice.

Flagged for FW3.2b-2/FW4 adjudication (documented in code):
FlushFartherThan(building distance) vs retail flush-all
FlushAlphaList(0f); terrain-before-statics within the landscape turn.

Suites: full Release build 0 warnings; Walk lane 200/1 skip;
InstalledDat Walk conformance 40/1 untouched; hermetic 6,752/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 14:23:17 +02:00
Erik
81c6531727 feat(render) Campaign FW3.2a: the walk-to-draw population layer
The piece that turns walk-visited static content into draws, with no
production frame wiring (FW3.2b roots the frame):

- TryClassifyBatch: ONE shared per-batch classify core (the #426
  untextured gate, #188 opacity promotion, texture resolve, foliage
  classification, in the exact original order) extracted from
  ClassifyBatches; the classic and packed classifiers now call it -
  behavior-identical, proven by the full hermetic + InstalledDat +
  Core Wb suites.
- ClassifyEntityForWalk / WalkClassifiedBatch: the per-entity seam
  yielding per-batch keys + instance data WITHOUT InstanceGroup
  bucketing, plus the per-part selection data (picking stays alive on
  the walk path - the survey's unlisted-consumer fix).
- WalkStaticStreamPopulator: per-entity walk-ordered opaque appends
  (under depth Less, opaque order is pixel-relevant only for coplanar
  surfaces, which retail resolves first-drawn-wins in ITS order -
  never material-grouped), translucent instances to the SAME
  RetailAlphaQueue via SubmitWalkAlphaInstance (identical viewer
  distances; walk-order submission improves retail's tie fidelity),
  selection parts published per entity.
- SubmitOrderedStream now owns _orderedDrawCullModes, retiring the
  FW2-recorded alpha-scope interleaving constraint;
  DrawIndirectRangeRhi takes an optional cull array (all existing
  call sites unchanged). The referee test was verified to FAIL
  against the old shared-scratch behavior.
- WalkDrawStage.OutdoorStatic added for the landscape turn.

Suites: full Release build 0 warnings; Walk lane 195/1 skip;
hermetic 6,747/0 (the two failures the implementation round reported
were transient - both pass in isolation and in the full run).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 13:52:23 +02:00
Erik
b10ad662b0 feat(render) Campaign FW3.1: production walk world data behind the seam
The retail frame walk's world model now materializes from production
landblock-build owners through the legal IDatReaderWriter seam, with
zero frame wiring (FW3.2 roots the frame):

- WalkCellFactory: WalkCell built in the SAME pass as LoadedCell
  (EnvCellLandblockBuild.BuildVisibilityCell) from the raw portal
  Flags/polygons/planes/stab lists already parsed there; stored as
  LoadedCell.Walk, committed atomically with the cell. The
  fixture-pinned decodes (inverse-0x2 portal side, 0xFFFF->0xFFFFFFFF
  exit widening) live here.
- WalkBuildingFactory + WalkBuildingRegistry: the production
  WalkBuilding build (drawing BSP with PORT nodes, degrade ladder,
  portal sides/stab lists, sort center, model frame) from the SAME
  LandBlockInfo the streaming build already fetches, under the
  factory's existing DAT lock - closing the gap where BuildingLoader
  drops every walk field at load.
- WalkLandscapeAssembler: the retail 51x51 viewer-centred grid
  (mid_radius 25) fed incrementally from landblock publish/retire;
  per-block z-slab (heightTable[max]+200 / [min]-1) computed
  worker-side in LandblockBuildFactory from the heights already in
  hand. O(1) SetViewer on same-block frames.
- WalkProductionFrameContext: the walk's frame contexts over
  CellVisibility + WalkBuildingRegistry with a generic
  inverse-view-projection ray caster (rays feed cross products only -
  scale-free) and the znear=0.1 CY plane.
- Publication: LandblockRenderPublisher owns both walk registries,
  publishing in the same AdvanceCompleteOne step as BuildingRegistry
  and retiring in RemoveBuildingRegistry - same commit, same
  retirement, no new ticket stage.

Conformance: ALL TEN oracle fixtures replay identically through the
PRODUCTION builders (WalkProductionWorldConformanceTests) - same
signatures as the test adapter, first run. Known gap documented for
FW3.2: far-tier landblocks carry no EnvCell transaction, so their
z-slab never reaches the assembler.

Suites: full Release build 0 warnings; Walk lane 186/1 skip;
hermetic 6,738/0 (+24); RuntimeDatAccessArchitectureTests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 13:19:22 +02:00
Erik
e65644cb33 feat(render) Campaign FW2: OrderedDrawStream + walk-order submitter
The walk-order submission layer over the existing RHI (plan section FW2):

- OrderedDrawStream: append-only walk-ordered draw commands
  (GroupKey + transform + per-instance data + WalkDrawStage + cell
  provenance), struct-of-arrays with one lockstep Reset (#193 shape).
  The PortalPunch stage exists but has no FW2 submission path - the
  submitter throws on it; punch emission lands with FW3 wiring.
- WbDrawDispatcher.OrderedStream partial: per-instance-first emission
  (the deferred-alpha shape - command i owns instance i, walk order
  survives into the indirect array), each SSBO section written once,
  then one DrawIndirectRangeRhi call per maximal merge run. Runs are
  built by pure-CPU BuildOrderedMergeRuns and may never span a stage,
  pipeline-bucket, or cull boundary; ValidateMergeRun re-checks every
  emitted run and throws (the campaign fail-loud rule). Nothing is
  sorted, reordered, or dropped: N commands in, N indirect commands
  out, covered exactly once.
- WorldDepthContract: retail world depth verified verbatim from the
  decomp - Render::zfuncVal @0x00820e1c = 0x2, SetDepthBufferMode
  @0x005a2d10 writes the enum directly as D3DRS_ZFUNC so the value IS
  D3DCMP_LESS, applied by the surface-state applier @0x0059c80a with
  Z-write toggled by blend; the LESSEQUAL sites are GameSky::Draw-local.
  Seven world pipeline sites now cite the named constant (no value
  changes).
- Plan updated: FW1 status block + gate amendment (the ten pose-stamped
  retail traces supersede re-expressing the old-builder replay
  fixtures; those retire with the old builder at FW4 and their
  scenario classes re-verify at the FW3/FW4 connected gates).

Known FW2 scope notes recorded in the code: the building-detail
overlay replay is production wiring (FW3); the _drawCullModes scratch
may not interleave with a mid-flight RetailAlphaQueue scope (FW3
sequencing constraint). The pixel A/B equivalence proof rides FW3's
cutover toggle where a walk-driven scene first exists.

Suites: full Release build 0 warnings; Walk lane 154/1 skip;
hermetic 6,714/0 (+27 new).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 12:31:31 +02:00
Erik
77f5342b62 feat(render) Campaign FW1: doorway-still conformant - deg_mul is dynamic
Render::deg_mul is auto-tuned by frame load and SWUNG between oracle
captures: doorway-still ran right after the heavy terrace-edge capture
with the multiplier depressed (mul <= 0 puts thresholds at/below ideal,
so 001e/0026/002f select the portless level 1 - retail's zero look-in
floods, reproduced exactly at mul = 0), while every other fixture pins
~ +0.99 (thresholds at max). The recon session's "-0.99" live dump was
real - taken under the same cdb load. RetailFrameWalk now exposes the
multiplier; the doorway test pins 0, the rest use the default.

foundry-entry: frames 1-66 reproduce exactly; the F67-F79 standing
segment diverges ONLY in building 0036's intra-building DC order
(retail 116,118,11d vs replay 11d,116,118). The BSP-traversal microscope
pins the flip to 0036's ROOT plane (N=(0,0,-1) D=2.8): replay eye z
2.33 (d=+0.47, NEG-first) vs retail behaving as d<0 (POS-first) - a
structural ~0.5 m frame question (positionPush(2)/part-scale), only
adjudicable live. Turnkey probe:
tools/walk-oracle/fw1-f67-viewpoint-probe.cdb.

Fixture status: nine of ten fully conformant; foundry-entry exact
through F66 with the 13-frame order segment parked on the probe.
Suites: Walk 127/1 skip; hermetic 6,687/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 11:49:27 +02:00
Erik
c1a029edf9 feat(render) Campaign FW1: NINE of ten fixtures conformant - the degrade arm
The moving-fixture divergence was the building degrade ladder:
GfxObjDegradeInfo::get_degrade @0x0051e4b0 (Ghidra-verified - BN's
FPU-flag pseudo-C misread BOTH arm selection and one formula) slides
each level's threshold from ideal toward MAX as the multiplier
approaches 1, and the live client runs the positive arm at ~0.99, so
level 0's portal-bearing BSP survives to ~max_dist (48 for the Holtburg
cottages), not ideal (24). The recon note's "deg_mul = -0.99" was a
sign misread; the negative arm's threshold slides toward MIN and
contradicts the fixtures from both directions. With the two-arm port,
holtburg-walkout, holtburg-transitions, and holtburg-walkabout pass
every pairable frame - the walkout-F2 microscope's prediction
(103,100 | 100x3 | 124 through the cottage exit views) landed exactly.

Also this round, falsified and reverted: a BN-driven swap of the
portal walker's negative/in-plane arms (Ghidra shows side 1 = negative
EMITS, side 2 = in-plane does not - the original port was correct; the
swap broke four fixtures). The walker docs and unit tests now pin the
Ghidra-verified truth table.

Parked with findings: foundry-entry F67 (right flood set, one
plane-side classification at the +/-eps boundary orders 11d before
116/118) and doorway-still (retail shows zero floods at a pose one
meter from walkout-F2's flooding pose; multi-portal clip boundary).

Suites: Walk 124/3 skips; hermetic 6,687/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 11:34:45 +02:00
Erik
17ee543cb1 test(render) Campaign FW1: moving-tail state + falsified hypotheses recorded
The moving driver gains adjacent-pose tolerance (the marker-lag capture
artifact) and stays parked: the four moving fixtures diverge at
punch-edge frames under BOTH adjacent poses. FALSIFIED this round and
reverted: the raw-decode + flipped-gates convention (it broke three
still fixtures - the inverted-decode convention stands, six still
fixtures frame-exact). Remaining instruments in the Skip note: znear,
per-view punch ordering inside DrawMesh, exit-view precision.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 11:12:22 +02:00
Erik
66f9e0d459 feat(render) Campaign FW1: SIX OF TEN FIXTURES FULLY CONFORMANT
Three final pins complete the still-fixture set: (1) the CELL portal
side decode is the INVERSE of the 0x2 bit (uniform with the building
convention; the doorway-still flood proved it - ov=2 n=3 exact, and
foundry-deep stays green); (2) interior frames key the landscape order
off the OUTSIDE-projected landcell (get_outside_cell_id - derived from
the camera origin); (3) the outdoor pview has draw_landscape=FALSE so
look-in floods discard exit portals - the ov=0 pattern of every traced
look-in. CONFORMANT: foundry-deep (every frame), doorway-still,
street-outdoor, terrace-center, terrace-edge (the #456 acceptance
pose), cathedral-arrival - full frames identical to retail. The moving
four diverge only at punch-edge frames (walkabout F9, foundry-entry
F67) - pose-timing sensitivity parked in the driver Skip note.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 11:07:29 +02:00
Erik
28475d0544 feat(render) Campaign FW1: street fixture retail-exact except flood depth
Three pinned corrections close the outdoor frame: (1) the camera basis
RE-pinned to +Y-forward - the terrace-edge fixture EXTERNAL ground
truth (the ledge faces the F518 vista, east) overrules the walkabout
motion sweep, whose camera was mouse-turned off the run line; (2)
alwaysDrawObjects (.data default 1 @0x00820ed4) drawn into the
DrawSortCell gate - cell contents draw for every cell of an in-view
block; (3) the building portal side decode data-pinned as the INVERSE
of the cell 0x2 bit (the sweep winning arm). Street result: all 13
buildings in retail exact order (a9b3003c in, aab50002 correctly
absent), punches at exactly 001a and 0022 opening the exact first
cells 16e/164. SOLE remaining delta: look-in flood depth (mine 1 cell,
retail 3-4) - the doorway-still fixture adjudicates the interior flood
in isolation next.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 11:02:16 +02:00
Erik
5f18ba5507 test(render) Campaign FW1: the portal-gate microscope + its verdict
Per-portal dump for the street fixture (archived beside the fixtures):
retail punched exactly the buildings whose portal polygons MY camera
model clips to NOTHING (001a/0022: clipN=0, doorways ~75 deg off my
north-facing axis) and skipped the ones mine puts on-screen (001e/0026:
clipN=4-6) - a clean inversion isolating the final look-in delta to
either the camera FACING decode (re-check the terrace-edge fixture
against its known vista direction; the motion sweep's 20-degree
residual is now suspect) or a portal-pass clip mechanism that does not
gate on the active view (cdb ConstructView(CBldPortal) trace next
retail session). All layers above are retail-pinned.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 10:58:09 +02:00
Erik
e23a589a6b feat(render) Campaign FW1: port the exact retail degrade selection
UpdateViewerDistance @0x0050e030 + get_degrade @0x0051e4b0 ported with
live-pinned globals: distance measured to the part SCALED SORT CENTER,
effective = max(0, dist - s_rDegradeDistance [live 100]), level = first
with effective < IdealDist (the live auto_update_deg_mul<=0 arm), else
the last level. WalkBuildingDegradeLevel carries the full authored
bands; the adapter fills sort centers; the replay context measures to
the transformed sort center. Sweep state after the rule: the level-0
building set is now correct; the residual divergence is the per-portal
side/clip gate (my arms punch a near-complement of retail two) - the
sweep driver carries the next instrument in its Skip note.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 10:55:19 +02:00
Erik
844a6c097d test(render) Campaign FW1: park the sweep driver with the degrade-gate state
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 10:51:34 +02:00
Erik
b3ac5872e9 feat(render) Campaign FW1: the degrade-level BSP gate kills the wrong punches
The offline degrade probe proved the mechanism: every Holtburg building
carries PORT nodes ONLY in its level-0 GfxObj (out to ~24-48 m); every
degraded level has zero. Retail walks the CURRENT degrade level BSP
(part->gfxobj[deg_level]) - that is what limits look-in punches to the
nearest buildings. WalkBuilding gains the degrade ladder +
SelectDrawingBsp (band pick; UpdateViewerDistance hysteresis is a port
TODO), the walk selects per viewer distance, the adapter builds
per-level BSPs, and the stab-list load rule (CLandBlock::init_buildings
@0052fd80: a full-res block loads exactly its buildings portal stab
cells) replaces load-everything in the landscape builder. The sweep now
shows clean rosters with all far-building punches gone; remaining
deltas: the near buildings 001a/0022 (50 m/28 m center distance vs the
48 m band edge - sphere-adjusted distance/hysteresis to port) and the
one ring-1 frustum boundary pair (aab50002/a9b3003c).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 10:51:05 +02:00
Erik
e5cdd2364e feat(render) Campaign FW1: look-in adjudication - the GetVisible load gate
The per-building join diagnostic PASSES (001a/0022 portal tables lead
exactly to the traced a9b4016x punch cells; every BSP PortalRef indexes
validly), and the eight-arm gate-decode sweep proves NO plane-sign x
side-flag combination reproduces retail. Together they pin the missing
mechanism: CEnvCell::GetVisible gates punches by the LOADED interior
cell set around the player - retail punched only the two buildings
nearest the player cell; the replay loads every interior so geometry
alone over-punches. Next port piece: the interior load radius (the
landcell stab-list pull). Both diagnostics stay in the suite (the sweep
Skip-parked with the verdict).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 10:43:27 +02:00
Erik
faba9be635 feat(render) Campaign FW1: pin the camera basis; outdoor adjudication state
The moving-fixture sweep pinned the pose decode (quat storage w,x,y,z;
facing = rotated +X; the consistent triple right=-rot(Y), fwd=rot(X),
up=rot(Z) - mean 20.8 deg vs motion, alternatives >=52). With it the
street-outdoor BLD roster and order match retail EXACTLY except one
ring-1 boundary pair (aab50002 extra / a9b3003c missing). Look-in
punches fire at the wrong buildings under BOTH GfxObj plane-winding
conventions (retail punched only 001a and 0022 into the a9b4016x
cells) - the next adjudication targets the PortalRef.PortalIndex join
and the BuildingPortal side-flag decode via a per-building dump. The
driving diff test carries the state in its Skip note; the flip toggle
stays for the next arm.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 10:40:22 +02:00
Erik
e3f0c6a1a1 feat(render) Campaign FW1: the landscape replay world + first outdoor diff
WalkLandscapeDatBuilder assembles the replay grid with the LIVE-dumped
retail landscape model (LScape mid_radius=25 -> 51x51; the resolution
pyramid observed on the capture client: side_cell_count 8 in the 3x3
core, 4 at ring 2, 2 at rings 3-4, 1 beyond; buildings attach only to
full-res blocks - matching the traces roster), retail z-slabs
(heightTable[max]+200 / [min]-1 per CLandBlock unpack @0052f297), and
camera-block-local coordinates. The replay context gains the building
half (placements, active-view install, building-polygon clip). The
street-outdoor conformance diff now reaches real adjudication: the walk
over-culls six traced buildings (jagged boundary - not a clean frustum
edge) and the look-in floods differ; the driving test is parked Skip
while the visibility map is instrumented.

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