Files

35 KiB

Performance experiment ledger

Campaign: OpenGL performance campaign. Live work status: central Beads beads-jod2.

Record unsuccessful, incomplete and rejected experiments as well as accepted ones. Local game-derived artifacts are not release fixtures. Hashes below make the retained local evidence identifiable without distributing a savestate/ROM.

CRAB-DISCOVERY-001: verify a usable starting scene

Date: 2026-09-04 (session date). Verdict: exploratory evidence only; no candidate optimization, A/B comparison, production claim or correctness qualification.

  • Host: Windows, Ryzen 7 9800X3D, 8 cores / 16 logical processors. Graphics adapters reported by the OS: RTX 3080 Ti, AMD integrated graphics, Meta virtual monitor. Actual GL device and power state were not recorded in this capture.
  • Historical binary source: _wt-ape-crabperf-20260901/build-dbg, CMake Release with PSX_DEBUG_TOOLS=ON. Existing report identifies build v0.3.2-alpha-139-g1bf70960-dirty. Current source HEAD is not proof of binary provenance. This executable was not built from the campaign branch.
  • Executable SHA256: 3b27bda4417522eb1ab331e4d2b194828f1d38fb9bbb165d1a78eb2156142ba1.
  • State: saves-dbg/scph1001/state_800A3660_slot01.pst, version 5, BIOS wordsum F67ECB99, entry 800A3660, codegen hash A4319B6F, ABI 21, codegen version 10. SHA256 2dfc142e65eb32e179b67d352f510d15f6d97efd22b3b43a9eef24301f3ad5a9.
  • Executable, saves, assets, BIOS assets and mods were copied into the campaign worktree's .local/crabby-baseline. Writable cards/settings use that copy. CLI selected OpenGL, no launcher, debug port 4680. Settings selected 2x SSAA and antialiasing. PSX_RUNTIME_PERF_DIAG=1; no host CPU cap was applied.
  • Restore receipt: savestate_status generation advanced from 0 to 1 with pending=0,last_ok=1,last_op=load,last_slot=1. Presented screenshot visibly confirms the sandcastle area on the beach facing the water.
  • Original ApeEscapeRecomp/saves/scph1001 slots 00/01 were v3 and slots 06/07/08/09/11 were v2; they cannot be used as v5 checkpoints.

The passive 10-second sample is retained locally at .local/crabby-baseline/stall-10s.json, SHA256 448d6a77b08479e0cca9bf11ffa21af1b3c2408ba70b9a3f1a0a7696f4a10b28. Snapshot start timestamps differ by 10.038749 seconds; commands are sequential, so these are not atomic snapshots of a fixed amount of guest work.

Observation Value Interpretation limit
Overlay native dispatch delta 0 Does not include statically compiled game execution
Overlay interpreter fallback delta 33,702 Dispatch events, not CPU-time share
Interpreted instruction delta 425,410 Some kernel fallback is expected
Interpreter block delta 33,714 No claim that reducing calls changes the primary bottleneck
Compilation runs/configured 0 / 0 Cache/compilation provenance differs from a prepared release campaign
Rolling phase samples: interpreter / static / GPU 1.59% / 80.11% / 18.30% Instrumented phase stamps, not an independent host CPU profiler

This observation rejects the inference that zero overlay-native dispatches alone prove interpretation dominates the slowdown. It does not reject overlay optimization as a candidate. No controlled compiler/process/power-state audit was performed during this exploratory sample.

Separately queried frame_perf reported approximately 16.68 ms total and 16.65 ms emu_cpu_ms_avg. The latter is calculated from total wall time minus present wall time, not OS thread CPU time. GPU queries span command-stream intervals; do not interpret these fields as independent exclusive CPU/GPU busy time or add their values together. Run without timer-query profiling and obtain host-thread CPU samples before choosing an optimization from these values.

HOST-BUDGET-001: verify the throttle denominator

Date: 2026-09-04 (session date). Verdict: launcher calibration, not an accepted runtime optimization or a substitute for low-end hardware.

On the same 16-logical-CPU host, a single Python process busy-looped until time.process_time() advanced by 2.5 seconds. Every run used affinity mask 0x1. The game was stopped. The sequence below was a calibration sequence, not an interleaved optimization campaign. Reports live in .local/host-calibration/{unrestricted,core50,core25}.json.

