A human on a GPU at ~140 fps: "the logos just switch, there is no animation at
all." Measured from a real boot with --film at 0.05 s, then per-frame change:
splash moves 1.30 s of 7.95 s = 16.4 %
publisher splash 0.30 s of motion, then 3.20 s FROZEN
developer splash 0.35 s + 0.25 s, then 2.40 s FROZEN
distinct luma states in 7.95 s 26
A 45-unit build-in cannot be drawn in 26 states, and a fade does not hold one
picture for 3.20 s. The frame counter says 24.8 fps achieved; both are true --
the port is DRAWING 25 times a second and CHANGING almost never.
🔴 Why every check passed, which matters more than the bug:
frozen sweep drives the clock BY HAND -- proves the renderer can draw
pose N, never that the poses are drawn in sequence
settled compare 0.01 % against the capture -- a screen frozen 84 % of the
time matches a settled reference PERFECTLY, that is what
frozen means
achieved fps counts frames DRAWN -- the same pixels 25x/s scores
identically to animating
Every one measured throughput or a pose. None measured CHANGE. Same shape as
InputEventAction bypassing the input map: the instrument sat below the thing
that was broken, so the break could not appear in it.
tools/motion-census closes the class. It measures change and nothing else, and
its --selftest asserts it separates a fade (97.4 % moving) from a switch (2.6 %)
from a frozen film (0.0 %) -- a detector that cannot tell those apart would
report the same green line on all three.
Both briefs: this is the SOLE focus. The port reproduces before changing
anything and gates every fix on a film rather than a still. The decoder maps the
whole pipeline end to end -- disc bytes, the game's per-frame update (does it
interpolate between keyframes or hold?), what is submitted per frame, and what
Canary does to it before a capture records it -- delivered as a SERIES, not a
settled value.
The port should also record the refutation against itself: H2 reads ANSWERED on
the strength of the frozen sweep. The mechanism half stands, the blur is a baked
companion texture. The behaviour half does not.
115 lines
5.5 KiB
Markdown
115 lines
5.5 KiB
Markdown
# Play-test, 2026-09-02 — **the splash does not animate at all**
|
||
|
||
**This is the only thing either agent works on until it is fixed.**
|
||
|
||
The human, watching the port on real hardware, on a GPU, at ~140 fps:
|
||
|
||
> *"The port does no blur animation at all! I cannot discern if there is any
|
||
> animation at all. The logos just switch without the animation."*
|
||
|
||
## Measured, not paraphrased
|
||
|
||
Filmed from a **real boot** at 0.05 s (`--film`), then per-frame change measured
|
||
with [`tools/motion-census`](../../tools/motion-census):
|
||
|
||
| | |
|
||
|---|---|
|
||
| splash moves | **1.30 s of 7.95 s — 16.4 %** |
|
||
| publisher splash | 0.30 s of motion, then **3.20 s frozen** |
|
||
| developer splash | 0.35 s + 0.25 s, then **2.40 s frozen** |
|
||
| distinct luma states in 7.95 s | **26** |
|
||
|
||
A 45-unit build-in cannot be drawn in 26 states, and a fade does not hold one
|
||
picture for 3.20 s. **This is a switch with a flicker on either side.**
|
||
|
||
The frame counter says 24.8 fps achieved. Both are true: the port is *drawing*
|
||
25 times a second and *changing* almost never.
|
||
|
||
## 🔴 Why three instruments all said it was correct
|
||
|
||
This is the part that matters more than the bug, because it is the fourth time
|
||
this shape has cost a milestone.
|
||
|
||
| instrument | what it proved | what it could not see |
|
||
|---|---|---|
|
||
| **frozen sweep** (`--time=`, 3 units a step) | the renderer CAN draw pose *N* | whether the poses are ever drawn **in sequence, while running** |
|
||
| **settled comparison** (0.01 % against the capture) | the resting pose is right | a screen frozen 84 % of the time matches a settled reference **perfectly — that is what frozen means** |
|
||
| **achieved-fps counter** | frames are being drawn | drawing the **same pixels** 25×/s scores identically to animating |
|
||
|
||
> **Every one measured throughput or a pose. Not one measured CHANGE.**
|
||
|
||
That is why the port could write *"the companion quads are drawn, verified by a
|
||
frozen sweep"* and be simultaneously right and useless: the sweep drives the
|
||
clock by hand. It is the same defect as `InputEventAction` bypassing the input
|
||
map — **the instrument sat below the thing that was broken**, so the thing that
|
||
was broken could not appear in it.
|
||
|
||
`tools/motion-census` exists to close this class. It measures change and nothing
|
||
else, and its `--selftest` proves it separates a fade (97.4 % moving) from a
|
||
switch (2.6 %) from a frozen film (0.0 %) — because a detector that cannot tell
|
||
those apart would report the same green line on all three.
|
||
|
||
## What both agents do now
|
||
|
||
**Nothing else.** Not the clock rate, not the plate, not blend, not audio. This
|
||
first.
|
||
|
||
### Port
|
||
|
||
1. **Reproduce it** with `--film` + `motion-census` before changing anything, and
|
||
quote the numbers. If your run does not reproduce 16.4 %, say so — the
|
||
disagreement is then the finding.
|
||
2. **Find why the clock does not advance the poses.** Candidates, unranked and
|
||
none established: the keyframe interpolation returns the same pose for a
|
||
range of *t*; `rest()`/plateau logic snapping to an endpoint; the group clock
|
||
not integrating; interpolation between keyframes not happening at all
|
||
(nearest-keyframe rather than lerp); the screen advancing by *keyframe index*
|
||
instead of by time.
|
||
3. **Every fix is gated by a film**, never by a still. A change that improves a
|
||
settled frame and leaves the film at 16 % has not fixed this.
|
||
4. `motion-census` goes into `check-all`, so a future regression fails a check
|
||
instead of waiting for a human.
|
||
|
||
### Decoder — **map the whole graphics pipeline, end to end**
|
||
|
||
The human's instruction, in their words: *"get the whole graphics pipeline, from
|
||
the xex/pe + the disc files to the final screen displayed, and take Xenia Canary
|
||
processing into account too."*
|
||
|
||
So: one continuous account, each stage with evidence and each labelled by the
|
||
instrument that produced it —
|
||
|
||
```
|
||
disc bytes → RATC/T8aD decode → what the GAME CODE does per frame
|
||
→ the draw calls it submits → Canary's own processing
|
||
→ the presented frame
|
||
```
|
||
|
||
Specifically, and none of it inferable from a file alone:
|
||
|
||
* **The game's per-frame update.** Which code advances a UI group's clock, in
|
||
what units, and what it does *between* keyframes — does it interpolate, or
|
||
hold to the next key? That single question decides whether the port should
|
||
lerp at all. It is in the image; find the function.
|
||
* **What the game submits per frame during the splash** — the draw list frame by
|
||
frame, not one settled frame. If the alpha changes, it changes *somewhere*
|
||
observable: a vertex colour, a PS constant, a blend factor, a texture swap.
|
||
**Name which, with the per-frame series.**
|
||
* **What Canary does to it.** Present cadence, any resolve/scaling/gamma between
|
||
the guest's draw and the pixels a capture records. A capture is evidence about
|
||
*Canary's output*, and the difference between that and the guest's intent has
|
||
bitten this corpus before (the `kernel_display_gamma_type` entry).
|
||
|
||
⚠️ **Deliver a per-frame SERIES, not a settled value.** Follow
|
||
[`TEMPORAL-VERIFICATION.md`](TEMPORAL-VERIFICATION.md): film it, align by
|
||
content, report ordering/counts/durations. The port needs to know what the
|
||
alpha *trajectory* is, and a single frame cannot carry one.
|
||
|
||
## And this is a refutation the port should record against itself
|
||
|
||
`BLOCKED.md` H2 currently reads ✅ **ANSWERED**, on the strength of the frozen
|
||
sweep. The mechanism half stands — the blur is a baked companion texture, that
|
||
is decoded and correct. **The behaviour half does not**: the port draws those
|
||
quads and does not animate them, so *"the companions are drawn"* was true and
|
||
did not mean what the row used it to mean.
|