docs(vm4): every Campaign AR document says what its evidence shows

Dated 'VM4 correction' notes, not silent rewrites:
- AR plan: NoOpRenderPackProductionIntegrationTests is a 2x2 recording-
  device fixture, not a production pin; the real invariance is VM0's.
  Both '14,928/14,928 zero skips' totals qualified: hermetic lane filter.
- Stage-2 connected report: the dense-town CPU figures ran under the
  automation observer (9.8 MB/frame alloc) and say nothing about the
  owner's hitch on the product; clean numbers are VM0's.
- Track A report + findings doc: TerrainUtils.GetNormal only orients
  scenery; the render normals were already smooth; A2 replaced central
  differences with retail's incident-face average (the reviewer's own
  premise error).
- Findings doc: 'retail brightens' is the fallback path only; real
  hardware runs the single-pass lerp (VM2). LandscapeDetailTextures = 0
  answers the landscape open question. VM0 production table added as the
  baseline.
- Completion audit retail-path row and the review's F1/F2/F3/F5 headers
  annotated with their closures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-22 22:51:59 +02:00
parent dcdd102824
commit 6e76314b08
7 changed files with 117 additions and 30 deletions

View file

@ -342,7 +342,10 @@ Current authored-celestial Stage 1 automated gate (2026-08-22):
including all source, tests, tools, and SDK samples, builds with **0 warnings
and 0 errors** after that restore.
- The repository-owned fresh-process hermetic gate passes
**14,928/14,928** tests with zero skips or failures across 14 assemblies;
**14,928/14,928** tests with zero skips or failures across 14 assemblies
**under the repository's hermetic lane filter** (Manual/Timing/Live/
InstalledDat/Diagnostic lanes excluded; a raw `dotnet test AcDream.slnx`
reports those lanes as skips/failures by design — VM4 correction);
`AcDream.App.Tests` contributes **5,823/5,823**. Evidence is under
`artifacts/atmospheric-rendering/stage1-moon-release-gate/`.
- The App total includes the 9,500-caster 256-frame zero-managed-allocation
@ -363,7 +366,8 @@ Recorded pre-moon automated checkpoint (not a current moon-scope completion
claim):
- The repository-owned fresh-process Release test stage passes
**14,880/14,880** tests with zero skips or failures across 14 assemblies.
**14,880/14,880** tests with zero skips or failures across 14 assemblies
(hermetic lane filter, as above).
`AcDream.App.Tests` contributes **5,783/5,783**; campaign-focused App cases
cover descriptor/asset/SPIR-V
validation, pack-off/no-op invariants, atomic asynchronous candidate swaps,
@ -760,10 +764,16 @@ require their own rows.
acdream's default path has a stricter gate: zero new enhancement passes,
images, buffers, submissions, or shader variants, with CPU/GPU deltas within
the existing run-to-run noise envelope and deterministic reference captures
unchanged. `NoOpRenderPackProductionIntegrationTests` pins the pre-campaign
pass list, pipeline set, draw/dispatch tuple, framebuffer SHA-256, and complete
resource ledger; the matrix's six physical retail rows independently record
zero pack work at all three resolutions and both pacing modes.
unchanged. `NoOpRenderPackProductionIntegrationTests` pins the pass list,
pipeline set, draw/dispatch tuple, framebuffer SHA-256, and resource ledger of a
**2x2 synthetic, one-draw composition fixture on a recording device that does
not rasterize** — it proves the controller arm adds nothing to that shape, not
that the production frame is unchanged. The matrix's six physical retail rows
record zero pack work at all three resolutions and both pacing modes. The
actual pack-off pixel and production-performance invariance against the
pre-campaign build `6c79d35c` was established later by Campaign VM slice VM0
([report](../research/2026-08-22-vm0-default-path-invariance.md), corrected
2026-08-22).
Measurement protocol:

View file

