diff --git a/docs/re/mission-freeze-heap-exhaustion.md b/docs/re/mission-freeze-heap-exhaustion.md index 92f8cbe5..052158c1 100644 --- a/docs/re/mission-freeze-heap-exhaustion.md +++ b/docs/re/mission-freeze-heap-exhaustion.md @@ -512,3 +512,59 @@ this session has used goes EXTRAS → MISSION SELECT. The 2026-08-10 run that fl for 500 s did not necessarily; `newgame_path.sh` and `tutorial_launch.sh` reach a mission by other menus. Testing a non-MISSION-SELECT route is the next experiment, and it is now the most promising one available. + +--- + +# 🔴 MISSION SELECT is **not** special — corrected the same day + +The section above concluded the freeze is "entering MISSION SELECT" and made +avoiding that screen the next experiment. Ran it, and **that conclusion is too +narrow.** + +## A liveness metric that actually separates the two states + +`tools/re-capture/route_liveness_probe.sh`. Two frames five seconds apart, report +the percentage of pixels that changed. The menu animates, so the separation is +not marginal: + +| state | pixels changed in 5 s | +|---|---| +| main menu, healthy | **99.80 – 99.97 %** | +| frozen | **0.00 %** | + +This is the measurement that should have been used from the first run. It needs +no navigation script to be honest about where it is. + +## The NEW GAME route freezes too + +Walking the menu's **first** item — NEW GAME, which never touches MISSION SELECT: + +| step | liveness | `screen_id` says | +|---|---|---| +| main menu | **99.80 %** | menu | +| after Ⓐ on NEW GAME | 7.33 % | title | +| after the next Ⓐ | **0.00 %** | *"flight"* | + +Frozen two presses off the main menu, with the same +`AllocRange unable ... 134217728 bytes` in the log. + +## So the correct statement is broader + +**The game freezes on the first content load after the main menu, whichever menu +item is taken.** MISSION SELECT was simply the route every earlier run happened +to use. The previous section's "avoid MISSION SELECT" recommendation is +withdrawn — there is nothing to avoid. + +That puts the memory account back at the centre: if ~379 MB is already live and +a content load wants 128 MB, *any* content load fails, which is exactly what two +different routes now show. + +⚠️ Note also how badly `screen_id.py` misleads here: it called a frozen frame +`flight` on a run that never left the menus. **Liveness first, classification +second** — a classifier's answer is meaningless until the frame is known to be +moving. + +❔ Still open: whether the ~171 MB allocated at boot (112.88 MB at `B50C0000`, +58.50 MB at `BC220000`, both before any menu) is avoidable, since that plus a +128 MB content load is most of the console. That is the last untested lever, and +it is a question about what those two blocks are. diff --git a/tools/re-capture/route_liveness_probe.sh b/tools/re-capture/route_liveness_probe.sh new file mode 100755 index 00000000..0002079d --- /dev/null +++ b/tools/re-capture/route_liveness_probe.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# Boot to the main menu, then walk menu steps, measuring LIVENESS after each by +# comparing two frames a few seconds apart. Never trusts a progress log. +set -u +export HOME=/sylph-home/re SDL_AUDIODRIVER=dummy DISPLAY=:98 XENIA_PAD_FILE=/tmp/xenia_pad.txt +export PYTHONPATH=/sylph-home/.local/lib/python3.12/site-packages +SD=/tmp/claude-1000/-home-fabi-RE-Project-Sylpheed/b113cc12-4769-4ed8-ad66-c2b48f800773/scratchpad/wt-root/Syplheed-Reborn/tools/re-capture +export PATH="$SD/bin:$PATH" +OUT=/tmp/route; rm -rf $OUT; mkdir -p $OUT +pad(){ python3 "$SD/pad.py" "$@"; } +shot(){ screenshot "$OUT/$1.png" >/dev/null 2>&1; } +live(){ # two frames 5s apart -> % pixels changed + screenshot "$OUT/_a.png" >/dev/null 2>&1; sleep 5; screenshot "$OUT/_b.png" >/dev/null 2>&1 + python3 - "$OUT/_a.png" "$OUT/_b.png" <<'PY' +from PIL import Image; import numpy as np, sys +a=np.asarray(Image.open(sys.argv[1]).convert('RGB'),dtype=float) +b=np.asarray(Image.open(sys.argv[2]).convert('RGB'),dtype=float) +d=np.abs(b-a); print(f"{(d.max(2)>4).mean()*100:.2f}") +PY +} +say(){ echo "[$(date +%T)] $*"; } + +say "waiting for the title, then one A" +for i in $(seq 1 240); do + shot probe; s=$(python3 "$SD/screen_id.py" "$OUT/probe.png" | awk '{print $1}') + [ "$s" = title ] && break + [ "$s" = menu ] && break + sleep 2 +done +say "screen=$s ; liveness here = $(live)%" +if [ "$s" = title ]; then pad tap A; sleep 6; fi +for i in $(seq 1 60); do + shot probe; s=$(python3 "$SD/screen_id.py" "$OUT/probe.png" | awk '{print $1}') + [ "$s" = menu ] && break; sleep 2 +done +say "MAIN MENU (screen=$s) liveness = $(live)%" +shot 00-mainmenu