4 Commits

Author SHA1 Message Date
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
sylph-decoder
1a2f4fdc2d re: the boot splash black gap is ~9 units, measured in draws not luminance
The port found its boot had no black frame between the publisher and developer
splashes and authored 12 units 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.

I had agreed with the dismissal that hid the defect: told 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. The real gap was 0.2 s.

Measured in the draw stream, which separates true black from a fade tail where
luminance cannot: palogo_sqex is drawn to frame 125 at alpha 7, frames 126-129
submit NO sprite quad at all, and the developer fades in at frame 130 from
alpha 34. Four presented frames, the only such run in the sequence.

Converted with the disc as its own clock rather than a frame rate -- this run
presented at 13.1 fps against 28 elsewhere -- palogo_sqex declares alpha >= 1
for 239.8 units and is drawn in 105 frames, giving 2.284 units per presented
frame, which the title capture independently corroborates at 2.231. So the gap
is ~9.1 units (0.152 s), against the 12 authored; +-1 frame is 6.9-11.4. And
the true black is SHORTER, since both boundary frames still carry picture.

Second finding: the developer splash is ONE composited 525x259 quad at the
bounding box of its three declared logos, none of whose individual sizes is
ever submitted. That is why an earlier pass reported "developer splash: 0
frames".

Declaration sites ruled out: the splash bundles (no fade quad) and the
top-level +0x08 (a family constant, 300/60, slack 12-226 units). The
executable is NOT looked at and is named as the next place rather than
claimed.

Also answers the port's sweep question: +0x08 canNOT settle it, because
ptloop01/ptloop02 have zero slack and a zero-slack record cannot distinguish
"loops" from "runs once and stops". The oracle settles it for the TITLE -- the
sweep oscillates over its whole range and resets hard to the same start, once
in dwell 1 and twice in dwell 2, so it does not park. The MENU is unmeasured
and stays open.

Tooling: GRACE and NOTAP knobs for ui_draw_capture.sh. The script taps A on
"the screen changed a lot", which is also true of a fading splash -- a first
run tapped through the publisher and the developer never appeared. The
instrument was perturbing what it measured.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsEPXWVaEpyfudtR6re1Pd
2026-08-29 20:36:15 +00:00
Sylpheed RE agent
ceb8c8b572 tools: capture targets, and a Z-aware draw-order decoder
`ui_draw_capture.sh` grows three knobs the second iteration needed:

* ARM=early presses F10 before the title exists, so a long window contains the
  frames in which a screen is BUILT (it turns out none are — the title screen
  submits the same 11 draws every frame and never rebuilds);
* TARGET=menu taps A once on the title and arms on the main menu, skipping
  attract movies on the way — it does not get there, but the blocker it hits is
  documented rather than worked around;
* EXTRA_FLAGS passes emulator cvars through (--create_profile_if_none,
  --mem_watch=false, --log_level).

`ui_draw_order.py` follows the capture's new vertex format (x, y, z) and reports
the Z it now has.
2026-08-18 20:15:48 +00:00
Sylpheed RE agent
81b391d8d5 tools: drive and decode a UI draw-order capture
`ui_draw_capture.sh` boots to the title screen and arms Canary's new
`log_ui_draws` there — deliberately WITHOUT tapping A on the title, which is the
subject and which sends the guest into a save-data probe. ARM=early presses F10
before the title exists, for the frames in which a screen is built. A grab that
is not full-width is a hard error rather than something to classify.

`ui_draw_order.py` turns the capture into a named paint order: the UI shader
emits NDC, so a quad's pixel rect is exact, and the disc's sprites have
near-unique decoded sizes, so the rect's SIZE names the sprite. Matching is
nearest-within-6px with the distance printed, because a quad comes back a few
pixels under its sprite for a reason that is not yet measured.
2026-08-18 18:57:43 +00:00