Exact-pixel and production-perf comparison of6c79d35c(+ only the A2 normal files, so no terrain mask is needed) againstc51b07efwith no pack: connected as +Acdream, visible window, one isolated config clone per variant, pinned clocks. Open field: the only differences are idle pose, mana digits and a passing flyer. Holtburg: same-binary-twice defines the dynamic mask (9.8%); in the static 90% both self-diffs have ZERO pixels with |d|>=8 while base+normals vs HEAD-off has 841/729 - all streaks inside the animated lifestone. Buildings, ground, trees, sky and UI are clean. Perf (uncapped Release, no automation observer, ACDREAM_FRAME_PROF=1): Holtburg CPU p50 4.7 -> 4.1 ms, Arwic 6.0 -> 5.2 ms, GPU unchanged, alloc/frame 574 KB -> 21 KB. No regression; F5b's '27.8 ms retail CPU' was the observer. Three false alarms recorded so nobody repeats them: the isolated gate settings lack fieldOfView (90 vs the real 86.33 -> a 0.952 zoom); the real %APPDATA% settings still selected acdream.atmospheric/low (pack ON); a minimized GLFW window is throttled and never settles. Tools: tools/vm0/capture-visible.ps1 (pre-campaign gate + -Exe/-Live/ -ConfigDir/-CharacterName/-PreCaptureCommand), tools/vm0/perf-run.sh, and -BuildingDetailTextures on run-offline-pixel-gate.ps1. Baseline patches under docs/research/evidence/vm0/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
119 lines
6.3 KiB
Markdown
119 lines
6.3 KiB
Markdown
# VM0 — is Campaign AR's pack-off path the pre-campaign renderer?
|
||
|
||
**Date:** 2026-08-22 · **Campaign:** VM slice VM0 · **Status:** CLOSED — PASS
|
||
**Question:** with no render pack selected, does `c51b07ef` (Campaign AR) draw
|
||
the same pixels and cost the same as `6c79d35c` (pre-campaign main), outside
|
||
the two intended parity changes (A2 terrain normals, #226 building detail)?
|
||
**Answer:** yes, to the measured noise floor — and the pack-off path is
|
||
faster and allocates less.
|
||
|
||
## Binaries
|
||
|
||
| Name | Source | Notes |
|
||
|---|---|---|
|
||
| `base` | `6c79d35c` | + `evidence/vm0/baseline-configdir.patch` (HEAD's 13-line `ACDREAM_CONFIG_DIR/DATA_DIR/CACHE_DIR` hunk in `ApplicationPathSet`, path resolution only) so it can run from an isolated config |
|
||
| `base+normals` | `6c79d35c` + `evidence/vm0/baseline-normals.patch` (only `LandblockMesh.cs` + `TerrainVertex.cs` from the campaign) + the config-dir hunk | isolates A2 so the key comparison needs no terrain mask |
|
||
| `HEAD` | `c51b07ef` | `BuildingDetailTextures` off and on |
|
||
|
||
All Release. Worktrees `.claude/worktrees/vm0-base` and `vm0-base-normals`
|
||
were throwaway; the two patches reproduce them.
|
||
|
||
## Method that finally worked (and the three that did not)
|
||
|
||
Every run: connected to the local ACE as `+Acdream` (name-selected via
|
||
`--session-config`; ACE reorders the roster by last login, so an index is not
|
||
stable), **visible normal window** (the product condition), one **isolated
|
||
config clone** per variant under `artifacts/vm0/cfg-*` with
|
||
`renderPack = retail/off`, pinned `ACDREAM_DAY_GROUP=0`,
|
||
`ACDREAM_WORLD_TIME=0.5`, `ACDREAM_SKY_PHASE_SECONDS=0`, `ACDREAM_MSAA_SAMPLES=0`,
|
||
1280×720, 12 s settle, one screenshot. Helper:
|
||
`tools/vm0/capture-visible.ps1` (the `6c79d35c` pixel-gate script with `-Exe`,
|
||
`-Live`, `-ConfigDir`, `-CharacterName`, `-PreCaptureCommand`).
|
||
|
||
Three earlier attempts produced 88–91 % pixel differences that were **all
|
||
configuration, none renderer** — recorded so nobody repeats them:
|
||
|
||
1. **Hidden-window captures vs old-tool captures differed by a 0.952 zoom.**
|
||
HEAD's gate writes an isolated `settings.json` with no `fieldOfView`, so
|
||
HEAD rendered at the default 90° gameFOV while the old binary read the real
|
||
file's `86.33°`. Not a renderer change. Consequence: captures from the new
|
||
`run-offline-pixel-gate.ps1` and from the pre-campaign tool are **not
|
||
comparable** to each other; each tool is self-consistent.
|
||
2. **Connected HEAD frames were brighter with tree shadows.** The real Roaming
|
||
`settings.json` still held `renderPack = acdream.atmospheric/low` from the
|
||
owner's live gate — the pack was ON. (The reviewer had been reading and
|
||
editing a stale `%LOCALAPPDATA%\acdream\settings.json`; the client's
|
||
Windows config root is `%APPDATA%\acdream`.) Every HEAD run must pin the
|
||
selection explicitly.
|
||
3. **A minimized HEAD window rendered brighter than hidden/visible.** Windows
|
||
throttles an iconified GLFW surface; the 12 s settle had not completed.
|
||
Minimized is not a product condition and is not used.
|
||
|
||
## Pixel results
|
||
|
||
Exact per-pixel comparison (`max |ΔRGB|`), no tolerance.
|
||
|
||
**Open field (logout spot, 60.7S 89.1W, few dynamics):**
|
||
|
||
| Pair | px differing | notes |
|
||
|---|---|---|
|
||
| base vs base+normals (A2 only) | 12.7 %, mean Δ 1.5 | terrain shading, low amplitude |
|
||
| **base+normals vs HEAD-off (KEY)** | **4.3 %, mean Δ 9** | all of it: idle-pose outline, regenerating mana digits, one ambient flyer crossing the camera — ground, trees and UI clean (`diff-I: HEAD-off vs base+normals (KEY).png`) |
|
||
| HEAD-off vs HEAD-on (detail) | 3.3 % | no buildings in view; same noise class |
|
||
|
||
**Holtburg (`/telepoi Holtburg`, buildings, NPCs, animated lifestone):**
|
||
the same binary twice differs in 25–35 % of pixels by ±1–7 (sun/time
|
||
sub-steps), so a raw count is meaningless. Pixels with |Δ| ≥ 8 between two
|
||
logins of the *same* binary define the dynamic mask (lifestone, NPCs, particle
|
||
emitter, radar blips, digits; 9.8 % of the frame after a 6 px dilation). In
|
||
the remaining 90.2 %:
|
||
|
||
| Pair | |Δ| ≥ 8 px in static region | max |
|
||
|---|---|---|
|
||
| base+normals self (two logins) | **0** | 7 |
|
||
| HEAD-off self (two logins) | **0** | 7 |
|
||
| **base+normals vs HEAD-off (KEY, run 1)** | **841 (0.10 %)** | 128 |
|
||
| **base+normals vs HEAD-off (KEY, run 2)** | **729 (0.09 %)** | 128 |
|
||
| base vs base+normals (A2 only) | 7,884 (0.95 %) | 212 |
|
||
| HEAD-off vs HEAD-on (detail) | 513 (0.06 %) | 255 |
|
||
|
||
The 841/729 KEY pixels are streaks inside the animated lifestone crystal that
|
||
escaped the mask dilation (`evidence/vm0/holtburg-KEY-static-strong-diff.png`);
|
||
buildings, roofs, ground, trees, sky and every UI element are clean. The A2
|
||
row is the expected shading change on terrain. The detail row is below the
|
||
threshold on building shells because the fallback blend's mean factor is
|
||
1.033 (+3 %); VM1's single-pass re-port (−10 %) will make it measurable.
|
||
|
||
Evidence: `docs/research/evidence/vm0/*.png`, full captures under
|
||
`artifacts/vm0/` (not committed).
|
||
|
||
## Production performance (no observer)
|
||
|
||
`tools/vm0/perf-run.sh`: connected, **uncapped Release**, no
|
||
`ACDREAM_AUTOMATION_ARTIFACT_DIR` (so no render-scene observer),
|
||
`ACDREAM_FRAME_PROF=1`, same isolated config, 75 s at each spot; last six
|
||
5-second `[frame-prof]` windows (`evidence/vm0/perf-frame-prof.txt`):
|
||
|
||
| Spot | Binary | CPU p50 / p95 / p99 | GPU p50 / p95 | alloc KB/frame | Gen0 per 5 s |
|
||
|---|---|---|---|---|---|
|
||
| Holtburg | base | 4.7 / 5.1 / 5.5 ms | 0.4 / 0.5 | 574 | 12–13 |
|
||
| Holtburg | **HEAD off** | **4.1 / 4.4 / 4.8 ms** | 0.4 / 0.5 | **21** | 0–1 |
|
||
| Arwic (dense) | base | 6.0 / 6.5 / 7.0 ms | 0.7 / 0.8 | 582 | 10 |
|
||
| Arwic (dense) | **HEAD off** | **5.2 / 5.6 / 6.1 ms** | 0.7 / 0.9 | **29** | 0–1 |
|
||
|
||
No regression. The pack-off path is ~13 % cheaper on CPU with ~25× less
|
||
managed allocation per frame; GPU unchanged. This also settles review F5b:
|
||
the "27.8 ms retail-path CPU" in the Stage-2 connected report was the
|
||
automation observer, not the product.
|
||
|
||
## Verdict
|
||
|
||
VM0 PASSES. Campaign AR's default path is the pre-campaign renderer plus the
|
||
two declared parity changes. The F1 blocker is cleared.
|
||
|
||
## Carried forward
|
||
|
||
- The new pixel gate's isolated settings should carry `fieldOfView` (and
|
||
`gamma`) so its captures mean the same as the product's — filed for VM7's
|
||
tool tidy; not a renderer issue.
|
||
- `run-offline-pixel-gate.ps1` gained `-BuildingDetailTextures` (VM0).
|