@ -80,12 +80,12 @@ single-pass path darkens by ~10 %. Neither explains a scene-wide level.
| VM1 | F2/F3 — #226 single-pass re-port + fade removal | **CLOSED 2026-08-22**`05970306`, `388457a7`, `ae651312`, closeout; Opus APPROVE |
| VM2 | F3 — which retail detail path ran | **CLOSED 2026-08-22** — single-pass; see [cdb note](../research/2026-08-22-vm2-retail-detail-path-cdb.md) |
| VM3 | F4 — linear-light post stack | automated + **owner visual** |
| VM4 | F5 — overclaiming docs, incl. the reviewer's own | docs |
| VM4 | F5 — overclaiming docs, incl. the reviewer's own | **CLOSED 2026-08-22** |
| VM5 | F8 — volumetric banding; F7 filed | automated |
| VM6 | Foliage wind | automated + **owner visual** |
| VM7 | Closeout: full gates, register, roadmap, merge | **owner** |
Order is VM2 (done) → VM0 (done) → VM1 (done) → VM4 → VM5 → VM3 → VM6 → VM7. VM0 goes first because everything after it is measured
Order is VM2 (done) → VM0 (done) → VM1 (done) → VM4 (done) → VM5 → VM3 → VM6 → VM7. VM0 goes first because everything after it is measured
against the baseline it establishes.
---
@ -220,6 +220,17 @@ clipping, highlights roll off". Budget unchanged (two `pow` per pixel).
## VM4 — Truthful documents (F5)
**OUTCOME (2026-08-22): CLOSED.** Corrected in place, each with a dated
"VM4 correction" note: the AR plan's 2x2-oracle sentence and both
"zero skips" totals (now "under the hermetic lane filter"); the Stage-2
report's turning-hitch paragraph (observer tax, not product); the Track A
report's and the findings doc's `TerrainUtils.GetNormal` premise; the findings
doc's "retail brightens" section (fallback only; single-pass lerp is what runs),
its landscape-detail open question (`LandscapeDetailTextures = 0`, answered by
VM2), and its open question 5; the completion audit's retail-path row; and the
review's own F1/F2/F3/F5 headers. The VM0 production table is now the baseline
in the findings doc §5.
Correct, in the AR plan and its reports, the four sentences the review
named: the 2×2 oracle described as a production pin; the turning-hitch
conclusion drawn under the automation observer; the "zero skips" headline

View file

