feat(render): S4 chunk 2 — retail's two-list alpha FIFO cutover
Replaces the single scope-global distance-sorted RetailAlphaQueue with
retail's own two independent FIFO lists (CLIP/ALPHA, capacity 3000 each,
D3DPolyRender::AddMeshToAlphaList's exact append-only/capacity-drop
behavior — Ghidra-verified 2026-09-04), routed by a new
RetailAlphaMeshRouter porting DrawMesh's five-row immediate/delayed
branch table and ConstructMesh's subset-mask formula as pure functions,
and drained at retail's four normal-world FlushAlphaList sites
(DrawBuilding/DrawBlock/PView::DrawCells/RenderNormalMode) under the
exact Ghidra-verified no-op predicate (both counts strictly below
threshold*3000). A new WalkFrameEventKind.SortCellExit /
IWalkEventSink.OnSortCellExit / IWalkFrameLeafRenderer.FlushSortCellExit
fires once per admitted land-block cell for DrawBlock's 0.75f valve,
pinned by a dedicated far/near ordering test in RetailFrameWalkTests.cs.
WbDrawDispatcher's two submit sites and ParticleRenderer's one route
through the router; since none of the three ever draws during the Sky
leaf, installs a detail surface, or sets MultiPassAlpha, rows 1/2/4/5
are provably unreachable there and the call sites assert loudly rather
than building unexercisable immediate-draw plumbing. FlushFartherThan,
RetailAlphaOrdering.ComputeViewerDistance, and every viewerDistance
argument on the submit path are deleted.
Scope note (packet s4-depth-alpha-packet.md §10): C4 (routing EnvCell's
transparent shell batches through the shared queue) was not attempted —
EnvCell draws one per-cell MultiDrawIndexedIndirect call with no
per-subset deferred-replay abstraction, and building one without visual
verification (no graphical client in this worktree) was judged out of
this bounded chunk's scope. AP-34 is therefore retired and replaced by
two narrower rows rather than deleted outright: AP-236 (the carried-
forward EnvCell-immediate residual) and AP-237 (a newly identified gap:
TranslucencyKind.AlphaBlend can arise from either retail's Alpha/
Translucent bits, mask 0x02/ALPHA, or the Translucent+ClipMap "cloud"
override, mask 0x08/CLIP — GroupKey doesn't retain the raw bit to tell
them apart, so the router always picks ALPHA; only known example is
cloud GfxObj 0x01004C35). Both are compositing-order-only divergences,
never blend/visual ones.
Mutation checks (each applied, confirmed failing, then reverted):
- FIFO drain order reversed -> 5 RetailAlphaQueueTests fail (order).
- FlushAlphaList `<` -> `<=` -> boundary/scratch tests fail (2250 case
reads drained=0 instead of 2250).
- Capacity check loosened (3000 -> 6000) -> overflow-drop test fails
(TryAppend returns true, PendingCount reads 3001).
- IsFirstForList forced true -> flag test fails once inspected on the
pre-flush two-entry snapshot (the post-flush single-survivor version
of this test was vacuous and rewritten).
- Router row 3 condition inverted -> both the hand-traced Theory (6
cases) and the 160-cell independent-truth-table brute force fail (20
mismatches).
- SortCellExit emitted before OnLandscapeCellTurn instead of after ->
RetailFrameWalkTests ordering pin fails ("SCX must immediately follow
its own cell's SC").
- Prepare-per-list instead of prepare-once-combined -> the CLIP/ALPHA
boundary batching test throws (index out of range).
Gates: Release build 0 warnings/0 errors. Hermetic lane (Lane!=Installed
Dat&...&Status!=KnownFailure) 6855/6855 passed. InstalledDat lane 249
passed / 10 failed — exactly the 4 pre-existing failures (#383 x2
LayoutImporter, TowerAscent KnownFailure, #458 Oh_doorway_still
KnownFailure) plus 6 NEW KnownFailure Facts
(AlphaFlushTranscript_*_MatchesRetailFrame2, one per capture) extending
this gate from PM/PC to AM/FL: the flush SITE+THRESHOLD sequence matches
the capture exactly for all six poses (including zero SortCellExit
drains in every capture, confirming the 0.75 valve is inert at these
scene complexities in both retail and this replay); the drained-COUNT-
per-list dimension diverges because this hermetic harness (matching the
existing PM/PC gate's own EmptyAlphaDepthWorldData design) carries no
live GfxObj/particle content, so every observed count reads (0,0)
against retail's real per-frame volume — both sequences quoted in full
per pose in the packet's new §10. Shader classes (VulkanShaderDescriptor
ContractTests/VulkanShaderManifestTests/RenderPackSpirvValidatorTests)
32/32 passed.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
3f69f1126b
commit
89f1e2676f
19 changed files with 1913 additions and 837 deletions
|
|
@ -328,7 +328,8 @@ research and is no longer active.
|
|||
| AP-27 | PlayerDescription trailer: GameplayOptions skipped by a 4-byte-aligned heuristic scan for a valid inventory parse; options blob captured opaque, never decoded (retail decodes + applies UI options) | `src/AcDream.Core.Net/Messages/PlayerDescriptionParser.cs:69` | Variable-length opaque blobs; mirrors holtburger's heuristics; follow-up issue extends when panels consume those sections | An options blob that coincidentally parses as a valid inventory (or inventory not landing at EOF) yields wrong/empty inventory+equipped at login; retail-persisted UI options silently ignored | ACE GameEventPlayerDescription.WriteEventBody; holtburger events.rs:195-218 |
|
||||
| ~~AP-28~~ | **RETIRED 2026-08-08 (Campaign A slice A2).** The three picked AL parameters and the gain-driven eviction are both gone. `RetailSoundMixer` now carries the byte-decoded retail curve — `g = dist < 5 ? vol : 25·vol/dist²`, clamped to 1, ONE master multiply, `db = ceil(20·log10 g)`, and a hard −50 dB no-allocate floor (audible radius ≈94.2 m at unity) — with pan as retail's `−15·sin(Δbearing)` in whole decibels and a 5-metre integer deadzone. Every AL source is source-relative with `AL_ROLLOFF_FACTOR = 0` and the global distance model is `None`, so AL contributes no attenuation of its own; the old `InverseDistanceClamped` ref-2 m curve was inverse FIRST power (`2/d`), quieter than retail up close and far louder at range with no cutoff at all. Voice eviction now compares the DAT-authored float priority strictly-less in ring order per `SoundManager::PlaySoundInternal` @ `0x0054FEC0` (the row's old `FUN_00550ad0` citation was wrong — that address is inside an `IntrusiveHashTable` constructor). The residual pan-LAW approximation is AP-173; retail's own `s_bPlaySoundOnlyWhenActive` gate is TS-64. | retired | — | — | `SoundManager::GetAttenuation @ 0x00550020`; `SoundManager::PlaySoundInternal @ 0x00550170` and `@ 0x0054FEC0`; `docs/research/2026-08-08-audio-retail-soundmanager-core.md` |
|
||||
| AP-31 | Scenery placement drift + the 0xA9B1 road-edge tree — WB-upstream divergences from retail, ACCEPTED (**#49/#50**, 2026-05-11) | `src/AcDream.Core/World/SceneryGenerator.cs` (via `WbSceneryAdapter`) | Piecemeal patching against WB upstream is net-negative (the `e279c46` road-check attempt over-suppressed scenery elsewhere, reverted `677a726`); visible impact = a handful of trees a few meters off | The same WB-upstream class could hide a *larger* placement divergence elsewhere; revisit only via a coherent ACME-style per-vertex filter port | `CLandBlock::get_land_scenes`; ACME GameScene.cs:1074 per-vertex road filter |
|
||||
| AP-34 | The world now shares one delayed alpha queue across Wb GfxObj/Setup entities and scene particles and drains it at retail's landscape/final boundaries. Residual: the modern reconstruction uses one stable scope-global CYpt sort rather than retail's per-`CPartCell` `CShadowPart` sort followed by cell traversal; `EnvCellRenderer` transparent shell batches also remain immediate and outside this queue | `src/AcDream.App/Rendering/RetailAlphaQueue.cs`; `RetailPViewPassExecutor.cs` (`FlushLandscapeAlpha`); `Rendering/Wb/WbDrawDispatcher.cs`; `ParticleRenderer.cs` | The mandatory modern renderer no longer owns retail `CPartCell` shadow lists. The shared queue restores the material consequence that motivated the port—particles and ordinary translucent parts can interleave—without rebuilding a second scene graph; stable sequence retains authored order on equal CYpt | Transparent objects from different cells can exchange order at a narrow overlap compared with retail cell traversal; an alpha-blended EnvCell shell cannot interleave with a particle or Wb entity, so those rare overlaps can still overpaint differently | `RenderDeviceD3D::DrawObjCellForDummies` 0x005A0760; `CShadowPart::insertion_sort` 0x006B5130; `D3DPolyRender::FlushAlphaList` 0x0059D2E0; `PView::DrawCells` 0x005A4840 |
|
||||
| AP-236 | **S4-c2 (2026-09-04): AP-34's distance-sort residual is CLOSED — retired, not narrowed.** `RetailAlphaQueue` is now retail's own two independent FIFO lists (CLIP/ALPHA, capacity 3000 each, `D3DPolyRender::AddMeshToAlphaList`'s exact append-only/capacity-drop behavior), routed by `RetailAlphaMeshRouter`'s port of `DrawMesh`'s branch table and drained at retail's four normal-world `FlushAlphaList` sites (`DrawBuilding`/`DrawBlock`/`PView::DrawCells`/`RenderNormalMode`) under retail's exact no-op threshold rule. No acdream-only sort remains anywhere on the alpha path. The ONE residual AP-34 documented that this chunk does NOT fix: `EnvCellRenderer` transparent shell batches still draw immediately in their own per-cell MDI-indirect call, never through the shared queue — wiring EnvCell's batched multi-draw-range submission into the per-subset FIFO token protocol (`IRetailAlphaDrawSource`) is a substantial, separate architecture change (EnvCell has no per-subset deferred-replay abstraction today), out of this bounded chunk's scope and deliberately deferred rather than attempted without visual verification. | `src/AcDream.App/Rendering/EnvCellRenderer.Rhi.cs` (the `renderPass == WbRenderPass.Transparent` MDI-indirect block); `src/AcDream.App/Rendering/RetailAlphaQueue.cs`; `src/AcDream.App/Rendering/RetailAlphaMeshRouter.cs` | EnvCell's per-cell transparent batch is homogeneously mask 0x02 (ALPHA-family; EnvCell shells never carry a ClipMap bit in this path) so, when a future chunk wires it in, the WHOLE per-cell batch can be ONE atomic FIFO token — coarser than retail's per-subset entries but consistent with how WbDrawDispatcher/ParticleRenderer already submit at instance granularity, not per-D3D-surface-subset. Until then, EnvCell's own `detailEnabled` gate already matches retail's row-1 "detail active → immediate" case exactly; only the "detail off → append instead of draw-now" branch is missing. | An EnvCell's transparent shell (glass, water, grates) with no environment detail texture set composites in strict draw-call order relative to ordinary GfxObj/particle content instead of interleaving through the shared FIFO — a narrow-overlap ordering difference, not a blend/visual one (same class of risk AP-34 originally carried, now scoped to exactly this one renderer). | `RenderDeviceD3D::DrawEnvCell` 0x0059F170; `D3DPolyRender::AddMeshToAlphaList` 0x0059C230; `D3DPolyRender::FlushAlphaList` 0x0059D2E0 |
|
||||
| AP-237 | **Filed 2026-09-04 (S4-c2).** `RetailAlphaMeshRouter.MaskFromTranslucencyKind` reconstructs retail's `ConstructMesh` queue-routing mask (spec: alpha-family bits always win; else ClipMap; else Translucent) from acdream's already-collapsed `TranslucencyKind`, which was built for a DIFFERENT priority chain (`TranslucencyKindExtensions.FromSurfaceType`'s blend-STATE selection: a "Translucent override" step checks Translucent+ClipMap-without-alpha-family FIRST and calls it `AlphaBlend`). A raw `Surface.Type` with Translucent(0x10) and Base1ClipMap(0x04) bits but no Alpha/InvAlpha/Additive bit (observed: cloud GfxObj `0x01004C35`) therefore reconstructs to mask 0x02 (routes to ALPHA) here, while retail's real `ConstructMesh` mask is 0x08 (routes to CLIP) — `GroupKey` does not retain the raw ClipMap bit needed to distinguish the two `AlphaBlend` origins, and threading it through `GfxObjSubMesh`/`GroupKey`/every mesh-classification call site was judged out of this bounded chunk's scope. | `src/AcDream.App/Rendering/RetailAlphaMeshRouter.cs` (`MaskFromTranslucencyKind`'s own doc comment); `src/AcDream.Core/Meshing/TranslucencyKind.cs` (`FromSurfaceType`'s Translucent-override step) | Compositing-ORDER only (which FIFO list a subset enters), never a blend-mode or visual difference — the affected surface still renders alpha-blended exactly as retail's `SetSurface` configures it; only its position in the shared drain order can differ at a narrow overlap with other CLIP-list content. Extremely narrow: the only known example client-wide is the cloud GfxObj. | A translucent+clipmap surface (clouds being the only known instance) interleaves with ordinary ALPHA-list content instead of CLIP-list content at a rare compositing overlap. | `D3DPolyRender::ConstructMesh` 0x0059DFA0 (mask priority); `D3DPolyRender::SetSurface` 0x0059C4D0 (the Translucent-override blend selection acdream's `TranslucencyKind` already matches) |
|
||||
| AP-36 | Dungeon streaming gate triggers on the player's CURRENT cell being a sealed EnvCell (`CurrCell.IsEnv && !SeenOutside`), an approximation of ACE's full landblock `IsDungeon` (all-heights-zero + NumCells>0 + Buildings.Count==0). The retail BEHAVIOR (a dungeon loads no adjacent landblocks) is faithful — only the runtime TRIGGER is the cheap cell predicate instead of classifying the center landblock. **#135 pre-collapse:** at login/teleport the same collapse is triggered EARLY (the instant the streaming center is recentered onto the spawn/dest cell) via `IsSealedDungeonCell` reading the EnvCell **dat** `SeenOutside` flag — because the physics `CurrCell` is null until placement, which waits for hydration; without the early trigger the full 25×25 ocean-grid window loads then unloads (the ~30 s login FPS ramp). **#215 cell identity:** the pre-collapse/recenter decision compares the player's current `Position.objcell_id` landblock with the received destination `objcell_id`; it never reconstructs the source from XYZ because dungeon frame origins may be negative. **#145/#138 teleport-hold suppression:** during a teleport arrival HOLD the player is unplaced, so `CurrCell` is the frozen SOURCE cell, not the destination; the gate is suppressed for the hold (`DungeonStreamingGate.Compute(isTeleportHold:true)` → not-inside-dungeon) so a teleport OUT of a dungeon follows the destination (the PortalSpace observer pin) and `ExitDungeonExpand`s, instead of re-pinning streaming onto the source dungeon (which left the outdoor destination un-hydrated → 600-frame readiness timeout → force-snap to ocean — the #145 "second teleport does nothing" + #138 incomplete-world) | `src/AcDream.App/Streaming/TeleportLandblockTransition.cs` (source/destination cell-ID classification) + `src/AcDream.App/Streaming/DungeonStreamingGate.cs` (`Compute` — per-frame predicate + teleport-hold suppression) + `src/AcDream.App/World/LiveEntityHydrationPorts.cs` (`LiveEntityWorldOriginCoordinator.TryInitialize` — login pre-collapse) + `src/AcDream.App/Physics/LiveEntityNetworkUpdateController.cs` (`OnPosition` — first accepted canonical Position) + `GameWindow:AimTeleportDestination`/`IsSealedDungeonCell` (teleport pre-collapse and DAT predicate) + `src/AcDream.App/Streaming/StreamingController.cs` (collapse/expand/`PreCollapseToDungeon`) | The predicate is already computed for sun/sky gating (playerInsideCell) and exactly matches for sealed dungeons vs windowed building interiors (SeenOutside=true → not gated); no landblock re-classification needed. The dat-flag read is the same `EnvCellFlags.SeenOutside` the hydrated `ObjCell.SeenOutside` is built from (`EnvCell.cs:72`/`PhysicsDataCache.cs:224`), so the pre-collapse decision matches the eventual per-frame gate exactly. The cell-ID comparison matches retail's complete `Position` flow. | A dungeon cell that reports SeenOutside (an entrance cell open to the surface) briefly un-collapses and re-streams the window; a hypothetical windowless building back-room (IsEnv && !SeenOutside but HasBuildings) would wrongly collapse its outdoor neighbors; a sealed-dungeon entrance cell that is itself SeenOutside is simply MISSED by the early trigger and falls back to the existing late collapse (no worse than before #135) | ACE `LandblockManager.GetAdjacentIDs` (dungeons→empty) Landblock.cs:577-582; `IsDungeon` Landblock.cs:1264-1277; retail `SmartBox::TeleportPlayer` 0x00453910 |
|
||||
| AP-43 | Per-object torch (point/spot) lighting AND sun are both gated on the OBJECT's own cell via the same `IndoorObjectReceivesTorches(ParentCellId)` predicate (`(id & 0xFFFF) >= 0x0100`): indoor objects (EnvCell-parented) get torches + NO sun; outdoor objects get the SUN + ambient + NO torches. This is the faithful per-draw port of retail's `useSunlight` gate — `DrawMeshInternal` (0x0059f398) calls `minimize_object_lighting` only `if (Render::useSunlight == 0)`, and `PView::DrawCells` (0x005a4840) calls `useSunlightSet(1)` (0x005a485a) for the outdoor stage and `useSunlightSet(0)` (0x005a49f3) for the interior-cell stage. **#142 (2026-06-20):** the sun gate is now PER-INSTANCE in the shader (binding=6 `instanceIndoor[]` flag in `mesh_modern.vert`, filled by `AppendCurrentLightSet`) — it was previously a per-FRAME global keyed on the PLAYER cell (`UpdateSunFromSky`). The per-frame global is retained for sealed dungeons (correctly kills the sun frame-wide when no sky is visible). **Residual:** the `ebp_2` second seen-outside test in `CellManager::ChangePosition` (0x004559B0) is unaudited — unclear whether it changes the ambient/sun regime for a subset of cells. No observed behavioral impact in tested cells. | `src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs` (`IndoorObjectReceivesTorches`, `ComputeEntityLightSet`, `AppendCurrentLightSet`, `_instIndoorSsbo`/`_indoorData`/`InstanceGroup.IndoorFlags`); `src/AcDream.App/Rendering/Shaders/mesh_modern.vert` (binding=6 `instanceIndoor[]` gate on sun loop); per-frame sun `src/AcDream.App/Rendering/WorldRenderFrameBuilder.cs` (`RuntimeWorldFrameEnvironmentPreparation.UpdateSunFromSky`) | Torches: outdoor objects never torch-lit (exact retail). Sun: indoor objects (furniture, NPCs, player in a windowed building) never sun-lit (exact retail per-stage). Ambient: per-player-cell regime unchanged (exact retail `ChangePosition`). | The `ebp_2` unaudited test in `ChangePosition` could affect a narrow class of cells (entrance cells? sub-cells with special flags?) — no symptom observed; audit it if a lighting edge case arises in an unusual cell type | `useSunlight` gate `DrawMeshInternal` 0x0059f398; `useSunlightSet` 0x0054d450; per-stage `PView::DrawCells` 0x005a4840 (`useSunlightSet(1)` 0x005a485a / `useSunlightSet(0)` 0x005a49f3); `minimize_object_lighting` 0x0054d480; `CellManager::ChangePosition` 0x004559B0 (ambient + seen_outside) |
|
||||
| AP-35 | Point/spot lights are now PER-VERTEX Gouraud (`pointContribution` ~line 153 of `mesh_modern.vert`) matching retail's `SetStaticLightingVertexColors` bake path. Half-Lambert wrap (`(1/1.5)·(N·D + 0.5·d)`) AND norm distance attenuation (`distsq>1 ? distsq·d : d`) ARE ported (A7 Fix A, `aa94ced`). Point-light sum clamped to [0,1] on its own accumulator before adding ambient+sun (A7 Fix D D-1, mirrors retail's per-vertex bake clamp). CPU oracle: `src/AcDream.Core/Lighting/LightBake.cs`, locked by `tests/AcDream.Core.Tests/Lighting/LightBakeConformanceTests.cs`. **Residual (two parts):** (a) acdream lights in-shader each frame (per-frame GPU evaluate); retail bakes into the vertex buffer ONCE — an architecture/performance difference; the wrap + norm + clamp formula is the same, but bake-once is cheaper for static geometry; (b) acdream's `SelectForObject` keeps only the 8 NEAREST reaching point/spot lights per object/cell (`MaxLightsPerObject=8`, see AP-16), whereas retail's bake sums ALL reaching static lights per vertex — a surface reached by >8 point lights is dimmer in acdream than retail's bake result (rare in practice; a room has a handful of torches) | `src/AcDream.App/Rendering/Shaders/mesh_modern.vert` (`pointContribution` ~line 153; wrap ~line 163; norm ~line 167; point-sum clamp line 210) | Per-vertex Gouraud + wrap + norm + clamp all match retail. The two residuals are: (a) per-frame GPU vs bake-once — architecture/perf only; (b) 8-light cap dimming when >8 lights reach one surface — rare. `LightInfoLoader.cs:81` folds static_light_factor 1.3 into Range | (a) A new frame-time consumer bypassing `accumulateLights` would need to replicate the wrap + norm formula; per-frame GPU re-evaluate has higher per-frame cost than bake for static geometry. (b) A densely lit scene (>8 torches reaching one wall) renders dimmer than retail — see AP-16 for the 8-cap ownership | `calc_point_light` 0x0059c8b0 (line 0x0059c9a2 ramp; 0x0059c925 wrap); `SetStaticLightingVertexColors` 0x0059cfe0; static_light_factor 0x00820e24 |
|
||||
|
|
|
|||
|
|
@ -459,267 +459,269 @@ PASS/FAIL with file:line findings; two fix rounds maximum, then the ledger.
|
|||
|
||||
**Lenses (sequential, session model, narrowed to the round-2 diff):** retail-faithful (R2-3's predicate and R2-2's seeding argued from the bytes/decomp — the counter's persistence across frames at `PView::DrawCells` and the reset site), gate-honesty (every sentence in the commit body and every test doc comment is something the tests evaluate; both sequences compared together), production (no allocation/behavior change outside the test project except R2-5(a) comments).
|
||||
|
||||
> §10 (the implementer's S4-c2 landing note) lives in the chunk commit `048d5b12f` in worktree `s4-c2-impl` and arrives with the cherry-pick; §11 below was written on the campaign branch so the fixer reads it from here.
|
||||
## 10. S4-c2 landing (Sonnet implementer, 2026-09-04) — gate G-c2 result and open scope
|
||||
|
||||
## 11. S4-c2 fix round 1 (lead, 2026-09-04 — after the two-lens review of `048d5b12f`; round 1 of the two the plan allows)
|
||||
**C1/C2/C3/C5 landed as specified.** `RetailAlphaQueue` is retail's own two FIFO
|
||||
lists (`RetailAlphaEntry`, capacity 3000 each, `TryAppend` returns `false` and
|
||||
drops the subset at capacity, `Flush(site, threshold)` implements the exact
|
||||
Ghidra-verified no-op predicate — `if (clipCount < threshold*3000 && alphaCount
|
||||
< threshold*3000) return;` — and drains CLIP fully then ALPHA fully, only
|
||||
adjacent same-source runs batched). `RetailAlphaMeshRouter` ports `DrawMesh`'s
|
||||
five-row branch table and `ConstructMesh`'s mask formula as pure functions,
|
||||
independently verified against a fresh 2026-09-04 Ghidra decompile of both
|
||||
functions plus `AddMeshToAlphaList` and `FlushAlphaList` (not just the
|
||||
packet's own researched spec — see the commit body for the exact quoted
|
||||
predicates). `WbDrawDispatcher`'s two submit sites and `ParticleRenderer`'s
|
||||
one route through the router with a shared `SubmitToAlphaQueue`/inline
|
||||
equivalent; **S4-c2 fix round 1 correction (M1, blocking):** the claim that
|
||||
"rows 1/2/4/5 are provably unreachable there" was WRONG for the particle
|
||||
site, which the round's own retail-faithful review lens caught as a
|
||||
production crash. `WbDrawDispatcher`'s sites genuinely never reach a mask
|
||||
other than 0x02 (`IsOpaque` pre-filters both Opaque and ClipMap upstream, so
|
||||
row 3 always fires there — this part of the claim holds). But
|
||||
`ParticleRenderer.TryAppendMeshDraws` applies NO such pre-filter: a
|
||||
mesh-particle batch's `TranslucencyKind.Opaque` classification (mask 0x00) is
|
||||
a real, reachable input, hitting row 5 (Immediate) — and the throw-on-non-
|
||||
Append guard fired in the render loop. The fix round deleted both throws,
|
||||
implemented rows 1/5's immediate draw at the particle site (reusing the
|
||||
non-deferring `DrawOrdered` code, no new renderer), and derived
|
||||
`materialHasAlpha` for row 4 from `CMaterial::CheckAlphaValues`
|
||||
@0x005396a0/`SetTranslucencySimple` @0x005396f0's retail rule — a particle's
|
||||
current interpolated alpha (already carried in `ColorArgb`'s top byte) stands
|
||||
in for retail's live per-instance material alpha. `FlushFartherThan`, `RetailAlphaOrdering.ComputeViewerDistance`,
|
||||
and every `viewerDistance` argument on the submit path are deleted. A NEW
|
||||
`WalkFrameEventKind.SortCellExit` / `IWalkEventSink.OnSortCellExit` /
|
||||
`IWalkFrameLeafRenderer.FlushSortCellExit` fires once per admitted land-block
|
||||
cell, immediately after that cell's `OnLandscapeCellTurn` and before the next
|
||||
cell's `OnLandCellTurn` — pinned by a dedicated two-block far/near ordering
|
||||
test in `RetailFrameWalkTests.cs`, mutation-checked (reordering the two calls
|
||||
in `RetailFrameWalk.cs` makes the pin fail with "SCX must immediately follow
|
||||
its own cell's SC").
|
||||
|
||||
**Verdicts on `048d5b12f` (worktree `s4-c2-impl`, on `766f9e749`):** retail-faithful lens
|
||||
FAIL (one blocking, four major, three minor — every predicate of the core port confirmed exact
|
||||
against the bytes: `AddMeshToAlphaList` @0x0059C230 list select/capacity/return,
|
||||
`FlushAlphaList` @0x0059D2E0 early-return `test ah,5; jp` = strict-less on BOTH counts so 2250
|
||||
drains, CLIP-then-ALPHA drain, `DrawMesh` @0x0059D4A0 rows 1–5, the four call sites' bytes and
|
||||
symbol ranges, `::flush` = 0.75f @0x820ed0, `s_AlphaDelayMask` = 0x0E @0x820d88,
|
||||
`MultiPassAlpha` = 0 @0x81ef96, `alwaysDrawObjects` = 1 @0x820ed4, `SortCellExit` placed at
|
||||
DrawBlock's `0x5a19e6 DrawSortCell → 0x5a1a07 FlushAlphaList` under the same gate); lead
|
||||
verification agrees on every one of those and adds the C4 ruling. Production/gate lens: see
|
||||
the addendum at the end of this section when it reports. The failures are at the three call
|
||||
sites, the register, and the gate's CI value — the list/router/flush core stands.
|
||||
**C4 (EnvCell) — SUPERSEDED, S4-c2 fix round 1 (M6, 2026-09-04).** This
|
||||
landing's original claim (EnvCell "deliberately NOT done," needing "a
|
||||
genuinely new deferred-replay abstraction") was WRONG in the same round's own
|
||||
two-lens review: `EnvCellRenderer.RenderTransparentOrdered` already replayed a
|
||||
cell's transparent batch on demand, so no new abstraction was needed. The fix
|
||||
round wired it: `RetailPViewPassExecutor.SubmitOrDrawTransparentCellShell`
|
||||
routes every real transparent subset through the SAME
|
||||
`RetailAlphaMeshRouter` table every other alpha submitter uses. Fix round 2
|
||||
retains `TextureBatchData.RetailSurfaceMask` on each `ObjectRenderBatch`:
|
||||
canonical `0x08` reaches CLIP, `0x02` reaches ALPHA, and a mixed cell appends
|
||||
at most one filtered replay token per `(cell,list)`; detail-active eligible
|
||||
subsets remain immediate at the cell turn. AP-34's residuals are now filed as **AP-238**
|
||||
(EnvCell — narrowed to the per-cell-vs-per-subset token GRANULARITY only,
|
||||
not "never through the shared queue") and **AP-239** (the mask-reconstruction
|
||||
gap, formerly filed as the colliding id "AP-237" — see A2/M8c below); a THIRD
|
||||
residual, **AP-240**, was filed this round for M2's separate finding: ordinary
|
||||
clip-mapped GfxObj/scenery/building content never reaches the CLIP list at
|
||||
all (`WbDrawDispatcher.IsOpaque` filters it out upstream). CLIP nevertheless
|
||||
has two genuine production feeder families: clip-mapped mesh particles and
|
||||
exact-mask EnvCell subsets. AP-239's own cited example (cloud
|
||||
GfxObj `0x01004C35`) was DISPROVEN this round (M7) — its surface's alpha-family
|
||||
bits win in both retail's real priority and acdream's reconstruction, so it
|
||||
never actually diverges; a 2026-09-04 DAT scan found 27 real instances instead
|
||||
(e.g. surface `0x08000015` on GfxObj `0x010001EC`). These residuals do not
|
||||
change the affected subset's blend mode, but overlap-dependent compositing
|
||||
order can be visibly different; AP-238/AP-240 state the concrete overlap.
|
||||
|
||||
**Items (each is a contract; quote decomp predicates, never paraphrase — `feedback_quote_decomp_predicates`; every new or changed pin carries a mutation text in the commit body — `feedback_every_new_pin_must_be_shown_to_fail`):**
|
||||
**Gate G-c2 result, all six poses, real InstalledDat run
|
||||
(`RunAlphaFlushTranscriptGate`, `WalkTraceConformanceTests.AlphaDepthTranscript.cs`):**
|
||||
the flush **SITE+THRESHOLD sequence matches the capture EXACTLY for all six
|
||||
poses** — same count and order of `DrawBuilding`(0f)/`LandscapeFlush`(0f)/
|
||||
`RenderNormalMode`(0f) calls, and (confirming the packet's own capture-profile
|
||||
prediction) **zero `SortCellExit`(0.75f) drains in any of the six captures'
|
||||
frame 2** — every real per-land-cell valve call stayed under the 2250-entry
|
||||
no-op threshold at these scene complexities, exactly matching this gate's own
|
||||
zero-content replay. The **drained-count-per-list dimension diverges on every
|
||||
0f flush for every pose** — this hermetic replay (`EmptyAlphaDepthWorldData`,
|
||||
the same zero-content design the PM/PC gate it extends already uses) submits
|
||||
no real GfxObj/particle content, so every observed count is `(0,0)` where
|
||||
retail's capture shows real volume. Full quoted sequences (site, threshold,
|
||||
clip, alpha), expected vs. actual, per pose:
|
||||
|
||||
**M1 (BLOCKING) — the particle site throws on a data-driven row.** `ParticleRenderer.TryAppendMeshDraws` (`ParticleRenderer.cs:645-663`) appends EVERY `renderData.Batches[i]` with `IndexCount > 0` and an assigned texture; `ObjectRenderBatch.Translucency` is the mesh classification (`ObjectMeshManager.cs:2253`), so `TranslucencyKind.Opaque` is an ordinary value for a `FullMesh` particle GfxObj → `MaskFromTranslucencyKind(Opaque) = 0x00` → `Route(..., mask 0x00, materialHasAlpha:false)` → row 5 `Immediate` → the `InvalidOperationException` at `ParticleRenderer.cs:361-372` fires in the render loop. Retail defines row 5 (`0x0059d58c RenderMeshSubset`, draw now) and row 4 (`(delayMask & 4) != 0 && material != 0 && *(material+8) != 0` → append ALPHA). Fix: (a) delete the throw at BOTH sites (`ParticleRenderer.cs:361-372`, `WbDrawDispatcher.SubmitToAlphaQueue`) — a router row is a data-driven outcome, never an invariant (`feedback_retail_dispatch_is_data_driven`); (b) for particles derive `materialHasAlpha` from retail's particle material: find where the emitter's `CMaterial` is created/`translucency` set (grep `ParticleEmitter::` / `CParticle` / `CMaterial::has_alpha` in the named pseudo-C; `has_alpha` is the field at `+0x8` DrawMesh row 4 reads) and QUOTE the rule (expected: a particle whose translucency < 1 or whose material carries alpha has `has_alpha != 0` → row 4 ALPHA; a fully opaque particle material → row 5 immediate); (c) implement the immediate outcome for rows 1/5 at the particle site by drawing the submission now through the existing non-deferring particle draw path (the same code `DrawOrdered`/`DrawOrderedRhi` uses for one submission) — no new renderer; (d) pins: an Opaque-classified mesh-particle batch with has_alpha routes to ALPHA and is drawn at the flush; the same batch with no material alpha draws immediately and never enters a list; mutation text for each.
|
||||
- **cathedral-arrival:** expected `[(DrawBuilding,0,108,40), (DrawBuilding,0,3,0), (DrawBuilding,0,28,7), (DrawBuilding,0,7,5), (DrawBuilding,0,70,24), (DrawBuilding,0,13,0), (DrawBuilding,0,0,0), (DrawBuilding,0,0,4), (DrawBuilding,0,0,0), (DrawBuilding,0,0,0), (DrawBuilding,0,0,0), (DrawBuilding,0,34,7), (DrawBuilding,0,0,0), (DrawBuilding,0,0,0), (LandscapeFlush,0,0,0), (RenderNormalMode,0,0,0)]`; actual: same 16-entry SITE sequence, every count `(0,0)`.
|
||||
- **cathedral-leak:** expected `[(DrawBuilding,0,108,40), (DrawBuilding,0,3,0), (DrawBuilding,0,28,7), (DrawBuilding,0,7,5), (DrawBuilding,0,70,24), (DrawBuilding,0,13,0), (DrawBuilding,0,0,0), (DrawBuilding,0,0,4), (DrawBuilding,0,0,0), (DrawBuilding,0,0,0), (DrawBuilding,0,0,0), (DrawBuilding,0,34,9), (DrawBuilding,0,0,0), (DrawBuilding,0,0,0), (LandscapeFlush,0,0,0), (RenderNormalMode,0,0,0)]`; actual: same 16-entry SITE sequence, every count `(0,0)`.
|
||||
- **cathedral-stair-arch:** expected `[(DrawBuilding,0,0,0), (DrawBuilding,0,0,0), (DrawBuilding,0,47,30), (DrawBuilding,0,21,0), (DrawBuilding,0,2,0), (DrawBuilding,0,34,27), (DrawBuilding,0,43,1), (DrawBuilding,0,0,0), (DrawBuilding,0,0,0), (DrawBuilding,0,0,0), (DrawBuilding,0,2,4), (LandscapeFlush,0,0,0), (RenderNormalMode,0,32,6)]`; actual: same 13-entry SITE sequence, every count `(0,0)`.
|
||||
- **foundry-deep:** expected `[(DrawBuilding,0,1,0), (DrawBuilding,0,0,0)×11, (LandscapeFlush,0,0,0), (RenderNormalMode,0,18,7)]` (12 DrawBuilding entries total — **S4-c2 fix round 1 correction, A5: the landing note originally said "×10", undercounting by one against its own stated 12-entry total; 1+11=12**); actual: same 14-entry SITE sequence, every count `(0,0)`.
|
||||
- **holtburg-doorway-still:** expected `[(DrawBuilding,0,12,0), (DrawBuilding,0,23,6), (DrawBuilding,0,0,0), (DrawBuilding,0,15,0), (DrawBuilding,0,0,0), (DrawBuilding,0,0,0), (DrawBuilding,0,0,1), (DrawBuilding,0,0,0)×6, (LandscapeFlush,0,0,0), (RenderNormalMode,0,33,0)]` (13 DrawBuilding entries total — **S4-c2 fix round 1 correction, A5: the landing note originally said "×5", undercounting by one against its own stated 13-entry total; 7+6=13**); actual: same 15-entry SITE sequence, every count `(0,0)`.
|
||||
- **terrace-edge:** expected `[(DrawBuilding,0,158,452), (DrawBuilding,0,22,204), (DrawBuilding,0,0,0)×3, (DrawBuilding,0,22,523), (DrawBuilding,0,13,7), (DrawBuilding,0,14,5), (DrawBuilding,0,0,0)×3, (DrawBuilding,0,11,0), (DrawBuilding,0,0,0), (DrawBuilding,0,13,12), (RenderNormalMode,0,56,0)]` (14 DrawBuilding entries, no LandscapeFlush — this pose never runs a qualifying interior turn, matching its own PM/PC KnownFailure history); actual: same 15-entry SITE sequence, every count `(0,0)`.
|
||||
|
||||
**M2 (MAJOR) — the CLIP list is structurally unreachable for ordinary content, undeclared.** `WbDrawDispatcher.IsOpaque(t) => t == Opaque || t == ClipMap` (`WbDrawDispatcher.cs:3567`) keeps every clip-mapped subset out of `_translucentDraws`/`SubmitWalkAlphaInstance`; retail's `ConstructMesh` gives them mask 0x08 → row 3 → CLIP, and the six captures show 4,183 of 10,556 `AM` lines on CLIP (e.g. the §10 quotes `(DrawBuilding,0,158,452)`: 158 CLIP entries acdream reports as 0). Fix (no pipeline change in this round — the alpha-to-coverage treatment of clip-mapped surfaces predates S4 and is a VisualMaster-era translucency decision): file a NEW AP row stating exactly this — clip-mapped GfxObj/scenery/building subsets draw immediately on the `OpaqueAlphaToCoverage` pipeline and never enter retail's CLIP FIFO; consequence = compositing in draw-call order instead of retail's deferred CLIP drain (depth-writing cutouts, so ordering is mostly invisible; edges differ by the older alpha-to-coverage choice); the only CLIP-list feeders left are clip-mapped mesh PARTICLES; cite `D3DPolyRender::ConstructMesh` 0x0059DFA0 / `DrawMesh` 0x0059D4A0 row 3 / `AddMeshToAlphaList` 0x0059C230 — and correct AP-236's sentence "the ONE residual AP-34 documented that this chunk does NOT fix" (false: there are two, this and EnvCell).
|
||||
All six `AlphaFlushCounts_*` Facts are tagged `[Trait("Status",
|
||||
"KnownFailure")]`; the paired `AlphaFlushSites_*` Facts are live and pass.
|
||||
The count Facts remain KnownFailure for exactly this reason, never weakened (the real
|
||||
comparison runs and genuinely fails on the count dimension; the site
|
||||
dimension is asserted unconditionally in the same method and genuinely
|
||||
passes). Root cause is a HARNESS scope limit shared with the pre-existing
|
||||
PM/PC gate, not a routing defect: closing it for real would mean feeding
|
||||
`WalkFrameDriver` genuine per-pose GfxObj/particle content from the installed
|
||||
DAT (the production `WalkProductionWorldData` path, not
|
||||
`EmptyAlphaDepthWorldData`) — a materially larger apparatus than this chunk's
|
||||
bounded scope, flagged here for a future chunk rather than attempted.
|
||||
|
||||
**M3 (MAJOR) — contract C3's detail-surface input is hardcoded false and the drain applies detail.** Retail `DrawBuilding` @0x0059f2a0: `0059f2eb curr_detail_surface = building_detail_surface; 0059f30b FlushAlphaList(0f); 0059f31d CPhysicsPart::Draw(parts,1); 0059f336 CPhysicsPart::Draw(parts,0); 0059f345 curr_detail_surface = nullptr` → with a non-null building detail EVERY shell subset takes row 1 (immediate, WITH detail); the delayed replay always passes `detailEnabled = 0` (`FlushAlphaList` @0x0059d3c7/@0x0059d45d call `RenderMeshSubset(..., 0, entry.clip, ...)`). acdream: `SubmitToAlphaQueue` passes `detailSurfaceActive:false` while building shells DO reach the alpha path (`WalkClassify.cs:310 detailCategory = entity.IsBuildingShell ? 1u : 0u`) and `DrawPreparedAlphaBatchRhi` (`WbDrawDispatcher.Rhi.cs:642-694`) runs the detail pass ON THE DRAIN — the exact state spec §4 says replay never has. Fix: (a) router input `detailSurfaceActive = entity.IsBuildingShell && RetailDetailTextureContract.ShouldRender(_buildingDetailEnabled(), _buildingDetail)` at both Wb sites (the walk site knows the entity; `DeferTransparentGroups` must carry the category per instance — it already has `DetailCategories`); (b) row 1 for a translucent building-shell instance = draw it NOW at its own turn (adaptation: retail draws the subset in place inside the mesh; the walk's opaque instances are stream-batched, so "now" = at the entity's stream mark, i.e. when Replay reaches that instance's `AlphaSubmitMark`) through a new single-instance immediate draw on the alpha source — bind the blend pipeline, one indirect draw, then the detail pass — reusing `DrawPreparedAlphaBatchRhi`'s pipeline/detail code; (c) the drain never applies detail: delete the `hasDetail`/`DrawBuildingDetailRangeRhi` branch from `DrawPreparedAlphaBatchRhi` (retail replay is `detailEnabled = 0`); (d) a register row for the "in place → at the entity's stream mark" granularity adaptation; (e) pins: a translucent building-shell instance with building detail ON draws at its turn with the detail pass and never enters a list; with detail OFF it is appended and drained WITHOUT detail; an ordinary (non-shell) translucent instance is unaffected by the detail flag; mutation texts.
|
||||
**§11 result (fix round 1, Sonnet implementer, 2026-09-04):** all items
|
||||
M1–M8/A1–A8 from the campaign branch's §11 landed in one commit on top of
|
||||
this landing. M1 (blocking) deleted both unreachable-branch throws and
|
||||
implemented the particle site's real rows 1/4/5; M3 wired building-detail
|
||||
Row 1 into `SubmitToAlphaQueue` with a new single-instance immediate-draw
|
||||
path (`DrawImmediateAlphaInstance`/`Rhi`), removing the drain's own detail
|
||||
branch (M3c) since detail-eligible content no longer reaches it; M6 wired
|
||||
EnvCell's whole transparent shell into the SAME router/queue as one ALPHA
|
||||
token per cell when detail was off, disproving this section's own "needs a
|
||||
new deferred-replay abstraction" claim above; that round's false
|
||||
homogeneous-`0x02` premise and whole-cell replay were then corrected by the
|
||||
exact-mask/list-filtered §12 implementation; A1 deleted a mislabelled
|
||||
outdoor-root `LandscapeFlush` call retail never makes there (its content now
|
||||
joins the pass-end `RenderNormalMode` drain like retail's own outdoor root);
|
||||
M5 split the six former `AlphaFlushTranscript_*` Facts into live `AlphaFlushSites_*`
|
||||
(unconditional pass) and `AlphaFlushCounts_*` (KnownFailure, three honest
|
||||
reasons instead of one) and added a live boundary pin through
|
||||
`WalkFrameDriver.Replay`'s own `SortCellExit` arm; M4 deleted the unread
|
||||
`IsFirstForList` field (retail's per-DrawMesh, per-list value genuinely
|
||||
varies: 9,685 `new=1`, 871 `new=0`; acdream has no one-to-one retained
|
||||
producer or Vulkan consumer); M2/A2/M8c retired `AP-34` (restored as
|
||||
`~~AP-34~~`, not left deleted), renumbered the colliding `AP-236`/`AP-237` to
|
||||
`AP-238`/`AP-239`, and filed `AP-240` for the CLIP-list-for-ordinary-content
|
||||
gap M2 found; M7 disproved AP-239's cloud citation and replaced it with a
|
||||
real scanned instance; A4 fixed `ApplyScratchRetention`'s source-count
|
||||
argument; A7 renamed/redocumented the mesh-router truth-table oracle
|
||||
honestly. See the fix-round commit body for every mutation check's actual
|
||||
failing-assertion text and the real gate output lines.
|
||||
|
||||
**M4 (MAJOR) — `IsFirstForList` has the wrong quantifier.** Retail: `0059d4cc arg3 = 1; 0059d4d0 var_c = 1` at `DrawMesh` ENTRY, cleared after the first append to each list within THAT call (`0059d5ef if (var_4_1 == 0) var_c = 0; else arg3 = 0;`); spec §5: "Each DrawMesh invocation owns two independent first-for-this-list flags"; the captures: `new=1` on 9,685 of 10,556 `AM` lines — impossible under "first since the last drain" (`RetailAlphaQueue.TryAppend` `isFirstForList = target.Count == 0`). Each acdream append is one instance = its own DrawMesh call, so the truthful port is `IsFirstForList = true` on every append — or delete the field. Fix: choose one, fix the doc comments (`RetailAlphaEntry`, `RetailAlphaQueue.TryAppend`, `WalkAlphaDepthTrace`'s "new" comment), delete or rewrite `TryAppend_FlagsOnlyTheFirstEntrySinceTheLastDrain` (a pin of the wrong semantic), mutation text if a pin remains.
|
||||
**§12 result (fix round 2 plus the owner-authorized post-stop repair,
|
||||
2026-09-04):** the owner-authorized repair supersedes the earlier §12 result's
|
||||
EnvCell exactness, scratch-bound, and allocation claims. The repaired real
|
||||
leaf path now excludes deferred non-additive transparent subsets from the
|
||||
opaque pass, classifies retained ClipMap presence with `(mask & 0x08) != 0`
|
||||
(including legal positive-stipple mask `0x09`), and keeps canonical row 3 at
|
||||
`OverrideClipmap=false`. Its CLIP base pipeline is the paired-retail
|
||||
`ONE/INVSRCALPHA`, alpha test `GREATER_EQUAL`, reference `100/255` for a
|
||||
paletted texture or `200/255` for DDS/non-paletted, with depth test/write on
|
||||
and alpha-to-coverage off. Both fragment shaders recognize only those two
|
||||
named references (within 1e-6 for GLSL constant folding); arbitrary `0.5`
|
||||
falls back to the ordinary `0.05` empty-fragment cutoff. Exact `uParamB=1`
|
||||
remains `mesh_detail.vert`'s pre-existing building-category sentinel and also
|
||||
uses `0.05`, so the new reference does not erase building detail. Detail-
|
||||
active ClipMap replay retains the selected cutoff and a depth-writing clip
|
||||
base before the detail contribution. ALPHA remains straight-alpha with depth
|
||||
write off and the two subsets do not cross-feed.
|
||||
|
||||
**M5 (MAJOR) — gate G-c2 has no CI signal.** All six `AlphaFlushTranscript_*` Facts are `Status=KnownFailure`, so the ONE dimension the harness proves (the (site, threshold) sequence) is excluded from the hermetic lane and expected-red in InstalledDat; a future site-sequence regression is invisible. Also the sequence contains zero `SortCellExit` entries in every pose (0 < 2250 on both sides), so the gate exercises nothing the chunk added. Fix: split over the SAME replay into `AlphaFlushSites_<pose>_MatchesRetailFrame2` (live in the lane, asserts (site, threshold) only) and `AlphaFlushCounts_<pose>_MatchesRetailFrame2` (KnownFailure). Rewrite the KnownFailure reason truthfully in the Facts' doc comments AND packet §10: the counts can never match by construction — (1) this harness has no mesh content, (2) acdream appends one entry per INSTANCE where retail appends one per SUBSET per DrawMesh, (3) acdream's CLIP list is empty for ordinary content (M2) — not "harness content only". Add ONE live pin that exercises the valve the chunk added: a driver test where ≥2250 entries sit in a list when the SortCellExit event replays and the drain fires there (and 2249 does not) — `RetailAlphaQueueTests` has the boundary; this one goes through `WalkFrameDriver.Replay`'s `SortCellExit` arm; mutation text.
|
||||
Rejected EnvCell reservations now roll back the tail cell id while keeping
|
||||
the source registered for exactly one flush/end/abort reset. Storm coverage
|
||||
uses two sources, 3,000 accepted tokens, and 9,000 rejected attempts; rejected
|
||||
pending count returns to zero, retained capacity stays at or below 4,096 (the
|
||||
bounded geometric backing for the 3,000-list limit), and rejected payload is
|
||||
never prepared or drawn. The allocation claims now execute the real whole-
|
||||
leaf route scan, `DrawCellShell`, list-filtered replay and
|
||||
`EnvCellRenderer.SubmitRhi`, and the real private particle
|
||||
`DrawImmediateParticleSubmissionRhi`, against a preallocated RHI with call
|
||||
recording disabled. Both report 0 managed bytes per warmed iteration. This
|
||||
replaces the prior static/delegate-only evidence.
|
||||
|
||||
**M6 (MAJOR) — contract C4 (EnvCell) is smaller than claimed; do it.** Retail `DrawEnvCell` @0x0059f1c2 sets `curr_detail_surface = environment_detail_surface` (may be null) before `DrawMesh` @0x0059f212 and clears it @0x0059f21a: detail ON → row 1 immediate (acdream already matches — `EnvCellRenderer.Rhi.cs:254 detailEnabled`); detail OFF → row 3 append (mask from the cell surface: alpha-family → 0x02 ALPHA). `EnvCellRenderer.RenderTransparentOrdered(IReadOnlyList<uint>)` (`EnvCellRenderer.cs:828`) already replays a cell's transparent batch on demand, so no "new deferred-replay abstraction" is needed. Fix: an EnvCell `IRetailAlphaDrawSource` owned by `RetailPViewPassExecutor`: `WalkProductionLeafRenderer.DrawCellShell` (`RetailPViewPassExecutor.WalkLeaf.cs:262`) appends ONE token (the cellId) to ALPHA when `detailEnabled == false` (route through the router with the shell's mask so the decision is the table's, not a hand-coded branch), draws immediately when `detailEnabled == true` (today's path); `PrepareAlphaDraws` collects the cell ids in token order; `DrawPreparedAlphaBatch(first, count)` calls `RenderTransparentOrdered` on that slice. One token per cell is coarser than retail's per-subset entries — AP-236 shrinks to exactly that granularity residual (rewrite the row; drop its "substantial separate architecture change" text). Pins: detail-off cell shell is drained at the next AlphaBarrier/LandscapeFlush/pass end and never before; detail-on draws at its turn; a particle appended between two cell tokens keeps its position (adjacent-only batching); mutation texts.
|
||||
The exact state is backed by `D3DPolyRender::SetSurface` @0x0059c4d0 and the
|
||||
paired executable: @0x0059c72a selects blend ids 2/6
|
||||
(`ONE/INVSRCALPHA`), globals 0x00820d8c and 0x00820d90 contain 0x64 and 0xc8,
|
||||
@0x0059c821 calls `SetAlphaTestEnable`, @0x0059c838 calls
|
||||
`SetAlphaTestFunction` with function 7 (`GREATER_EQUAL`), and @0x0059c866
|
||||
passes depth-write byte 1. No Content model, serializer, or package-format
|
||||
file changed. AP-238 and AP-240 now state only the repaired production truth;
|
||||
the remaining AP-238 divergence is token granularity, while ordinary Wb
|
||||
ClipMap A2C remains AP-240 and is unchanged.
|
||||
|
||||
**M7 (MINOR) — AP-237's only cited example is disproven.** `TranslucencyKind.cs:65-68` records cloud surface `0x08000023` as `Type=0x10114` = ADDITIVE|ALPHA|TRANSLUCENT|BASE1_CLIPMAP; `0x10114 & 0x10300 ≠ 0` → retail mask 0x02 → ALPHA — identical to the port. The abstract class (Translucent+ClipMap with NO alpha-family bit) may still exist: run a DAT scan over every Surface (`SurfaceType` bits: Translucent 0x10, Base1ClipMap 0x04, alpha family 0x00010300) and either cite a real instance (id, type, where used) or rewrite AP-237 as "no known instance in the installed DATs (scan date, count scanned)"; delete "extremely narrow… only known example is the cloud GfxObj".
|
||||
Real clean-state return matrix:
|
||||
|
||||
**M8 (MINOR) — citations and register convention.** (a) `RenderDeviceD3D::DrawBlock` is @0x005a17c0 (symbols.json); 0x005a18d0 is its loop head — fix `RetailAlphaQueue.cs:41`, `RetailPViewPassExecutor.cs:350`, `WalkEvents.cs:159`, `WalkFrameDriver.cs:242,1479`, `RetailFrameWalk.cs:250`. (b) `RetailAlphaQueue.cs:39-50` label the CALL instructions (0x0059f30b / 0x005a1a07 / 0x005a4872 / 0x00453b8b) as "return site"; the return addresses are +5 (0059f310 / 005a1a0c / 005a4877 / 00453b90) — say "call at … (returns to …)". (c) Register: keep `~~AP-34~~` struck through with "RETIRED 2026-09-04 (S4-c2) — residuals AP-236 / AP-237 / <M2's new row>" per the register's own convention (see `~~AP-28~~`, `~~AD-23~~`), fix AP-236's lead sentence ("retired, not narrowed" contradicts its content), and update the "## 3. Approximation (AP) — N active rows" header count. (d) `docs/architecture/worldbuilder-inventory.md:420` cites AP-34 — update it (that file is added to this round's allowed list).
|
||||
- Release solution build: `Build succeeded`, `0 Warning(s)`, `0 Error(s)`.
|
||||
- Hermetic lane: every project green, `16,735 passed / 0 skipped / 0 failed`
|
||||
across 14 assemblies; App `6,882/6,882`.
|
||||
- InstalledDat: exactly `Passed: 255`, `Failed: 10`, `Skipped: 1`,
|
||||
`Total: 266`. The failures are only
|
||||
`TowerAscent_StaircaseStaysConeVisible_EveryStep`,
|
||||
`MainGameUiAndChatInput_MediaBearingChildrenNowBuildAsRealWidgets`
|
||||
(#383), `EveryAuthoredInvisibleWidget_StartsHiddenAcrossAllLayouts`
|
||||
(#383), `Oh_doorway_still_first_frame_diff` (#458), and the six
|
||||
`AlphaFlushCounts_*`; every `AlphaFlushSites_*` passes.
|
||||
- Checked-in SPIR-V was rebuilt with `tools/compile-shaders.ps1`: 24/24;
|
||||
shader descriptor/manifest/pack validator filter: `32/32`.
|
||||
- Focused queue/router/walk/driver/particle/Wb/EnvCell/PView production
|
||||
filter: `239/239`. The explicit real production allocation filter is
|
||||
`2/2`, and both pins assert 0 B.
|
||||
- Register read: AP-238, AP-239, AP-240, AD-120, and `~~AP-34~~` each have
|
||||
exactly one physical row; active physical rows remain AP=159 and AD=92.
|
||||
- `git diff --check`: PASS.
|
||||
|
||||
**Gates (real output lines in the commit body; a lens reproduces each):** Release build 0 warnings; hermetic lane green; InstalledDat lane = the four pre-existing failures + the six `AlphaFlushCounts_*` KnownFailure Facts (and NOTHING else — the six `AlphaFlushSites_*` Facts pass in the lane); shader classes; `RetailAlphaQueueTests`, `RetailAlphaMeshRouterTests`, `RetailFrameWalkTests`, `WalkFrameDriverTests`, `ParticleRenderer*Tests`, `EnvCellRenderer*Tests` classes green. Allowed files: src/AcDream.App, tests/AcDream.App.Tests, docs/architecture/retail-divergence-register.md, docs/architecture/worldbuilder-inventory.md (line 420 only), this packet (§10 correction + a "§11 result" note). One commit on top of `048d5b12f` in `s4-c2-impl`. Must NOT: reintroduce any distance; change the alpha-to-coverage classification of clip-mapped surfaces (M2 is a register row this round); touch S4-c1's punch/seal machinery; add flush sites.
|
||||
Evidence-history disclosure: the first hermetic invocation was stopped after
|
||||
App exposed two stale test oracles introduced by this repair: the old
|
||||
`mesh_modern.frag.spv` SHA (expected
|
||||
`b702b644862aca31ce1fb0677adc5872b39c4ea87f595a89363b44d10f2cc50e`, actual
|
||||
`e094a966f7653a79a57d1c208f36d3100a8dcb20891584e6c74b4697e95c1950`) and the old
|
||||
five-blend cardinality (expected 5, actual 6). Both assertions were corrected
|
||||
to the exact new shader/state contract, then the complete lane above ran once
|
||||
without an internal retry. The same evidence directory was mistakenly reused,
|
||||
so script startup replaced that first invocation's TRX/log; the exact failures
|
||||
remain recorded here rather than being presented as a clean first attempt.
|
||||
The observed Core.Net PIDs belonged to those two separate script invocations,
|
||||
not a project retry: Core.Net passed 1,077 in the stopped invocation and again
|
||||
in the clean invocation. The first InstalledDat command also omitted
|
||||
`ACDREAM_RUN_INSTALLED_DAT_TESTS=1` and was invalid evidence (161 pass / 8 fail
|
||||
/ 97 skip); the documented environment produced the 255/10/1 result above.
|
||||
A narrower exploratory focused filter hit the pre-existing global
|
||||
`Console.Out` capture race once (`218 passed / 1 failed`,
|
||||
`OutdoorRoot_TranscriptFlagOn_PrintsLandscapeThenLandCellThenSortCellThenBuilding_InOrder`,
|
||||
"LS must precede LC"); that Fact passed isolated 1/1 and in the broader
|
||||
239/239 focused run above. A direct unfiltered `WalkTraceConformanceTests`
|
||||
probe also produced its seven documented InstalledDat failures and is not the
|
||||
focused clean lane.
|
||||
|
||||
**Lenses after the round:** retail-faithful (narrowed to M1/M3/M6's decomp claims and the router inputs) and production/gate-honesty (the six live Sites Facts, the valve pin, the immediate-draw paths' allocation, the register). A third round stops the chunk (plan §5).
|
||||
Actual owner-repair mutation failures (each production mutation was applied,
|
||||
run to the named first failure, and exactly reversed before the final gates):
|
||||
|
||||
### 11.1 Addendum — production/gate-honesty lens (reported 2026-09-04 ~10:45, VERDICT FAIL; all four claimed gates reproduced; four mutation checks re-performed; lead verified A1 and A2 at the source)
|
||||
1. Restoring the opaque predicate to `!IsAdditive` fails
|
||||
`WholeLeaf_MixedCellDrawsOpaqueAtTurnThenClipAndAlphaAtDrain`
|
||||
first at draw count: expected 1, actual 3.
|
||||
2. Selecting `_alphaPipeline` for CLIP fails
|
||||
`WholeLeaf_ClipDrainBindsExactStateAndTextureClassReference` first at the
|
||||
bind sequence: expected `[envcell-clip]`, actual `[envcell-alpha]`.
|
||||
3. Disabling CLIP depth write fails that same production Theory first at
|
||||
`Assert.True(clipPipeline.Depth.Write)`: expected true, actual false.
|
||||
4. Swapping the palette/DDS references fails the DDS row first: expected
|
||||
`0.784313738`, actual `0.392156869` (and the palette row reports the
|
||||
inverse).
|
||||
5. Mutating `mesh_modern.frag` from `<` to `<=` (strict `GREATER`) fails
|
||||
`ClipShaders_UseGreaterEqualForThePerRangeReference` first at the preceding
|
||||
`Assert.Contains("if (color.a < alphaCutoff) discard;")`: the required `<`
|
||||
source spelling is absent after the mutation. The later
|
||||
`Assert.DoesNotContain` is not the first failure.
|
||||
6. Restoring row-3 `OverrideClipmap=true` fails the real-interface clip Theory
|
||||
first at `Assert.False`: expected false, actual true.
|
||||
7. Deleting failed-append rollback fails all flush/end/abort rejection-storm
|
||||
rows at the first bounded pending-count assertion: expected 0, actual
|
||||
9,000.
|
||||
8. Selecting `_transparentDetailPipeline` for a ClipMap detail contribution
|
||||
fails the leaf detail pin's bind sequence: expected second bind
|
||||
`envcell-retail-detail-clip`, actual `envcell-retail-detail-alpha`.
|
||||
9. Resetting detail `ParamB` to zero fails the same detail pin first at the
|
||||
second pushed reference: expected `0.784313738`, actual 0.
|
||||
10. Classifying CLIP with exact mask equality excludes legal mask `0x09` and
|
||||
fails `WholeLeaf_PositiveStippleClipMaskUsesClipPipelineAndDdsReference`: expected
|
||||
`envcell-clip`, actual `envcell-alpha`.
|
||||
11. Mapping the new blend to `SRC_ALPHA/INVSRCALPHA` fails
|
||||
`AllRetailBlendModesAreRepresentable`: expected
|
||||
`(One, OneMinusSrcAlpha)`, actual `(SrcAlpha, OneMinusSrcAlpha)`.
|
||||
12. Restoring `mesh_detail.vert`'s former `uParamB > 0.5` category predicate
|
||||
fails `ClipShaders_UseGreaterEqualForThePerRangeReference` first because
|
||||
`vDetailCategory = uParamB == 1.0` is absent; the DDS reference must remain
|
||||
EnvCell rather than masquerading as the building sentinel.
|
||||
13. Treating every positive detail `uParamB` as a cutoff fails the same source
|
||||
pin first because `isRetailClipReference(uParamB) ? uParamB : 0.05` is
|
||||
absent; this is the negative proof that exact building sentinel 1 is not a
|
||||
cutoff.
|
||||
14. Adding arbitrary `0.5` as an accepted reference to either
|
||||
`mesh_modern.frag` or `mesh_detail.frag` fails that source pin first at
|
||||
`Assert.DoesNotContain("value - 0.5")`; both shader mutations were run and
|
||||
reversed independently.
|
||||
|
||||
**A1 (MAJOR) — a third `FlushLandscapeAlpha` caller the gate never sees, mislabelled.** `RetailPViewRenderer.cs:485`: `if (ctx.RootCell.IsOutdoorNode) passes.FlushLandscapeAlpha();` — the #132 outdoor-root drain after `DrawDynamicsLast`, before `DrawUnattachedSceneParticles(ctx, outdoorCells:false)`. It flushes under the `LandscapeFlush` label (`PView::DrawCells` @0x005a4872), but that retail flush runs ONLY inside `outside_view.view_count > 0` on the INTERIOR PView; an outdoor root has no such call (terrace-edge's capture: zero `005a4877` FL lines). Retail's outdoor-root drains are DrawBlock's 0.75 valve per cell and `SmartBox::RenderNormalMode`'s `FlushAlphaList(0f)` @0x00453b8b at the pass end — which acdream already performs in `RetailAlphaQueue.EndFrame`. Fix: delete the :485 call if (verify and quote) `EndFrame`'s RenderNormalMode flush runs after `DrawUnattachedSceneParticles` and before any further opaque world draw (then #132's "drain after the finished opaque world" invariant is kept by the pass-end flush alone, and the interior unattached emitters join the same final drain as retail); if some opaque draw intervenes, keep ONE drain but label it `RenderNormalMode`, move it after those draws, and file a register row for the relocation. Pin at the renderer level (not the driver gate): an outdoor-root `DrawInside` produces flush sites `[DrawBuilding×N, RenderNormalMode]` and never `LandscapeFlush`; mutation text.
|
||||
|
||||
**A2 (MAJOR) — register id collision.** AP-236 was filed AND retired on main by #132 (`e102fb363`; `docs/ISSUES.md:19978` "register AP-236 retired in the same commit"). Ids are never reused. Renumber the chunk's AP-236 → the next free id and AP-237 → the one after (the register's highest live id is AP-235; verify by grep over `^| ~*AP-` before choosing), fix the dangling `register row AP-236b` citation at `RetailAlphaMeshRouter.cs:101`, keep `~~AP-34~~` struck through per the convention (M8c), and recount the "## 3. Approximation (AP) — N active rows" header honestly (it reads 161 while the live count is 157/158 — state the real number).
|
||||
|
||||
**A3 (MAJOR) — the unreachability enumeration at `WbDrawDispatcher.cs:2474` is wrong.** `MaskFromTranslucencyKind` returns only 0x08 / 0x00 / 0x02; it never sets 0x01 and never returns 0x04; 0x08 and 0x00 are filtered upstream by `IsOpaque`, so the only reachable mask there is 0x02. Rewrite the comment to the truth (with M1 the throw is gone anyway; the comment must describe what CAN reach the router and why each row is or is not reachable).
|
||||
|
||||
**A4 (MINOR) — `ApplyScratchRetention(observedClip + observedAlpha, observedClip + observedAlpha)`** at `RetailAlphaQueue.cs:330` and `:408` passes the ENTRY count as `observedSourceCount`, so `_sources`/`_sourceDrawOffsets` never shrink (HEAD~1 passed the real source count). Capture `_sources.Count` before `Clear()` and pass it.
|
||||
|
||||
**A5 (MINOR) — text truth.** `WalkStaticStreamPopulatorTests.cs:554` banner still says "same viewer distance"; §10's inline sequences are off by one (foundry-deep `×10` should be `×11` for its 12 DrawBuilding entries; holtburg-doorway-still `×5` should be `×6` for 13); the commit body's "6 cases" for the hand-traced Theory is 5, and the 2250 mutation's first failing assertion is `RetailAlphaQueueTests.cs:197` (`PendingCount`), not `:198`. The new commit body states each mutation's ACTUAL first failing assertion.
|
||||
|
||||
**A6 (MINOR) — `WalkAlphaDepthTrace` parses `new=` into `AmEvents[i].IsNew` and never uses it.** Either use it (after M4: with per-append `new = true` a comparison is meaningless — say so) or drop the column and its comment; no dead parse presented as evidence.
|
||||
|
||||
**A7 (MINOR) — `RetailAlphaMeshRouterTests.cs:159-166` "independent oracle" is the same if-chain restated.** Replace `IndependentReferenceRoute` with a hand-written expected-decision TABLE transcribed from spec §4 (one literal row per (mask, sky, detail, multipass, hasAlpha) cell, no control flow), or delete the word "independent" and the sentence "each of the five rows' conditions was flipped in turn and this test failed every time" unless every flip's failing text is in the commit body.
|
||||
|
||||
**A8 (NOTE, no action)** — the non-deferring particle path keeps `ParticleSubmissionOrdering.Sort` (`ParticleRenderer.Rhi.cs:298`); it is outside the queue and pre-existing. M1's immediate draws for rows 1/5 must not add a sort (retail's immediate `RenderMeshSubset` draws in mesh order).
|
||||
|
||||
**Gate additions for this round:** the renderer-level A1 pin; the register grep proving no id collision (`grep -c "^| ~*AP-<new>"` == 1 for each new id). Allowed files unchanged except `RetailPViewRenderer.cs` (A1) and its tests.
|
||||
|
||||
## 12. S4-c2 fix round 2 (lead, 2026-09-04 — FINAL allowed round; any further code-fix finding stops the chunk and is written up)
|
||||
|
||||
**Input under review:** `048d5b12f` (chunk implementation) plus `cc8e5677a`
|
||||
(fix round 1), clean in `s4-c2-impl` and directly descended from S4-c1
|
||||
`766f9e749`. The lead verified the paired binary before writing this contract
|
||||
(`C:\Users\erikn\Downloads\acclient.exe` matches the exact retail PDB).
|
||||
|
||||
**Sequential verdicts on `cc8e5677a`:** retail-faithful **FAIL** (two major,
|
||||
five minor); production/gate-honesty **FAIL** (six major, three minor). The
|
||||
Release build, hermetic lane, InstalledDat identities, shader lane, allowed
|
||||
file scope, core two-list FIFO, strict-less valve, and the three re-performed
|
||||
round-1 mutations all pass. Fix ONLY the items below. This is round 2 of the
|
||||
two allowed by §8/the campaign process.
|
||||
|
||||
### 12.1 Retail facts that bound the fix
|
||||
|
||||
1. `CMaterial::CheckAlphaValues` @0x005396a0 uses `fcomp 1.0; test ah,5`:
|
||||
any material-alpha channel **below** 1 sets `has_alpha`; channels at or
|
||||
above 1 leave it clear (unordered does not take the claimed `else` path).
|
||||
Production particle opacity is clamped to `[0,1]`, so the top-byte
|
||||
`!= 0xff` predicate is behaviorally exact only under that stated domain.
|
||||
`SetTranslucencySimple` @0x005396f0 writes the same `1-translucency` value
|
||||
to all four channels; `Particle::Update` @0x0051c290 reaches it through
|
||||
`CPhysicsPart::SetTranslucency` at the call `0x0051c79d`.
|
||||
2. A row-5 opaque immediate subset reaches `RenderMeshSubset` @0x0059ca10
|
||||
and `SetSurface` @0x0059c4d0. For opaque/no-material-alpha the paired bytes
|
||||
at `0x0059c6e4..0x0059c6f9` and `0x0059c7b7..0x0059c806` select ONE/ZERO,
|
||||
disable alpha blending, and pass depth-write 1 to `SetDepthBufferMode`
|
||||
(call at `0x0059c866`). Immediate timing on an alpha/no-depth-write
|
||||
pipeline is therefore NOT a faithful row-5 port.
|
||||
3. `DrawEnvCell` @0x0059f170 installs environment detail at `0x0059f1c2`,
|
||||
calls `DrawMesh` at `0x0059f212`, and clears detail at `0x0059f21a`
|
||||
(fallback `0x0059f287`). With detail absent, list selection is still the
|
||||
exact constructed **per-subset** mask. `ConstructMesh` @0x0059dfa0 maps an
|
||||
alpha-family surface to `0x02`, then a pure `Base1ClipMap` surface to
|
||||
`0x08`; `DrawMesh` row 3 sends `0x08` to CLIP. Canonical installed-DAT
|
||||
witness: cell `0xF4180104`, surface `0x08000BFF`, type
|
||||
`BASE1_CLIPMAP (0x4)`, including eight reversed-side copies (see
|
||||
`oh2-cellstruct-surface-contract.md`). The extracted
|
||||
`TextureBatchData.RetailSurfaceMask` already preserves this fact; the App
|
||||
upload currently discards it.
|
||||
4. `DrawMesh` initializes two independent first-for-list flags at entry and
|
||||
clears a given flag after the first append to that list **within the same
|
||||
DrawMesh call**. The capture's 9,685 `new=1` / 871 `new=0` entries prove
|
||||
that it is not “true for every subset.” The port may continue to omit this
|
||||
unread optimization flag, but every comment and evidence claim states the
|
||||
actual quantifier.
|
||||
5. `PView::DrawCells`' `LandscapeFlush` at call `0x005a4872` is inside the
|
||||
nonzero outside-view branch. An outdoor-root frame has DrawBuilding
|
||||
barriers and the final `SmartBox::RenderNormalMode` drain, not a
|
||||
`LandscapeFlush`; no opaque world draw follows the removed call before
|
||||
`RetailAlphaQueue.EndFrame` performs that final drain.
|
||||
|
||||
### 12.2 R2-1 — opaque particle row 5 must use opaque depth state
|
||||
|
||||
Add the mesh-particle opaque pipeline beside the existing mesh alpha/additive/
|
||||
inverse pipelines, using the existing `particle_mesh` shaders and vertex
|
||||
layout, `GpuBlendMode.None`, depth test ON, depth write ON, retail world
|
||||
compare, the existing dynamic per-batch cull override, and no alpha-to-
|
||||
coverage. Row-5 immediate mesh submissions bind this pipeline. Do not change
|
||||
billboard behavior, queued row-4 replay, additive/inverse replay, the
|
||||
pre-existing non-queue `DrawOrderedRhi` sort, or add a shader.
|
||||
|
||||
The production pin must drive `DeferToRetailAlphaQueue` (not only
|
||||
`RouteParticleSubmission`): an opaque mesh particle with alpha `<1` appends
|
||||
to ALPHA, draws once at flush, and never invokes the immediate draw; the same
|
||||
mesh at alpha `1` invokes immediate once, appends nothing, and binds a
|
||||
pipeline whose description proves Blend=None and Depth=(test true, write
|
||||
true). Restoring the throw, deleting the immediate call, or selecting the
|
||||
old `_meshAlphaPipeline` must each fail a named assertion. Correct the M1
|
||||
comments: at this hardcoded scene/no-detail/no-multipass site only row 5 is
|
||||
reachable as Immediate; row 1 is not.
|
||||
|
||||
### 12.3 R2-2 — EnvCell routing is exact per subset and replay is list-filtered
|
||||
|
||||
Carry `TextureBatchData.RetailSurfaceMask` into `ObjectRenderBatch` at the
|
||||
existing upload boundary. The detail-off cell-shell path scans the real
|
||||
transparent render batches and applies `RetailAlphaMeshRouter.Route` to each
|
||||
batch's retained mask. It may coalesce only to at most one token per
|
||||
`(cellId, RetailAlphaList)`; a CLIP token's replay draws only batches routed
|
||||
to CLIP and an ALPHA token's replay draws only batches routed to ALPHA. A
|
||||
mixed cell therefore contributes to both lists without either replay drawing
|
||||
the other list's subsets. Any mask whose table result is Immediate is drawn
|
||||
at the cell turn and is excluded from both deferred replays; do not replace
|
||||
the table with “detail on/off” branching. With environment detail active,
|
||||
eligible subsets remain immediate with their detail pass at that turn.
|
||||
|
||||
Use separate CLIP/ALPHA source identity, or an equivalently discriminating
|
||||
retained token, so CLIP-then-ALPHA adjacency cannot lose the list filter.
|
||||
Retain bounded, reusable scratch; no per-frame LINQ/closure/array allocation.
|
||||
The accepted residual is **only** per-cell-per-list token granularity (rather
|
||||
than retail per-subset tokens), not list selection. Rewrite AP-238 to say
|
||||
that. Rewrite AP-240 because EnvCell pure-ClipMap subsets are now genuine
|
||||
CLIP feeders; ordinary Wb GfxObj/scenery/building ClipMap remains the
|
||||
registered alpha-to-coverage structural gap.
|
||||
|
||||
Production pins (not a hand-constructed source-only test):
|
||||
|
||||
- canonical/synthetic `RetailSurfaceMask=0x08` reaches CLIP, while `0x02`
|
||||
reaches ALPHA;
|
||||
- a mixed `0x08`/`0x02` cell creates one token in each list and each drain
|
||||
renders only its own batch set, exactly once;
|
||||
- detail OFF draws neither deferred set before the next real queue drain;
|
||||
detail ON draws at the cell turn, with detail, and queues nothing;
|
||||
- a particle/ordinary source between two cell tokens remains between them;
|
||||
- hardcoding `MaskAlphaFamily`, inverting the production immediate predicate,
|
||||
or removing the replay filter fails the named assertion. Structural IL-call
|
||||
order and a manually assembled `EnvCellAlphaDrawSource` alone do not count.
|
||||
|
||||
### 12.4 R2-3 — rejected sources still participate in frame cleanup
|
||||
|
||||
All three production callers reserve source-owned payload before
|
||||
`TryAppend`. When a list already holds 3,000 entries, `TryAppend` must still
|
||||
register that source for the frame's reset, then return false without adding,
|
||||
preparing, or drawing the rejected token. Pin with source A filling ALPHA and
|
||||
source B making its first, rejected append: B is never prepared/drawn but is
|
||||
reset exactly once at flush/end/abort, and its retained payload is empty for
|
||||
the next frame. This test must use two sources; the existing same-source
|
||||
overflow pin cannot detect the leak. Preserve retail's drop/no-immediate-
|
||||
fallback behavior and the queue's warmed scratch-retention policy.
|
||||
|
||||
### 12.5 R2-4 — replace structural/pure claims with real production pins
|
||||
|
||||
1. **M3:** drive both Wb production submit sites. Building shell + detail ON
|
||||
draws immediately at its `AlphaSubmitMark`, performs the detail pass, and
|
||||
queues nothing; detail OFF queues and drains without a detail pass; an
|
||||
ordinary non-shell translucent batch is unchanged by the building-detail
|
||||
flag. Hardcoding `detailSurfaceActive=false` at the production line must
|
||||
fail. No source-text/IL-only substitute.
|
||||
2. **M1/M6:** the tests in §§12.2–12.3 call the production dispatch methods.
|
||||
Keep the pure router/source tests as narrow unit coverage, but delete their
|
||||
false claims that a production mutation fails “here.”
|
||||
3. **A1:** add the positive renderer-level outdoor sequence pin through the
|
||||
real `RetailPViewRenderer` frame owner: `[DrawBuilding x N,
|
||||
RenderNormalMode]`, never `LandscapeFlush`, with pass-end after the last
|
||||
outdoor particle/world contribution. Restoring the deleted direct outdoor
|
||||
`FlushLandscapeAlpha` fails this test. The existing source/IL absence test
|
||||
may remain but is not the positive proof.
|
||||
4. Every new/changed behavior pin has a commit-body mutation report naming
|
||||
the exact production mutation and the **actual first failing assertion**.
|
||||
A test that still passes the mutation is not a pin and is not claimed.
|
||||
|
||||
### 12.6 R2-5 — evidence, register, and prose truth
|
||||
|
||||
- `ParticleRenderer` says CheckAlphaValues is `<1` and explicitly bounds the
|
||||
equivalence to the clamped `[0,1]` production domain.
|
||||
- `RetailAlphaQueue`/trace prose gives the true per-DrawMesh first-for-list
|
||||
lifecycle; it does not say every append is `new=1`.
|
||||
- Packet §10 names the landed `AlphaFlushSites_*` (live) and
|
||||
`AlphaFlushCounts_*` (KnownFailure) split. Zero mesh content is sufficient
|
||||
to explain the present count failure. If future-content residuals are
|
||||
described, name only the proven ones: EnvCell's coarser one-token-per-cell-
|
||||
per-list replay and ordinary-Wb CLIP's AP-240 structural gap. Do not claim
|
||||
all acdream entries are per instance—Wb and mesh particles already submit
|
||||
per batch/subset instance. Fix the stale removed Fact names and the packet's
|
||||
off-by-one sequence multipliers.
|
||||
- AP-239 says its blend mode is unchanged but overlap-dependent compositing
|
||||
can be visibly different; delete “never a visual difference.” AP-238 and
|
||||
AP-240 change as in §12.3. Fix `RetailPViewPassExecutor`'s stale AP-236
|
||||
citation to AP-238. No new AP row is needed if the production fixes satisfy
|
||||
this contract; keep the one-occurrence/id-count checks honest.
|
||||
- Focused-gate prose lists the exact filters it actually runs. The round-1
|
||||
claimed filter produced **123/123**, not 120, and the corrected filter must
|
||||
explicitly include `EnvCellAlphaDrawSourceTests` and
|
||||
`RetailPViewPassExecutorTests`. Report the new post-fix total from output,
|
||||
never a forecast.
|
||||
- Add a concise “§12 result” note only after every gate below passes; it lists
|
||||
real output and real mutation failures, not intent.
|
||||
|
||||
### 12.7 Scope, gates, and stop rule
|
||||
|
||||
**Allowed files:** `src/AcDream.App/**`, `tests/AcDream.App.Tests/**`,
|
||||
`docs/architecture/retail-divergence-register.md`, and this packet. The
|
||||
Content model/serializer already retains the mask and must not be changed.
|
||||
`docs/architecture/worldbuilder-inventory.md` may change only if a now-stale
|
||||
S4-c2 citation introduced by round 1 must be corrected. One implementation
|
||||
commit on top of `cc8e5677a` in `s4-c2-impl`.
|
||||
|
||||
**Must NOT:** reintroduce distance; add a flush site; alter S4-c1 punch/seal;
|
||||
change ordinary Wb ClipMap alpha-to-coverage (AP-240); add alpha-to-coverage
|
||||
to particles; add a shader; recover an overflow with an immediate draw; hide
|
||||
a production gap behind an IL/source-text-only test; run a graphical client
|
||||
from the implementation/review worktree.
|
||||
|
||||
**Automated gates (real output copied into the commit body/result note):**
|
||||
Release solution build, 0 warnings/errors; hermetic lane wholly green;
|
||||
InstalledDat exactly 255 pass / 10 fail / 1 skip with only TowerAscent,
|
||||
the two #383 layout sweeps, #458 doorway, and six `AlphaFlushCounts_*`
|
||||
failures (all six `AlphaFlushSites_*` pass); shaders 32/32; corrected focused
|
||||
filter including queue/router/walk/driver/particle/Wb/EnvCell/PView production
|
||||
tests; the new capacity-cleanup pins; register identities/counts; warmed
|
||||
allocation proof for the changed immediate/filter/source paths.
|
||||
|
||||
**Sequential lenses after implementation:** (1) retail-faithful, narrowed to
|
||||
the row-5 pipeline and exact EnvCell per-subset mask/list; then (2)
|
||||
production/gate-honesty, narrowed to rejected-source cleanup, production-path
|
||||
mutation discrimination, scratch bounds, gate identities, register/prose,
|
||||
and allowed scope. Each returns PASS/FAIL with file:line findings. Because
|
||||
this is the final allowed fix round, any finding that requires another code
|
||||
change is a third round: STOP, leave the implementation unlanded, and write
|
||||
the chunk failure into the packet/ledger. Comment-only clarification that
|
||||
does not change a claim may be adjudicated by the lead; it may not conceal a
|
||||
behavior or test defect.
|
||||
No graphical client was launched. G3/G4 remain unpassed pending the campaign
|
||||
branch's fresh build, reviews, and owner-visible graphical/soak gates.
|
||||
|
||||
## 13. S4-c2 final-round result — STOPPED UNLANDED (2026-09-04)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue