diff --git a/docs/re/guest-frame-rate-preregistration.md b/docs/re/guest-frame-rate-preregistration.md new file mode 100644 index 00000000..4a8543c3 --- /dev/null +++ b/docs/re/guest-frame-rate-preregistration.md @@ -0,0 +1,113 @@ +# Pre-registration — is the guest 30 fps or 60 fps? The movie is the ruler + +**Status: ❔ open, and this page is written BEFORE the capture.** It exists so the +number below cannot be chosen after the fact. +[`METHOD.md`](METHOD.md) and +[`../agents/TEMPORAL-VERIFICATION.md`](../agents/TEMPORAL-VERIFICATION.md) both +require the expected value to be stated first; this corpus has two withdrawn +findings from not doing it. 2026-09-01. + +--- + +## The one number the port is still blocked on + +Everything else about the `PRESS Ⓐ` plate is settled. `HANDOFF.md` §H3 states it +plainly: + +> *"**Units per second is still open**, and it is now the only place the +> disagreement lives. `units/s = (units/frame) × (guest frames/s)`. This pins the +> first at **2**. The second is untouched."* + +| | units/s | plate at `t = 236` | +|---|---|---| +| **H_A** — guest presents at 30 fps | 60 | **3.93 s** ← what the port ships | +| **H_B** — guest presents at 60 fps | 120 | **1.97 s** | + +The play-test says the port's plate is **late**, and H_B is 1.96 s earlier — +*"about two seconds early"* is the size of what was reported. So this is not a +tie-break between two tidy numbers; one of them is the reported defect. + +⚠️ **Why no capture so far has settled it.** Canary presents at ~27–28 fps and +runs the guest slower than real time by an unknown factor. A wall-clock rate off +this emulator therefore cannot tell *a 30 Hz guest at full speed* from *a 60 Hz +guest at half speed* — they produce the identical observation. Both existing +measurements (2.13 s and 0.73 s for the same interval, **2.9× apart**) are +wall-clock readings, which is why they disagree. + +## The ruler: a disc fact that is not a wall clock + +`ADV.wmv` — the boot intro and the attract movie, the same asset +([`movie-binding.md`](movie-binding.md)) — declares its video rate in its own +ASF header: + +``` +/disc/dat/movie/ADV.wmv + FileProperties play duration 142.714 s, preroll 5.000 s -> net 137.714 s + StreamProperties video, 1280x720, fourcc WMV3 + ExtendedStreamProperties stream #2 avgTimePerFrame = 333333 x100ns + -> 30.0000 fps EXACTLY +``` + +(Stream #1 is the audio; its `avgTimePerFrame` of 3 276 559 is a packet rate and +is not a video rate. Reading it as one gives a nonsensical 3.05 fps — noted +because it is the obvious way to get this wrong.) + +**30.000 fps is a property of the shipped file**, not of a run. A decoded movie +frame is therefore a tick of a clock that Canary's speed cannot stretch: however +slowly the emulator runs, the guest still decodes 30 movie frames per second *of +movie time*, and movie time is what the movie was authored in. + +## The measurement + +During attract-movie playback, the guest triple-buffers the decoded YUV planes — +the splash census already saw them, and correctly excluded them: + +> *"six `640×360` textures and three `1280×720` ones … the attract **movie's** +> chroma and luma planes, triple-buffered, and they first appear at frame 234."* + +So: **count how many consecutive swap labels bind the same movie luma texture +base.** One decoded movie frame = one base change. + +## 🔴 The predictions, stated now + +| | labels per movie frame | movie-luma base changes per 100 labels | +|---|---|---| +| **H_A** guest 30 fps ⇒ **60 units/s** | **1.0** | **100** | +| **H_B** guest 60 fps ⇒ **120 units/s** | **2.0** | **50** | + +A factor of **two**, on a ratio of counts, with no wall clock anywhere in it. +I will accept H_A if the ratio is within `1.0 ± 0.15` and H_B within +`2.0 ± 0.30`, and report "neither" otherwise rather than picking the closer. + +## What would make this instrument lie, and the guard for each + +1. **The guest drops movie frames** to stay in sync with real time while Canary + runs slow. That *raises* labels-per-movie-frame and biases toward H_B — the + dangerous direction, because H_B is the answer I would find more interesting. + **Guard:** a dropped frame shows as a base persisting for an unusual run + length. I will report the **distribution** of run lengths, not the mean. H_A + is a spike at 1; H_B is a spike at 2; frame-dropping is a smear with a tail. +2. **The base cycles for a reason other than a new frame** (e.g. re-binding the + same buffer). **Guard:** the buffers are triple-buffered, so a clean cycle + visits three distinct bases in a fixed order. I will check the order is a + 3-cycle before counting anything. +3. **The movie is not playing at its declared rate** because the guest is + frame-locked to its own presentation instead of to the movie clock. This is + the assumption the whole method rests on and it is **not** guarded — if the + run-length distribution is clean but disagrees with both predictions, that is + the likely cause, and the finding is then "undecodable by this route". + +## Control, to be run before the measurement is believed + +The same log carries the **boot splashes** (frames 4…226), where the alpha step +is already established at **+34 per presented frame** on six quads. If the +recovered log does not reproduce 34, the capture is not comparable to the one +that number came from and nothing else in it should be read. + +## Reach, in advance + +⟨capture⟩ for the ratio; ⟨disc⟩ for the 30.000 fps. If it comes out clean, the +result is **measured**, not decoded: it is a property of how this game drives its +own clock, observed once. It would want a second, independent boot before the +port bakes it in — and if it says H_B, it says the port's plate is 1.96 s late +and that is worth a second run before anyone rewrites a timeline.