# Photon-latency bench: feed-start → panels emitting

2026-06-12, HFENDULEAM, Beyond firmware 0.3.19, NVIDIA 610.47. The first
camera-instrumented measurement of how long the Beyond takes from "software
starts feeding video" to "panels actually emit light" on a cold DirectMode
bringup — and the discovery of how prox gates emission along the way.

## Result

**First Present → panels emitting: 3.1–4.8 s (n=5, median ≈ 3.2 s).**
Process start (INIT_START) → photons: ≈ 4.0–5.7 s.

| Stage | Time |
|---|---|
| Host acquire + modeset + POWER_ON (all NVAPI calls return OK) | ~1.0–1.4 s |
| Firmware/VXR first reports a video mode | +1.5–2.7 s after first Present |
| Link-retrain bounce (status word drops to 0, recovers) | observed in **every** trial |
| DSC enabled | ~0.8–1.0 s after first video detect |
| `DISPLAYS_ON` (photons, camera-confirmed) | +50–100 ms after DSC |

The host stack is *not* the bottleneck: the host believes it is presenting at
75 Hz (zero present errors, healthy vsync waitable) for ~3.5 s before any
photon exists. The cost sits in firmware/VXR video-mode detection and DSC
settling, including a consistent mid-train drop-out/retrain.

Panel-off on teardown is fast: dark ~0.1–0.2 s after the DirectMode release.

Per-trial raw numbers (FIRST_DRAW → DISPLAYS_ON): 3.06, 3.07, ~3.2 (camera-
derived), 3.59, 4.77 s. The spread tracks the retrain-bounce timing.

## Warm parked→wake baseline (the number doze must beat)

2026-06-12, same rig. `panel_bench cycle` mode: cold-init once, then loop
warm → `requestPanelPower(false)` (POWER_OFF, park) → dwell → `requestPanelPower(true)`
(SetDisplayMode + POWER_ON, wake), stamping `WAKE_REQUEST` → firmware
`DISPLAYS_ON` per cycle. This is the *parked* wake path of ADR-0003 (host
POWER_OFF), the case people hit on every resume.

**Warm parked→wake: ~4–6 s (4.07, 6.21 s; n=2 clean). NOT cheaper than cold
(~4.1 s this run).**

| Wake | WAKE_REQUEST → DISPLAYS_ON |
|---|---|
| Cycle 1 | 4074 ms |
| Cycle 2 | 6207 ms |
| Cycle 3 | not captured — 32 s run budget ended before disp=1 |

**Park buys nothing on wake.** POWER_OFF drops the panel *with the video
signal*, so POWER_ON re-runs the full panel re-init + DSC/link retrain — the
same bounce as cold. It is visible in the status word on every wake:

```
0x0000 → 0x2310 → 0x0000 → 0x2310 → 0x2318 (DSC) → 0x2319 (DISPLAYS_ON)
```

The mid-train drop to `0x0000` and recovery is the retrain bounce from the
cold-path table above — it dominates (~4–6 s, nondeterministic). Host-side
cost is negligible: the present loop's 50 ms park-poll + 50 ms telemetry
period sum to <100 ms against a 4–6 s wake. So the number is firmware/link,
not host.

This is the distinction the cold-path corollary (below) hinges on: keeping the
DirectMode **acquisition** alive is not enough — `requestPanelPower` keeps the
acquisition and still pays the retrain, because POWER_OFF stops **scanout**.
Doze's whole bet is to never POWER_OFF: keep scanout running (present black),
keep the link + DSC locked, and darken in firmware (brightness sweep → reg 0,
then OLED display-off). Wake then skips the retrain entirely → ~340 ms target,
**~12–18× faster than this parked baseline**. Validates ADR-0005.

Measurement caveats: cycle-3 wake was clipped by the 32 s run budget (bump
`panel_bench cycle 3 2 2` or fewer cycles to capture all three); n=2 is enough
to establish the order of magnitude but not the variance — the 4 vs 6 s spread
tracks retrain-bounce timing, same as the cold n=5 spread.

## Doze wake — the payoff (validated 2026-06-12)

Same rig, headset flashed with the doze firmware (beyond_synaptics 0.4.0,
ADR-0005). `panel_bench doze` mode: bring DirectMode video up, then drive the
firmware doze pair over HID — `'H'` display-sleep / `'h'` display-wake — while
the host **keeps presenting** (no `POWER_OFF`, link never torn down). Stamps
`DOZE_SLEEP_REQUEST` / `WAKE_REQUEST` and measures `WAKE_REQUEST` → firmware
`DISPLAYS_ON`; the optic-cavity camera gives the photon onset.

**Doze wake: ~94 ms to display-on (93.2, 94.7 ms); camera photon onset
~110–310 ms. vs the ~4–6 s parked baseline — a 45–65× win.**

| | Parked (POWER_OFF) | Doze (firmware `'H'`/`'h'`) |
|---|---|---|
| `WAKE_REQUEST` → `DISPLAYS_ON` | 4074, 6207 ms | **93.2, 94.7 ms** |
| Camera photon onset | ~4–6 s (with retrain) | **~110–310 ms** (125 ms frames) |
| Status-word transition | `0x0000↔0x2310` retrain thrash | clean `0x2318↔0x2319`, **DSC stays set** |
| DSC / link | de-locks, full retrain | **never de-locks** |
| Present loop during sleep | drops to 0 fps | **holds 75 fps** (video stays warm) |
| Errors | — | 0 present / 0 free-run / 0 displayLost |

