acdream/docs/research/2026-08-22-vm0-default-path-invariance.md
Erik 30e72a2af8 research(vm0): Campaign AR's pack-off path IS the pre-campaign renderer - PASS
Exact-pixel and production-perf comparison of 6c79d35c (+ only the A2
normal files, so no terrain mask is needed) against c51b07ef with 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>
2026-08-22 21:40:40 +02:00

6.3 KiB
Raw Blame History

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 8891 % 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 2535 % of pixels by ±17 (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 1213
Holtburg HEAD off 4.1 / 4.4 / 4.8 ms 0.4 / 0.5 21 01
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 01

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).