NV3D Vision Restoration Project

Connecting the actual emitter

Hardware update 2026-09-15: both emitters have arrived. The RP2040 is flashed, its approved WinUSB driver is installed, and the app’s connection check passes. A ten-second 120 Hz command test recorded 1200 opens and closes; the user subsequently confirmed good OLED 120 Hz stereo. LCD/QLED separation and OLED 240 Hz still need optical testing. See RP2040 setup and lower-refresh experiments. The instructions below describe the original NVIDIA emitter; do not use its RAM firmware loader to flash an RP2040.

With no driver, Windows lists the emitter as USB\VID_0955&PID_7003, bus description “NVIDIA stereo controller”, problem code 28. The app uses live Windows PnP discovery to find that driverless boot identity; 0955:0007 is the runtime identity after firmware upload.

  1. Connect the USB IR emitter directly to the PC. Open Glasses check → Refresh USB. The supported protocol profile recognizes 0955:0007 (runtime) and 0955:7003 (boot state); other NVIDIA devices are listed but receive no writes.
  2. This backend uses WinUSB via libusb, not NVIDIA’s old stereoscopic graphics driver. The application ships an x64 libwdi bootstrap and generates, signs and installs an exact-device WinUSB package for 0955:7003 and 0955:0007 on each target PC. Approve the UAC prompt for each identity the first time it appears. No machine-specific instance, USB port, GPU identity, local path or oem*.inf name is copied between PCs.
  3. Press Connect emitter. Runtime descriptors must expose bulk OUT endpoints 1 and 2 in alternate setting zero. Unknown endpoint layouts are rejected.
  4. A boot interface with no endpoints needs matching RAM firmware. Download NVIDIA’s standalone USB controller package 390.41, run the portable release’s Prepare NVIDIA Firmware.cmd, and select that downloaded EXE directly. The included extractor opens the package without running NVIDIA’s installer, validates the firmware and writes emitter.fw beside the app. It also accepts an already extracted nvstusb*.sys. No Quadro GPU or separate archive-tool installation is required. Source builders can manually extract the SYS from NV3DVisionUSB.Driver using 7-Zip and use the low-level command below. Proprietary firmware is not distributed publicly.
  5. Verified 2026-09-12: after RAM upload the emitter does not re-enumerate on its own. The application finds the active 0955:7003 PnP instance, requests an elevated Windows PnP restart, waits for 0955:0007, and reconnects automatically. Port moves use live PnP discovery and the portable installer for both identities. An unplug loses RAM firmware and starts the boot state again; the app reloads it without resetting the stereo session or calibration.
  6. Start the 120 Hz glasses test. Confirm shutter activity through the actual lenses, then eye order, phase and duration. A successful USB transfer is not a successful optical test.

The application detects and connects the emitter automatically, including after unplug/replug. USB device-change messages no longer close fullscreen stereo or discard calibration. Calibration changes autosave immediately and are not tied to window focus. When 0955:7003 returns on any port, the app installs WinUSB if necessary, reloads the volatile RAM firmware, restarts to 0955:0007, and resumes shutter commands.

CLI extraction (output must not exist):

.\build\bin\Release\vision_firmware.exe 'path\nvstusb.sys' 'path\emitter.fw'

Protocol and scheduling

What the emitter firmware actually does (disassembled 2026-09-13)

See the app/emitter timing correction for the corrected timer signs, eye parity and regression evidence.

The RAM firmware (8051 on the Cypress FX2) does not execute eye commands directly. Verified from the disassembly of STUFF/emitter.fw:

The internal packet layout follows libnvstusb. Timing configuration goes to endpoint 2, explicit left/right commands to endpoint 1. Initialization, watchdog and enable/disable registers are kept internal. The W timing register retains the upstream constant because its physical meaning is not established. The X/Y register interpretation must be verified on this hardware before treating UI microseconds as calibrated optical values.

A dedicated worker handles USB; bulk transfers have a 20 ms timeout and stop cancellation. Firmware control writes are bounded to 200 ms each and checked between blocks. The presentation thread does not wait for USB. Pending triggers are replaced rather than accumulated; late replacements/rejections are counted. A trigger already handed to USB cannot be retracted atomically with a new presentation or stop request.

At startup, use equal 1500 µs durations and phase zero only as exploratory defaults. They are not claimed to fit either display. The application predicts presentation time and submits the USB eye command at that deadline. Feedback detects missed predictions; USB latency and optical timing remain unmeasured.

Do not install a legacy graphics driver or flash persistent firmware. The loader only supports the upstream volatile RAM record format and CPU reset/release addresses.

Schedule change 2026-09-14: shortest X first

nvidiaSchedule used to hold X at 1300 us and move the boundary distance; X only left its centre when the boundary would leave [1000, period - 1000]. The window could therefore never be wider than period - 2300 - band correction - 1000 us guard, which at the 120 Hz emitter rate of a four-slot sequence is 4980 us, less than a slow panel’s refresh plus scan. The schedule now starts from X = 300 us and moves the boundary; X grows only when the boundary would leave its range (over about a quarter of the phase circle, by up to 2000 us), and there the shutter is shortened to fit the period (nvidiaEffectiveShutterUs; the Live tab reports it). The requested window start is unchanged for every phase, so tuned profiles keep their optical position; only the split between boundary distance and X differs.