Campaign OVERHAUL S3 chunk 1 fix round 2 (docs/research/2026-09-01-overhaul/
s3-walk-ownership-map.md §11.6), applied on top of 36be6b598 after the
three-lens re-review. H1-H6, the last round the plan allows.
H1 — the weather OC printed 2-4 times per interior-rooted frame instead of
once: the print (and the `_sky.RenderWeather` mesh draw it sits beside) lived
inside `DrawLandscapeSliceLate`, which `RetailPViewRenderer` calls once per
active `OutsideViewSlices` entry. Moved both into a new
`RetailPViewPassExecutor.DrawWeatherOnce`, called ONCE, unclipped/no-scissor,
after the slice loop in `DrawLandscapeDynamicsPhase` — retail's own
`GameSky::Draw(1)` runs once, after `LScape::draw`'s whole landblock loop.
The gate is extracted as a pure `ShouldDrawWeatherOnce(bool,bool,uint)`
predicate (retail's `SmartBox::is_player_outside` ANDed with the two render
toggles) so `RetailPViewPassExecutorTests` can pin "no OC while the player
stands indoors" without a live GL/DAT `SkyRenderer`; two structural
(CompiledCallGraph) tests prove the call moved out of the per-slice loop and
that the mesh draw + print each fire exactly once per invocation — this
codebase has no existing runtime-construction fixture for
`RetailPViewPassExecutor`, so the pin is structural + a testable pure gate
rather than an end-to-end GL drive. One deliberate deviation from the literal
"gated exactly as today": the print now runs through `_sky?.RenderWeather(...)`
(null-conditional, matching every other `_sky?.RenderSky(...)` call site in
this codebase) instead of an explicit `if (_sky is not null)` wrapper — a
missing sky asset no longer also suppresses the transcript print, since the
print's only job is trace fidelity and retail's own `GameSky` is never null.
H2 — WalkFrameDriver's EC-print comment still claimed "EC and OC counts are
always exactly equal"; replaced with the real citation
(holtburg-doorway-still.walk.log:1126,1131,1134,1137 — four EC prints for one
cell across four look-in DC turns), matching WalkTranscriptDump.
PrintEnvCellShell's own comment (already corrected in round 1).
H3 — the eight-kind signature's ORACLE side
(WalkTraceReplayContext.Signature8(WalkOracleFrame)) derived EC/OC from each
DC's cell list — the SAME derivation the REPLAY side's Recorder already used,
so the comparison could never disagree with itself on EC/OC placement or
content (how G7's SC-ordering regression shipped green with LC/SC). Now reads
its own literally captured EC/OC events, excluding only the trailing
per-frame weather OC — refined beyond the spec's literal "last event + P-cell
id" rule with an additional "P cell itself looks outdoor" check, after
cathedral-arrival and foundry-deep both proved the naive rule also strips an
interior root's OWN real trailing object-list turn when its nearest cell (the
reversed flood loop's last draw) happens to be the camera's own root cell.
All four kit-pose rows still reproduce: three exactly; #458 (this round's new
issue, formerly an inline-only note) re-verified at the SAME token index 165
under the new literal comparison.
H4 — OnSortCellTurn ran RequireOpenFrame + two range validations before
testing the flag. Since the hook is print-only (no stream side effect, unlike
OnLandCellTurn's unconditional WalkFrameEvent record), the flag check now
runs FIRST and returns immediately when off — flag-off cost drops to one
interface dispatch per visited land cell.
H5 — launch-options.md's ACDREAM_DUMP_WALK_TRANSCRIPT row: measured
≈1,200-1,400 lines per outdoor frame (terrace-edge 1,384; cathedral-arrival
1,269; doorway 1,187), replacing the earlier "600-800" estimate; documents
H4's residual flag-off interface-dispatch cost.
H6 — filed docs/ISSUES.md #458 for round 1's LOD-boundary land-cell
divergence (previously only an inline test comment); the InstalledDat lane's
known-failure set is now four (two #383 layout tests, TowerAscent, #458) —
confirmed by a clean run.
MUTATION CHECKS (both restored after confirming failure):
- H1: deleting the OC print inside DrawWeatherOnce made
DrawWeatherOnce_DrawsTheWeatherMeshAndPrintsExactlyOnce fail with
"Assert.Single() Failure: The collection did not contain any matching
items".
- H3: reversing AppendFloodTurns (OC before EC) made
Still_fixture_first_frame_reproduces_exactly(cathedral-arrival.walk) fail
("walk diverged from retail (cathedral-arrival.walk)"), diverging at token
index 1241: expected "EC:f4180112" vs actual "OC:f4180112".
Gates: hermetic App suite 6823/6823 passed; InstalledDat lane 244/249 passed
with exactly the four known failures (two #383 layout tests, TowerAscent,
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Campaign OVERHAUL S3 chunk 1 fix round 1 (docs/research/2026-09-01-overhaul/
s3-walk-ownership-map.md §11.5), applied on top of 079483bb6 after the
three-lens review. G1-G10, each verified against source and (where the item
adds a pin) mutation-checked.
G1 — the flag-off integration test was vacuous: Collect_TranscriptFlagOff_
ProducesNoConsoleOutput drove ONE frame, and WalkOracleTrace.Parse
structurally discards the last (open) frame, so it passed whether or not
the flag gate worked. Now drives TWO frames and asserts BOTH that Parse
sees zero frames AND that no captured line starts with any of the ten
transcript-kind prefixes (F /P /LS/LC /SC /BLD /DI /DC /EC /OC ) — robust
to unrelated console noise from parallel test classes.
G2 — LC/SC computed LodCellId before testing the flag (~1,300 land-cell
turns/frame of division/modulo/checked-conversion work paid even when the
flag is off). Both OnLandCellTurn and the new OnSortCellTurn now guard the
LodCellId call itself, not just WalkTranscriptDump's internal check.
G3 — the flag-on round trip never exercised LC/SC/BLD (the T2 fixture's
stub 1x1 landscape has zero blocks). New
OutdoorRoot_TranscriptFlagOn_PrintsLandscapeThenLandCellThenSortCellThen
Building_InOrder drives RetailFrameWalk.DrawLandscape directly (the same
deterministic "CY-only" admission technique the existing outdoor LandCell
test uses) over a landscape with one block + one building, and asserts
LS < LC < SC < BLD by position, with the exact cell/building ids.
G4 — the parts/alpha-depth smoke tests asserted FL/PC (Flushes/
DrawCellsSamples) but never AM/PM (MeshAdds/PortalPolyDraws) for the
alpha-depth log, even though PD/DM were already asserted for the parts
log. Added the missing Assert.Contains for MeshAdds and PortalPolyDraws.
G5 — launch-options.md's side-effects column now states the flag off/on
costs explicitly ("one bool read per print site, nothing else" / "one
console line per walk turn, ≈600-800 lines per outdoor frame").
G6 — removed the "DI f4180108... no, a9b4013f" self-correction in
Oh_doorway_still_first_frame_diff's comment; added a one-line note on why
it and Oh_foundry_deep_... stay bespoke Facts rather than folding into the
Theory (no functional reason, just history).
G7 — SC printed from OnLandscapeCellTurn, which RetailFrameWalk.
DrawLandscape calls AFTER DrawBuilding — retail's own order is
LC -> SC -> BLD (terrace-edge.walk.log:2331-2333), not LC -> BLD -> SC.
Fixed by adding IWalkEventSink.OnSortCellTurn, fired at the DrawSortCell
entry point (same alwaysDrawObjects||cellInView gate, before DrawBuilding);
the driver moves its SC print there and OnLandscapeCellTurn no longer
prints one.
G8 — the trailing "OC <viewer land cell>" retail prints once per complete
outdoor frame is GameSky::Draw's weather branch calling
DrawObjCellForDummies(after_sky_cell) @0x005070da, not a walk turn.
RetailPViewPassExecutor.DrawLandscapeSliceLate now prints it at the point
it actually calls RenderWeather; PrintObjectCellTurn's doc, which denied
this line existed, is corrected. New
Oh_terrace_edge_outdoor_frame_ends_with_the_weather_ObjectCellTurn pins
this directly against the OH capture: every complete outdoor frame's last
event is an OC whose cell id equals that same frame's own P-line cell id.
G9 — "EC and OC counts are always exactly equal per pose" was false
(terrace-edge: 12 EC vs 16 OC, the extra 4 being G8's weather OC, which
has no EC counterpart). The visit-scoped/stamp-dedupe conclusion was
right; only the count claim needed fixing. Both doc sites corrected.
G10 — the conformance signature dropped LC/SC/EC/OC entirely, which is
how G7 shipped green: no test ever compared their placement. Recorder now
tracks LC/SC via the new OnLandCellTurn/OnSortCellTurn hooks and derives
EC/OC per DC from PView::DrawCells's own two reverse loops (shell
far-to-near, then object-list far-to-near) — a look-in flood's DC derives
them immediately, the interior root's OWN flood defers to
OnInteriorFloodDrawTurn (verified against cathedral-leak.walk.log:4,1328,
where the root DC's own EC/OC print last). WalkTraceReplayContext.
Signature8 applies the identical derivation to oracle frames and never
produces the trailing weather OC (G8's own dedicated pin covers that
separately, matching the spec's "exclude from the comparison, assert
separately" instruction). Used ONLY for the four OH-capture-rooted
conformance rows (the older FW0 fixtures predate the LC/SC/EC/OC
breakpoints and carry none of those lines).
Applying the eight-kind check surfaced a real, separate bug: WalkLandscape
DatBuilder never set WalkLandBlock.LandblockId, so every LC/SC token any
DAT-built world ever produced read landblockId=0 — latent because the old
four-kind signature filtered LC/SC out. Fixed (one field, test-fixture-
only, no production behavior change). With that fixed, three of the four
OH rows (terrace-edge, cathedral-arrival, foundry-deep) reproduce EXACTLY
at the eight-kind level. Oh_doorway_still_first_frame_diff still diverges
by one extra LC/SC pair (token index 165: replay draws LC:a9c90001|
SC:a9c90001 that retail never does) — a real WalkLandscape.CheckBlocks
visibility question at a ring-2/ring-3 LOD boundary, out of this round's
print-only scope, needing the retail oracle to resolve. Marked
[Trait("Status","KnownFailure")] with the exact position recorded in its
own doc comment, matching TowerAscent's existing pattern — the InstalledDat
lane now shows FOUR known failures (the pre-existing two #383 layout tests
+ TowerAscent, plus this new finding), not three; never weakened the pin
itself.
Mutation checks (all four restored after observing the failure):
- G1: forcing WalkTranscriptDump.Enabled true made
Collect_TranscriptFlagOff_ProducesNoConsoleOutput fail with
"Assert.DoesNotContain() Failure: Filter matched in collection ... Collection:
[\"DI 00000100\", \"DC pv=00000000 ov=1 n=2: 00000100 00000101\", \"LS\", ...]".
- G3: deleting PrintLandCell's Console.WriteLine made the new outdoor test
fail with "expected an LC line".
- G4: making MeshAddPattern unmatchable made all five
Alpha_depth_log_parses_with_nonempty_samples rows fail with
"Assert.Contains() Failure: Filter not matched in collection".
- G7/G10: moving OnSortCellTurn back after DrawBuilding made the
terrace-edge conformance row fail with "walk diverged from retail
(terrace-edge.walk)", first divergence exactly at its first building
(f518002e): expected ...SC:f518002e|BLD:f518002e..., actual
...BLD:f518002e|SC:f518002e....
Gates: hermetic lane 6,816/0 (baseline 6,814 + G3's new test + G8's new
test); InstalledDat lane 244/4 known failures (two pre-existing #383
layout tests + TowerAscent + the new Oh_doorway_still_first_frame_diff
finding, documented above).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Campaign OVERHAUL S3 chunk 1 (docs/research/2026-09-01-overhaul/s3-walk-ownership-map.md
§11): the transcript-kinds/fixtures/print-only-emitter half of the walk work,
built AFTER chunk 3 landed LC/SC (the per-land-cell interleave).
B1 — the emitter (print-only, never gates admission/depth/order):
- ACDREAM_DUMP_WALK_TRANSCRIPT=1 is read once into RuntimeOptions.DumpWalkTranscript
(rule 4) and handed to RenderingDiagnostics.DumpWalkTranscriptEnabled (rule 5,
a settable static, not a second env read) once at GameWindow construction — the
deep walk call sites have no reachable RuntimeOptions reference.
- WalkTranscriptDump (new) prints the OH line kinds — F/P/LS/LC/SC/BLD/DI/DC/EC/OC —
to Console at the exact points retail's cdb breakpoints sit
(tools/walk-oracle/oh/oh-capture-walk.cdb.template), gated internally so every
method bails out before any string work when the flag is off.
- Every call site lives in WalkFrameDriver.cs, at the point the driver already
processes that turn: Collect (F/P, after BeginFrame), Emit's DI/LS/DC/BLD cases,
OnLandCellTurn/OnLandscapeCellTurn (LC/SC, at LOD resolution via the new
WalkTranscriptDump.LodCellId helper, before the 8x8-bucket expansion), and the
EmitFloodTurns/EmitCellContentsTurn loops (EC/OC — both UNCONDITIONAL per flood
visit, matching the OH captures' always-equal EC/OC counts; retail's own
DrawEnvCell stamp dedupe sits past the breakpoint, inside the function).
- DC's "pv=" field encodes interior(0)/outdoor(1) as an 8-hex-digit 0/1 so it
satisfies the same pv=[0-9a-f]{8} regex real captures use; derived from
_currentDcStage at the DC event (CellStatic = interior pview, else outdoor).
- The frame-root pose (origin, quaternion) is a reasonable orthonormal basis built
from the walk's own CyPlane.Normal forward vector and WalkLandscape's own
ViewerWorldOriginX/Y block origin — self-consistent for the round-trip parser,
not a byte-exact reproduction of retail's Frame (B4's diff never compares P).
B2 — WalkOracleTrace learns EC/OC event kinds (LC/SC already existed from chunk 3).
New WalkOraclePartsTrace.cs holds two small read-only parsers for the parts log
(PD/DM) and the alpha-depth log (AM/FL/PM/PC) — records only, no validator, no
canonical JSONL, no new tool.
B3 — fixtures: the five OH walk captures join WalkOracleTraceTests.AllFixtures
(now (root, name) pairs — FW0's own root plus the OH capture directory) for
parse + complete-frame pins. The four kit poses join WalkTraceConformanceTests'
still-fixture coverage as NEW rows (the OH cathedral-arrival root is f4180108,
not FW0's f4180106): terrace-edge/cathedral-arrival extend the existing theory
(now (root, fixture) parameterized); holtburg-doorway-still and foundry-deep get
dedicated tests mirroring their FW0 siblings' own structure. Finding: the OH
foundry-deep capture's own retail transcript draws 12 real town buildings through
its exit chain (unlike the FW0 capture, which apparently reached none at that
pose) — the FW0 test's stub 1x1 landscape undershoots it (first divergence:
nothing after "LS" vs retail's real BLD content); fixed by building the full
landscape/building world via WalkLandscapeDatBuilder.Build, matching the shared
theory's own approach, not by skipping or weakening the row.
WalkTraceReplayContext.Signature(WalkOracleFrame) now filters to the DI/DC/BLD/LS
kinds (LC/SC/EC/OC never had a WalkEvent analogue in RetailFrameWalk's own
four-kind vocabulary) instead of mapping them to a "?" placeholder, so the still-
fixture comparison stays apples-to-apples on both sides.
B4 — WalkTranscriptSignatureDiff (test-side only, no runner/tool): diffs two
transcripts (raw lines or parsed frames) at the full DI/DC/BLD/LS/LC/SC/EC/OC
level, reporting the first divergent event and position per frame. Proven over a
synthetic self-vs-self-minus-one-event pair (SignatureDiff_ReportsTheExactRemovedEvent).
Tests: T1 (flag off) is split into a unit-level zero-allocation/zero-output check
on WalkTranscriptDump itself (the walk's pre-existing allocation, e.g.
RetailFrameWalk.EmitDrawCells's per-call array, is untouched by this chunk and not
independently zero-alloc) and an integration-level Collect() check; both assert via
WalkOracleTrace.Parse returning zero frames rather than raw string equality, which
is robust to unrelated Console.WriteLine noise from other test classes running in
parallel (a real, observed hazard — WalkFrameDriverTests joins
CameraDiagnosticsCollection for the same reason CornerFloodReplayTests/
Issue181WallPressEquilibriumTests already do, issue #251). T2 proves the
emitter/parser round trip on a synthetic interior frame. T3's InstalledDat rows all
pass. T4: LaunchOptionsDocumentationTests green with the ACDREAM_DUMP_WALK_TRANSCRIPT
row (both directions).
Gates: hermetic lane 6,814/0 (was 6,795 baseline + new tests), three consecutive
clean runs; InstalledDat lane 245/3 known-failures (the two pre-existing #383
layout tests + TowerAscent) unchanged from baseline.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The re-expressed F4(c) pin installed an empty landscape view, so nothing
classified visible, the ordered stream never grew, and no StreamMark
fired: the one behavior round 2 changes was unpinned (review finding).
The test now installs a real full-viewport view and records the trace,
asserting two FLUSH lines ahead of the merged three-cell terrain batch.
The LandCell doc no longer claims an identical submission order; the
resolved image is identical (opaque-vs-opaque order is depth-resolved,
every observable order still flushes first).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Measured (§9.7): the connected R6 soak at e10765aa showed the round-1
batching fix (§9.6 F2) was inert in practice — CPU p50 flat at 31.2 ms
and FPS flat at 32 across every outdoor destination (baseline 2.0-15.5 ms
/ 51-452 FPS), GPU p50 2-4x baseline. Root cause: nearly every admitted
land cell carries scenery statics, so a StreamMark (recorded whenever the
walk's ordered stream grows) follows almost every LandCell event and was
itself treated as a flush point, splitting the pending terrain batch back
down to ~500 submissions/frame — each paying TerrainModernRenderer's full
bind sequence plus a ring allocation and an MDI.
F10: a StreamMark no longer flushes the pending terrain batch. Citation —
the walk's ordered stream holds ONLY opaque batches
(WalkStaticStreamPopulator.cs:179, `if (batch.IsOpaque)` routes translucent
batches to the alpha list instead of the stream) drawn through the
Opaque/OpaqueAlphaToCoverage pipelines, both created with depth test AND
write ON and GpuBlendMode.None (WbDrawDispatcher.Rhi.cs:240-248,343:
`Depth = new GpuDepthState(Test: true, Write: depthWrite, depthCompare)`
with depthWrite: true); DrawOrderedRange resolves every walk-stream
command's bucket to PipelineBucket.Opaque because every command it holds
is opaque (WbDrawDispatcher.OrderedStream.cs:569-571). Opaque terrain and
opaque statics are therefore depth-resolved identically in either
submission order, so deferring the terrain batch across a StreamMark is
pixel-identical (except exact z-ties, which retail itself leaves
order-independent).
Flush points that REMAIN, because their GPU order against terrain IS
observable: PunchFan (DEPTHTEST_ALWAYS + write — the far-Z stamp this
chunk's interleave exists to order correctly), AlphaBarrier and
LandscapeFlush (translucent drains), ClearInteriorDepth, ExitSeals, Sky,
CellShell, a particle turn whose cell has a renderable emitter, and the
end of Replay. AlphaSubmitMark stays a non-flush point (unchanged — it
only enqueues into the CPU alpha list). No ordered-stream stage found
that blends or disables depth write, so no amendment to the contract was
needed; the Replay doc comment records the citation so a future stage
that does must become a flush point again.
F11: no per-batch bind latch added (tens of batches/frame is acceptable).
F12: the F4(c) driver pin is re-expressed to prove a StreamMark from a
cell with real content (not just an empty particle turn) no longer
splits, while PunchFan/AlphaBarrier/a has-emitter particle turn still do;
a new punch-order pin (LandCell(far), building turn with a PunchFan,
LandCell(near)) proves the far terrain flushes before the punch and the
near terrain starts a fresh batch after it. Hermetic App lane: 6,796
passed (6,795 base + 1 new fact), 0 failed.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- the LandCell event doc states the deferred cross-block batch rule (the
same-landblock lookahead text was stale);
- the AlphaSubmitMark arm's comment gives the true reason it is not a
flush point (it only enqueues into the CPU alpha list; the drain leaves
flush first);
- the pending terrain batch is cleared with the other transient frame
lists in AbortFrame/BeginFrame;
- CompleteWalkTerrainFrame runs in a finally so a throwing Replay cannot
leak its accumulated ticks into the next frame's sample;
- CopyRenderableEmittersInCell keeps its own doc comment;
- the outdoor-root LandCell pin now observes one real cell turn after its
terrain;
- the oh1 landscape contract's stale "whole pre-stage" prose is resolved.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Fixes the three-lens review blockers against 671eb3ad4 (S3 section 9.6 F1-F6).
F1 - slot key (blocking, retail). TerrainModernRenderer.DrawLandCells
normalizes every incoming landblockId to (id & 0xFFFF0000u) | 0xFFFFu
before the _idToSlot lookup: the walk hands 0xXXYY0000
(WalkLandBlock.LandblockId) but AddLandblock stores under the DAT id
0xXXYYFFFF (LandblockRenderPublisher.LandblockId) - every walk lookup
was missing and the walk path drew NO terrain. Unit-tested end-to-end
through a real RecordingGpuDevice-backed TerrainModernRenderer
(TerrainWalkSlotKeyNormalizationTests): AddLandblock(0xA9B4FFFF, ...)
is found by a 0xA9B40000 lookup, an unknown landblock is a silent
per-entry no-op, and a batch mixing a known and unknown entry submits
only the known one.
F2 - deferred cross-block batching (blocking, driver). Retail's
DrawSortCell always follows DrawLandCell (LC/SC strictly alternate,
never two LC in a row - S3 section 9 R1), so chunk 3's "merge
consecutive same-landblock LandCell events" rule never actually
merged anything; the driver review flagged batching as inert.
WalkFrameDriver.Replay now keeps ONE pending terrain batch across
landblocks ((landblockId, side, cellIndex) entries, cleared at
Replay's own start); a LandCell event only appends; every OTHER event
kind that will itself submit GPU work (StreamMark, Sky, CellShell,
PunchFan, AlphaBarrier, LandscapeFlush, ClearInteriorDepth,
ExitSeals) flushes the pending batch first; a StaticParticles/
CellParticles turn asks the new ParticleSystem.
HasRenderableEmittersInCell (an allocation-free sibling of
CopyRenderableEmittersInCell) and, when the cell has no renderable
emitter, submits nothing and does NOT flush either - the whole point
of the deferred rule. The end of Replay flushes the remainder. This
is order-preserving by construction: a flush always lands at the
exact point the unbatched draw would have, so GPU submission order -
and therefore pixels - is identical to the unbatched baseline; only
the number of small terrain draw calls shrinks.
TerrainModernRenderer.DrawLandCellRuns becomes DrawLandCells(
viewProjection, IReadOnlyList<(uint LandblockId, int SideCellCount,
int CellIndex)>) - one MultiDrawIndexedIndirect over every entry's
runs, unknown slots skipped per-entry. IWalkFrameLeafRenderer.
DrawLandCellBatch drops its separate landblockId parameter to match
(a batch can span several landblocks now) and gains
HasRenderableEmittersInCell.
Batch-count demonstration: driven through a real WalkFrameDriver
Replay (OnLandCellTurn_MergesAcrossLandblocksOverAnEmptyParticleTurn_
RealSubmissionsSplit), 4 LandCell turns across 3 distinct landblocks,
separated only by an empty particle turn, a real StreamMark, and a
building's alpha barrier, submit as exactly 3 DrawLandCellBatch calls
(2+1+1) instead of 4 - the empty particle turn's non-flush merges two
otherwise-separate cross-landblock entries. At production scale the
same mechanism is expected to cut the terrace-edge frame's ~578
individual DrawLandCell events (S3 section 9's captured transcript
count) to "tens" of submitted batches, per the contract's own
expectation: most terrain cells have no particle owner nearby, so the
strict LC/[empty-SC]/LC/[empty-SC]/... run collapses into one batch
per region bounded by real content (a building, a StreamMark-worthy
cell, or a genuine emitter) rather than per cell.
F3 - per-frame terrain diagnostic (blocking, build/test). The walk
leaf no longer brackets each batch with TerrainDrawDiagnosticsController
.Begin()/Complete() (a per-batch Stopwatch Restart/Stop pair that was
pushing one timing SAMPLE per batch, not per frame).
RetailPViewPassExecutor.DrawWalkLandCellBatch instead times its own
call with a raw Stopwatch.GetTimestamp() delta (no allocation) and
hands the ticks to the controller's new AccumulateWalkBatch;
RetailPViewRenderer.DrawWalkDrivenStatics calls the new
CompleteWalkTerrainFrame() exactly once, immediately after
driver.Replay finishes - "the end of the walk replay", where the
deleted whole-stage terrain leaf's own Begin()/Complete() bracket
used to close - which pushes ONE elapsed-time sample (even a
zero-batch frame pushes a zero sample: one sample per frame, not per
landscape turn) and publishes on the existing 5-second cadence.
TerrainRenderDiagnosticFacts gains a Draws field alongside
VisibleSlots (both were the same field before); TerrainModernRenderer
tracks its own per-frame WalkVisibleSlotCount/WalkDrawCount (a
HashSet<int>/int cleared in BeginFrame, populated by DrawLandCells),
and the diagnostics source reports those whenever the walk drew at
least one batch this frame, falling back to the non-walk Draw()
path's VisibleSlots otherwise (the two paths never both run in the
same frame). The [TERRAIN-DIAG] line's meaning (cpu_us per frame) is
unchanged, so the S3 section 9.5 before/after compare stays valid.
F4 - driver pins for the LandCell position (major). Three RunFrame-
level pins replace the deleted TERRAIN:0 pins: an outdoor-root
sequence (SKY, then one LANDCELL, driving RetailFrameWalk.
DrawLandscape directly with a one-view/zero-vertex WalkPortalView so
WalkLandscape.CheckBlocks' admission stays the same deterministic
"CY-only" test RetailFrameWalkTests already relies on, while still
satisfying WalkFrameDriver's real >=1-active-view fail-loud guard);
an interior-root test with one real exit view and one populated
block (SKY, LANDCELL, LFLUSH, SEALS, SHELL...) built on the existing
RunFrame_InteriorFloodWithExitView_... fixture; and the T4 batching
pin re-expressed for the F2 rule (OnLandCellTurn_
MergesAcrossLandblocksOverAnEmptyParticleTurn_RealSubmissionsSplit,
described above). The fake leaf's DrawLandCellBatch now logs
LANDCELL:<lb>:<side>:<idx>[,...] per batch and gains
HasRenderableEmittersInCell backed by an opt-out CellsWithoutEmitters
set (default true - has-emitters - so every pre-existing pin in the
file keeps its old unconditional-submission behavior unchanged).
F5 - no code change: the walk's in-view gate is unchanged; no
whole-block terrain re-added.
F6 - minor/notes: DrawLandCells' own comment now states the walk's
CheckBlocks/landcell_check admission is the sole terrain culling
authority (retail has no separate terrain frustum test); the
HandleLandscapeTurn comment's inverted claim is corrected (a FARTHER
building's punch survived because NEARER terrain was drawn BEFORE
it, not after - the interleave now draws it after, matching retail);
the "flat/directional-shadow paths" claim is corrected to the one
actual caller, WorldScenePassExecutor.DrawFlatTerrain (a directional-
shadow receiver selects its pipeline inside the SAME DrawRhi call,
not through a second caller); the cathedral order-trace token gains
the LOD side/index (":LC<lb>/<side>:<idx>"); T2's vacuous "no
TERRAIN event" assertion in RetailFrameWalkTests is replaced by a
comment pointing at the F4 driver-level pins; and the stale
"Confirmed OH5 defect" row in oh1-construction-landscape-contract.md
is retired with "FIXED by S3 chunk 3 (commit 671eb3ad4 + fix round
1)".
App hermetic lane: 6,795/6,795 (up from 671eb3ad4's 6,786 baseline -
net +9 tests: 3 F1 slot-key tests, 2 F4a/b driver RunFrame pins, 3
TerrainDrawDiagnosticsController walk-frame tests, plus the T4->F4c
rewrite and the RetailPViewPassExecutorTests split are net neutral).
InstalledDat lane: 241 passed, the same 3 accepted failures (2
pre-existing #383 layout fixture-drift tests, 1 TowerAscent
Status=KnownFailure) - unchanged from baseline. Core Vfx tests:
109/109 (108 baseline + 1 new HasRenderableEmittersInCell lifecycle
pin mirroring CopyRenderableEmittersInCell's own add/move/remove
test).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Ports RenderDeviceD3D::DrawBlock @0x005a17c0's real per-cell order:
loop 1 (@0x005a1876) prepares shadow lists; loop 2 (@0x005a197d)
DrawLandCell(cell) @0x005a19c0 fires ONLY when the cell is in view,
STRICTLY BEFORE DrawSortCell(cell) @0x005a19e6, which fires whenever
alwaysDrawObjects (retail default 1 @0x00820ed4) or the cell is in
view. RetailFrameWalk.DrawLandscape now emits sink.OnLandCellTurn(
landblockId, side, cellIndex) at that exact point, per admitted cell,
before the existing DrawBuilding + OnLandscapeCellTurn (the
DrawSortCell half). WalkFrameDriver records one LandCell frame event
per turn and deletes the whole-stage TerrainSlice(0) emission and the
DrawTerrainSlice leaf outright — drawing all terrain before every
building let a nearer building's far-Z punch survive under farther
terrain drawn afterward, the doorway-behind-a-hill fragment bug from
the owner's G2 Holtburg screenshot; this chunk removes it by ORDER
alone, with no depth-compare change (S4's punch z-func question is
untouched, per the contract).
Index-run arithmetic (S3 §9.1 R3): the terrain mesh is cell-major
(LandblockMesh.Build: cy outer, cx inner, 6 indices/cell, 384/land-
block). A retail LOD cell (side n, LOD coords X,Y) covers cx in
[X*8/n,(X+1)*8/n), cy in [Y*8/n,(Y+1)*8/n) — one contiguous run per
covered cy row: side 8 -> one run of 6, side 4 -> two runs of 12,
side 2 -> four runs of 24; side 1's single coarse cell covers every
row contiguously so its 8 per-row runs collapse into ONE run of all
384 indices. TerrainModernRenderer.AppendCellIndexRuns (pure, no GPU,
no baked table) and DrawLandCellRuns (resolves the landblock's slot,
builds one DrawElementsIndirectCommand per run, reuses the existing
DrawRhi bind-and-submit path) implement this; TerrainModernRenderer
.Draw(...) is untouched and keeps serving non-walk callers (directional-
shadow receivers, the flat terrain path).
Order-preserving batching (S3 §9.2 B2): WalkFrameDriver.Replay merges
consecutive same-landblock LandCell events with no intervening event
into ONE DrawLandCellBatch leaf call; any other event splits the
batch. In production this rarely fires because retail's own
DrawSortCell (AlwaysDrawObjects=true) always interposes an object-
list turn between one cell's LandCell event and the next's — see
perfNote in the task report for the resulting command-count increase.
Deleted as dead: the _walkTerrainInViewLandcells field and
SetWalkTerrainInViewLandcells setter on RetailPViewPassExecutor (fed
only DrawWalkTerrainSlice's inViewLandcells filter, which no longer
exists — the walk's own per-cell CellInView admission is now the
sole terrain-visibility authority) and its two call sites in
RetailPViewRenderer.DrawWalkDrivenStatics.
Weather placement (S3 §9.1 R5): confirmed unchanged. GameSky's
weather pass (RenderWeather, gated on is_player_outside) already
runs after every LandCell event for both root kinds — for an
outdoor root, DrawLandscapeDynamicsPhase is called directly after
driver.Replay() completes (which processes the whole per-cell
_events list first); for an interior root with ov>0, it fires via
the LandscapeFlush leaf (RetailPViewRenderer.FlushWalkLandscape ->
_walkPreClearDynamics), and OnInteriorFloodDrawTurn only emits
LandscapeFlush AFTER DrawLandscape's per-cell loop has fully run
and recorded every LandCell event ahead of it in the same _events
list Replay walks in order. No code change needed; verified by
reading the call sites.
Tests: WalkEvents/RetailFrameWalk/WalkFrameDriver's existing pins
updated (every "TERRAIN:0" expectation deleted, matching the deleted
event); new coverage for T1 (WalkLandCellOrderTests — LandWalkOrder +
WalkLandscape.CalcDrawOrder + WalkLandscapeAssembler
.SideCellCountForRing reproduce both cathedral captures' frame-2
LC/SC sequences byte-for-byte, 533/698 arrival and 531/757 leak, cross-
verified against the retail ring-to-LOD table), T2 (a synthetic two-
block landscape with a real WalkVisibilityMath-driven out-of-view
column, proving LC-before-building/statics, far-to-near, no-LC-but-
keeps-SC for the excluded cells, and no TerrainSlice event of any
kind), T3 (TerrainLandCellIndexRunsTests — the index-run arithmetic
for every side/cell, disjoint and exhaustive over the 384-index
landblock), T4 (batching merge/split), and the RetailPViewPassExecutor
CompiledCallGraph pin retargeted at DrawWalkLandCellBatch /
DrawLandCellRuns. WalkOracleTrace gains LC/SC event kinds and a
Load(root, name) overload for the OH capture directory — the one
parser change this chunk needs, no validator, no other infrastructure.
App hermetic lane: 6,786/6,786 (6,765 baseline + 21 new). InstalledDat
lane: 241 passed, 3 accepted failures (2 pre-existing #383 layout
fixture-drift tests, 1 TowerAscent Status=KnownFailure) — unchanged
from baseline. Core terrain tests: 116/116 (Core untouched by this
chunk).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
RetailFrameWalk.DrawInside now passes _interiorPView.OutsideView.ViewCount
into IWalkEventSink.OnInteriorFloodDrawTurn. WalkFrameDriver's own
implementation reproduces PView::DrawCells @0x005a4840's exact gating
(0x005a4852-0x005a49eb, all inside `if (outside_view.view_count > 0)`):
the landscape flush (retail FlushAlphaList(0f) @0x005a4872 plus the
pre-clear dynamics hook), the device-stamp advance @0x005a4886, a GATED
depth clear (pc:432731-432732), and the exit-portal seals
(pc:432785-432786) — all four skipped entirely when outsideViewCount == 0.
The depth clear is gated on a new driver field, PortalsDrawnCount, which
models retail's D3DPolyRender::portalsDrawnCount (uint16 @0x008719b4):
read-then-zeroed at the interior root's own flood turn
(@0x005a489c-0x005a489e), and re-armed at Replay by the count
IWalkFrameLeafRenderer.DrawExitSeals now returns (the SAME portal
enumeration RetailPViewPassExecutor.DrawPortalDepthWrite already performs
— OtherCellId==0xFFFF, >=3 vertices). The field persists across frames
(never cleared by BeginFrame/AbortFrame/EndFrame/Replay), reproducing
retail's documented quirk: a fresh driver's first ov>0 frame never clears;
every later ov>0 frame clears because the previous frame's own seals armed
the counter.
_skyDrawnThisFrame — the proxy for outside_view.view_count != 0 that used
to gate the stamp re-arm — is deleted; its "second Landscape turn in one
frame" fail-loud guard moves to a frame-scoped counter
(_landscapeTurnsThisFrame). RetailPViewRenderer.ClearWalkInteriorDepth
splits into FlushWalkLandscape (pre-clear dynamics + FlushLandscapeAlpha)
and ClearWalkInteriorDepth (the Z clear only), both wired through the new
IWalkFrameLeafRenderer.FlushLandscape leaf and the WalkLeaf production
adapter.
Tests: flipped the ov==0 pin to expect no landscape-flush/clear/seals at
all (T1); added the two-frame first-frame-no-clear / armed-clear pin plus
a no-exit-portal-never-clears pin (T2); added a look-in-neither-arms-
nor-consumes-the-counter pin (T3); added RetailFrameWalk's two-PView
draw_landscape wiring pin and a WalkPView.ConstructView reset pin (T4);
updated every direct OnInteriorFloodDrawTurn caller to pass the
outsideViewCount it models (T5). The four per-category leaf-contract pins
(whole-once shell, Boolean sphere admission, portal-polygon-only clip,
local-player repeated submission) already existed and needed no additions
(B4).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Retail-lens review of chunk 6 (no blocking finding). Fixed:
- a cell that gets two object-list turns in one frame (a chamber reached
through two portals) submitted its emitters twice; retail's particle parts
sit in the same shadow_part_list as every other part and CPhysicsPart::Draw's
frame stamp suppresses the second draw, so the walk now dedupes the particle
turn with the same frame-scoped set that dedupes the cell shell;
- every visited land cell paid the full per-cell draw setup even with no
emitter; DrawForCell now returns after the cell lookup, retail's own cost
(DrawPartCell 0x005a07a0 `num_shadow_parts > 0`);
- CopyRenderableEmittersInCell maintains LastRenderScopeEmitterVisitCount;
- the OutdoorSceneParticleEntityIds / outdoorOwnerIds stub chain (permanently
empty, never read) is deleted through IWorldSceneRenderer,
WorldScenePViewRenderer, IWorldScenePasses and the composition root;
- AD-117 item 4 names the two behavioral residuals (owner-cell substitution;
no per-emission AddPartToShadowCells);
- ParticleHookSinkTests pins that an emitter's draw cell is its owner's pose
cell and survives the projection-visibility switch across the per-frame
view pass.
Gates: Core 4,988/4,988 (Vfx 108/108), App hermetic 6,760/6,760, Runtime
1,884/1,884.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Owner G2 finding: the purple cloud around an arriving character no longer
drew. The server keeps the player Hidden until acdream sends LoginComplete at
reveal completion (retail-correct); the Hidden-state script's emitters spawn
in the arrival cell and are view-eligible when the world appears, but the
walk drew an owner's emitters only through the owner's registry rows, and a
hidden owner's shadow is suspended. Retail's
CPhysicsObj::add_particle_shadow_to_cell (0x00514a70) gives an emitter one
shadow in its OWN current cell, drawn at that cell's turn regardless of the
parent's hidden state (add_shadows_to_cells 0x00514aed skips the flood for
state & 0x1000).
Port: ParticleSystem keeps a per-pass cell -> renderable-handles index
(maintained at every renderable/OwnerCellId change) and
CopyRenderableEmittersInCell; ParticleRenderer.DrawForCell; the walk draws
particles BY CELL at the existing turns (interior CellParticles, landscape
LandscapeCellParticles), the events fire for every visited cell, and every
owner-union particle path is deleted (UnionOwners/UnionNewOwners for
particles, the outdoor drawn-owner dedupe, the executor's owner
classification sets, the context ParticleOwnerIds members). The post-replay
per-cell pass double-submitted the root flood's emitters and is deleted: an
emitter draws once, at its cell's replay turn. AD-117 item 4 becomes a port
note (the index lives in the particle system; an emitter is not a physics
object in acdream). The temporary [pes-spawn]/[pes-vis] traces are removed
and the ACDREAM_DUMP_PLAYSCRIPT row restored.
Verified: timed arrival route logs/selfgate-20260903-062522-haze-chunk6,
frame h02-arrive-400ms shows the cloud at the character in Facility Hub.
Gates (Release): Core 4,987/4,987; Content 214/214; Runtime 1,884/1,884; App
hermetic lane 6,760/6,760; App InstalledDat 217 pass / 2 pre-existing #383.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
CObjCell::find_cell_list (0x0052b4e0) with num_sphere == 0 adds only the
current cell — interior CELLARRAY::add_cell at 0x0052b563, outdoor
CLandCell::add_all_outside_cells (0x00533630, the arg2 <= 0 branch:
adjust_to_outside + gid_to_lcoord + add_outside_cell) — and skips the
portal transit walk (`arg4 != 0 && arg2 != 0` at 0x0052b576). So a shapeless
object's transition cell array is its destination cell alone, exactly what
RefreshPositionRows publishes. Not an adaptation; the register row keeps
its three residual readings, the S3 packet drops the capture request, and
the registry/test comments cite the verified function.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Chunk 5 (consumer cutover): WalkProductionWorldData's per-cell views are
borrowed from ShadowObjectRegistry.GetRetailPartEntriesInCell and resolved
through RenderSceneQuery.TryGetByLocalEntityId; every render-side sweep,
bucket, parent-cell and root-position fallback is deleted (AD-116 for the
one-frame registry→scene window, counted in UnregisteredRenderMembershipCount).
A live entity with visual parts but no collision geometry registers
render-only (LiveEntityCollisionBuilder computes the part array before the
empty-shapes gate).
Closeout fixes found while landing it:
- RefloodOwnerForLandblock forwards the retained part array — a reflood is
retail's recalc_cross_cells over the SAME CPartArray; without it every owner
touched by a landblock replacement commit lost its render membership.
- Non-colliding DAT statics register render-only from BOTH publishers
(LandblockPhysicsPublisher.PublishStaticEntity,
LandblockPhysicsContentBuilder.RegisterRenderOnlyStatic). The G2 self-gate
pixel diff caught them vanishing (Facility Hub wall panels): retail floods
every object regardless of collision (CEnvCell::init_static_objects
0x0052c350, add_shadows_to_cells 0x00514ae0).
- S2 dual review fix batch (arch + retail lens, lead-verified):
Suspend clears the retail product (remove_shadows_from_cells 0x00511230 is
one transaction); AttachChild/DetachChild advance the mutation revision so
a prepared SetPosition cannot clobber a child's rows; an attached child
never floods on its own re-registration; RemoveLandblock and the non-rooted
RetireOwnerFromLandblock prune retail rows (render-only statics end with
their landblock); a render-only owner's no-cell-array commit republishes at
its destination cell (AD-117); an empty non-null part array is treated as
null; per-move closures/LINQ replaced by index loops; EnvCell shells stay
out of the scene's LocalEntityId index (payload-less records); the index
predicate compares the id; the dead per-cell scene indices are deleted.
Register: AD-116 (chunk 5), AD-117 (four residual Contract A/B readings).
Evidence: s2-membership-ownership-map.md §8 (chunk 5) and §9 (closeout).
Gates (Release): Core 4,984/4,984; Content 214/214; Runtime 1,884/1,884;
App hermetic lane 6,760/6,760; App InstalledDat lane 217 pass / 1 skip /
2 pre-existing #383 layout-fixture failures; App Windows lane 1/1.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Movement: CommitSetPosition's RefreshPositionRows/ReplacePositionRows and
the staged apply publish the retail render product from the exact cell
list collision just used, the transition's cell_array retail feeds
add_shadows_to_cells in CPhysicsObj::SetPositionInternal @0x00515330
(pseudo-C 283526-283539); the separate move-path bbox recompute is deleted.
calc_cross_cells @0x00515230 stays the distinct full-recompute path
(PhysicsShadowCommitAction.Recalculate).
Children (Contract B recursion): ShadowObjectRegistry.AttachChild/DetachChild
give an attached object the root's current cells as part entries only,
republished whenever the root's array changes, detached at withdrawal and
cascaded from the root's Deregister; nested attachment resolves to the root
with a bounded, cycle-safe chain. EquippedChildRenderController attaches at
realization (FromSetupRenderParts over the child's Setup) and detaches at
its single removal funnel. WalkProductionWorldData's dynamic sweep reads
TryGetRetailCellArray directly; the 64-hop parent-chain walk and its
FindParentLocalId plumbing are deleted. CollisionWorldState.Clear now
also clears the retail products.
Gates (implementer's isolated worktree at identical content): Release
build 0/0; Core 4,970/4,970; App hermetic 6,761/6,761; targeted
walk/child/live-entity/placement/comparator 166/166; Runtime 1,884/1,884.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ShadowObjectRegistry computes the CELLARRAY once per registration under
Contract A (cylsphere route from the Setup's collision cylspheres, else the
bbox route over the whole visual part array) and derives BOTH products from
it: the collision rows (_entityToCells/_cells, CShadowObj per cell via
add_shadows_to_cells @0x00514ae0) and the per-part render entries
(AddPartsShadow @0x00517e40). The second, independent collision flood is
gone. A caller that supplies no part array floods from its collision shapes
exactly as before, so every legacy expectation holds byte-for-byte.
The staged SetPosition pipeline now carries the retail part array, cell
array, route, and entries through TryCaptureOwnerState/InstallOwnerState
and publishes them beside the collision cell replacements, honoring the
keep-when-empty rule (SetPositionInternal num_cells gate, pc:283540) for
both products together; two new tests pin a cross-cell move and the
keep-when-empty case. Behavior change, retail-exact: an object with
decorative non-BSP parts now has its collision shapes registered in every
cell those parts reach (pinned by a two-cell fixture); all-BSP objects are
unchanged. Movement paths still take collision cells from the transition
and recompute the retail product separately; chunk 4 unifies them on the
transition's array as retail does. No particle emitter reaches this registry.
Gates (implementer's isolated worktree at identical content): Release
build 0/0; Core 4,961/4,961; App hermetic 6,760/6,760; collision/InstalledDat
fixtures 63/63; Runtime 1,884/1,884; Content 213/213.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
WalkProductionWorldData no longer floods; its indoor and outdoor static
sweeps read ShadowObjectRegistry.TryGetRetailCellArray (retail's
calc_cross_cells_static @0x00515160 -> AddPartsShadow @0x00517e40
CELLARRAY, computed once at registration). The App-owned render flood
(ResolveStaticRenderCells, its fingerprint cache, the primitive-Setup
special case) and Core's ComputeStaticRenderCells are deleted. The one
remaining fallback, an entity the physics publisher has not registered yet
while the projection journal already published it, buckets to the authored
parent cell and is counted per frame (UnregisteredStaticRenderFallbackCount)
for chunk 5 to judge on the connected route.
The Facility stair pin now registers at the projection's own entity id
(the old pure-function test never carried identity) and reads the retail
array; the installed-DAT comparator compares retail against collision.
Gates (run in the implementer's isolated worktree at identical content):
Release build 0/0; Core Physics 2,202/2,202; App hermetic 6,760/6,760 (the
two added WalkProductionWorldData tests); installed-DAT walk/flood/stair
family 24/24; Runtime 1,884/1,884.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Every production registration now hands ShadowObjectRegistry the object's
whole visual part array beside its collision dispatch: static publication
(App LandblockPhysicsPublisher and the headless Content twin) through
ShadowShapeBuilder.FromStaticRenderParts, live entities (Runtime
LiveEntityCollisionBuilder) through the new FromSetupRenderParts, which walks
every Setup part with the same physics-sphere-else-drawing-sphere and
part-box rule from the PhysicsDataCache Runtime already reaches. Nothing
consumes the retail products yet; the App hermetic lane still passes
6,758/6,758.
The Lane=InstalledDat comparator registers five real fixtures through the
real publication inputs and prints retail CELLARRAY, collision cells, and
the old render cells side by side: Facility Hub stair Setup 0x02000623
(7 cells incl. 0x8A02015F/015E), cathedral ramp 0x020009A2 (3 cells, the
genuine multi-part case), the #334 Neftet formation (25 cells), and a
landblock-edge crosser (6 cells, 2 in the neighbor block). All three
answers agree for BSP-bearing objects, as the shared primitive predicts;
the divergence chunk 3 expects appears only for decorative non-BSP parts.
Core Physics 2,202/2,202; Runtime 1,884/1,884; App hermetic 6,758/6,758;
comparator + stair pin 5/5; solution Release build 0 warnings / 0 errors.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Campaign OVERHAUL S2 chunk 1 (Core half). ShadowObjectRegistry accepts the
object's whole visual part array as an optional registration input and
computes retail's CELLARRAY from it as a retained side product:
- route per CPhysicsObj::calc_cross_cells_static @0x00515160: cylsphere
(CObjCell::find_cell_list @0x0052b9f0 over the Setup's authored
cylspheres, carried as the Cylinder shapes of the COLLISION dispatch) when
(state & 0x10000) == 0 and a cylsphere exists, else the bbox route
(find_bbox_cell_list @0x00510fc0 via CellTransit.BuildShadowCellSetFromParts
over every visual part: physics sphere else drawing sphere, part box);
- per-cell part entries in array-then-part order for every part
(CPartArray::AddPartsShadow @0x00517e40), clip planes required iff the
array has more than one cell;
- Deregister clears everything (remove_shadows_from_cells @0x00511230);
UpdatePosition/CommitSetPosition recompute; ReplaceMultiPartPayload swaps
the part array without re-flooding.
_entityToCells and _cells are untouched; no production caller passes the
part array yet (chunk 1b), so this is a zero-behavior change. The lead
corrected the implementer's route decision, which had inspected the visual
part array for cylinders; retail's GetNumCylsphere reads Setup collision
data. Core Physics 2,202/2,202.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>