docs(pipeline): MP0 baseline captured - gate PASSED, alloc-triage amendment

Full-route capture (Holtburg -> dungeon 0x0007 -> town 0xCE94 -> dungeon
-> Holtburg, 4 teleports) plus the supplemental town run. Verdict: the
render-side-CPU split is confirmed (GPU <= ~2.7ms, upd/upl/imgui ~= 0),
steady medians beat the spec assumption (worst town p50 3.6ms), and the
smoothness gap is GC: 1.5-3 MB allocated per frame drives gen2
collections 1-2/s and every town p99/max violation, while dungeon
windows (zero gen1/gen2) are spike-free at ~2000 fps. Teleport hitch
quantified at 211ms worst frame / 75.7MB single-frame allocation.

Gate decision: PROCEED to MP1 unchanged, with one recorded amendment -
a bounded post-MP1 allocation-triage session for churn sites outside
the MP3 rewrite surface. Fort Tethana axiom view still to be
re-measured at the MP2/MP3 gates.

MP0 complete: profiler shipped (7d74c68c..4b44a152) + baseline + gate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-05 19:42:36 +02:00
parent f4e4418273
commit 86d7094451
3 changed files with 107 additions and 2 deletions

View file

@ -0,0 +1,94 @@
# MP0 baseline — frame-profiler capture, attribution verdict, gate decision
**Date:** 2026-07-05. **Build:** Release, `ACDREAM_FRAME_PROF=1`, vsync off.
**Character:** `+Horan` (`0x5000000B`, testaccount2). **Logs:** `mp0-baseline.log`
(primary run: full route with 4 teleports, ~2 min) + `mp0-baseline-run3.log`
(supplemental: longer town session, same build) — both untracked in the worktree
root; the numbers below are transcribed from them.
**Route (reconstructed from log markers):** login → Holtburg outdoor
(`0xA9B4`) → movement/turning → teleport 1 → **dungeon `0x0007`** (dungeon
collapse streaming path) → walk → teleport 2 → **outdoor town `0xCE94`**
pan/walk → teleport 3 → **dungeon again** → walk → teleport 4 → Holtburg →
close (graceful).
## The numbers (per 5-second `[frame-prof]` window)
| State | cpu_ms p50 | p95 | p99 | max | gpu_ms p50 | alloc/frame p50 | GC gen0/gen1/gen2 per 5 s |
|---|---|---|---|---|---|---|---|
| Login settle (Holtburg) | 2.1 | 10.2 | **52.8** | **123.3** | 0.4 | 1.57 MB | 93 / **50** / **36** |
| Holtburg steady/turning | 1.62.0 | 2.22.6 | 2.83.7 | 1520 | 0.4 | 1.581.60 MB | ~80 / 313 / 35 |
| Dungeon `0x0007` walk | **0.40.5** | 0.50.6 | 0.9 | 1.21.5 | 0.1 | 0.500.63 MB | 119 / **0** / **0** |
| Town `0xCE94` pan/walk | 2.93.6 | 4.05.0 | 7.215.7 | 2637 | 1.21.6 | 2.53.0 MB | ~60 / 522 / 511 |
| Teleport→dungeon window | 2.8 | 3.3 | 14.1 | **211.2** | 1.3 | (max **75.7 MB** in one frame) | 62 / 11 / 10 |
| Teleport→outdoor windows | 1.22.9 | 3.43.8 | 4.57.0 | 33 | 0.21.5 | (max 2031 MB single frame) | ~8093 / 1526 / 5 |
| Holtburg end-of-run moving | 2.62.9 | 3.35.1 | 8.5**23.0** | 20**87** | 2.52.7 | 2.14 MB | ~5058 / 2432 / 78 |
| (run 3) town views | 1.72.4 | 2.73.4 | 7.915.8 | 2939 | 0.42.2 | 1.72.2 MB | ~70109 / 1732 / 611 |
Stage attribution (`upd` / `upl` / `imgui`): **≈ 0.0 ms p50 in every window**
(occasional upd p95 0.11.7 during streaming). GPU never exceeded p95 3.7 ms.
## Attribution verdict
1. **Steady-state medians are far better than the spec §1 assumption.** The
spec assumed ~6 ms / ~165 FPS dense-town CPU. Captured: worst steady town
view is p50 3.33.6 ms (~280300 FPS); Holtburg ~1.62.4 ms (~400600 FPS);
dungeons 0.40.5 ms (~2000 FPS). **Caveat:** the canonical Fort
Tethana/Arwic "dense worst-case axiom view" was NOT in this route — the
§1 numbers came from there. The 300-FPS throughput goal is close to met in
the captured views but must be re-verified at that view before any
"target met" claim.
2. **The frame is CPU-render-side, as assumed.** GPU ≤ ~2.7 ms everywhere;
upd/upl/imgui ≈ 0 → virtually all CPU time is unattributed render-side
submission. The spec's MP3 thesis stands.
3. **The smoothness gap is real, and it is GC.** Every town window violates
the ≤16 ms goal at max (2087 ms) and several at p99 (up to 23 ms), while
dungeon windows — the ONLY windows with **zero gen1/gen2 collections**
are spike-free (max 1.5 ms). Steady-state allocation is **1.53.0 MB per
frame** (≈ 0.61.2 GB/s at these frame rates), driving gen2 collections
roughly 12 per second in towns. The correlation is airtight across all
28 windows: spikes appear exactly where gen1/gen2 counts are non-zero.
4. **The hitch scenario is confirmed and quantified.** Teleport-into-dungeon
produced the worst frame of the session (**211 ms**) with a **75.7 MB**
single-frame allocation (decode + hydrate storm); login settle hit 123 ms
max with 36 gen2 collections in one window. This is exactly the class MP1
(bake — load becomes mmap reads, not managed decode) targets.
## Gate decision (spec §5)
**PROCEED to MP1, with one amendment.** The assumed cost split (render-side
CPU dominant, GPU idle, hitches at load boundaries) is CONFIRMED, so the
phase order stands. The amendment: the capture elevates **steady-state
allocation churn** from "MP4 cleanup" to a first-class smoothness lever —
1.53 MB/frame of per-frame garbage is the direct cause of the town p99/max
violations, and it is NOT load-related (it persists in steady windows).
**Amendment (recorded in the spec):** after MP1 ships, run a bounded
**steady-state allocation triage** (one session: `dotnet-trace` /
allocation-sampling on the live client, identify the top ~5 per-frame churn
sites). Sites *inside* the MP3 rewrite surface (per-frame submission
rebuild — the prime suspect for MB-scale per-frame lists) are left for MP3
to remove structurally; sites *outside* it get targeted fixes immediately.
The full zero-alloc pass remains MP4. This front-loads the biggest
smoothness win without letting "fix allocations" sprawl into an unphased
rewrite.
## Follow-ups noted (not gates)
- Re-measure the Fort Tethana axiom view at the MP2 and MP3 gates (the
degrade port materially changes exactly that view).
- `upd ≈ 0.0` everywhere is suspicious-adjacent but plausible (light sim in
these scenes); if a future capture shows heavy OnUpdate work still reading
0.0, verify the stage scope wasn't bypassed by an early return upstream.
- Dungeon at ~2000 FPS with zero GC pressure is the existence proof that the
engine's frame loop CAN run allocation-free at four-digit FPS — towns are
paying for churn, not for fundamentals.
## Apparatus state
`[frame-prof]` (env `ACDREAM_FRAME_PROF=1`, DebugPanel "Frame profiler"
checkbox) is PERMANENT — it is the measurement instrument for every MP gate.
Implementation: `src/AcDream.App/Diagnostics/` (FrameStatsBuffer /
GpuFrameTimer / FrameProfiler), plan
`docs/superpowers/plans/2026-07-05-mp0-frame-profiler.md`, commits
`7d74c68c`..`4b44a152`.