The status word is the proof of mechanism. Parked wake thrashes
`0x0000 → 0x2310 → 0x0000 → … → 0x2319` (the DSC/link retrain bounce). Doze
wake is a single `0x2318 → 0x2319` step — bit3 (`DSC_ENABLED`) **stays set the
whole time**; only bit0 (`DISPLAYS_ON`) toggles. Doze never POWER_OFFs, so the
VXR never de-locks and the wake skips the retrain entirely. That bounce was the
whole 4–6 s cost (see the parked-baseline section), and doze sidesteps it.

Camera (optic-cavity luma, `left-optic` region): the doze fade is a smooth
~600 ms ramp 21 → 7.5 (the firmware brightness sweep to register 0, then OLED
display-off → true black) — it *looks* asleep, not a hard cut. Wake is a
single-frame step dark → lit then a short ramp to full (~400 ms), photon onset
within ~1–2 camera frames of the request. Both wakes consistent.

Caveat: `DISPLAYS_ON` rises when the firmware re-enables the OLED display
(~display-on + telemetry resolution); full brightness follows over the ~300 ms
wake sweep, so the felt wake is ~100 ms to first light, ~400 ms to full. Still
> 10× under the parked floor on every measure.

**Eased sweep (firmware 0.4.1, validated 2026-06-12).** The 0.4.0 sweep was
linear in register space; the camera caught the consequence — a register-linear
ramp reads as a fast jump + slow crawl because the OLED is perceptually gamma'd
(wake luma `7.5 →17.1→18.5→19.2→19.8→20.5`, i.e. ~71 % of the range in the
first 125 ms frame, then a long tail). 0.4.1 gamma-maps the written register
(`DOZE_SWEEP_GAMMA`, default 2.2) while keeping the sweep *intent* linear (so
the target-seeking reversal stays trivial). Re-measured: wake luma
`7.4 →9.9→14.9→17.6→18.6→19.8→21.0` — the rise is now spread evenly across
~6 frames (first-frame delta dropped from +9.6 to +2.5). Wake *latency* is
unchanged (display-on still ~46–94 ms); only the perceived ramp evened out.

## Two findings that outrank the number

**1. Prox gates *emission*, not scanout.** With the headset unworn, a cold
bringup produces a fully healthy link — 75 fps presents, vsync pacing, zero
free-run symptom — and **zero photons**. The firmware (`video_proc.c`) holds
the OLEDs dark until the prox says worn (or prox gating is disabled). This is
a different mechanism from the known doff-kill (worn→away transition taking
the video signal and vsync down): at cold start with prox=away, vsync never
dies, so **free-run detection cannot see "user sees nothing"**. Relevant to
ADR-0003.

Corollary for instant-on UX: if scanout is already running while the panels
are prox-dark, the firmware lights them autonomously within its video-proc
loop (~50 ms) of the prox triggering. The 3–5 s penalty is paid only when the
host must re-acquire/modeset. Keeping the acquisition + scanout alive while
"asleep" would make don-wake photon latency effectively firmware-instant;
the trade is link power while parked.

**2. The MCU telemetry carries a host-visible "photons" ground truth.**
Telemetry bytes 24–25 (`video_get_display_status`): bit0 `DISPLAYS_ON`
(panels emitting), bit1 `PROX_ON`, bit3 `DSC_ENABLED`, high nibble byte 0 =
video mode, byte 1 = DP link rate/lanes. `DISPLAYS_ON` agreed with an
external camera watching the optic to within one ~125 ms video frame. Exposed
as `McuProx::displayStatus()` / `displaysOn()`. This is a better instrument
than inferring panel state from vsync symptoms.

## Prox bypass (bench rigs only)

Firmware debug commands on the control HID (35BD:0101, feature report id 0,
single byte): `'p'` = prox disabled, firmware believes worn, displays on
regardless; `'['` = restore normal gating. `'p'` **persists until `'['` or a
power cycle** — always restore. Exposed as `McuProx::setProxBypass(bool)`;
`panel_bench` sends `'p'` before init and always restores on exit.

## Method (reproducible)

Rig: phone camera → VDO.Ninja → Chrome window on the host, framing the Beyond
(face toward camera) and a desktop monitor together. Both the headset glow and
an on-screen wallclock travel the same camera/feed path, so feed latency
(~0.3 s measured) cancels exactly when reading the clock out of the frame.

1. `tools/bench_timer.html` fullscreen on the visible monitor
   (`msedge --guest --kiosk file:///...` — guest mode kills the sync popup).
   Giant wallclock; seconds+tenths survive the camera feed, hundredths blur.
2. `panel_bench.exe <seconds>` (hard 30 s cap — full-white OLED safety):
   left eye white, right black; prints `INIT_START` / `INIT_DONE` /
   `FIRST_DRAW` wallclock-ms milestones and stamps every display-status
   transition (`DSTAT_CHANGE`, 50 ms telemetry resolution).
3. screen-timelapse MCP repository capture of the VDO.Ninja window @ 125 ms
   across the run. Frames land in `.screen-timelapse/frames/<sessionId>/`;
   `startedAt` + per-frame `elapsed_ms` give wallclock per frame.
4. Photon detection: mean luminance over the optic-cavity region of each
   frame. The step is unambiguous (≈28 dark → ≈152 lit at default
   brightness 266/1023). Cross-check: read the timer digits in the first lit
   frame; compare with the `DSTAT_CHANGE disp=1` stamp.

Caveat: a window-targeted capture does NOT include overlay windows (e.g. a
ShareX pin marking the watch region) — only a desktop capture shows those.