Requested budget Applied Job CpuRate (out of 10,000) Wall seconds Process CPU seconds
Unrestricted none 2.580994 2.500000
50% of one logical CPU 313 4.679147 2.531250
25% of one logical CPU 157 9.515031 2.500000

--cpu-percent is a percentage of the whole host's CPU scheduling budget. --core-percent normalizes to one logical CPU equivalent independently of affinity; it limits the whole launched job, including helper threads/processes. This does not selectively slow the emulation thread, lower CPU frequency, or simulate an older GPU/cache/memory subsystem. Reports retain requested and applied rates, including integer rounding.

An earlier 0.5-CPU-second witness did not demonstrate effective throttling even at a 3% host budget. Longer witnesses did, so sub-second smoke timing is not a valid cap calibration. Repeat calibration on each host and use sustained game windows rather than assuming the scheduler's short-term behavior is linear.

ROUTE-SMOKE-001: live runner protocol validation

Date: 2026-09-04 (session date). Verdict: successful diagnostic smoke only. No optimization candidate, target-provenance manifest, or acceptance comparison.

Used the isolated executable/state from CRAB-DISCOVERY-001, OpenGL, no host cap, PSX_RUNTIME_PERF_DIAG=1, and explicitly PSX_GL_PERF=0. A local shortened route held neutral input for 120 ticks. The runner confirmed completed slot-1 restore (generation 1, last_ok=1) and route completion (active=false, one step consumed, remaining=0). Raw evidence: .local/crabby-baseline/runner-smoke.json.

  • Route wall time: approximately 2.141 seconds, including setup/status overhead.
  • Counter observation window: approximately 2.672 seconds and 161 frame ticks, including sequential snapshots/control traffic, not exactly 120 guest ticks.
  • GL frame timing correctly reported unavailable with timer queries disabled.
  • No native overlay compilation was configured; cache provenance remains unsuitable for a prepared-release acceptance baseline.

The process exited after the smoke. Original saves/cards were not modified. The committed 7,200-tick route is a longer stationary workload manifest; this smoke does not validate its full-duration timing or the analog approach route. See tool usage and remaining gates.

MEMORY-TRACE-001: remove empty production write-trace calls

Dates: 2026-09-04 to 2026-09-05. Verdict: retain as a narrowly scoped dead-work simplification. Local write benchmark improves; gameplay gain is unproven. This does not complete the campaign or establish the cause of the beach slowdown.

Change and correctness evidence

Guard the six RAM/scratchpad byte, halfword and word calls to debug_server_trace_write_check with the existing PSX_NO_DEBUG_TOOLS switch. That function already returns immediately in production. This also removes its old-value argument loads. Debug builds keep the original calls. No functional memory observer, store, cycle charge, MMIO path, runtime setting, ABI or state layout changes. Separate -O2 -fno-lto object inspection found the baseline's undefined trace symbol and confirmed its absence from the candidate object.

runtime/tests/test_memory_debug_trace_guard.py compiles actual memory.c against real runtime headers, with external device/observer stubs. It checks all six writes through four RAM mirrors and scratchpad aliases, expected bytes and adjacent sentinels, observer/store counts, and production/debug trace-call witnesses. Optional --baseline-memory also checks the old production source. The compact state hash covers touched bytes and selected counters, not complete guest/device state. Structural checks preserve functional RAM observer order. An earlier synthetic store model and then a fake-header fixture were rejected during review; neither is used by the committed test.

Build provenance

Framework baseline is 8ec6498d323a7abd04a3058afcbb5e36e62a8ec3; candidate differs only by the six guards in runtime/src/memory.c. Source SHA256:

  • Baseline: 693be507fa53fded3e8e276f4ac0f7293d3879f3d53a8044fa8c383b4e097b99.
  • Candidate: 4fdb391d4f74da1ef2d31ce7cd61a0e0b958abab8304da37d83a2c1bb0e85253.

