Files
Sylpheed/docs/re/units-per-second-preregistration.md
sylph-decoder 60b05bcd90 re: the game blends in the ENCODED space -- no gamma render target, anywhere
The Port asks whether the blend is evaluated on sRGB-encoded values or
linearised and re-encoded. It is answered by one register field I already
log, and the answer is the same on every draw of two full captures.

RB_COLOR_INFO.color_format is k_8_8_8_8 (0) on 2402/2402 splash draws and
33779/33791 draws of the boot-through-title capture; the other 12 are
k_32_FLOAT and k_16_16_FLOAT and are not colour passes. k_8_8_8_8_GAMMA (1)
appears ZERO times, and color_exp_bias is 0 everywhere so nothing stands in
for a gamma either.

The mechanism is Canary's own source, not our inference: k_8_8_8_8_GAMMA is
the ONLY colour format around which a piecewise-linear gamma<->linear
conversion is applied (spirv_shader_translator.h:510 PWLGammaToLinear /
LinearToPWLGamma, render_target_cache.h:720, dxbc_shader_translator_om.cc).
With k_8_8_8_8 there is none, so the blender operates on the stored values
as they are.

So a renderer that linearises before blending and re-encodes after is doing
a different operation -- and the difference is gamma-shaped and exactly zero
on unblended pixels, which is the divergence signature the port reports.

Also renamed my units-per-second pre-registration off the 'h4' prefix: the
Port's BLOCKED.md numbers this blend-space question H4 and two different H4s
in one corpus is how a citation goes wrong.
2026-09-01 17:01:11 +00:00

4.2 KiB
Raw Blame History

Units per SECOND: is the animation clock frame-counted or time-integrated?

Pre-registration. Written 2026-09-01 BEFORE the capture was taken, per ../agents/TEMPORAL-VERIFICATION.md. Committed first so the prediction cannot be edited afterwards.

The question, and why the last answer did not close it

h3-units-per-frame-measured.md pinned 2 declared units per guest frame. The port needs units per second, and units/s = (units/frame) × (frames/s). That is only a valid decomposition if the game's animation clock counts frames. If it integrates elapsed time, then units/second is the invariant, units/frame is an artefact of whatever rate the host managed, and multiplying by an assumed 30 fps is meaningless.

There is already evidence it is time-integrated, and it is the anomaly the last page had to quote as a caveat: across the empty label 5376 the plate's alpha moved +82 where three adjacent labels each moved +23. 82 / 23 = 3.57not an integer number of ticks. A fixed per-frame increment cannot produce a fractional multiple of itself. That is suggestive and it is one observation.

The instrument, and why it is not a wall clock

The draw logger now stamps every frame boundary with Clock::QueryGuestTickCount() and Clock::guest_tick_frequency() — the timebase the guest reads, 50 MHz (emulator.cc:225), with guest_time_scalar_ = 1.0 (clock.cc:37, unchanged by any flag this harness passes). A duration computed from those is the duration the game experienced, which is the only one its own integrator could have used. No host wall clock enters the calculation.

⟨canary-source⟩ for the scalar and the frequency; ⟨capture⟩ for everything measured with them.

The discriminator — within one run, no second capture needed

For every consecutive pair of frame labels on ptbtn00's ramp, compute the label's guest duration and the alpha step across it. Then:

if the clock is Δα vs guest duration what it means
frame-counted flat — Δα ≈ 23 whatever the frame took units/frame is the invariant; the port multiplies by an assumed fps
time-integrated proportional, through the origin units/second is the invariant; units/frame is not a property of the game at all

The slope of that line, if it is one, is the answer: slope × 22 / 255 = units per guest second.

Frame durations under this emulator vary by a factor of several — the previous capture's labels ran from nominal to 3.5× nominal — so the regressor has real dynamic range and this is not a null test.

The predictions, stated before the run

  1. The clock is time-integrated. Δα correlates with guest frame duration, r > 0.9.
  2. The rate is 60 units per guest second. I accept 5565. This is not independent: it is what the previous capture's run-average implies (27.2 labels/s × 2.02.25 units/label ≈ 5461), so it is a consistency prediction, not a blind one, and I say so. What is new is that this measurement is per-label rather than a run average, in guest time rather than host time, and carries a shape test the average cannot.
  3. 120 units/s and 30 units/s are both excluded if 1 and 2 hold.

If prediction 1 fails — Δα flat against duration — then the clock is frame-counted, units/s genuinely depends on the console's frame rate, and the +82 anomaly needs another explanation. That outcome is reportable and I will report it.

The control that removes the test's own subject

The plate's ramp is short. The same regression is run independently on ptcopyright's ramp in the same capture — a different element, a different declared segment, a different part of the screen. Two elements must give the same units/second under the time-integrated model and need not under any other. Disagreement between them fails the test regardless of either slope.

What this still will not answer

The console's frame rate. Even a confirmed 60 units/s says nothing about whether the retail game ran at 30 or 60 fps; it says the port should not need to know, because it should drive its clock in seconds and not in frames.