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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jc4pciRArGHfxGGhEbwp5t
This commit is contained in:
92
docs/re/clock-rate-follows-the-vblank.md
Normal file
92
docs/re/clock-rate-follows-the-vblank.md
Normal file
@@ -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.
|
||||
Reference in New Issue
Block a user