Handover documentation for continuing on another computer

- docs/steamframe/README.md: entry point with status, branches/remotes, setup,
  commands, Steam Frame facts, open points.
- CLAUDE.md: working rules and code conventions (fork-only).
- docs/steamframe/OVRAS-FEATURES.md: OVRAS's features, how they are
  implemented, issues found while reading.
- PROGRESS.md: status and wrap-up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Matthias Janetschek
2026-10-07 14:57:20 +02:00
co-authored by Claude Opus 5.5
parent 3ed686dec5
commit 7c5a9a7a1e
4 changed files with 475 additions and 0 deletions
+197
View File
@@ -0,0 +1,197 @@
# OVRAS: features and how they are implemented
Analysis of 2026-10-07, based on upstream `master` (`31df8b4`, 5.8.17). Line numbers refer to
that state. `qt6` has the same features, so the numbers are close. Paths are relative to
`src/`. Which of these features SteamVR now offers itself is in
[REQUIREMENTS.md](REQUIREMENTS.md), "Feature classification".
## Architecture
- **Dashboard overlay:**
- `VR_Init(VRApplication_Overlay)`, app key `steam.overlay.1009850`.
- QML UI (1200×800) rendered off-screen via `QQuickRenderControl`.
- The texture goes to `SetOverlayTexture`. On `steamframe` the render backends in `render/`
do this; before, it was an OpenGL FBO, and on `qt6` QRhi.
- **Input:** SteamVR mouse/scroll events are turned into Qt events (`overlaycontroller.cpp`,
`mainEventLoop()`).
- **Main loop (`OnTimeoutPumpEvents` → `mainEventLoop()`):**
- Timer; "vsync" mode once per compositor frame (`GetTimeSinceLastVsync`).
- Per tick:
1. input actions
2. VR events
3. poses
4. `eventLoopTick` of the tab controllers (always)
5. `dashboardLoopTick` (only while the dashboard is visible)
- **SteamVR Input:** `package_files/action_manifest.json`, 5 action sets:
- `motion`: space drag/turn, gravity, snap/smooth turn, height toggle, offsets, auto-turn
- `music`: media keys
- `misc`: axis locks, chaperone toggle, keyboard shortcuts
- `system`: push-to-talk, exclusive input, haptic clicks
- `haptic`: vibration, proximity sensor
Default bindings exist for Vive, Index, WMR, HP, Touch, generic HMD and Frame controllers.
- **Persistence:**
- App settings in a `QSettings` INI file (`settings/`). Profiles are stored as arrays
(`ISettingsObject`).
- SteamVR's own settings go directly through `vr::VRSettings()` (`openvr/ovr_settings_wrapper`).
- **Platform:**
- Windows: WASAPI / `IPolicyConfig`, `SendInput`.
- Linux: PulseAudio, X11 XTest, D-Bus MPRIS.
- Elsewhere: dummies.
## Features
### Playspace movement (`tabcontrollers/MoveCenterTabController.cpp`)
Almost everything shifts or rotates the **working standing zero pose** through
`IVRChaperoneSetup` and shows it live with `ShowWorkingSetPreview()`. Nothing is committed
per frame. This mechanism does **not work on the Frame** (inside-out tracking).
- **Core, `updateSpace()` (:2497–2721):**
- Offsets X/Y/Z and the rotation (centidegrees) are multiplied with the stored basis.
- The result goes to `SetWorkingStandingZeroPoseToRawTrackingPose`, in seated mode
additionally to the seated pose.
- Safety limit: about 40 km.
- **Space drag:**
- The controller's position delta, multiplied by `dragMult`, is added to the offsets.
- Axis locks; the speed is kept for fling.
- Comfort mode skips frames.
- **Space turn:** the change of the yaw angle via quaternions
(`q·conj(q_last)`, `quaternion/quaternion.h`). Rotation pivots on the HMD by default.
- **Snap/smooth turn:** fixed angle per press resp. per tick, so smooth turn depends on the
frame rate.
- **Gravity/momentum:** Euler integration with friction, terminal velocity 50 m/s, landing on
the floor, reversible.
- **Height toggle:** Y offset plus a new gravity floor.
- **Offset profiles, apply/reset:**
- "Apply" commits with `CommitWorkingCopy(Live)` (limit 990 m).
- `zeroOffsets()` / `updateChaperoneResetData()` also commit at start-up. On the Frame
this shifts the play area, hence R7.
### Rotation (`tabcontrollers/RotationTabController.cpp`, via `setRotation`)
- **Auto-turn:**
- Rotates the space when the user gets near a wall, either as a snap or smoothly.
- Direction: from the corner shared with the last wall, otherwise cord untangling (HMD
total rotation count).
- **Vestibular motion:** rotation as if walking on a circle of radius r
(`2·asin(d/2r)`).
- **View ratchetting:** amplifies head turns away from the wall.
### Fix floor / recenter (`tabcontrollers/FixFloorTabController.cpp`)
- **Fix floor:**
- Measures the lower controller over 25 frames.
- Subtracts the controller thickness per model and orientation (Vive / Knuckles).
- Commits the result as a Y offset; undo is possible.
- **Recenter:** `VRChaperone()->ResetZeroPose(...)`.
### Chaperone (`tabcontrollers/ChaperoneTabController.cpp`)
- **Settings:**
- Visibility, color, fade distance, wall height, center marker, floor bounds and style.
- These are keys in `k_pch_CollisionBounds_Section`.
- "Disable" sets the fade distance to 0 and remembers the old value.
- **Own center marker overlay:** shows the rotation count (cord).
- **Dim when crouching:** below a height, the fade distance goes to 0 over 10 s.
- **Proximity warnings:**
- Distance of HMD and controllers to the walls (`utils/ChaperoneUtils.cpp`: projection onto
each wall segment).
- Reactions: switch to the beginner style, vibration (own thread,
`TriggerHapticVibrationAction`), alarm sound with distance-dependent volume, open the
dashboard.
- **Chaperone profiles:**
- Save the geometry (`GetLiveCollisionBoundsInfo`, zero pose, size) and the settings.
- Apply them with `SetWorking…` + `CommitWorkingCopy`.
- Autosave profile.
### Video (`tabcontrollers/VideoTabController.cpp`)
- **Brightness:** a black overlay right in front of the HMD. Its alpha `(1−x)^(1/3)`
compensates for perception.
- **Color tint:**
- either a colored overlay (`SetOverlayColor`)
- or SteamVR's `HmdDisplayColorGainR/G/B`
- **SteamVR keys:** supersampling (incl. manual override), motion smoothing, supersample
filtering.
- **Video profiles.**
### Audio (`tabcontrollers/AudioTabController.cpp`, `audiomanager/`)
- **Interface:** `AudioManager` with a Windows, a PulseAudio and a dummy implementation.
- **Windows:**
- WASAPI/MMDevice, `IAudioEndpointVolume`.
- The default device is set via the undocumented `IPolicyConfig`.
- External changes come in via `IMMNotificationClient`.
- **Linux:**
- PulseAudio: `pa_context_set_default_sink/source`; existing source outputs are moved to
the new source.
- The original state is restored at exit.
- **No mirror device.**
- **Mirror device and device override:** these are SteamVR keys (`k_pch_audio_*`), not
OVRAS's own routing.
- **Push-to-talk / -mute:** SteamVR Input action → mic mute + notification overlay on the
HMD.
- **Mute the mic when the HMD is taken off:** proximity sensor action.
- **Audio profiles.**
### SteamVR tab (`tabcontrollers/SteamVRTabController.cpp`)
- **Toggles:** each is a SteamVR setting.
- performance graph, multiple drivers, do not disturb, no-HMD mode
- camera options
- system button to all apps, don't fade to grid, controller power auto-launch
- **Restart SteamVR:** Windows only, via `.bat`.
- **Per-game controller bindings:**
- Use SteamVR's web server `localhost:27062`, with a spoofed `Referer`.
- Saved as JSON and applied when the scene app changes.
- **Receiver/tracker pairing:** WebSocket to the lighthouse web interface (`start_pairing`).
- **`openvr/lh_console_util.cpp`** (`lighthouse_console`) is compiled but **not used**.
- There is no base-station power management.
### Utilities (`tabcontrollers/UtilitiesTabController.cpp`, `keyboard_input/`, `media_keys/`, `alarm_clock/`)
- **Keyboard:**
- Buttons (Enter, Alt-Tab, Ctrl-C/V, …) and 3 freely defined shortcuts.
- A parser for the syntax `^`=Ctrl, `*`=Alt, `>`=Shift, `#`=Super.
- Sent via Windows `SendInput` or X11 XTest. On the Frame (gamescope) the X11 path probably
doesn't reach games.
- **Media keys:**
- Windows: `VK_MEDIA_*`.
- Linux: D-Bus to every `org.mpris.MediaPlayer2.*`.
- **Tracker battery:** icon overlay on each tracker (`Prop_DeviceBatteryPercentage_Float`),
only while the dashboard is visible.
- **Alarm clock:** HMD overlay at a set time, shown for 3 s.
### Statistics (`tabcontrollers/StatisticsTabController.cpp`)
- **Frame counters:** `VRCompositor()->GetCumulativeStats()` gives presented, dropped,
reprojected and timed-out frames. "Reset" only stores an offset.
- **Distance traveled:** horizontal HMD movement, every 11th tick, steps ≥ 1 cm.
- **Rotation count, maximum controller speed.**
### Settings tab (`tabcontrollers/SettingsTabController.cpp`)
Autostart (`SetApplicationAutoLaunch`), native chaperone import, block Oculus SDK, desktop
mode, tick rate (vsync / custom tick rate).
## Issues found while reading (not verified)
| Place (master) | Issue |
|---|---|
| `MoveCenterTabController.cpp:1198` | `updateChaperoneResetData()` allocates `new vr::HmdQuad_t[]` on every call and never frees the old array (memory leak) |
| `MoveCenterTabController.cpp:2512` | `if ( m_chaperoneHasCommit ) { m_chaperoneHasCommit = true; … }` looks like a slip |
| `MoveCenterTabController.cpp:1332` | `leftHandSpaceDrag`: the final `else` overwrites the swap/override choice |
| `MoveCenterTabController` | Rotation is wrapped inconsistently (snap/smooth/auto-turn 0–36000 vs. ±18000 elsewhere) |
| `VideoTabController.cpp:114` | Sets the sort order of the brightness overlay instead of the color overlay |
| `ChaperoneTabController.cpp:903` | Floor bounds are read as Bool but written as Int32 |
| `MoveCenterTabController::zeroOffsets` | If `GetCalibrationState() == 200`, init never completes and the commit repeats every tick (with commits enabled) |
| `utils/FrameRateUtils.cpp`, `openvr/lh_console_util.cpp` | Compiled but unused |
Found on the `qt6` branch and fixed on `steamframe` (see PROGRESS.md):
- the build fails on Linux
- QRhi Vulkan path: wrong `m_nFormat` (Qt enum instead of `VkFormat`); no layout transition,
no SteamVR extensions, no choice of the HMD's GPU
- mouse y not flipped on Linux
- no `VR_Shutdown`
- hard-coded version 5.7.4