Files
OpenVR-AdvancedSettings-frame/docs/steamframe/OVRAS-FEATURES.md
T
Matthias JanetschekandClaude Opus 5.5 7c5a9a7a1e 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>
2026-10-07 14:57:20 +02:00

9.5 KiB
Raw Blame History

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 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