From 8b05b6b692cbe9ed3d2e87e53267db94aa9c1d75 Mon Sep 17 00:00:00 2001 From: sylph-decoder Date: Tue, 1 Sep 2026 17:04:23 +0000 Subject: [PATCH] re: the animation clock is TIME-INTEGRATED -- one prediction held, one failed Reported against the pre-registration, failure first. HELD, and by a cleaner argument than the regression: the clock is NOT frame-counted. The same animation takes 21 frame labels in one capture and 33 in another, and splash A's logo steps +136,+34 in one and +17,+51,+34,+34,+17,+17 in the other. A fixed per-frame increment cannot do that. Steps are always integer multiples of 17 (255/15 = one unit), so the clock advances in whole units at a rate set by how long the frame took. That retires '2 units per submitted frame' as a MECHANISM -- including my own page from two hours ago. The measurement stands; 2 was that run's frame pacing. units = 2 x frames computes an emulator artefact. Recorded as a proposal against the register, not enacted, and noted at the head of the H3 page rather than by editing it. FAILED: predicted 60 units per guest second, accept 55-65. Measured median 29.9 over six elapsed-ratio estimates (25.2-36.6). Prediction 3 said 30 was excluded and 30 is what came out. Why the rate is not a number yet: units/s = (d_alpha/dt) * T/255, and T is the load-bearing term. d_alpha/dt is measured cleanly six ways. T=15 for these elements comes from a corpus row whose usual derivation is circular with the thing just retired -- a step of 34/frame implies T=15 only GIVEN 2 units/frame. T=30 would give ~60. No amount of re-measuring alpha settles it. Settles with ptbtn00, whose T=22 is attested by two independent readers with no clock anywhere in the chain. That capture did not reach the title in 531 s of attract loop against 243 s in the previous run -- the variable attract loop capture-harness-status.md already documents at up to 604 s. Instrument built and control-passed; one run is missing. Instrument: guest timebase, 50 MHz per emulator.cc:225, scalar 1.0 per clock.cc:37, so no host wall clock enters any number. Control: 123.24 guest seconds across a capture running ~118 wall seconds. Told the port to change NOTHING today: ~30 rests on an unverified T, and retiring 2 x frames does not by itself supply a replacement. --- docs/re/INDEX.md | 1 + docs/re/h3-units-per-frame-measured.md | 20 ++++ docs/re/units-per-second-measured.md | 128 +++++++++++++++++++++++++ 3 files changed, 149 insertions(+) create mode 100644 docs/re/units-per-second-measured.md diff --git a/docs/re/INDEX.md b/docs/re/INDEX.md index 2f8e40a8..50ab29c2 100644 --- a/docs/re/INDEX.md +++ b/docs/re/INDEX.md @@ -137,6 +137,7 @@ files, which is how the same ground got covered twice. | [`structures/stage-mission-tables.md`](structures/stage-mission-tables.md) | The stage table set — phases, routes, sub-objectives and AI parameters | ✅ the table set and how the stage record reaches it, validated across; **`AIParams` disc-wide: 23 objects, one shared 34-profile roster (782 records), loader `sub_8233C368`; `Type`→field-count holds except the two `_Test` templates** | | [`structures/texture-color-k8888.md`](structures/texture-color-k8888.md) | Texture colour interpretation — `k_8_8_8_8` (32bpp UI/HUD textures) | — | | [`ui-keyframe-time-unit.md`](ui-keyframe-time-unit.md) | What a keyframe time is worth, and what shape the ramp has | ✅ CONFIRMED from the running game's own draw stream — the ramp is **linear** (a declared 15-unit fade lands on `round(255·k/15)` for all seven samples) and the animation clock advances **2 time units per submitted frame**. 🟡 the seconds conversion (`1 unit = 1/60 s`) rests on a measured 27.6 present-frames/second | +| [`units-per-second-measured.md`](units-per-second-measured.md) + [pre-reg](units-per-second-preregistration.md) | Is the animation clock frame-counted or time-integrated, and at what rate | 🟡 **half-answered, 2026-09-01, one prediction HELD and one FAILED.** ✅ **The clock is NOT frame-counted**: the same animation takes 21 labels in one capture and 33 in another, and splash A's logo steps `+136,+34` in one and `+17,+51,+34,+34,+17,+17` in the other. Steps are always integer multiples of **17** (255/15 = one unit), so the clock advances in whole units at a rate set by frame duration. 📌 **This retires "2 units per submitted frame" as a MECHANISM** — 2 was that run's frame pacing, so `units = 2 × frames` computes an emulator artefact. ❌ **The rate FAILED its prediction**: predicted 60 units/guest-second, measured **median 29.9** (25.2–36.6) — but that rests on `T=15`, which I have not read off the disc and whose usual derivation is circular with the thing just retired. **Settles with the plate (`T=22`, attested by two readers with no clock in the chain) captured with tick stamps; that run did not reach the title in 531 s.** Instrument: guest timebase (50 MHz, scalar 1.0), control passed — 123.24 guest s over ~118 wall s. | | [`h3-units-per-frame-measured.md`](h3-units-per-frame-measured.md) | How many keyframe units elapse per guest frame, and which declared time the title's settle anchor is | ✅ **measured against a pre-registration, 2026-09-01. 2 units per guest frame**, on `ptbtn00`'s own declared ramp (`t=214→236`, T=22): three consecutive gap-free steps of **exactly 23** = 255×2/22, ramp span 10 labels against a predicted 11 (±1). The Port's inferred **5 is excluded by >2×**. 🔴 Why 5 appeared: **an alpha step is not a clock rate** — `Δα/frame = 255·(units/frame)/T`, so splash B's 34-with-T=15 and the plate's 23-with-T=22 are ONE clock. Also: the **title settle anchor is t≈160**, not t=118 — it is `ptcopyright`, the last build-in element and the only glyph one, full at label 5350 → t≈168–176. 🔴 **units/SECOND is NOT settled** and two of my own captures disagree ~2.9× on it; the gap is in frames→seconds, not units→frames. | | [`h3-units-per-frame-preregistration.md`](h3-units-per-frame-preregistration.md) | The prediction, committed before the capture was read | ✅ kept as the control on the row above | | [`input-pad-read-path.md`](input-pad-read-path.md) | What the game asks the console for, on the pad | ✅ **decoded from the image, 2026-09-01.** `sub_82457038` is the only function that reads controller *data*, and it compares **every** field of `XINPUT_GAMEPAD` — `wButtons` (full 16 bits), both triggers, all four stick axes — against the previous state. **14/14** loads verified byte-for-byte against `/image/sylpheed.pe`, database used as an index only. **Two input paths**: the polled state and an 8-byte-record `XamInputGetKeystrokeEx` ring. ⚠️ This is the **superset the game can see, not the per-screen set** — reading a field is not acting on it. ❔ Which bits each screen tests is open; footholds are the image's own `C_PAD_DECODER` / `C_PAD_RINGBUF` trace strings (`sub_8220B610` / `sub_821A6470`). | diff --git a/docs/re/h3-units-per-frame-measured.md b/docs/re/h3-units-per-frame-measured.md index 8ce782cb..d9e9ec26 100644 --- a/docs/re/h3-units-per-frame-measured.md +++ b/docs/re/h3-units-per-frame-measured.md @@ -7,6 +7,26 @@ Canary, per-draw. 2026-09-01. Answers the Port's `BLOCKED.md` H3 (`auto/port-p6-audio` at `6eccfa8`), both halves. Neither answer rests on a renderer of ours. + +> ## 🟡 WEAKENED THE SAME DAY, by my own next experiment — read this first +> +> **"2 units per guest frame" is a correct measurement and a wrong mechanism.** +> [`units-per-second-measured.md`](units-per-second-measured.md) shows the clock +> is **time-integrated, not frame-counted**: the same splash animation occupies +> **21 labels in one capture and 33 in another**, and splash A's logo steps +> `+136, +34` in one and `+17, +51, +34, +34, +17, +17` in the other. The three +> consecutive plate steps of exactly 23 below are real; **2** was a property of +> that run's frame pacing. +> +> **What still stands unchanged:** everything on this page that is a *ratio* or a +> *count* — the T-vs-step arithmetic that explains the Port's factor of 2.7, and +> the settle anchor at t≈160, which is derived from label counts *calibrated on +> the plate's own ramp within the same run* and so does not depend on the rate +> being constant between runs. +> +> **What must not be used:** `units = 2 × frames` as a conversion. It computes an +> emulator artefact. + --- ## The prediction, and what happened diff --git a/docs/re/units-per-second-measured.md b/docs/re/units-per-second-measured.md new file mode 100644 index 00000000..5a25ba8b --- /dev/null +++ b/docs/re/units-per-second-measured.md @@ -0,0 +1,128 @@ +# Units per second — the clock is **time-integrated**, and the rate is **not yet a number** + +**Status: 🟡 half-answered.** One pre-registered prediction **held**, one +**failed**, and the failure is informative. Instrument: ⟨capture⟩ with +⟨canary-source⟩ for the clock. 2026-09-01. + +Against [`units-per-second-preregistration.md`](units-per-second-preregistration.md), +committed before the capture was taken. + +--- + +## What was predicted, and what happened + +| # | prediction | outcome | +|---|---|---| +| 1 | the clock is **time-integrated**, Δα correlates with guest frame duration, **r > 0.9** | **🟡 direction held, threshold missed.** r = **0.8396** over 19 rising steps spanning a **12.5× duration range** (16.4 → 204.6 ms). Positive and structured, but not r > 0.9 | +| 2 | the rate is **60 units per guest second**, accept 55–65 | **❌ FAILED.** Six elapsed-ratio estimates give a median of **29.9**, range 25.2–36.6 | +| 3 | 120 and 30 are both excluded | **❌ FAILED in the worst way** — 30 is what came out | + +**A failed prediction is the result.** It is written down before the explanation, +because the explanation below is new and untested and the number is not. + +## ✅ The part that IS settled: the clock is not frame-counted + +Prediction 1's threshold was missed but its *subject* is decided, and by a +cleaner argument than the regression: + +**The same animation takes a different number of frames in two captures.** + +| element | capture A (2026-09-01, first) | capture B (with tick stamps) | +|---|---|---| +| splash A's logo `Q0` rising steps | `+136, +34` | `+17, +51, +34, +34, +17, +17` | +| splash B's logo trio, labels present | `127…147` (21) | `115…147` (33) | + +A fixed per-frame increment cannot do that. The steps are always integer +multiples of **17** (= 255/15, one time unit), so the clock advances in **whole +units**, but *how many* per frame is whatever that frame took. + +📌 **This retires "2 units per submitted frame" as a description of the +mechanism.** The [H3 measurement](h3-units-per-frame-measured.md) is not wrong — +three consecutive plate steps really were exactly 23 = 2 units — but 2 was a +property of *that run's frame pacing*, not of the game. Anything the port +computes as `units = 2 × frames` is computing an emulator artefact. + +⚠️ **This is my own ✅ row weakened, from `ui-keyframe-time-unit.md` and from my +own page of two hours ago.** Recording it here rather than editing either, and +proposing rather than enacting a change to the register. + +## ❌ Why the rate is not a number yet, and it is a `T` problem + +``` +units per second = (Δα / Δt) × T / 255 +``` + +`Δα/Δt` is measured, cleanly, six ways: + +| quad | Δα | guest s | α/s | units/s **if T = 15** | +|---|---|---|---|---| +| splash A logo | 170 | 0.273 | 622.5 | 36.6 | +| splash B logo ×3 | 187 | 0.368 | 508.7 | 29.9 | +| splash B companion ×2 | 51 | 0.119 | 428.2 | 25.2 | + +**`T` is the load-bearing term and I have not read it off the disc myself.** +`T = 15` comes from `ui-keyframe-time-unit.md`'s ✅ row, and the way I used it +here is circular: that row's *shape* result (the ramp is linear, `round(255·k/15)` +fits) is independent, but a step of 34 per frame only implies `T = 15` **given** +2 units/frame — which is exactly the thing this page has just retired. + +If `T = 30` for these elements the rate is ~60. If `T = 15` it is ~30. **The +factor between the two answers is the same factor as the unknown**, so no amount +of re-measuring alpha settles it. + +## What settles it, and why the plate is the right element + +**`ptbtn00`.** Its `T = 22` is attested independently of any clock: the four +`(time, pose)` pairs `214/236/238/244` are read the same way by two different +readers — my own `screen info` dump on a different branch and the port's +exporter — differing only in the record association, which is ✅ decoded in +[`ui-keyframe-record-layout.md`](ui-keyframe-record-layout.md). Nothing in that +chain uses a clock. + +So: capture the plate's ramp **with the guest tick stamps**, take the elapsed +ratio over it, and `rate = (Δα/Δt) × 22 / 255` is the answer with no circularity. + +🔴 **That capture did not complete this iteration.** The run reached 531 s of +attract loop without presenting the title, against 243 s in the previous run — +which is the variable-attract-loop behaviour +[`capture-harness-status.md`](capture-harness-status.md) already documents at up +to 604 s. The instrument is built and verified; what is missing is one run that +gets there. + +## The instrument, and its control + +The draw logger now stamps every frame boundary with the **guest** timebase — +`Clock::QueryGuestTickCount()` at `guest_tick_frequency()` — so no host wall +clock enters any number above. `emulator.cc:225` sets that frequency to +**50 MHz** and `clock.cc:37` leaves `guest_time_scalar_` at **1.0** +(⟨canary-source⟩). + +**Control, run before trusting it:** the stamps span **123.24 guest seconds** +across a capture that had been running ~118 wall seconds at the time of reading. +Guest time tracks real time, as the source says it should. An instrument that +disagreed with its own source here would be dead. + +📌 And the guest frame rate is wildly non-uniform — **16.4 ms to 204.6 ms per +frame in one splash**. That is 12.5×, in a stretch a wall-clock instrument would +have averaged into a single meaningless "fps". It is also why prediction 1's +regression is honest but noisy: the steps are quantised to 17 and the residual +structure is real, with the long frames advancing **less** than a constant rate +predicts. + +🟡 **That residual is unexplained and is a candidate finding in itself** — a +clamped `dt`, a capped number of logic steps per frame, or a decoupled logic +tick would all produce it. Not tested. + +## What the port should do with this today + +**Nothing yet.** The 60 units/s constant is neither confirmed nor refuted: +this page's ~30 rests on a `T` I have not verified, and the argument that +retires `2 × frames` does not by itself supply a replacement. Changing it on +the strength of a failed prediction would be worse than leaving it. + +## Reach + +Two captures, the boot splashes in both, one title capture without stamps. The +"not frame-counted" conclusion rests on a **comparison between captures** and is +as strong as the two captures being of the same animation, which their element +rects and declared ramps make certain. The rate has no reach at all yet.