Add a Steam Frame build option; no chaperone commit at start; timer-based tick
- CMake option OVRAS_STEAM_FRAME. It is evaluated only in src/utils/platform_capabilities.h, which turns it into named capability constants and platform defaults. - Where the chaperone can't be committed (Steam Frame: it shifts the play area), MoveCenterTabController no longer commits the working copy at start-up or when applying reset data, and doesn't create the init autosave profile. Skips are logged once. - In "vsync disabled" mode the pump timer now runs directly at the custom tick rate instead of firing every 1 ms and counting. The Frame defaults to this mode at 11 ms (~90 Hz). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
266a2f9ce9
commit
423ea57db7
@@ -111,6 +111,7 @@ set(HEADERS
|
|||||||
src/utils/paths.h
|
src/utils/paths.h
|
||||||
src/utils/setup.h
|
src/utils/setup.h
|
||||||
src/utils/update_rate.h
|
src/utils/update_rate.h
|
||||||
|
src/utils/platform_capabilities.h
|
||||||
src/utils/Matrix.h
|
src/utils/Matrix.h
|
||||||
src/quaternion/quaternion.h
|
src/quaternion/quaternion.h
|
||||||
)
|
)
|
||||||
@@ -290,6 +291,14 @@ target_compile_definitions(AdvancedSettings PRIVATE
|
|||||||
APPLICATION_VERSION=\"${PROJECT_VERSION}\"
|
APPLICATION_VERSION=\"${PROJECT_VERSION}\"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Build for the Steam Frame (SteamOS, aarch64). Switches off what doesn't work
|
||||||
|
# there and changes some defaults; the details are named capability constants
|
||||||
|
# in src/utils/platform_capabilities.h, the only place reading this definition.
|
||||||
|
option(OVRAS_STEAM_FRAME "Build for the Steam Frame" OFF)
|
||||||
|
if (OVRAS_STEAM_FRAME)
|
||||||
|
target_compile_definitions(AdvancedSettings PRIVATE OVRAS_STEAM_FRAME=1)
|
||||||
|
endif ()
|
||||||
|
|
||||||
#find_library(OPENVR_LIBRARIES
|
#find_library(OPENVR_LIBRARIES
|
||||||
# NAMES
|
# NAMES
|
||||||
# openvr_api
|
# openvr_api
|
||||||
|
|||||||
@@ -26,3 +26,34 @@
|
|||||||
- `libopenvr_api.so` is resolved from the binary's directory (`ldd`).
|
- `libopenvr_api.so` is resolved from the binary's directory (`ldd`).
|
||||||
- Not started: without a running SteamVR the start hangs in OpenVR init (expected; tested
|
- Not started: without a running SteamVR the start hangs in OpenVR init (expected; tested
|
||||||
on the Frame in step 5).
|
on the Frame in step 5).
|
||||||
|
|
||||||
|
### Step 2: platform option, R7, tick (2026-10-07)
|
||||||
|
|
||||||
|
**Changes:**
|
||||||
|
- **Platform option:**
|
||||||
|
- CMake option `OVRAS_STEAM_FRAME` (default OFF) sets the compile definition
|
||||||
|
`OVRAS_STEAM_FRAME=1`.
|
||||||
|
- New `src/utils/platform_capabilities.h` evaluates it as the only place. It holds
|
||||||
|
`k_chaperoneCommitSupported`, `k_defaultVsyncDisabled` and `k_defaultCustomTickRateMs`.
|
||||||
|
- **Tick (R6):**
|
||||||
|
- In "vsync disabled" mode, `m_pumpEventsTimer` runs directly at `customTickRateMs`
|
||||||
|
(`applyTickInterval()`, `Qt::PreciseTimer`); the 1 ms counting is gone.
|
||||||
|
- Changing the setting restarts the timer.
|
||||||
|
- Setting defaults in `settings_controller.h` come from the constants: Frame `true`/11 ms,
|
||||||
|
otherwise unchanged `false`/20 ms.
|
||||||
|
- **R7:** with `!k_chaperoneCommitSupported`:
|
||||||
|
- `updateChaperoneResetData()` and `applyChaperoneResetData()` skip
|
||||||
|
`CommitWorkingCopy`/`RevertWorkingCopy`. Each logs once per function.
|
||||||
|
- The init autosave profile is skipped.
|
||||||
|
|
||||||
|
**Results:**
|
||||||
|
- Both variants (default and `-DOVRAS_STEAM_FRAME=ON`) build on x86_64. The definition is
|
||||||
|
only set in the Frame variant (`compile_commands.json`).
|
||||||
|
- Not tested at runtime yet (Frame, step 5).
|
||||||
|
|
||||||
|
**Notes:**
|
||||||
|
- Another commit at start-up remains, but only on opt-in. `SetWidget()` applies a chaperone
|
||||||
|
profile and commits when "auto-apply chaperone" (`autoApplyChaperoneToggle`, default
|
||||||
|
`false`) is on. That's a chaperone-profile feature, so it belongs to R5 (iteration 2).
|
||||||
|
- clang-format reflows comment blocks next to new comments. Separate new comments from
|
||||||
|
existing commented-out code with a blank line, otherwise the diff grows.
|
||||||
|
|||||||
+22
-14
@@ -480,9 +480,7 @@ void OverlayController::SetWidget( QQuickItem* quickItem,
|
|||||||
this,
|
this,
|
||||||
SLOT( OnTimeoutPumpEvents() ) );
|
SLOT( OnTimeoutPumpEvents() ) );
|
||||||
|
|
||||||
// Every 1ms we check if the current frame has advanced (for vsync)
|
applyTickInterval();
|
||||||
m_pumpEventsTimer.setInterval( 1 );
|
|
||||||
|
|
||||||
m_pumpEventsTimer.start();
|
m_pumpEventsTimer.start();
|
||||||
|
|
||||||
m_steamVRTabController.initStage2( this );
|
m_steamVRTabController.initStage2( this );
|
||||||
@@ -966,6 +964,8 @@ void OverlayController::setVsyncDisabled( bool value, bool notify )
|
|||||||
{
|
{
|
||||||
settings::setSetting( settings::BoolSetting::APPLICATION_vsyncDisabled,
|
settings::setSetting( settings::BoolSetting::APPLICATION_vsyncDisabled,
|
||||||
value );
|
value );
|
||||||
|
// The timer interval depends on the mode, see applyTickInterval().
|
||||||
|
applyTickInterval();
|
||||||
if ( notify )
|
if ( notify )
|
||||||
{
|
{
|
||||||
emit vsyncDisabledChanged( value );
|
emit vsyncDisabledChanged( value );
|
||||||
@@ -1076,6 +1076,8 @@ void OverlayController::setCustomTickRateMs( int value, bool notify )
|
|||||||
settings::setSetting( settings::IntSetting::APPLICATION_customTickRateMs,
|
settings::setSetting( settings::IntSetting::APPLICATION_customTickRateMs,
|
||||||
verifiedTickRate );
|
verifiedTickRate );
|
||||||
m_verifiedCustomTickRateMs = verifiedTickRate;
|
m_verifiedCustomTickRateMs = verifiedTickRate;
|
||||||
|
// In "vsync disabled" mode the timer runs at this interval.
|
||||||
|
applyTickInterval();
|
||||||
|
|
||||||
if ( notify )
|
if ( notify )
|
||||||
{
|
{
|
||||||
@@ -1083,6 +1085,19 @@ void OverlayController::setCustomTickRateMs( int value, bool notify )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets the interval of m_pumpEventsTimer for the current tick mode:
|
||||||
|
// - vsync: every 1 ms, OnTimeoutPumpEvents() checks whether the compositor
|
||||||
|
// started a new frame.
|
||||||
|
// - vsync disabled: the timer fires directly at the custom tick rate, so the
|
||||||
|
// process wakes up once per tick instead of every 1 ms.
|
||||||
|
// QTimer::setInterval() also restarts a running timer with the new interval.
|
||||||
|
void OverlayController::applyTickInterval()
|
||||||
|
{
|
||||||
|
m_pumpEventsTimer.setTimerType( Qt::PreciseTimer );
|
||||||
|
m_pumpEventsTimer.setInterval( vsyncDisabled() ? customTickRateMs() : 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by m_pumpEventsTimer, see applyTickInterval().
|
||||||
// vsync implementation:
|
// vsync implementation:
|
||||||
// this function triggers every 1ms
|
// this function triggers every 1ms
|
||||||
// this function should remain lightweight and only check if it's time to
|
// this function should remain lightweight and only check if it's time to
|
||||||
@@ -1091,17 +1106,10 @@ void OverlayController::OnTimeoutPumpEvents()
|
|||||||
{
|
{
|
||||||
if ( vsyncDisabled() )
|
if ( vsyncDisabled() )
|
||||||
{
|
{
|
||||||
// check if it's time for a custom tick rate tick
|
// The timer already runs at the custom tick rate: every call is a
|
||||||
if ( m_customTickRateCounter > customTickRateMs() )
|
// tick.
|
||||||
{
|
mainEventLoop();
|
||||||
mainEventLoop();
|
updateRate.incrementCounter();
|
||||||
m_customTickRateCounter = 0;
|
|
||||||
updateRate.incrementCounter();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_customTickRateCounter++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// vsync is enabled
|
// vsync is enabled
|
||||||
|
|||||||
@@ -158,7 +158,6 @@ private:
|
|||||||
uint64_t m_currentFrame = 0;
|
uint64_t m_currentFrame = 0;
|
||||||
uint64_t m_lastFrame = 0;
|
uint64_t m_lastFrame = 0;
|
||||||
int m_vsyncTooLateCounter = 0;
|
int m_vsyncTooLateCounter = 0;
|
||||||
int m_customTickRateCounter = 0;
|
|
||||||
int m_verifiedCustomTickRateMs = 0;
|
int m_verifiedCustomTickRateMs = 0;
|
||||||
|
|
||||||
input::SteamIVRInput m_actions;
|
input::SteamIVRInput m_actions;
|
||||||
@@ -200,6 +199,8 @@ private:
|
|||||||
return m_renderControl.rhi();
|
return m_renderControl.rhi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void applyTickInterval();
|
||||||
|
|
||||||
bool m_exclusiveState = false;
|
bool m_exclusiveState = false;
|
||||||
bool m_keyPressOneState = false;
|
bool m_keyPressOneState = false;
|
||||||
bool m_keyPressTwoState = false;
|
bool m_keyPressTwoState = false;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "../../utils/setup.h"
|
#include "../../utils/setup.h"
|
||||||
#include "specific_setting_value.h"
|
#include "specific_setting_value.h"
|
||||||
#include "../../tabcontrollers/MoveCenterTabController.h"
|
#include "../../tabcontrollers/MoveCenterTabController.h"
|
||||||
|
#include "../../utils/platform_capabilities.h"
|
||||||
|
|
||||||
namespace settings
|
namespace settings
|
||||||
{
|
{
|
||||||
@@ -303,10 +304,11 @@ private:
|
|||||||
SettingCategory::Application,
|
SettingCategory::Application,
|
||||||
QtInfo{ "previousShutdownSafe" },
|
QtInfo{ "previousShutdownSafe" },
|
||||||
true },
|
true },
|
||||||
|
// Default depends on the platform (fixed-interval tick on the Frame).
|
||||||
BoolSettingValue{ BoolSetting::APPLICATION_vsyncDisabled,
|
BoolSettingValue{ BoolSetting::APPLICATION_vsyncDisabled,
|
||||||
SettingCategory::Application,
|
SettingCategory::Application,
|
||||||
QtInfo{ "vsyncDisabled" },
|
QtInfo{ "vsyncDisabled" },
|
||||||
false },
|
platform::k_defaultVsyncDisabled },
|
||||||
BoolSettingValue{ BoolSetting::APPLICATION_crashRecoveryDisabled,
|
BoolSettingValue{ BoolSetting::APPLICATION_crashRecoveryDisabled,
|
||||||
SettingCategory::Application,
|
SettingCategory::Application,
|
||||||
QtInfo{ "crashRecoveryDisabled" },
|
QtInfo{ "crashRecoveryDisabled" },
|
||||||
@@ -610,10 +612,11 @@ private:
|
|||||||
SettingCategory::Application,
|
SettingCategory::Application,
|
||||||
QtInfo{ "debugState" },
|
QtInfo{ "debugState" },
|
||||||
0 },
|
0 },
|
||||||
|
// Default depends on the platform (about 90 Hz on the Frame).
|
||||||
IntSettingValue{ IntSetting::APPLICATION_customTickRateMs,
|
IntSettingValue{ IntSetting::APPLICATION_customTickRateMs,
|
||||||
SettingCategory::Application,
|
SettingCategory::Application,
|
||||||
QtInfo{ "customTickRateMs" },
|
QtInfo{ "customTickRateMs" },
|
||||||
20 },
|
platform::k_defaultCustomTickRateMs },
|
||||||
|
|
||||||
IntSettingValue{ IntSetting::UTILITY_alarmHour,
|
IntSettingValue{ IntSetting::UTILITY_alarmHour,
|
||||||
SettingCategory::Utility,
|
SettingCategory::Utility,
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
#include "MoveCenterTabController.h"
|
#include "MoveCenterTabController.h"
|
||||||
#include <QQuickWindow>
|
#include <QQuickWindow>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
#include <QtLogging>
|
#include <QtLogging>
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
#include "../overlaycontroller.h"
|
#include "../overlaycontroller.h"
|
||||||
#include "../quaternion/quaternion.h"
|
#include "../quaternion/quaternion.h"
|
||||||
#include "../settings/settings.h"
|
#include "../settings/settings.h"
|
||||||
|
#include "../utils/platform_capabilities.h"
|
||||||
|
|
||||||
void rotateCoordinates( double coordinates[3], double angle )
|
void rotateCoordinates( double coordinates[3], double angle )
|
||||||
{
|
{
|
||||||
@@ -1106,7 +1109,13 @@ void MoveCenterTabController::zeroOffsets()
|
|||||||
if ( calState != 200 )
|
if ( calState != 200 )
|
||||||
{
|
{
|
||||||
// all init complete, safe to autosave chaperone profile
|
// all init complete, safe to autosave chaperone profile
|
||||||
parent->m_chaperoneTabController.createNewAutosaveProfile();
|
// Not where the chaperone can't be committed: the autosave
|
||||||
|
// exists to restore a committed play area later.
|
||||||
|
if ( platform::k_chaperoneCommitSupported )
|
||||||
|
{
|
||||||
|
parent->m_chaperoneTabController
|
||||||
|
.createNewAutosaveProfile();
|
||||||
|
}
|
||||||
m_initComplete = true;
|
m_initComplete = true;
|
||||||
m_chaperoneBasisAcquired = true;
|
m_chaperoneBasisAcquired = true;
|
||||||
qInfo() << "Chaperone Initilization Complete, Space Drag "
|
qInfo() << "Chaperone Initilization Complete, Space Drag "
|
||||||
@@ -1172,6 +1181,19 @@ void MoveCenterTabController::clampVelocity( double* velocity )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logs once per caller that a chaperone commit was skipped, so the log shows
|
||||||
|
// it without repeating it every tick.
|
||||||
|
static void logSkippedChaperoneCommit( const char* caller )
|
||||||
|
{
|
||||||
|
static std::set<std::string> logged;
|
||||||
|
if ( logged.insert( caller ).second )
|
||||||
|
{
|
||||||
|
qInfo() << caller
|
||||||
|
<< ": CommitWorkingCopy skipped, not supported on this "
|
||||||
|
"platform (logged once)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MoveCenterTabController::updateChaperoneResetData()
|
void MoveCenterTabController::updateChaperoneResetData()
|
||||||
{
|
{
|
||||||
//auto cstate = vr::VRChaperone()->GetCalibrationState();
|
//auto cstate = vr::VRChaperone()->GetCalibrationState();
|
||||||
@@ -1183,9 +1205,20 @@ void MoveCenterTabController::updateChaperoneResetData()
|
|||||||
// }
|
// }
|
||||||
// else
|
// else
|
||||||
//{
|
//{
|
||||||
|
|
||||||
|
// Committing writes the working copy as the live play area. Where that
|
||||||
|
// isn't supported (it shifts the play area on the Steam Frame), only the
|
||||||
|
// working copy is read below.
|
||||||
|
if ( platform::k_chaperoneCommitSupported )
|
||||||
|
{
|
||||||
vr::VRChaperoneSetup()->CommitWorkingCopy(
|
vr::VRChaperoneSetup()->CommitWorkingCopy(
|
||||||
vr::EChaperoneConfigFile_Live );
|
vr::EChaperoneConfigFile_Live );
|
||||||
vr::VRChaperoneSetup()->RevertWorkingCopy();
|
vr::VRChaperoneSetup()->RevertWorkingCopy();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logSkippedChaperoneCommit( "updateChaperoneResetData" );
|
||||||
|
}
|
||||||
//}
|
//}
|
||||||
unsigned currentQuadCount = 0;
|
unsigned currentQuadCount = 0;
|
||||||
vr::VRChaperoneSetup()->GetWorkingCollisionBoundsInfo( nullptr,
|
vr::VRChaperoneSetup()->GetWorkingCollisionBoundsInfo( nullptr,
|
||||||
@@ -1228,7 +1261,16 @@ void MoveCenterTabController::applyChaperoneResetData()
|
|||||||
// vr::VRChaperoneSetup()->SetWorkingSeatedZeroPoseToRawTrackingPose(
|
// vr::VRChaperoneSetup()->SetWorkingSeatedZeroPoseToRawTrackingPose(
|
||||||
// &m_seatedCenterForReset );
|
// &m_seatedCenterForReset );
|
||||||
|
|
||||||
vr::VRChaperoneSetup()->CommitWorkingCopy( vr::EChaperoneConfigFile_Live );
|
// See updateChaperoneResetData(): no commit where it isn't supported.
|
||||||
|
if ( platform::k_chaperoneCommitSupported )
|
||||||
|
{
|
||||||
|
vr::VRChaperoneSetup()->CommitWorkingCopy(
|
||||||
|
vr::EChaperoneConfigFile_Live );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logSkippedChaperoneCommit( "applyChaperoneResetData" );
|
||||||
|
}
|
||||||
|
|
||||||
unsigned checkQuadCount = 0;
|
unsigned checkQuadCount = 0;
|
||||||
vr::VRChaperoneSetup()->GetWorkingCollisionBoundsInfo( nullptr,
|
vr::VRChaperoneSetup()->GetWorkingCollisionBoundsInfo( nullptr,
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// What the target platform supports, and platform-specific defaults.
|
||||||
|
//
|
||||||
|
// This is the only place that evaluates the build option OVRAS_STEAM_FRAME
|
||||||
|
// (CMake option of the same name). All other code asks for the named constants
|
||||||
|
// below, never for the platform, so it stays clear *why* something differs.
|
||||||
|
// See docs/steamframe/DESIGN.md, section 7.
|
||||||
|
|
||||||
|
#ifndef OVRAS_STEAM_FRAME
|
||||||
|
# define OVRAS_STEAM_FRAME 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace platform
|
||||||
|
{
|
||||||
|
// IVRChaperoneSetup::CommitWorkingCopy() writes the working copy as the live
|
||||||
|
// chaperone. OVRAS does this at start-up and when applying offsets. On the
|
||||||
|
// Steam Frame (inside-out tracking) it shifts the play area, so it must not
|
||||||
|
// happen there.
|
||||||
|
constexpr bool k_chaperoneCommitSupported = !OVRAS_STEAM_FRAME;
|
||||||
|
|
||||||
|
// Default tick: by default OVRAS polls every 1 ms for a new compositor frame
|
||||||
|
// ("vsync"). The Frame defaults to a fixed-interval tick instead, which wakes
|
||||||
|
// the process about 11 times less often. 11 ms is about 90 Hz, the frame rate
|
||||||
|
// OVRAS's tick-counted intervals were designed for.
|
||||||
|
constexpr bool k_defaultVsyncDisabled = OVRAS_STEAM_FRAME;
|
||||||
|
constexpr int k_defaultCustomTickRateMs = OVRAS_STEAM_FRAME ? 11 : 20;
|
||||||
|
} // namespace platform
|
||||||
Reference in New Issue
Block a user