From befcb747807e7b94862503e68e7f34103cb3dac2 Mon Sep 17 00:00:00 2001 From: sylph-decoder Date: Wed, 2 Sep 2026 15:05:03 +0000 Subject: [PATCH] re: the clock rate follows the VBLANK -- reconciling three pages that disagreed h3 says 2 units per guest frame, units-per-second-measured says time-integrated at 56.8 units/s, and my own page says 1 unit per present. All three rest on real data and one mechanism explains all of them: the clock advances one unit per VBLANK, and presents can be dropped without the clock caring. That accounts for the corpus's steps of +17/+34/+51 (1, 2, 3 vblanks between two logged presents), for the same animation spanning 21 labels in one capture and 33 in another -- which a strict per-present clock cannot produce -- for h3's three plate steps of 23, and for my own 14 consecutive steps of 17 in a run that dropped almost nothing. My "1 unit per present" was nearly right and named the wrong clock. At --framerate_limit=30 Xenia vblanks at 30 Hz and the guest presents ~30/s, so presents and vblanks coincide and the test could not tell them apart. It correctly refuted time-integration; it could not locate the tick. Solid: the RATE follows the vblank rate. 255 declared units take 4.26/4.16 s at a 60 Hz vblank and 8.45 s at 30 Hz -- 59.8/61.3 against 30.2 units/s. A time-integrated clock predicts 4.25 s in both. So a console at 60 Hz gives 60 units/s, and the port's value stands on a third independent route. Not solid: per-vblank vs per-present. My step samples are ~12 per run, which cannot separate them, and I say so rather than reading 18% vs 0% of two events as support. The load-bearing observation for the mechanism is the corpus's 21-vs-33, not mine. The discriminating experiment is named and not run: log Xenia's D1MODE_V_COUNTER beside each present and check every step is exactly 17 x (vblanks elapsed). Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Jc4pciRArGHfxGGhEbwp5t --- docs/re/clock-rate-follows-the-vblank.md | 92 ++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 docs/re/clock-rate-follows-the-vblank.md diff --git a/docs/re/clock-rate-follows-the-vblank.md b/docs/re/clock-rate-follows-the-vblank.md new file mode 100644 index 00000000..74b6fed6 --- /dev/null +++ b/docs/re/clock-rate-follows-the-vblank.md @@ -0,0 +1,92 @@ +# The clock rate follows the **vblank rate** β€” and that reconciles three pages that disagreed + +**Status: βœ… measured for the rate; 🟑 for the mechanism, with the discriminating +test named and my own sample declared underpowered.** 2026-09-01. Instrument: +⟨capture⟩ Γ—3, one at a forced frame rate. + +--- + +## The disagreement this resolves + +Three pages held incompatible positions on how the UI clock advances: + +| page | says | +|---|---| +| [`h3-units-per-frame-measured.md`](h3-units-per-frame-measured.md) | **2 units per guest frame** (later self-weakened to 🟑) | +| [`units-per-second-measured.md`](units-per-second-measured.md) | **time-integrated at 56.8 units/s** β€” "how many per frame is whatever that frame took" | +| [`clock-is-frame-based-one-unit-per-present.md`](clock-is-frame-based-one-unit-per-present.md) (mine) | **1 unit per present**, from a forced-frame-rate test | + +Each rests on real data. **All three are explained by one mechanism.** + +## The reconciliation + +> **The clock advances one unit per VBLANK. Presents can be dropped; the clock +> does not care.** + +| observation | page | explained by | +|---|---|---| +| steps `+17, +51, +34, +34, +17, +17` β€” always multiples of 17 | `units-per-second` | 1, 3, 2, 2, 1, 1 **vblanks** between two *logged presents* | +| the same animation spans **21 labels in one capture and 33 in another** | `units-per-second` | different drop rates. A strict per-present clock cannot do this; a per-vblank clock must | +| three consecutive plate steps of exactly **23** (2 units, T=22) | `h3` | a run dropping every other present | +| **14 consecutive steps of exactly 17** | mine | a run dropping almost none | +| splash dwell **4.26 s** at a 60 Hz vblank, **8.45 s** at `--framerate_limit=30` | mine | the vblank *rate* sets the unit rate: 59.9 and 30.2 units/s | + +πŸ“Œ **My "1 unit per present" was nearly right and named the wrong clock.** At +`--framerate_limit=30` Xenia vblanks at 30 Hz and the guest presents ~30 times a +second, so presents and vblanks coincide and the test could not tell them apart. +It correctly refuted *time-integration*; it could not locate the tick. + +## What is solid, and what is not + +βœ… **Solid β€” the rate follows the vblank rate.** Two forced conditions: + +| vblank | splash dwell, 255 declared units | β‡’ units/s | +|---|---|---| +| 60 Hz (default) | **4.263 / 4.162 s** | **59.8 / 61.3** | +| 30 Hz (`--framerate_limit=30`) | **8.450 s** | **30.2** | + +A time-integrated clock predicts 4.25 s in *both*. It doubled. **So the rate is +set by the vblank, and on a console β€” which vblanks at 60 Hz β€” that is +60 units/s.** The port's value stands, and this is the third independent route to +it. + +🟑 **Not solid β€” per-vblank vs per-present**, because my own step samples cannot +separate them: + +``` +hashcap 52.2 presents/s +17 x13 (100%) +boot2 55.1 presents/s +17 x9 (82%) +34 x2 (18%) +fr30 28.7 presents/s +17 x11 (92%) +34 x1 ( 8%) +``` + +**Twelve-odd steps per run is far too few.** At 52–55 presents/s under a 60 Hz +vblank a per-vblank clock predicts ~10 % of intervals spanning two vblanks; boot2's +18 % and hashcap's 0 % straddle that, and both are two events either way. I am +**not** claiming the distribution supports the model β€” it is consistent with it and +powerless against the alternative. + +⚠️ **The per-vblank model is favoured by the corpus's data, not mine.** The +21-vs-33 label observation is the load-bearing one, and it is +`units-per-second-measured.md`'s, not this page's. A strict per-present clock +makes that impossible. + +## The discriminating experiment, not run + +Log the **vblank counter** beside each present in the draw logger β€” Xenia +increments `D1MODE_V_COUNTER` in `MarkVblank()` β€” and read the step against +*vblanks elapsed* rather than presents elapsed. If every step is exactly +`17 Γ— (vblanks since last present)`, the mechanism is decoded rather than inferred. +That is a small addition to `tools/canary-patches/`, and it would settle a +question three pages have now circled. + +## What this changes for the port + +**Nothing.** 60 units/s stands, now reached three ways. What changes is the +*reason*: not "2 units per frame", not "time-integrated at 56.8", but "one unit +per 60 Hz vblank, and a console vblanks at 60". + +## Reach + +⟨capture⟩ Γ—3, one machine, English locale, at two vblank rates. The rate result is +a manipulation; the mechanism is a synthesis of other pages' observations with +mine, and is 🟑 until the vblank counter is logged.