Files
Sylpheed/docs/re/structures/boot-splash-gap-measured.md
sylph-decoder 47faeaa7a9 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
2026-08-29 20:49:27 +00:00

120 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# The black gap between the boot splashes — measured in draws, not luminance
**Classification: measured.** Xenia Canary, `ui_draw_capture.sh GRACE=1 NOTAP=1
ARM=early`, 2026-08-29. Evidence:
[`boot-splash-gap-draws.csv`](../data/boot-splash-gap-draws.csv).
## Why this was open
The port found that its boot had **no black frame at all** between the publisher
and developer splashes, and authored **12 units** (0.200 s) by analogy with the
menus' transition quad. On the boot path that analogy has nothing behind it:
`palogo_eff0.prm` is a **single static keyframe**, so the splash bundles declare
no fade quad at all.
🔴 **And I had agreed with the dismissal that hid it.** Told that the residual was
0.03 s against a bound built from two measured ranges plus jitter slack, I said it
said more about the bound than the game. It did not — the underlying gap was 0.2 s
and the port had been missing it since P3. A plausible explanation for a small
number is exactly how a real defect stays hidden.
## The measurement
Luminance cannot separate the outgoing screen's fade tail from true black. The
**draw stream** can: it says exactly which frames submit a sprite quad at all.
| frames | what is submitted |
|---|---|
| 2 20 | `palogo_sqex_eff` (685×90) — the publisher's glow |
| 21 125 | `palogo_sqex` (666×65) — the publisher wordmark, fading to alpha **7** |
| **126 129** | 🔴 **nothing — 4 frames with no sprite quad at all** |
| 130 153 | the developer splash, fading in from alpha **34** |
**The gap is 4 presented frames**, and it is the only such run anywhere between
the first and last sprite of the sequence.
## Converting it without a frame rate
The run's presented rate is not usable — measured at **13.1 fps** while the title
was up, against the corpus's 28 fps for other runs, and it is not stable enough to
convert a 4-frame interval.
So the **disc's own timeline is the clock**. `palogo_sqex` declares alpha ≥ 1 from
t ≈ 15.06 to t ≈ 254.9 — **239.8 units** — and is drawn in **105 frames**:
> **2.284 units per presented frame**, from the same screen in the same capture.
> (The title capture, independently, gave 2.231.)
| | units | seconds at 60 units/s |
|---|---|---|
| if the gap were 3 frames | 6.9 | 0.114 |
| **measured — 4 frames** | **9.1** | **0.152** |
| if the gap were 5 frames | 11.4 | 0.190 |
| *the port's authored value* | *12* | *0.200* |
**The measured gap is ~9 units, and 12 is at or just past the top of the
quantisation range.** ⚠️ And the true black is *shorter* than this, not longer:
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.
## 🔴 RETRACTED: "the developer splash is ONE composited quad"
**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).
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
***In the splash bundles.** `palogo_eff0.prm` is one static keyframe
(`t0 a255`). No fade quad, no gap.
***In the top-level header `+0x08`.** It is a **family constant** — 300 for
every title/splash entry in `GP_TITLE`, 60 for the loading bundles — and the
slack against each bundle's last keyframe ranges from **12 to 226 units**
(the main menu's is 220, i.e. 3.7 s). It cannot be a declared gap.
***In the executable — not looked at.** The corpus has the boot phase machine
at `this+132`; whether a dwell or gap constant sits near it is untested. That is
the next place, and I am naming it rather than claiming reach I do not have.
So the port is right to author this, and should author **~9 units** rather than 12.
## Reach
⚠️ **One boot, one machine.** The 4-frame count is quantised and the ±1 frame is
the dominant uncertainty (6.9 11.4 units).
⚠️ The units-per-frame calibration assumes the guest's animation clock advances
uniformly across the gap, which is the same assumption the rest of this corpus's
frame→unit conversions make.
## 🔴 The instrument was perturbing what it measured
`ui_draw_capture.sh` taps Ⓐ whenever the screen changes a lot, to skip the attract
movie. **That trigger is also true while a boot splash is fading.** A first run
classified the publisher splash as a movie at t = 3 s, tapped through it, and the
developer splash never appeared. Two knobs now exist and a boot run needs both:
* `GRACE=1` — the fixed 8 s wait before arming meant an `ARM=early` capture always
missed both splashes, which run at ~1.2 9.5 s of guest time;
* `NOTAP=1` — no input at all.
## Reproducing
```bash
GRACE=1 NOTAP=1 FRAMES=9000 MAXDRAWS=5000000 ARM=early \
tools/re-capture/ui_draw_capture.sh 300 /tmp/uicap-boot
```