Both title executables were rebuilt, using the same copied Ape Escape generated code and assets from _wt-ape-crabperf-20260901. Provenance trees/build logs are retained under .local/ape-build-provenance. CMake Release uses MinGW GCC/G++ 15.2.0 from C:/msys64/mingw64/bin, -O3 -DNDEBUG, debug tools OFF, static runtime ON, SDL3, recomp UI and rewind ON, netplay OFF, and block cycles ON. Vulkan was enabled in the build but was not the selected/tested renderer. The first build failed with the wrong windres shim; successful builds explicitly selected C:/msys64/mingw64/bin/windres.exe.

  • A executable SHA256: f0c893a5743d8dfddcde75fe275d525c99c66294c5333fdf48b797de0b21096e.
  • B executable SHA256: 6fd00dc4ed8a7e9cf66f1bdd64ebaafd376d867dd5687c126bcd56f8e03dbf12.
  • A successful build-log SHA256: c9a042d2ccab65d248a9b91b0b74d1106fc80fe74f611ea5eccfb27f350ad252.
  • B successful build-log SHA256: 3d5326d5fa5de696cb02fd3c021a2336b92362c04fcfc3f00a560a79ec34ad8e.

The generated BIOS warned that it was stale against the current emitter fingerprint. It was deliberately identical in A/B to retain compatibility with the available v5 checkpoint. No native overlay compilation/loading was configured. These are controlled local integration artifacts, not clean release builds or qualification of regenerated code and shipped overlay caches.

Local write benchmark

Actual memory implementation, external device stubs, and a genuinely empty trace function in a separate translation unit; GCC C11 -O2 -fno-lto, production macro enabled. Each run executes 50 million loops of six writes. Both artifacts warm up with one million loops before measured A1/B1/A2/B2/A3/B3 order. Host is the Ryzen 7 9800X3D, High Performance power plan, affinity 0x1, no CPU cap. Primary metric is launch-to-completion wall seconds, lower is better.

Pair A seconds B seconds
1 1.4162425 1.1504406
2 1.4181166 1.1467513
3 1.4099258 1.1160858
Minimum 1.4099258 1.1160858
Median 1.4162425 1.1467513

Median improvement: 19.03%; minimum improvement: 20.84%. All six runs produced identical selected state/counters: state=dcc19de5, 300 million stores, 150 million parity calls, 150 million card checks, zero trace calls. This workload emphasizes write-call overhead and does not predict gameplay gain. The compiler-process monitor reported no observed compilers; thermal/frequency telemetry was not collected.

Crabby Beach OpenGL comparison

Same v5 state as CRAB-DISCOVERY-001, staged copies of saves/cards/settings. OpenGL context reported NVIDIA 3.3.0 driver 610.74, 2x SSAA and antialiasing, 165 Hz display, wall-clock 59.94 Hz pacing, driver vsync OFF. Both uncapped smokes sustained approximately 60 guest Hz with no reported audio underruns. A screenshot outside the acceptance runs confirmed the water-facing beach scene. Smoke timings are excluded: they are not three pairs and B's screenshot overlapped its smoke window.

Measured A/B uses PSX_LOAD_SLOT=1, PSX_RUNTIME_PERF_DIAG=1, PSX_GL_PERF=0, PSX_BENCH_WINDOW=120:420. Primary metric is the runtime's exact 300-frame BENCH wall_ms, not launcher lifespan. Every run uses a 25%-of-one-logical-core Job budget (raw CpuRate 157/10000) and affinity 0xFFFF, limiting the entire job. Every process is intentionally stopped after 35 seconds. Timed windows complete before timeout. This scheduling cap is not low-end hardware emulation.

Pair A wall ms B wall ms
1 8458.662 8588.874
2 8312.503 8274.390
3 8341.652 7946.127
Minimum 8312.503 7946.127
Median 8341.652 8274.390

Median improvement: 0.81%, within observed noise; minimum improvement: 4.41%. No convincing scene-level gain. Each window reports 180,114 interpreted instructions, 15,564 fallback dispatches, and zero overlay loads, invalidations, or captures. Those matching counters are not full-state parity. Under the severe budget both builds slow down and underflow host audio. No compilers or monitor errors were observed; configuration/mod hashes remained unchanged. No screenshots were taken during these six runs. No runs were discarded.

Retained evidence and remaining gates

All following paths are relative to the worktree and are intentionally ignored:

Artifact SHA256
.local/memory-trace-bench/campaign.json fd1944a1fc3a8ea750e37ceda9fb12367d9bb85adc8f12d01fdc28df3a3b06ad
.local/game-memory-ab/campaign.json 9d0c70ae9b9d221bc601e24cf7563dcdc61f84fecbae97806e7a7d068d8b5e1d
.local/run_memory_bench.py 90f6fec928f33f3d5e6395d31ce25d7f86ba16359d79851dd8903ef9ef48057d
.local/run_game_ab.py e3e9fd310600c3dbde14d4b2b4fff741cee2a063c14bbfc9ffe7f8416f71bff9

