commit 8fc49b4af4a755acf9db8a53cdf6224c6cde7f5c
parent 7a04b49062d6c59e7a68ea132da01aa922096b11
Author: umhau <umhau@example.invalid>
Date: Wed, 21 Oct 2026 12:00:14 +0000
Docs: recovery queue, SkyLight main-thread wait, refuted release-in-callback, DATA1=0x04, current status
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat:
2 files changed, 30 insertions(+), 7 deletions(-)
diff --git a/NOTES.md b/NOTES.md
@@ -46,6 +46,20 @@ Run `tools/verify.sh` (boot-args + log summaries only; no ioreg/system_profiler)
before the byte order was fixed. With correct encoding HRST is rejected by the firmware (!CMD), DRST works.
- HID screen_off (0x04 0x00) reliably induces the stuck state; screen_on / full wake alone do not undo it.
+## App recovery path (2026-10-20, vr0 implementation, vr1 review)
+- macOS SkyLight blocks the app's MAIN thread for exactly ~10.0 s right after delivering a display REMOVE
+ (sample: CGSPostLocalNotification -> SLSWaitThroughReconfigurationCycleForcingNotificationHandling ->
+ connection_fetch_updated_event_queue). Not triggered by our code; IMU callbacks on main stall with it.
+- Fix: recovery state machine on its own serial queue, fed directly from the CG callback before the main hop;
+ rendering on a dedicated CAMetalDisplayLink thread. Forced outage 14.4 s -> 4.4 s.
+- REFUTED hypothesis (vr1): "the window server waits for us to release the captured display". CGDisplayRelease inside
+ the REMOVE callback returns 1001 (display already gone) and outages stayed ~4.6 s. Removed.
+- Helper result: DATA1 byte0 = 0x04 observed after a DRST whose IOReturn was 0 and after which the display returned.
+ Meaning of 0x04 unknown (TI documents 0 = success, 1 = timeout/abort, 3 = rejected). The helper conservatively
+ exits 1; the app relies on display ADD.
+- Remaining outage = 2 s grace (avoids resetting during 0.6-0.8 s self-heals) + ~2.2 s DRST/retrain/re-add.
+ Millisecond continuity would need the HPD-blip ride-through below (untested, needs a reboot).
+
## Candidate: ride through brief HPD drops (millisecond-level, no display REMOVE) — static, UNTESTED
DCP firmware (FUD/DCP.img4, t8103) AppleDCPDPTXController config, verified in disassembly:
- 0x56c0c: key `hot_unplug_delay_ms` -> boot-arg `appledptx-hot_unplug_delay_ms` (prefix + "-" + key, see above).
diff --git a/SETUP.md b/SETUP.md
@@ -70,10 +70,15 @@ zsh tools/build.sh # builds the app + the signe
- `tools/build.sh` builds the app with `swift build`, then compiles `tools/hpmctl.c` into the same directory as the app
executable (`swift build --show-bin-path`) and ad-hoc signs it with `tools/hpmctl.entitlements`
(`com.apple.USBCEntitlement`).
-- The app runs as a normal non-root user. When the headset display is lost and does not return within a short
+- The app runs as a normal non-root user. When the headset display is lost and does not return within a 2 s
grace period, it runs that helper directly to send a USB-PD Data Reset to port 2's controller. No sudo, no password:
the kernel admits root OR that entitlement, and AMFI being off (`amfi_get_out_of_my_way=1`) lets an ad-hoc signature
carry it. Limits: 3 s helper timeout, at most 3 resets per minute.
+- Recovery runs on its own serial queue, fed directly from the CoreGraphics display callback (before any main-thread
+ work), because macOS's SkyLight blocks the main thread for ~10 s during a display reconfiguration
+ (`SLSWaitThroughReconfigurationCycleForcingNotificationHandling`). Any headset display ADD cancels a pending reset.
+- The helper reports failure (exit 1) unless the controller's DATA1 result is 0. A result of 0x04 has been seen after
+ an accepted DRST that still restored the display; the app treats ADD, not the helper's exit code, as proof.
- The app captures the headset display (no desktop on it) and uses its native unscaled 4320x2160 @ 90 Hz mode.
If macOS changes the mode by itself, the app restores 90 Hz (also checked every 10 s, CoreGraphics only).
- Rendering runs on a dedicated "Headset Metal rendering" thread: a `CAMetalDisplayLink` on that thread's run loop
@@ -83,12 +88,16 @@ zsh tools/build.sh # builds the app + the signe
- Health line every 10 s in the app log: `submitted`, `presented` (only frames Metal reports with a real
presentation time), `skipped`, `fps` (presented per second), `presentAge`, `GPUms` per frame, `GPUerrors`, IMU sample
count. Healthy looks like `fps=90.00 presentAge=0.01s GPUerrors=0` with `skipped` not increasing after startup.
-- Status (2026-10-20): automatic recovery verified (3/3 trials at 90 Hz, then from a stuck state with the render
- worker). Render worker 35 s test: rendering/stats good (89.90 presented fps, presentAge 0.01 s, GPU 1.47 ms/frame,
- 0 errors, 12 skips at startup only) BUT the process crashed at shutdown (exit -11: double CAMetalDisplayLink
- invalidate in MetalRenderLoop.run), so the test is NOT counted as passed. The user confirmed the 3D scene inside
- the headset (twice). A 660 s soak with injected faults is in progress (`evidence/final-metal-worker-soak.txt`);
- not final proof until the shutdown crash is fixed.
+- Status (2026-10-20, ~[time redacted]):
+ - Stereo 3D output was confirmed in hardware testing.
+ - Shutdown crash (exit -11, double CAMetalDisplayLink invalidate) fixed; 2 clean-exit/restart trials exit 0.
+ - Fault soak (`evidence/final-metal-worker-soak.txt`, build before the recovery queue): 3 forced outages all
+ recovered; after them ~7 min untouched with zero drops, 90.00 presented fps, GPU ~1.5 ms/frame, 0 GPU errors.
+ - Forced-outage duration (display lost -> back): 14.4 s before the recovery queue, 4.4 s with it
+ (= 2 s grace + ~2.2 s reset/retrain/re-add). Releasing the capture inside the callback did not help
+ (CGDisplayRelease returns 1001 there; outages 4.6 s) and was removed.
+ - Every DRST is followed ~5.7 s later by a brief lane-sync blip (IRQ_HPD/INTERLANE retrain) that the app recaptures.
+ - A no-fault 660 s soak at 90 Hz is in progress; not yet final proof.
## How the boot-arg was found
`amfi_get_out_of_my_way` controls code signing, not HDCP, so we searched this Mac's own kernel: