- 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>
9.5 KiB
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, "Feature classification".
Architecture
-
Dashboard overlay:
VR_Init(VRApplication_Overlay), app keysteam.overlay.1009850.- QML UI (1200×800) rendered off-screen via
QQuickRenderControl. - The texture goes to
SetOverlayTexture. Onsteamframethe render backends inrender/do this; before, it was an OpenGL FBO, and onqt6QRhi.
-
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:
- input actions
- VR events
- poses
eventLoopTickof the tab controllers (always)dashboardLoopTick(only while the dashboard is visible)
- Timer; "vsync" mode once per compositor frame (
-
SteamVR Input:
package_files/action_manifest.json, 5 action sets:motion: space drag/turn, gravity, snap/smooth turn, height toggle, offsets, auto-turnmusic: media keysmisc: axis locks, chaperone toggle, keyboard shortcutssystem: push-to-talk, exclusive input, haptic clickshaptic: vibration, proximity sensor
Default bindings exist for Vive, Index, WMR, HP, Touch, generic HMD and Frame controllers.
-
Persistence:
- App settings in a
QSettingsINI file (settings/). Profiles are stored as arrays (ISettingsObject). - SteamVR's own settings go directly through
vr::VRSettings()(openvr/ovr_settings_wrapper).
- App settings in a
-
Platform:
- Windows: WASAPI /
IPolicyConfig,SendInput. - Linux: PulseAudio, X11 XTest, D-Bus MPRIS.
- Elsewhere: dummies.
- Windows: WASAPI /
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.
- The controller's position delta, multiplied by
- 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.
- "Apply" commits with
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.
- Distance of HMD and controllers to the walls (
- Chaperone profiles:
- Save the geometry (
GetLiveCollisionBoundsInfo, zero pose, size) and the settings. - Apply them with
SetWorking…+CommitWorkingCopy. - Autosave profile.
- Save the geometry (
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
- either a colored overlay (
- SteamVR keys: supersampling (incl. manual override), motion smoothing, supersample filtering.
- Video profiles.
Audio (tabcontrollers/AudioTabController.cpp, audiomanager/)
- Interface:
AudioManagerwith 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.
- WASAPI/MMDevice,
- 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.
- PulseAudio:
- 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 spoofedReferer. - Saved as JSON and applied when the scene app changes.
- Use SteamVR's web server
- 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
SendInputor 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.*.
- Windows:
- 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 ofVkFormat); 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