JSON retains per-run evidence and artifact/configuration hashes; game/state assets are not distributed. The focused compiled regression passes, as do the FMV quiet and dirty-text admission/continuation guard tests. The combined campaign/host-launcher/stall-report suite passes all 63 tests, and CMake's registration guard finds all 120 runtime/recompiler test files registered. These are not a full runtime CTest pass or cross-platform validation. Two unrelated structural tests also fail on the immutable baseline; details are in the audit.

Retain the change only as removal of already-empty production work, under the contract's simplification allowance, not as a 5% gameplay optimization. Full checkpoint parity, multi-title/low-end hardware, moving/transition routes, long-window frame consistency, clean-install and cross-platform release gates remain open. Broader GPU/audio history and GL costs remain separate candidates; the flat gameplay result does not justify focusing the campaign on memory.

PACER-COST-001: attribute idle pacing CPU cost

Date: 2026-09-05. Verdict: baseline attribution only, no pacing change or gameplay speed claim. Compile actual frame_pacing.c and host_time.c against SDL3 with GCC 15.2.0 -O2 -DPSX_SDL3. The driver calls frame_pacer_wait 600 times at 59.94 Hz with no guest work, using GetThreadTimes for calling-thread CPU time and SDL's performance counter for wall time. The first call anchors without waiting, so 599 periods take approximately 9.9933 seconds.

Run Wall seconds Thread CPU seconds
1 9.993298 0.812500
2 9.993297 0.859375
3 9.993297 0.765625

This is approximately 7.7-8.6% of one logical CPU spent on idle pacing on the Ryzen 7 9800X3D, High Performance plan, unrestricted scheduling and inherited affinity. The existing sleep decision stops sleeping below two milliseconds, then spins until the deadline. This local cost makes the spin tail worth measuring in gameplay; it does not establish an actual pacer bug or explain exclusive wall-time phase attribution under a Job cap. No candidate was tested. Agents held compilation during these runs; no continuous compiler-process or thermal monitor was attached to this exploratory probe.

Local retained driver .local/pacer-cost.c, SHA256 d98305437fb4e8c86769f31893e43bf63ade8568d761f522e0842817a92ac105; executable .local/pacer-cost.exe, SHA256 7aa80698ca5d3a831d0d651601b3c5b2e8bc3665e2eec82f5e426c308eae7ddb; raw output .local/pacer-cost-results.txt, SHA256 749fc5185fa512e3a00390f0022fa8939993763ca3b9f6e123ba76ff6654fea1.

HISTORY-002: isolated GPU and audio history candidates

Date: 2026-09-05. Correctness milestone; controlled resource/time comparison pending. No gameplay gain accepted from the correctness runs below.

Candidate boundaries

  • GP0-HISTORY-001: compile the GP0 command-history allocation, recording and diagnostic guest-stack scan only when debug tools are enabled. Production history accessors remain linkable but report zero capacity/count and empty dumps. Keep source tracking, opcode counters, WS census, functional overhang detection, and all command execution unchanged. Removes a 104 MiB allocation request; debug builds retain always-on history.
  • AUDIO-PCM-001: compile out four PCM history arrays and their writes in PSX_NO_DEBUG_TOOLS builds. Preserve every tap's head, nonzero/audible/peak statistics, rates, event records and pump/underrun/mute counters. Production WAV dump returns the existing error result without opening a file; debug WAV history is unchanged. Actual GCC object BSS is 83,886,272 bytes with debug history and 16,777,408 without it: exactly 64 MiB less backing capacity.

Both use the existing build switch, not a new runtime selector or lazy allocator. Capacity is not equivalent to resident memory or a measured speed improvement. Packed production audio tap counters may share cache lines between callback and emulation threads; require the real bridge-audio path in performance checks.

Focused checks

The GPU test compiles actual gpu.c and real headers with external renderer stubs. It compares exact serialized GPU snapshots and the entire VRAM at eleven checkpoints across environment, fill, upload, copy, polyline and NOP commands. It also checks debug history count, copy source/stack pointer and polyline header, and production unavailability. This tests parser/state behavior, not the rendering correctness of a stubbed backend. Known pre-existing polyline source attribution was not changed or silently redefined by the test.

