re: the title's build-in measured in the guest's draw stream -- the flashes are real

The settle-time decode was confirmed only against a SETTLED frame, which shows
the end state is right and says nothing about whether the five flashes ever
happen. This runs the oracle: a draw capture armed before the title exists,
so the window contains the frames in which the screen is built.

The flashes fire in a six-frame window and are absent from all 155 other
sampled frames. `ptlogo_back2eff1` is drawn in exactly two frames at t = 54.0
against a decoded peak of t54-56; `ptlogo1` first appears at t = 42.2 against
a decoded t42. Units-per-frame was taken from the GLOW's period alone, a
different element, so the timings are not circular. The two holders are
continuous from frame 134.

The plate glow's quad carries a per-vertex colour whose alpha IS the element's
fade alpha, so the ramp is read straight out of the guest: observed range
0..80 against a decoded peak of 80, exact and unfitted; period 51.158
presented frames over 20 cycle starts. Fitting the decoded ramp gives RMS
13.16 alpha levels against 38.18 for the same ramp REVERSED -- if the shape
carried no information those would be equal, so the asymmetry is real and
correctly directed. Further controls: symmetric triangle 15.73, flat 31.13.

`ptlogo_back2eff3` was never drawn, and that is expected rather than a miss: a
2-unit flash peak is 0.85 of a presented frame, so catching one is a matter of
phase. A port drawing all five every time shows more sweep than the console.