@ -80,11 +80,14 @@ gated on the single `Render::m_RenderPrefs.EnvironmentDetailTextures`. **So
retail's setting is a BUILDINGS-AND-INTERIORS setting, not a terrain
setting** — which explains why its UI label is "Building Detail Textures".
`SmartBox::SetDetailTexturing` (0x00451df0) *can* pass landscape through — its
callers are **untraced**; that is the open question for whether terrain detail
is ever enabled in practice.
`SmartBox::SetDetailTexturing` (0x00451df0) *can* pass landscape through.
**ANSWERED 2026-08-22 (VM2 cdb read):** `Render::m_RenderPrefs.LandscapeDetailTextures`
is a real, separate preference and reads **0** on the live client
(`EnvironmentDetailTextures = 1`); `landscape_detail_surface` is null while
`building_detail_surface`/`environment_detail_surface` are set. Terrain detail
is off by preference, and there is no Options row for it.
### The blend — and retail's own bug
### The blend — the FALLBACK path (see the correction below)
`ACRender::SetDetailSurfaceInternal` (0x006b6280):
@ -117,12 +120,18 @@ category:
| `0x06006D57` | 0.414 | 0.210 | **1.204** | **+20% brighter** |
| `0x06006D58` | 0.165 | 0.132 | **1.033** | **+3% brighter** |
**Retail's own detail pass BRIGHTENS rather than roughens.** A proper
roughening modulate needs `dst = ZERO` so the result centres on 1.0; here the
`dest x (1-alpha)` term adds most of the original back on top. This
independently confirms the community observation that it looks like surfaces
"reflect more instead of being rougher", and it raises a real design decision
for #226 (see open questions).
**The FALLBACK detail pass brightens rather than roughens** — and, **CORRECTED
2026-08-22 (VM2/VM4)**, the fallback is not what real hardware runs. The
framebuffer blend above is taken only when `stage == 0`, i.e. when the adapter
cannot advertise `D3DTEXOPCAPS_PREMODULATE`. A live cdb read on the owner's AMD
GPU gave `m_caps.bCanDoSinglePassDetailing = 1` and `trysinglepass = 1`, so
retail uses the single-pass texture-stage path in `D3DPolyRender::SetSurface`
(0x0059c4d0): `lerp(base * diffuse, detail.rgb, detail.a * diffuse.a)` — a mild
blend toward the detail colour (about 10 % on mid-tones with the live category
texture). The community's "reflect more instead of being rougher" describes
the fallback only. Full evidence:
[`2026-08-22-vm2-retail-detail-path-cdb.md`](2026-08-22-vm2-retail-detail-path-cdb.md).
The #226 port was re-done to the single-pass math at Campaign VM VM1.
### Terrain draw path
@ -168,9 +177,14 @@ non-single-pass fallback that is **untraced**. Vertex lighting comes from
- Diagonal split direction comes from the `FSplitNESW` hash of world cell
coords (constants `0x0CCAC033`, `0x421BE3BD`, `0x6C1AC587`, `0x519B8F25`).
- `src/AcDream.Core/Rendering/Wb/TerrainUtils.cs` `GetNormal` returns the
**flat per-face normal** (cross product of whichever triangle the sample
lands in). **No averaging anywhere in the tree.** That is what produces the
faceted look.
**flat per-face normal**. **CORRECTED 2026-08-22 (VM4):** that function only
orients procedural scenery (`SceneryGenerator.cs:166`); it never fed the
render normal, and the claim that it produced a faceted look was wrong. The
rendered mesh already used smooth central-difference normals (Phase 3b,
`LandblockMesh`). Retail (`CLandBlockStruct::calc_lighting` 0x00531700)
averages the unit plane normals of each vertex's incident polygons; Campaign
AR's A2 ported that, replacing central differences — a real parity port with
a subtle visual delta (VM0: 1114 % of pixels at mean |Δ| ≈ 11.5).
**Open question, must be answered from the decomp, no guessing:** does retail
smooth terrain vertex normals? Entry points: `ACRender::landPolyDraw` (both
@ -233,6 +247,19 @@ registered bounded divergence.
profile is **CPU-SUBMISSION-BOUND** (memory:
`feedback_render_perf_measurement`).
**VM0 baseline (2026-08-22, connected, uncapped Release, no automation
observer, `ACDREAM_FRAME_PROF=1`; `2026-08-22-vm0-default-path-invariance.md`):**
| Spot | Binary | CPU p50 / p95 | GPU p50 | alloc KB/frame |
|---|---|---|---|---|
| Holtburg | `6c79d35c` | 4.7 / 5.1 ms | 0.4 | 574 |
| Holtburg | Campaign AR, pack off | 4.1 / 4.4 ms | 0.4 | 21 |
| Arwic (dense) | `6c79d35c` | 6.0 / 6.5 ms | 0.7 | 582 |
| Arwic (dense) | Campaign AR, pack off | 5.2 / 5.6 ms | 0.7 | 29 |
Any figure captured under `ACDREAM_AUTOMATION_ARTIFACT_DIR` carries the
render-scene observer's allocation and is NOT comparable with this table.
Consequences:
- GPU-side **fullscreen** work is nearly free in observed FPS — it fills GPU
@ -308,9 +335,9 @@ Design points the user asked for:
3. What is the non-single-pass detail fallback in `landPolyDraw`? (§2)
4. What uses the `BumpMap` / `DotProduct3` path, if not the detail textures?
(§3)
5. For #226: port retail's brightening blend **verbatim**, fix it to `dst=ZERO`
so it roughens as intended and register the deviation, or expose both behind
the setting? (§2)
5. ~~For #226: port retail's brightening blend verbatim, fix it, or expose
both?~~ **ANSWERED 2026-08-22:** neither — the brightening blend was the
fallback; VM1 ported the single-pass lerp that real hardware runs (§2).
## 8. Where this sits

View file

@ -51,9 +51,15 @@ The decisive named-retail function is
4. The following sunlight/ambient loop dots those normalized shared-vertex
normals with `LScape::sunlight` and writes per-vertex lighting.
That is incident-face normal averaging, not flat per-face shading. The
WorldBuilder-derived `TerrainUtils.GetNormal` identified in the findings §4
is therefore a simplified tool path and not the retail oracle.
That is incident-face normal averaging. **VM4 correction (2026-08-22):** the
findings doc's premise that `TerrainUtils.GetNormal` produced a faceted render
was wrong — that function only orients procedural scenery
(`SceneryGenerator.cs:166`) and never fed the render normal. The rendered
terrain mesh already had smooth central-difference normals (Phase 3b,
`LandblockMesh`). The real change A2 made is central-difference smoothing →
retail's split-aware incident-face average: a genuine, decomp-verified parity
port, but a subtler visual change than either document implied (VM0 measured
1114 % of pixels at mean |Δ| ≈ 11.5 on the same view).
The approved port is in `LandblockMesh.BuildRetailVertexNormals`. It uses the
same split hash and exact emitted triangle topology, accumulates each

View file

@ -24,7 +24,7 @@ been pushed.
| Requirement | Verdict | Authoritative evidence |
|---|---|---|
| Stage 1 project-owner stop | Pass | The owner's 2026-08-22 “Looks good!” acceptance, after exposure 1.0 → 0.80, is recorded in the [live-gate report](2026-08-22-atmospheric-stage1-live-gate.md). |
| Retail-faithful path stays default and authoritative | Pass | The checked-in production no-op oracle, six physical pack-off rows, connected disable/restore transitions, and package-failure rows all pass. A branch diff contains only newly named atmospheric/detail shader sources plus the generated shader manifest; no pre-campaign retail shader source is modified. |
| Retail-faithful path stays default and authoritative | Pass (evidence strengthened by VM0, 2026-08-22: exact pack-off pixel identity vs `6c79d35c` on static content and a clean production perf A/B — see `2026-08-22-vm0-default-path-invariance.md`; the no-op oracle named here is a 2x2 recording-device fixture) | The checked-in production no-op oracle, six physical pack-off rows, connected disable/restore transitions, and package-failure rows all pass. A branch diff contains only newly named atmospheric/detail shader sources plus the generated shader manifest; no pre-campaign retail shader source is modified. |
| Physics and collision unchanged | Pass | `git diff origin/main...HEAD -- src/AcDream.Runtime src/AcDream.App/Physics src/AcDream.Core/Physics` is empty. The connected routes use the existing gameplay simulation without changing it. |
| Tier 1 bloom, filmic tone mapping, colour grade, vignette, neutral settings, resize, and retained-UI/private-view isolation | Pass for implementation and available automation/physical rows | Slice 1 tests and the RX 9070 XT 30-row matrix pass; final project-owner image judgment remains below. |
| Tier 1 authored-sun rays | Pass for implementation and connected state transitions | Authored sun/day-group/weather, occlusion-mask, behind-camera/off gates, and pre-tonemap composition are automated; connected time/weather transitions pass. Final physical-display dawn/noon/dusk, occlusion, and edge-flicker judgment remains below. |

View file

@ -72,8 +72,20 @@ camera turn, server, character, entity population (15,522), and warmed scene:
| Six-second turn | 27.8 / 32.9 / 36.7 ms | 28.6 / 32.2 / 40.2 ms | 0.6 / 1.2 ms | 1.5 / 1.9 ms |
| Stationary | 28.6 / 30.2 / 31.1 ms | 29.1 / 30.9 / 32.6 ms | 0.9 / 0.9 ms | 1.7 / 1.8 ms |
The user's turning hitch reproduces on the retail path: Medium did not worsen
turn CPU p95 (32.2 versus 32.9 ms). It adds the expected GPU work, about
**VM4 correction (2026-08-22):** both runs below executed under the connected
automation observer (`ACDREAM_AUTOMATION_ARTIFACT_DIR` render-scene oracle),
which the Slice H closeout documents as allocating 3.910.5 MiB/frame
(`alloc_kb p50 = 9,830` here, 31 Gen0 collections in 216 frames). The absolute
CPU figures are therefore observer cost, not product cost, and this A/B says
nothing about the owner's turning hitch on the product. Clean production
numbers (no observer, uncapped Release) are in the
[VM0 report](2026-08-22-vm0-default-path-invariance.md): dense Arwic CPU p50
6.0 ms pre-campaign vs 5.2 ms pack-off. The paragraph below is kept as the
historical record of the Medium-minus-retail increment, which the observer does
not distort.
With that caveat: Medium did not worsen turn CPU p95 under the observer (32.2
versus 32.9 ms). It adds the expected GPU work, about
0.70.9 ms at p95/p50 in this run. The Atmospheric screenshot's pack-owned
window reports incremental CPU p50/p95/p99 of 0.209/0.248/0.302 ms, inclusive
GPU p50/p95/p99 of 1.374/1.818/1.869 ms, 14,890 casters, three cascades,

View file

@ -55,6 +55,10 @@ run; decide F2; schedule F3/F4 as follow-ups; then the owner visual gate.
### F1 — HIGH (evidence): default-path invariance is unproven
> **CLOSED (VM0, 2026-08-22): PASS.** Exact pixel identity on static content
> vs `6c79d35c` (Holtburg + open field) and a clean production perf A/B with no
> regression — `2026-08-22-vm0-default-path-invariance.md`.
The plan says `NoOpRenderPackProductionIntegrationTests` "pins the
pre-campaign pass list, pipeline set, draw/dispatch tuple, framebuffer
SHA-256". It does — for a **2×2 synthetic framebuffer, one draw call, one
@ -86,6 +90,14 @@ record shows it was run against a `6c79d35c` capture. The six physical
### F2 — HIGH (process): the #226 fade constants are invented and unregistered
> **Post-review correction (VM1, 2026-08-22):** "invented" was too strong. The
> 10 m → 50 m ramp is a real retail function, `ACRender::get_alpha_for_z`
> (0x006b6230). It is, however, dead for the surfaces #226 targets: retail
> evaluates it only in `D3DPolyRender::DrawPolyInternal` (immediate polygons)
> and only when the static `noFadeDetail` (0x00820e38, initialised 1) is 0;
> built meshes light with `Diffuse.a = 1`. The finding's conclusion (remove
> it; no register row) stands; the provenance claim is corrected.
`RetailDetailTextureContract.FullDetailDistanceMetres = 10f` /
`ZeroDetailDistanceMetres = 50f` drive `mesh_detail.vert:83`
(`clamp((50 z) / 40, 0, 1)`). The pseudocode note presents "full through
@ -104,6 +116,11 @@ keep it and file the register row naming it an acdream adaptation.
### F3 — MEDIUM (premise): #226 ports retail's two-pass fallback; which path real hardware ran is undetermined
> **ANSWERED (VM2, 2026-08-22):** `m_caps.bCanDoSinglePassDetailing = 1`,
> `trysinglepass = 1` on the owner's GPU — retail runs the single-pass path.
> The reviewer's "probably the fallback" was wrong. See
> `2026-08-22-vm2-retail-detail-path-cdb.md`; re-ported at VM1.
`SetDetailSurfaceInternal` sets the `DSTCOLOR + INVSRCALPHA` framebuffer blend
**only when `stage == 0`** (the two-pass fallback). With
`m_caps.bCanDoSinglePassDetailing`, `RenderMeshSubset` and `landPolyDraw`
@ -138,6 +155,10 @@ every other tool's controls. Schedule as a slice.
### F5 — MEDIUM (docs): closeout claims exceed their evidence
> **CLOSED (VM4, 2026-08-22):** (a)(d) corrected in place in the AR plan, the
> Stage-2 connected report, the Track A report and the findings doc, each with a
> dated "VM4 correction" note rather than a silent rewrite.
a. "Pins the pre-campaign … framebuffer SHA-256" — see F1.
b. "The user's turning hitch reproduces on the retail path" is drawn from
`connected-dense-town-20260822-133727` (retail/off: CPU p50 **27.8 ms**,