re: RETRACT "the game never draws eff3" -- a batched draw merged two quads

The console draws all five title flashes. My claim that ptlogo_back2eff3 is
never drawn was an instrument artefact, and I had reported it to the port with
three alternative explanations "ruled out".

A GPU draw can batch several quads -- indices=4 is one, indices=8 two,
indices=24 six -- and the UI draw log dumps only the first 8 vertices. Taking
min/max over a line's whole vertex list merges quads into one box.

eff3 is batched with eff4, and because the wipe family is right-aligned, eff3
(788..1196) lies ENTIRELY INSIDE eff4 (447..1196). The union is exactly eff4's
own extent, so the merged box matched eff4 to 1 px, eff3 vanished, and nothing
looked wrong.

Parsed per quad, all five fire in both title entries in the declared stagger:
eff1 130-131, eff2 133, eff3 133-134, eff4 133-135, eff/eff5 134+, back2 136+;
and 5953-5955 / 5955-5957 / 5957-5958 / 5957-5959 / 5958+ / 5962+ in entry 2.
Frames 133 and 134 are t=60.1 and 62.3, inside eff3's declared t in (58,64).

Also retracts "the developer splash is one composited quad" -- the same bug,
which the port refuted by arithmetic first (a 259-tall box cannot contain three
logos spanning y 164..585). It draws three logos and three glows as separate
quads in one indices=24 call; the 525x259 was gamearts_eff merged with
seta_eff. The 9-unit black hold is unaffected: those glows are the developer
splash's first draw.

The three "ruled out" explanations were all aimed at the wrong failure. In
particular the invisible-draw check counted draws with NO geometry line, when
the hiding place was draws with PARTIAL geometry. Refuting three wrong
hypotheses is not evidence for a fourth, and a list of failure modes written by
whoever built the instrument is the least likely to contain its blind spot.
Recorded in METHOD.md, along with the tell that was present and explained away:
a merged box carries the first quad's colour, which made one element's alpha
read 255/127/254 on consecutive frames.

New tool: tools/re-capture/quads_per_frame.py parses vertices in groups of four
and warns when the logged quad count falls short of indices/4.

Also guards a double-A-tap in ui_draw_capture.sh: the movie branch ignored that
TARGET=menu had already tapped, so a run tapped A on the title at t=23s and
again at t=27s on the transition; the guest faulted and Xenia dumped registers
to stdout until the file reached 519 MB.

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 20:49:27 +00:00
parent 0577ccd43d
commit da46663c8a
7 changed files with 238 additions and 50 deletions

View File

@@ -58,15 +58,24 @@ the last publisher frame still carries alpha 7 and the first developer frame
alpha 34, so both boundary frames contain some picture that this count treats as
black.
## ✅ A second thing the draw stream shows: the developer splash is ONE quad
## 🔴 RETRACTED: "the developer splash is ONE composited quad"
The developer bundle declares three logos — `palogo_gamearts` at (390,164),
`palogo_seta` at (521,316), `palogo_anima` — and **none of their sizes is ever
submitted**. What is submitted is a single **525×259 quad at (378,155)**, which is
the bounding box of the three. The game composites them and blits the group.
**It is not.** The three logos and their glows are drawn as separate quads,
**batched into a single draw call**`indices=24` is six quads — and the log dumps
only the first 8 vertices. The "525×259 quad at (378,155)" was min/max taken across
two *different* quads: `palogo_gamearts_eff` (525×91 at 378,154) and
`palogo_seta_eff` (262×108 at 512,306).
This is why an earlier pass reported "developer splash: 0 frames" — it was
matching individual logo sizes that the game never draws.
The port refuted it with arithmetic before I had checked: a 259-tall box cannot
contain three logos spanning y 164…585, and `palogo_anima` alone starts 35 px below
its bottom edge. They were right, and they were right to keep drawing three.
⚠️ **The gap measurement is unaffected.** Those glows are the *first* thing the
developer splash draws (`t0 a0 → t15 a255`), so frame 130 is still the developer
screen's first drawn frame, and frames 126129 still submit nothing at all.
See [`ui-title-buildin-measured.md`](ui-title-buildin-measured.md) for the same
trap producing a worse error on the title.
## Is the gap declared anywhere? Not that I can find

View File

