diff --git a/docs/plans/2026-08-21-atmospheric-rendering.md b/docs/plans/2026-08-21-atmospheric-rendering.md index 3e6c9cfb..65e20103 100644 --- a/docs/plans/2026-08-21-atmospheric-rendering.md +++ b/docs/plans/2026-08-21-atmospheric-rendering.md @@ -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: diff --git a/docs/plans/2026-08-22-visualmaster-campaign.md b/docs/plans/2026-08-22-visualmaster-campaign.md index f39c6c1f..72e96722 100644 --- a/docs/plans/2026-08-22-visualmaster-campaign.md +++ b/docs/plans/2026-08-22-visualmaster-campaign.md @@ -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 diff --git a/docs/research/2026-08-21-terrain-and-atmospheric-rendering-findings.md b/docs/research/2026-08-21-terrain-and-atmospheric-rendering-findings.md index 6add539b..6cd9efc0 100644 --- a/docs/research/2026-08-21-terrain-and-atmospheric-rendering-findings.md +++ b/docs/research/2026-08-21-terrain-and-atmospheric-rendering-findings.md @@ -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: 11–14 % of pixels at mean |Δ| ≈ 1–1.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 diff --git a/docs/research/2026-08-21-terrain-fidelity-track-a-report.md b/docs/research/2026-08-21-terrain-fidelity-track-a-report.md index 51c4b095..302b68e4 100644 --- a/docs/research/2026-08-21-terrain-fidelity-track-a-report.md +++ b/docs/research/2026-08-21-terrain-fidelity-track-a-report.md @@ -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 +11–14 % of pixels at mean |Δ| ≈ 1–1.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 diff --git a/docs/research/2026-08-22-atmospheric-campaign-completion-audit.md b/docs/research/2026-08-22-atmospheric-campaign-completion-audit.md index 386215d5..58093741 100644 --- a/docs/research/2026-08-22-atmospheric-campaign-completion-audit.md +++ b/docs/research/2026-08-22-atmospheric-campaign-completion-audit.md @@ -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. | diff --git a/docs/research/2026-08-22-atmospheric-stage2-connected-gate.md b/docs/research/2026-08-22-atmospheric-stage2-connected-gate.md index fcfde016..a7980b62 100644 --- a/docs/research/2026-08-22-atmospheric-stage2-connected-gate.md +++ b/docs/research/2026-08-22-atmospheric-stage2-connected-gate.md @@ -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.9–10.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.7–0.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, diff --git a/docs/research/2026-08-22-campaign-ar-review.md b/docs/research/2026-08-22-campaign-ar-review.md index 667aa03c..f40026e2 100644 --- a/docs/research/2026-08-22-campaign-ar-review.md +++ b/docs/research/2026-08-22-campaign-ar-review.md @@ -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**,