The audio test compiles actual audio_trace.c in both modes. All four taps' observed statistics/events compare equally, including a new event after ring wrap, signed extremes, audible thresholds, rates and resets. Debug WAV headers and sample bytes are checked for all taps and for wrap/clamped eviction; production must not create the requested WAV. Review rejected the first test's hard-coded output comparison and replaced it with real observations.

Both focused tests passed root execution. The 63 campaign/host/stall-report tests passed, as did the memory-trace regression. CMake's registration guard finds 122 test files registered. This is not a full cross-platform CTest pass.

Isolated build provenance

B is the previous memory-guard baseline, C adds only the GP0 gate, and D adds only the audio gate. All use identical local-only endpoint memory/restore/ renderer/audio witnesses and an optional checkpoint probe. Original B from MEMORY-TRACE-001 is preserved. Source trees, exact options/commands/logs and hashes are in .local/ape-build-provenance/builds-bcd-manifest.json, SHA256 176ed3d54939fab3498ced5d1e3dd38566a9ef76e7e4cb23f5514a8d9a740530.

Variant Executable SHA256
B metrics f1310b19eb3633d81e01717f36ab60f4d6cd4d53aa3838adf0e614d10bbf4914
C GP0 74063d9dadef152d023c0dead18eabde854773c4399f7f52f49713fb67315045
D PCM e9f525296dfdb0be2a50596a1198bb9735be0371d21afa627cc6e08dfe05bd39

Release GCC 15.2.0, SDL3, static runtime, debug tools OFF, rewind ON, netplay OFF, same donor generated game/BIOS and saved scene as MEMORY-TRACE-001. The stale generated-BIOS warning persists. A default all-target build failed in an unrelated donor title test on a long Windows dependency-file path; explicit psx-runtime builds succeeded. No release qualification is claimed.

Real OpenGL checkpoint comparison

Separate uncapped 20-second processes restore the same slot-1 v5 beach state. All variants report active OpenGL (gl_active=1, gr_backend=1), completed restore (generation=1,pending=0,last_ok=1,last_op=load,last_slot=1), and active non-legacy bridge output with advancing host-tap frames. The local-only probe writes raw boot-state blobs at frames 120, 121 and 420, plus CPU timing sidecars. For both C and D versus B, every blob and sidecar is byte-identical.

Frame Raw guest snapshot SHA256 (all B/C/D)
120 505f7d94c5c7e518e099669ed1623296d9210abc30eab4bc907956e91ff652c6
121 1e0b19c3a913733aba9bb403b1e01a1c9d309cc919e3accd9c52f3d341d3429b
420 55ebbe6a36ac2674952a4672575dfb619547ffec0a059f9a277bb955b9d074e2

Each blob is 3,685,569 bytes. The existing save format covers CPU registers, RAM, scratchpad, VRAM, SPU RAM and serialized device/IRQ/clock/cache/dirty state. The 411-byte sidecars add multiply/divide and GTE completion timestamps, all 33 read-absorb bytes and load-timing scalars. CPU function pointers, host audio queues, SDL/pacer/presentation histories and diagnostics are excluded. This is one scene's checkpoint evidence, not exhaustive state/restore or multi-title qualification. Full campaign correctness gates remain open.

Evidence: .local/history-correctness-002/report.json, SHA256 7feb56b0f0119688e460ff46b027d3aaf9549021e996a935830e26a6154b21f2; driver .local/run_history_checks.py, SHA256 f6f29b505225fc0ff8688d9a86921be60590a81288980c5fd7d4c8b56961d0f4. Raw game-derived snapshots remain local and uncommitted.

The first attempt, .local/history-correctness-001, failed closed because the staged checkpoint hook was absent despite an earlier build report. Explicit source and binary-string checks plus forced rebuilds corrected the artifact. That failed attempt is retained, not counted as successful state comparison.

Checkpoint saves can publish deferred cycles and force GL readback. These runs also overlapped unrelated builds, so none of their timing or memory values is acceptance evidence. The timed runner explicitly unsets checkpoint capture. The preregistered primary metric is exact-frame-420 process PrivateUsage; the secondary metric is frames-120:420 wall time, with three interleaved pairs for each independent candidate and a validated 25%-one-core Job cap. It requires stable input/artifact hashes, active OpenGL/audio and completed restore, and aborts on compiler contamination or missing/nonfinite metrics.