@@ -79,52 +79,58 @@ a different element**, taking the composite spike (frame 107) as t=0:
other 155 frames sampled.** The two holders are present continuously from frame
134 onward. That is the decoded mechanism, observed.
## 🔴 The game does not draw `ptlogo_back2eff3`
## 🔴🔴 RETRACTED: "the game does not draw `ptlogo_back2eff3`"
**Reproduced over two title entries, 5 800 frames apart, in one run.** The disc
declares `ptlogo_back2eff3` — alpha 0 at t58, **255 at t6062**, 0 at t64, at
(788, 117), sprite 408×203 — and the running game never submits it.
**It draws it. All five flashes fire, in both title entries, in exactly the
declared stagger.** The claim was an instrument artefact, and the instrument was
mine.
The wipe family is right-aligned: `eff2` 938+258, `eff3` 788+408, `eff4` 447+749,
`eff5` 64+1133 all end at x≈1196. It is a left-growing wipe in four widths, and
**the game draws three of them.**
| element | width | draws across both build-ins | expected at ~2.23 units/frame |
| element | entry 1 | entry 2 | declared |
|---|---|---|---|
| `eff1` | 167 | 4 | ~5 |
| `eff2` | 258 | 3 | ~5 |
| **`eff3`** | **408** | **0** | **~5** |
| `eff4` | 749 | 6 | ~5 |
| `ptlogo_back2eff1` | 130131 | 59535955 | flash t5458 |
| `ptlogo_back2eff2` | 133 | 59555957 | flash t5862 |
| **`ptlogo_back2eff3`** | **133134** | **59575958** | **flash t6064** |
| `ptlogo_back2eff4` | 133135 | 59575959 | flash t6266 |
| `ptlogo_back2eff` / `eff5` | 134 → | 5958 → | holds |
| `ptlogo_back2` | 136 → | 5962 → | holds |
⚠️ An earlier version of this page called this a **sub-frame phase effect**. That
was wrong and is withdrawn. Three explanations were tested and all fail:
Frames 133 and 134 sit at t = 60.1 and 62.3 — inside `eff3`'s declared t ∈ (58, 64).
The disc was right about every one of them.
* **Sampling phase.** `eff3`'s non-zero window is t ∈ (58, 64) — **six units**,
against a step of **2.23 units per presented frame**. A window wider than the
step *cannot* be missed: at least two frames must land inside it, and frames 133
(t = 60.1) and 134 (t = 62.3) do. Both draw `eff2` and `eff4`; neither draws
`eff3`.
* **A draw the log cannot see.** Exactly **2** draws per frame carry no geometry,
on all 932 settled title frames — always the same full-screen-triangle shader
(`prim=8 indices=3`, `vs=0x72CBCAA6A7984111`), present on frames where no wipe
element is active at all. `eff3` cannot be one of them.
* **A bad position guess on my side.** Dropping the position entirely and
searching *every* quad submitted anywhere on screen during both build-in
windows: **zero** have a width within ±30 of 408. The width spectrum jumps
straight from 262 to 748.
### Why it looked absent, and why the checks that "ruled that out" did not
**Why the game skips it is not established.** Nothing in its element record
distinguishes it — same kind (`0x0`), same keyframe shape, same `u4`/`u8`, same
scale, as `eff2` and `eff4` either side of it.
**A draw can batch several quads.** `indices=4` is one quad, `indices=8` is two,
`indices=24` is six — and the log dumps **only the first 8 vertices**. Taking
min/max over a line's whole vertex list therefore *merges* quads into a single
box.
🔴 **For the port: you draw `eff3` at t=6062 and the console does not.** That is
one extra frame of wipe, at the widest-but-one step. It is small, and it is real.
`eff3` is batched with `eff4` in an `indices=8` draw. And `eff3` (788…1196) sits
**entirely inside** `eff4`'s x-range (447…1196), because the wipe family is
right-aligned — so the union of the two is **exactly `eff4`'s own extent**. The
merged box matched `eff4` to 1 px, `eff3` disappeared, and nothing looked wrong.
🔴 **A second thing this page previously claimed, also withdrawn: that a port
drawing all five flashes shows "more sweep than the console".** There is no
evidence for that. The port draws them sequentially at their declared times, never
more than two at once, which is what frames 131135 above show the game doing.
The pile-up worth warning about was the `rest()` bug, and that is fixed.
Every check I ran was aimed at the wrong failure:
* "sampling phase" — correctly refuted, and irrelevant;
* "a draw the log cannot see" — I counted draws with **no** geometry line. The
hiding place was draws with **partial** geometry, which I never looked for;
* "a bad position guess" — I searched for a 408-wide box. The box did not exist
because it had been merged, not because the quad had not been drawn.
**Three refutations of the wrong hypothesis do not add up to one confirmation.**
`tools/re-capture/quads_per_frame.py` now parses vertices in groups of four, one
per quad, and warns when a batch exceeds the 8-vertex cap.
### What this also explains
`eff4`'s alpha read 255 / 127 / 254 on consecutive frames — non-monotonic, which I
flagged as "the vertex-alpha identity does not generalise". It was not the
identity failing; those were **merged boxes carrying the first quad's colour**.
🔴 **A second claim, also withdrawn: that a port drawing all five flashes shows
"more sweep than the console".** The opposite is true — the console draws all
five. Drawing them sequentially at their declared times is exactly right.
⚠️ **What a frame-by-frame comparison of the build-in WILL show, and it is not a
defect on either side.** The game's timeline is 60 units/s against a 30 Hz