From ac74fbd84e220ad1058c075527da93fe604eaf85 Mon Sep 17 00:00:00 2001 From: Erik Date: Fri, 4 Sep 2026 08:06:14 +0200 Subject: [PATCH] docs(overhaul): contract S4-c2 final fix round --- .../s4-depth-alpha-packet.md | 204 ++++++++++++++++++ 1 file changed, 204 insertions(+) diff --git a/docs/research/2026-09-01-overhaul/s4-depth-alpha-packet.md b/docs/research/2026-09-01-overhaul/s4-depth-alpha-packet.md index 8f0b2a56..4c9b7a58 100644 --- a/docs/research/2026-09-01-overhaul/s4-depth-alpha-packet.md +++ b/docs/research/2026-09-01-overhaul/s4-depth-alpha-packet.md @@ -516,3 +516,207 @@ sites, the register, and the gate's CI value — the list/router/flush core stan **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-"` == 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.