Timing gate outcome and disposition

The benchmark agent monitored the host for ten minutes without finding 30 consecutive seconds free of compiler/benchmark activity. Concurrent SNES builds included Mega Man X and Super Mario Kart. No timed candidate process was launched and .local/game-gp0-memory-ab was not created. Unrelated work was not stopped. The power plan remained High performance before and after; thermal state was not measured.

The prepared twelve-run driver is .local/run_game_gpu_memory_ab.py, SHA256 ce01b28c627dd5a8d0abe3217a49e2fd1d576fc7128153de502487f69a96f64b. It includes cc.exe in compiler-contamination detection. Resume the paired campaign in a quiet host window before making retention or speedup claims.

Disposition: committed candidate implementation in the isolated performance worktree, pending performance acceptance, not release qualification. The 104 MiB GP0 allocation request and 64 MiB PCM static-storage capacity removals are established; resident-memory savings and gameplay speedups are not. Focused parity tests and real OpenGL checkpoints passed, but the broader correctness matrix and the quiet-host comparisons remain open.

UNCAP-HISTORY-003: uncapped OpenGL throughput

Date: 2026-09-05. User-requested follow-up to HISTORY-002. Uncapped frame throughput removes a limiter that can hide spare capacity; it does not isolate the process from concurrent host work or simulate lower-end hardware.

Mechanism and provenance

PSX_VSYNC=0 alone leaves the wall-clock pacer active. Debug/manual/load turbo can skip presentation, and Ape's historical ape.framerate.uncapped plugin actually selects frame interpolation. None is appropriate here. The native VBlank-rate API already permits zero frontend pacing without changing guest cycle accounting (mod_plugins.h, psx_mod_set_native_vblank_rate).

Identical local-only staged main.cpp hooks set g_frame_period_ms=0 and g_video_vsync=0 after host-refresh selection and before GL context creation when PSX_PERF_UNCAPPED=1. These are the same host-pacing effects as the native API; the hook does not call the API or change guest clocks. It refuses active interpolation. A separate endpoint [BENCH_PACING] reports actual period, effective swap interval, wall-pacing decision, interpolation and load-turbo state. Normal presentation and the audio bridge remain enabled.

The three incremental Release psx-runtime builds succeeded. Original paced executables and the previous manifest remain unchanged; staged original main.cpp files are backed up. Tracked runtime sources were not changed for this experiment. New manifest: .local/ape-build-provenance/uncapped-builds-manifest.json, SHA256 184dfa8a1d0305fc9f1d5aafdd6dda9cec4d0d24b8a73bf684775eefa7103ca4.

Variant Uncapped executable SHA256
B baseline 8050206bda873fd42a8cc790c0b9b556680cdcb966893a1414d3774bbbf11dd6
C GP0 gate a43233c44f9c6f6f1a8b7115bc77260de0292571c556e44a5afd97bb4b00d042
D PCM gate 566297db442e5e062b2b90864302a20d4cbdbe769496d6cdf7d2560089aff5f3

Uncapped correctness smoke

Root ran separate 12-second uncapped processes with checkpoint capture enabled. All B/C/D raw guest snapshots and CPU sidecars at frames 120, 121 and 420 are byte-identical to one another and to the paced B checkpoints in HISTORY-002. All restore, OpenGL and bridge-audio witnesses passed. Endpoint pacing reports zero period, swap interval, wall pacing, interpolation and load turbo. These checkpoint runs are not timing or memory acceptance measurements.

Evidence: .local/uncapped-correctness-001/report.json, SHA256 551732b3411c74a113d14edf46f185d364684863ec199d223f3b444ee9474709; driver .local/check_uncapped_history.py, SHA256 8acb9e3894286237cfbba6b3f3e468c5ff3698c9762c2de488164fa151bd3e91. The existing save-format coverage/exclusions from HISTORY-002 still apply.

Timed protocol

Three interleaved B/C and B/D pairs, reversing within-pair order for pair two: B,C,B,D; C,B,D,B; B,C,B,D. Each process restores the same beach save and runs with unrestricted requested host CPU budget, affinity 0xFFFF, the same OpenGL 2x SSAA/AA settings and diagnostic counters, and no checkpoint capture. Primary metrics are wall time for guest frames 120:420 and guest VBlanks/s (300000 / wall_ms), not unique rendered game frames or physical display FPS. PrivateUsage at frame 420 is secondary. Twelve-second process timeouts provide bounded cleanup; whole-process CPU totals are not the fixed-window CPU cost.

