Files
Sylpheed/docs/re/present-rate-instrument-failed.md
sylph-decoder 14fced07b6 re: I cannot measure this emulator's clock -- and that answers the port's 2%
The port put two of my pages against each other: settle->plate 2.135 s and one
focus-ring revolution 2.177 s, both a declared 120 units during a static hold,
2% apart against a 6 ms run-to-run agreement. Fair challenge.

The resolution is that the question assumes a stable wall clock. Same interval,
same container, same day: 2.138, 2.132, and 2.549 s -- a 19% swing, caused by
adding --log_ui_draws=true. The 2% is a fifth of that. The two pages were never
in conflict about the game; they are three readings of one declared quantity
through a clock that moves. What settles the quantity is the disc.

Wall clock cannot separate the hypotheses, so I tried to measure frames instead.
Both instruments are recorded as failures rather than published as numbers:

  * Canary's own [UI-CAP] counter -- the one that produced the corpus's 28.5 fps
    -- costs a third of the frame rate. 300 frames in 16.567 s = 18.11 fps on a
    screen that gives ~28 without it. That reclassifies 28.5 as a load-dependent
    lower bound; it does not overturn it.
  * A distinct-frame counter over the spinning ring FAILED its decisive control:
    15.88 fps against the game's own 17.59 in the same window, 10% low, so the
    ring does not change on every presented frame. Its static control also read
    2.63 instead of ~0. Dead, not tuneable, per METHOD.md.

The rule that follows, and it applies to everything I hand the port: a measured
interval landing near a round number of declared units almost certainly IS that
number of units. Ship the units.

Also recovered here, because the same question needed it: the static PPC route.
Four tools open /work/xenia-rs/sylpheed.db and nothing in this repository builds
it -- no disassembler, no PPC decoder, and default.xex is encrypted (zero
plaintext "GamePart"). Xenia decompresses the image at load, so dump_image.py
reads it out of guest memory and validates it against the corpus's own landmarks:
the 29-entry GamePart id table at 0x820A1630 and the Xbox 360 D3D runtime
strings. String search and table dumps work again; instruction-level work does
not, and the present interval I wanted is an immediate, not a string.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014voBspJ6kFncNErZJuZcLw
2026-08-29 13:16:02 +00:00

5.7 KiB
Raw Permalink Blame History

🔴 I cannot measure this emulator's presentation rate — and the 2 % between two of my pages is not a disagreement about the game

Status: one 🔴 instrument failure (recorded, not published as a number), and one resolution of a challenge that follows from it. 2026-08-29.

The challenge

The port put two of my pages against each other. Both measure the same declared quantity — 120 keyframe units of wall clock during a static hold, in Canary — and they differ by 2 %:

page measured implied presentation
title-plate-delay-measured.md, settle→plate, 2 runs 2.138, 2.132 → 2.135 s 28.10 fps
focus-ring-spin-measured.md, 7 spacings 2.16 … 2.20 → 2.177 s 27.56 fps

0.042 s apart — seven times the 6 ms run-to-run agreement the plate page rests on. Its own corroboration argument ("the build-in is where frames are dropped; the static hold is not") is aimed at exactly this, and these are two static holds. Fair challenge.

The resolution: 2 % is far inside this emulator's own variation

The question assumes the wall clock is stable enough for 2 % to mean something. It is not, and the counterexample is the same interval, in the same container, on the same day:

run conditions settle → plate
1 8 fps grab 2.138 s
2 8 fps grab 2.132 s
3 8 fps grab + --log_ui_draws=true 2.549 s

A 19 % swing on the declared interval, from a logging flag. The 2 % the two pages differ by is a fifth of that. They were taken in different sessions under different load, and nothing in either can separate "the game timed it differently" from "the emulator ran slower" — because both are wall clock.

So the two pages were never in conflict about the game. They are three readings of one declared quantity through a clock that moves. What settles the quantity is the disc: t=118 → t=238 is 120 units, and the port's own structural rule for the ring (two keyframes differing only by a 360° rotation, first timed and second untimed — 16/212 elements matched, all of them focus rings) gives the ring's period the same way.

⚠️ This removes the evidence that the ring is not 120 units. It does not prove that it is. The disc-side rule does that, and it is the port's, not mine.

🔴 The instrument I built to answer it properly, and why it is dead

Wall clock cannot separate the two hypotheses; frames can. So I tried to measure the presentation rate.

Canary's own frame counter perturbs by a third

--log_ui_draws=true --ui_draw_capture_frames=N logs [UI-CAP] capture armed and [UI-CAP] done: D draws over F frames. This is the instrument that produced the corpus's 28.5 fps (ui-keyframe-time-unit.md).

Measured here, armed on the title with a concurrent 8 fps grab:

300 frames in 16.567 s = 18.11 fps, against ~28 for the same screen without it.

⚠️ A frame counter that costs a third of the frame rate cannot measure the frame rate. This does not overturn the 28.5 fps — that run had no concurrent grab — but it does mean the figure is a lower bound taken under its own instrument's load, and it should not be treated as the rate.

And the unperturbing replacement FAILED its own decisive control

The alternative: count distinct frames in an oversampled crop of something that moves every frame (the spinning focus ring). At 60 fps against a source presenting at R, the fraction of consecutive samples that differ is R/60.

Three controls were written before the run. It failed the one that matters:

control result
a static crop must read ≈ 0 2.63 fps of "change" — not clean
two sampling rates must agree 45 fps → 12.73, 60 fps → 12.08
must agree with the game's own counter while both run counter 15.88 vs [UI-CAP] 17.5910 % low 🔴

The third is decisive and it is a failure: the ring does not change on every presented frame, so the counter measures the ring's animation rate, not the presentation rate. It also drifted 12.1 → 15.9 → 18.1 across one session, which a real rate estimator on a settled screen should not do.

Dead, not tuneable — per METHOD.md. No rate is published from it. Controls preserved: data/present-rate-controls-2026-08-29.json.

What this means for the port, and for everything I hand over

Do not take a wall-clock duration off this container as a game constant. Demonstrated range for one declared interval: 2.13 2.55 s, and the emulator's own rate read anywhere from 12 to 28 fps depending on what was watching it.

The rule that follows: a measured interval landing near a round number of declared units almost certainly IS that number of units, and the units are what to ship. Wall clock is for ordering and for sanity, not for constants.

Reach

  • One container, one day, one machine. It says nothing about how a different host runs Canary, and nothing about hardware.
  • It does not refute the 28.5 fps in ui-keyframe-time-unit.md; it reclassifies it as a load-dependent lower bound.
  • The game's true update rate is still not grounded in the disc. "2 units per submitted frame" is grounded — it was read off a frame-indexed draw capture, so it is independent of how fast the emulator runs. What rests on the emulator is only the step from a submitted frame to 1/30 s, i.e. the present interval. That is a constant in the executable, and reading it is blocked on the missing disassembly route (static-route-recovered.md).