Last iteration I blamed four failed runs on the probe sampling every ~41 s, slower than the title screen lasts, and withdrew three earlier conclusions on that basis. Building the fix tested the claim and killed it. The speedup is real and control-verified. One long-lived ffmpeg x11grab stream, raw RGB, glyph counted in numpy -- no per-sample process startup, no PNG encode, no convert -crop: wrapper `screenshot` 3.98 s per sample (emulator running) import -window root -> PPM 1.20 s long-lived x11grab stream 0.29 s 13.7x The counter is byte-identical to is_title.py: 753 on the committed title capture, 327 on the main menu. Pointed at a running game it says the opposite of what I expected: 332 frames in 85.3 s = 3.89 fps; max glyph 0 1674 frames in 420.0 s = 3.99 fps; max glyph 0 1674 consecutive samples over seven unbroken minutes, four per second, zero green-(A) pixels. Sampling rate was a real defect that happened not to be the cause. So "neither locale reaches the interactive title without a pad press" -- withdrawn last iteration for want of evidence -- is reinstated, now as a dense measurement, with its reach stated: a MID-RUN window only, silent about the boot title. Leading hypothesis, unconfirmed: the PRESS (A) plate appears only in the boot title window and the attract loop's title carries none, which is exactly what title_states_capture.sh was written to test. The experiment is to start the fast probe from t=0 rather than attach to a run already in progress. METHOD: fixing the instrument is how you test the explanation that blamed it -- a plausible mechanism is a hypothesis, and the fix is its experiment, not its proof.
121 lines
5.3 KiB
Markdown
121 lines
5.3 KiB
Markdown
# 🔴 Why the boot harness stopped reaching the title — `screenshot` costs 10.8 s
|
||
|
||
**Status:** ✅ **diagnosed, with a control.** Four consecutive runs on
|
||
2026-08-28/29 failed to reach the interactive title, across two locales, two
|
||
launch paths and two display-gamma settings. The cause is none of those.
|
||
|
||
## The measurement
|
||
|
||
| condition | one `screenshot` call |
|
||
|---|---|
|
||
| while Xenia Canary is running | **10.8 s** |
|
||
| immediately after killing it | **0.117 s** |
|
||
|
||
**92×.** The 1-minute load average at the slow measurement was 1.80, so this is
|
||
contention with the emulator (both go through the same X server), not general
|
||
system load.
|
||
|
||
## Why that breaks the harness
|
||
|
||
`skip_intro.sh` takes **two** grabs 0.6 s apart per iteration, plus an
|
||
`is_title.py` numpy load. Its actual sample timestamps in the last run:
|
||
|
||
```
|
||
57, 95, 177, 218, 238, 298, 333, 380, 426, 471, 515 → intervals
|
||
38, 82, 41, 20, 30, 30, 35, 47, 46, 45, 44, 43, 21, 19 median 41 s
|
||
```
|
||
|
||
A **41-second** sampling interval against a title screen that the corpus already
|
||
documents as lasting *"a few seconds"* before auto-returning to the attract loop
|
||
(`wait_title.sh`'s own header). The harness is not seeing a stuck game; it is
|
||
blinking slower than the thing it is looking for.
|
||
|
||
That is also why runs at 16:43–18:05 the same day succeeded and later ones did
|
||
not — nothing about the game changed.
|
||
|
||
## 🔴 What this retracts
|
||
|
||
Three earlier conclusions were built on these runs and are **withdrawn as
|
||
causes**, though the observations stand:
|
||
|
||
* *"the Japanese-locale run never reaches the interactive title"* — it may well
|
||
have appeared, unsampled.
|
||
* *"neither locale reaches the interactive title without a pad press"* — the
|
||
English control shared the same defect, so it controlled for locale but not for
|
||
the sampling rate.
|
||
* *"the game sat in the attract loop for 604 s"* — what was observed is that
|
||
**every one of ~15 samples** landed on movie content, which at a 41 s interval
|
||
is a much weaker statement than it reads as.
|
||
|
||
## ⚠️ A confound I introduced
|
||
|
||
Setting `kernel_display_gamma_type = 0` makes the frame substantially brighter
|
||
(a mid-attract frame measured mean **122.8** against **52.5** and **82.8** on
|
||
comparable phases at type 2). `skip_intro.sh` classifies movie-vs-static on an
|
||
**absolute** rmse threshold of 1500 between two grabs, so a brighter output
|
||
inflates that difference and biases every frame toward "movie". **The capture
|
||
harness's tuning is coupled to the display settings** — changing gamma and
|
||
capture behaviour in one run confounds both.
|
||
|
||
## 🔴 The fix works — and it REFUTES the diagnosis above
|
||
|
||
Built and measured (`tools/re-capture/fast_title_probe.py`): one long-lived
|
||
`ffmpeg` x11grab stream, raw RGB frames, glyph counted in numpy. No per-sample
|
||
process startup, no PNG encode, no `convert -crop`.
|
||
|
||
| probe | seconds per sample, emulator running |
|
||
|---|---|
|
||
| the wrapper `screenshot` | **3.98** |
|
||
| `import -window root` → PPM | 1.20 |
|
||
| **long-lived x11grab stream** | **0.29** |
|
||
|
||
**13.7× faster**, and the counter is control-verified against the committed
|
||
frames — it returns **753** on `live-title-press-a.png` and **327** on
|
||
`live-main-menu.png`, byte-identical to `is_title.py`.
|
||
|
||
Then it was pointed at a running game:
|
||
|
||
```
|
||
332 frames in 85.3 s = 3.89 fps; max glyph 0
|
||
1674 frames in 420.0 s = 3.99 fps; max glyph 0
|
||
```
|
||
|
||
**1 674 consecutive samples over seven unbroken minutes, four per second, and the
|
||
interactive title never appeared.** So the sampling rate was a real defect and
|
||
*not* the cause. The hypothesis on this page — that the harness was blinking
|
||
slower than the event — is **mine, and refuted by my own fix**.
|
||
|
||
## What that restores
|
||
|
||
Last iteration I withdrew three conclusions on the strength of that hypothesis.
|
||
The withdrawal was right at the time (15 samples at 41 s intervals cannot support
|
||
them) and is now **superseded by better evidence**: dense sampling says the
|
||
interactive title genuinely does not appear in a mid-run window. Reinstated as a
|
||
**measurement**, with its reach:
|
||
|
||
* ✅ over **420 continuous seconds**, English, `gamma_type = 2`, ~13 minutes into
|
||
a run with no pad input, **zero** frames carried the green Ⓐ glyph.
|
||
* ⚠️ Reach: this covers a **mid-run** window only. It says nothing about the
|
||
first minutes of boot.
|
||
|
||
## 🟡 The leading hypothesis, not confirmed
|
||
|
||
The corpus already suspects the answer. `title_states_capture.sh` exists to test
|
||
*"whether the interactive one draws `ptbtn00` (the PRESS Ⓐ plate) and the other
|
||
does not"* — i.e. the title appears **twice**: once at the end of the boot
|
||
sequence, and again from the attract loop, and only the first may carry the
|
||
plate. If so, the plate's window is early and one-shot, and no amount of
|
||
mid-run sampling will ever find it.
|
||
|
||
That is consistent with everything measured, and it is **not confirmed**. The
|
||
test is to start the fast probe *before* the boot title — from t=0 rather than
|
||
attaching to a run already in progress.
|
||
|
||
## The original fix note, kept
|
||
|
||
Make the probe cheap enough to sample faster than the title window: grab a small
|
||
region rather than the full surface, drop the ImageMagick `convert` round trip,
|
||
or keep the glyph test in one long-lived process instead of re-importing numpy
|
||
per sample. None of that is done — this page is the diagnosis, and it is what
|
||
every remaining emulator-side question is waiting on.
|