The driver requires unchanged artifacts/settings/game/state/mod inventories, completed restore, active OpenGL/audio, effective uncapped pacing and matching guest-work counters. Compiler observations are retained instead of waiting for a quiet host; monitor failures invalidate results. No result is automatically accepted based only on the absence of observed compilers. Eight synthetic driver checks passed root execution.

Local driver .local/run_uncapped_history_ab.py, SHA256 ab3fd3efd56b3088a11221d907a5202101dbfff21fb19186b647cef97bf7c8d3; tests .local/test_run_uncapped_history_ab.py, SHA256 3a4b19138e1272490534b3bea84099e32a856d9d922c368fa9f85329abf23044.

Uncapped audio production outruns the physical playback timeline. An active audio device and advancing host tap do not establish identical callback counts, absence of overflow or normal-speed listening quality. Neither title-wide correctness nor low-end realtime pacing/audio acceptance follows from this throughput experiment.

Short-window results

All twelve runs passed the fixed-window, effective-pacing, restore, backend, audio and inventory checks. No monitored compiler processes or monitor errors were observed. High performance power plan before/after; no thermal telemetry or continuous monitoring of all other host workloads. Classification remains exploratory_no_compiler_observed, not automatic acceptance.

Comparison Baseline wall ms (three runs) Candidate wall ms (three runs) Median VBlanks/s B -> candidate Median throughput change
GP0 gate 1205.440, 1247.664, 1181.284 1178.411, 1181.626, 1177.182 248.872 -> 254.580 +2.29%
PCM gate 1212.670, 1185.505, 1215.057 1165.711, 1208.837, 1179.628 247.388 -> 254.317 +2.80%

Within-pair throughput changes were +2.29%, +5.59%, +0.35% for GP0 and +4.03%, -1.93%, +3.00% for PCM. This spread, especially the PCM sign reversal, prevents treating the modest median gains as robust. Fastest-run throughput changes were only +0.35% and +1.70%, respectively. No additive combined gain is established: each candidate was tested independently.

Endpoint PrivateUsage fell in every pair: GP0 by 102.87, 103.63 and 105.49 MiB; PCM by 62.00, 63.31 and 64.07 MiB. This measures process private commit in this scene, not resident physical memory. The amount includes allocator/driver variation and is not an exact measurement of the removed arrays alone.

Raw report .local/game-uncapped-history-ab/campaign.json, SHA256 5e6477c3969eb8799aee300ffa153903d39c48010a814be76f2cfc19413afb4d; power witness .local/game-uncapped-history-ab/power_plan.json, SHA256 034c141dd4dd57cdbd10819bf884aa01c628e1fb496dc1bee83979aa52a55da4. All stdout/stderr and host reports are retained beside the report. Root recomputed paired throughput and memory deltas from the raw rows.

Because the measured windows last only about 1.2 seconds, a separate replication uses frames 120:1920 (1,800 guest VBlanks), identical executables/settings and the same twelve-run order. This extends the workload past the GP0 history ring's wrap point without changing game speed settings or rendering quality. It uses a separately named driver/output directory and 15-second process timeouts; the short-window artifacts are preserved.

Longer-window replication

All twelve runs passed the same evidence checks at frames 120:1920. B/D GP0 history totals reached 1,180,535 commands, beyond the 1,048,576-entry capacity; C remained zero/zero. Work counters matched within each candidate comparison. This is a workload/wrap witness, not a new byte-level checkpoint at frame 1920.

Comparison Baseline wall ms (three runs) Candidate wall ms (three runs) Median VBlanks/s B -> candidate Median throughput change
GP0 gate 7102.496, 7222.937, 7202.571 7108.128, 7020.108, 6990.751 249.911 -> 256.406 +2.60%
PCM gate 7295.579, 7127.430, 7366.212 6982.187, 7070.005, 6977.142 246.725 -> 257.799 +4.49%

Within-pair throughput changes: GP0 -0.08%, +2.89%, +3.03%; PCM +4.49%, +0.81%, +5.58%. Fastest-run throughput changes: +1.60% and +2.15%. PrivateUsage fell in every pair: GP0 by 105.18, 106.13 and 106.69 MiB; PCM by 63.12, 61.83 and 63.25 MiB.

