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>
6.3 KiB
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:
- Hidden-window captures vs old-tool captures differed by a 0.952 zoom.
HEAD's gate writes an isolated
settings.jsonwith nofieldOfView, so HEAD rendered at the default 90° gameFOV while the old binary read the real file's86.33°. Not a renderer change. Consequence: captures from the newrun-offline-pixel-gate.ps1and from the pre-campaign tool are not comparable to each other; each tool is self-consistent. - Connected HEAD frames were brighter with tree shadows. The real Roaming
settings.jsonstill heldrenderPack = acdream.atmospheric/lowfrom 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. - 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(andgamma) so its captures mean the same as the product's — filed for VM7's tool tidy; not a renderer issue. run-offline-pixel-gate.ps1gained-BuildingDetailTextures(VM0).