METHOD.md gains the trap this cost: a 2D draw's identity is its vertex
geometry, not its bound texture. These sprites sample shared pages, and
matching texture dimensions produced a false negative (no flash is ever drawn)
and a false positive (the intro movie's 640x360 YUV planes read as `ptbase2`)
in the same pass.

Also records the top-level restriction on the settle window, which the port
raised and which is verified here: top-level [160,236] width 76, including the
`ptloop` leaves [269,540] width 271 -- an instant past the end of every
top-level element's timeline.

Evidence committed as a derived per-frame series, not the 7 MB raw log.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsEPXWVaEpyfudtR6re1Pd
This commit is contained in:
sylph-decoder
2026-08-29 19:56:34 +00:00
parent f9638a05cf
commit 1d2e653a7d
9 changed files with 3511 additions and 0 deletions

View File

@@ -73,6 +73,23 @@ This is a genuine test rather than a fit: the ring and the plate are different
elements in different bundles, measured in separate runs, and the only thing tying
them together is that both declare a 120-unit cycle.
## ✅ Measured in the running game
The glow's quad carries a per-vertex colour whose **alpha is the element's fade
alpha**, so the ramp can be read straight out of the guest's draw stream
([`ui-title-buildin-measured.md`](ui-title-buildin-measured.md)):
* **observed alpha range 0 … 80, against a decoded peak of 80** — exact, unfitted;
* **period 51.158 presented frames** over 20 consecutive cycle starts;
* the draw is **omitted entirely** while the glow is dark, which is the 11↔10
draw alternation visible in the settled title;
* fitting the decoded ramp gives RMS 13.16 alpha levels against **38.18 for the
same ramp reversed** — the asymmetry is real and in the decoded direction.
This does not re-measure the period in seconds (the run's frame rate was not
recorded), so the falsification below stands on the corpus's wall-clock timings
rather than on this run.
## What this replaces
* 🔴 **105 is wrong** and the port should stop shipping it. The number is **120**,

View File

@@ -26,6 +26,21 @@ vocabulary the file has.
`UiBuild::settle_window()` returns the interval, whose width is how much
confidence the midpoint deserves.
🔴 **Gather the times from the TOP-LEVEL elements only — not from nested leaf
records.** The port raised this after reproducing `[160, 236]` from its own
export, and it is worth stating because the implementation reads correctly either
way while only one is right. Including `GP_TITLE` build 4's `ptloop` leaves, whose
cycles run to 600 and 720, gives **`[269, 540]`** instead — a "settled instant"
that lies *past the end of every top-level element's timeline*, i.e. after the
screen has exited. Verified here: top-level `[160, 236]` width 76, with leaves
`[269, 540]` width 271. A leaf loops on its own clock and says nothing about when
the screen stops changing.
**Confirmed against the running game.** The mechanism this page decodes — five
transient flashes that fire and vanish — is observed in the guest's own draw
stream, with `ptlogo_back2eff1` drawn in exactly two frames at t = 54.0 against a
decoded peak of t5456: [`ui-title-buildin-measured.md`](ui-title-buildin-measured.md).
## The case that found it
`GP_TITLE` build 4. Seven elements share the light-arc band behind the logo:

View File

@@ -0,0 +1,134 @@
# The title's build-in, measured in the guest's own draw stream
**Classification: measured.** Xenia Canary, `ui_draw_capture.sh ARM=early
FRAMES=9000`, 2026-08-29. Evidence:
[`title-glow-alpha-per-frame.csv`](../data/title-glow-alpha-per-frame.csv) — the
per-frame series the numbers below come from. (The 7 MB raw draw log is a scratch
capture and is not committed.)
## What this tests
[`ui-settle-time.md`](ui-settle-time.md) decodes the title's light arc as **five
staggered two-frame flashes** (`ptlogo_back2eff1``eff5`) that fire around t5466
and are gone by t110, with `ptlogo_back2eff` and `ptlogo_back2` holding for the
rest of the screen. That decode was confirmed only against a **settled** frame:
posing at the settle time matched the console, which shows the *end state* is
right and says nothing about whether the flashes ever happen.
[`ui-record-loop-length.md`](ui-record-loop-length.md) decodes the `PRESS Ⓐ`
glow as a **120-unit cycle** containing a 105-unit ramp that peaks at **alpha 80**.
Both are predictions about the running game. This is the run.
## 🔴 First, the trap that nearly produced a false negative
The obvious instrument — match a bound texture's dimensions to a decoded sprite's
**does not work, and fails silently in both directions.**
A first pass reported that none of the five flashes are ever drawn. It also
reported `ptbase2` (640×360) and `pteff04` (1280×720) drawn during frames 75105.
Both were wrong. Those frames are the **intro movie**: four full-screen quads per
frame sampling a pair of 640×360 planes and a 1280×720 target — a video decode
whose plane sizes happen to collide with two sprite sizes. And the flashes were
missing because the title's sprites **sample large shared texture pages**, so the
bound texture identifies a page, not an element.
Canary's own capture code says so in a comment, and the corpus had already
recorded that the settled title's textures are all 1280×768. **The identity of a
2D draw here is its vertex geometry, not its texture.** Re-run against the quad
rects, everything appears.
## The structure of the title in the draw stream
| frames | draws/frame | what |
|---|---|---|
| 2105 | 35 | splashes, then the intro movie |
| **107** | **27** | 🔴 the title **composited**, once |
| 109167 | 6 → 14 | the build-in |
| 1681217 | 1011 | the settled title |
| 1218 | 45 | back to the attract loop |
The settled screen is 1011 draws that never name a sprite; the composite happens
in a single 27-draw frame. **A capture armed at the title therefore sees nothing**
which is why `ARM=early` exists.
## The flashes are real, and they are transient
Matching quads by design-space rect over frames 100260, and converting frames to
keyframe units with **2.346 units/frame — derived from the glow's period alone,
a different element**, taking the composite spike (frame 107) as t=0:
| element | drawn in frames | → t units | decoded |
|---|---|---|---|
| `ptlogo_back2eff1` | **130131** | **54.0 56.3** | flash, peak **t5456** ✅ |
| `ptlogo_back2eff2` | **133** | **61.0** | flash, peak t5860 |
| `ptlogo_back2eff3` | **never** | — | flash, peak t6264 ⚠️ |
| `ptlogo_back2eff4` | **133135** | 61.0 65.7 | flash, peak **t~64** ✅ |
| `ptlogo_back2eff` / `eff5` | 134260 | 63.3 … | 255 at t6466, **holds** ✅ |
| `ptlogo_back2` | 134260 | 63.3 … | 255 at t80, **holds** ✅ |
| `ptlogo1` | **125**148 | **42.2** | stops moving at **t42** ✅ |
**The flashes fire inside a six-frame window and are absent from every one of the
other 155 frames sampled.** The two holders are present continuously from frame
134 onward. That is the decoded mechanism, observed.
⚠️ **`eff3` was never drawn, and that is expected rather than a miss.** A flash's
peak is 2 keyframe units, and at 2.346 units per presented frame that is **0.85 of
a frame** — sub-frame. Whether any given flash is sampled at all depends on phase.
Four of five were caught here; a second run would likely catch a different four.
🔴 **A port that draws all five every time will show more of this sweep than the
console does.**
## The glow's ramp, read out of the guest
The plate glow's quad carries a **per-vertex colour whose alpha is the element's
fade alpha**, so the ramp can be read directly rather than inferred from pixels.
* **Observed alpha range: 0 … 80. Decoded peak: 80.** Exact, and not fitted.
* **Period: 51.158 presented frames**, from the first to the last of **20
consecutive cycle starts** (individual periods 4953).
* The draw is **omitted entirely** while the glow is dark — which is what the
11↔10 draw alternation in the settled title is.
Fitting the decoded 8-keyframe ramp to the 972 measured frames, with **one
disclosed free parameter** (a constant phase offset):
| curve | RMS residual, alpha levels |
|---|---|
| **the decoded ramp**, phase +2.25 units (= **+0.96 frames**, sub-frame) | **13.16** |
| the decoded ramp, no phase fit | 13.68 |
| a symmetric triangle of the same period and peak | 15.73 |
| flat at the mean | 31.13 |
| 🔴 **the decoded ramp REVERSED** | **38.18** |
The reversal control is the one that matters: if the shape carried no
information, forwards and backwards would fit equally. They differ by **2.9×**, so
the measured ramp has the decoded ramp's asymmetry — fast rise, slow fall — in the
decoded direction.
⚠️ The +0.96-frame phase is the expected bias, not a correction: a cycle start is
detected at the first frame with a non-zero alpha, and the decoded curve leaves
zero part-way through a frame.
## Reach and what is not settled
⚠️ **One run, one machine.** 2.346 units per presented frame is *this run's*
pacing, not a property of the game. The internal ratios are what transfer.
**The absolute frame rate of this run was not measured** — Canary logged no fps
and the log has no timestamps — so nothing here is stated in seconds. It did not
need to be: every comparison above is a ratio of measured quantities.
**`ptlogo_tm` was never matched.** At 37×17 it is below the rect tolerance used;
absence here is not evidence.
⚠️ **`eff5` and `ptlogo_back2eff` are the same rect at the same position and were
not separated.** Distinguishing them needs the per-vertex alpha, which the decode
says differs — not attempted.
## Reproducing
```bash
FRAMES=9000 MAXDRAWS=5000000 ARM=early tools/re-capture/ui_draw_capture.sh 900 /tmp/uicap
python3 tools/re-capture/buildin_timeline.py /tmp/uicap/xenia_re_ui_draws_01.log
```