Compiler activity was observed in two runs: GP0 pair-one C (32 observations) and PCM pair-one B (30). Observed names included cc.exe, cc1.exe, cmake.exe, collect2.exe, g++.exe, gcc.exe, ld.exe and ninja.exe. The monitor covers the full process lifetime, not precisely the measured frame window. No monitor errors occurred. All runs remain in the report; no post-hoc removal of the slower/contaminated runs was used. Classification: exploratory_contaminated. High performance power plan before/after; thermal state was not measured.

Evidence:

  • .local/run_uncapped_history_long_ab.py, SHA256 459c852c71fd0f4c927a331a91867566704e1fb811b4f2be38fb1a55db6a19d9.
  • .local/game-uncapped-history-long-ab/campaign.json, SHA256 f71891ad99b20ab5bb1f2e148931494c90951f713d25c8ddd6d4107b436007e7.
  • .local/game-uncapped-history-long-ab/power_plan.json, SHA256 7f115cc8e9fa6998f4e01f557098113881fbe2b1264f0434060fd0621a7179a0.

Root reviewed the longer driver's delta: only window/frame-count/FPS numerator, output path, metric labels and timeout differ from the short driver. Synthetic checks accepted a correctly converted 1,800-frame observation and rejected the old 300-frame observation. Root recomputed paired changes from raw results.

Disposition

Uncapped benchmarking is useful here: it exposes approximately 250 guest VBlanks/s of throughput instead of hiding spare capacity behind the 60 Hz limiter. Both passes suggest modest gains, but run variation and the longer pass's contamination prevent claiming a reliable general speedup. These are not additive gains or evidence that Crabby Beach slowdowns are fixed.

Reduced process private commit is consistent across all twelve candidate pairs. Together with focused parity and the three exact guest checkpoints, this supports retaining the candidates in the isolated burndown branch for further qualification. Normal-speed audio/pacing, constrained-host comparisons, multi-title correctness and actual low-end hardware remain open. Do not ship the local uncap hook as a gameplay default.

MANUAL-INTEGRATION-004: user sign-off and master integration

Date: 2026-09-05. The user reviewed the Tomba 2 and Mega Man X6 manual builds, reported that they "look good", and explicitly authorized committing and merging the work to main/master. This is user acceptance of the supplied test builds, not an instrumented audio comparison, full-title playthrough or a new performance measurement.

Both manual builds used the combined guards from 04371d60, normal pacing, OpenGL, recomp-ui, production debug-tools OFF, rewind ON, existing generated game/BIOS code and isolated copied memory cards. Recomp-ui displayed verified discs with BIOS paths supplied. Final executable SHA256 values:

  • Tomba 2: 6d978d8e2a736aa8b94cb076091117edbe76f6fa7c7f061df227233be8a648ad.
  • Mega Man X6: 4c99e7a6092122b6675daf83f52eddb76a71b0b5290b474c7f4d494df84135b2.

The initial Tomba build disabled rewind to bypass a dependency lookup; review rejected that workaround and the final build restored rewind. Mega Man X6's older recomp-ui headers were incompatible with the runtime, so its manual build used the compatible Tomba 2 recomp-ui checkout. Neither original game installation was edited. Local build logs/settings/screenshots remain under the original burndown worktree's .local/manual-validation directory.

Integration uses origin/master at 17f49ad3 as its base. Only the six campaign commits 6488414a, d38f97e1, 8ec6498d, 28e9c844, 0cf93d57, and 04371d60 were transplanted; the older review-branch ancestry was excluded. All six applied without conflicts. The runtime delta remains only the three production diagnostic guards; benchmark uncapping and local game artifacts are not part of the merge.

The first integration GPU test failed because its old polyline fixture supplied only one vertex before the terminator. Upstream now correctly requires two vertices. The fixture now supplies both; the upstream runtime fix is unchanged. On the integrated sources, all three actual-source guard regressions and the 63 campaign/host/stall-report tests pass. The previously tested game executables are not rebuilds of this newer upstream base; no such equivalence is claimed. The CMake registration guard also passes with 133 test files registered.

Disposition: user-approved integration of the scoped optimizations, tests and measurement tools. The original WIP label records the historical state at implementation time. The broader burndown, general speedup qualification and actual low-end hardware coverage remain open after this merge.