mirror of
https://github.com/lbgos/rift-s-linux.git
synced 2026-10-19 08:00:59 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8009f79310 | ||
|
|
d157d0ab51 | ||
|
|
1fb075c174 | ||
|
|
e16f571e57 | ||
|
|
5fb003aaab | ||
|
|
2d0c513209 |
No files matched your search
@@ -22,7 +22,7 @@ Free and open source, built on Monado and Basalt. Steam and SteamVR are installe
|
||||
|
||||
- Oculus Rift S, its Touch controllers, USB 3 and DisplayPort.
|
||||
- An x86-64 Linux desktop with a systemd user session and local USB seat access.
|
||||
- Native Steam with SteamVR installed from the Steam library.
|
||||
- Native Steam with SteamVR installed from the Steam library. Flatpak and Snap Steam are not supported: Valve does not support SteamVR in them because they break DRM leasing and asynchronous reprojection.
|
||||
- A Vulkan-capable GPU and drivers supported by SteamVR's direct display mode. Current testing uses an NVIDIA RTX 3090.
|
||||
- The patched Basalt build. The Arch packages include it; an unpatched Basalt lacks the required reconnect support.
|
||||
- KDE Plasma Wayland for the Desktop overlay.
|
||||
@@ -54,7 +54,7 @@ tar -xf rift-s-linux-r30-x86_64.tar.xz
|
||||
|
||||
Install a C/C++ toolchain, CMake, Ninja, Eigen, TBB, fmt, OpenCV, Vulkan headers and loader, glslang, libusb, SDL2, cJSON, libcap, systemd and the X11, Wayland and DRM development libraries. The Desktop overlay also needs PipeWire development files, wayland-scanner, wayland-protocols and plasma-wayland-protocols. Settings needs Rust/Cargo, Node.js, pnpm, GTK3, WebKitGTK 4.1, shaderc's `glslc` and desktop-file-utils.
|
||||
|
||||
Build Basalt at the supported revision, applying all three patches in order:
|
||||
Build Basalt at the supported revision, applying all four patches in order:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/lbgos/rift-s-linux.git
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
pkgbase = basalt-monado-rifts
|
||||
pkgdesc = Basalt tracking library with Rift S lifecycle and worker defaults
|
||||
pkgver = 0.1.r544.gdf6e970
|
||||
pkgrel = 1
|
||||
pkgrel = 2
|
||||
url = https://gitlab.freedesktop.org/mateosss/basalt
|
||||
arch = x86_64
|
||||
license = BSD-2-Clause
|
||||
@@ -35,6 +35,7 @@ pkgbase = basalt-monado-rifts
|
||||
source = 0001-keep-imu-frequency-per-tracker.patch
|
||||
source = 0002-limit-workers-with-driver-calibration.patch
|
||||
source = 0003-safe-recreation-and-realtime-worker-budget.patch
|
||||
source = 0004-skip-unread-execution-stats.patch
|
||||
sha256sums = SKIP
|
||||
sha256sums = SKIP
|
||||
sha256sums = SKIP
|
||||
@@ -45,5 +46,6 @@ pkgbase = basalt-monado-rifts
|
||||
sha256sums = a0bc2a766ad6ae5fdfa33cd80d45d12798d43efa4c34605ee6cf20bae157d18d
|
||||
sha256sums = 607d53dadbd8ca5b0978d10378e631539bc8047ca0fce317b90d68a52f785a52
|
||||
sha256sums = a434e76c66c129aa3a900ed151dd2dd9bc98b671451dda26855a438cff08b2ac
|
||||
sha256sums = 1f8848384b376addb471add3b9efea8c6effbfe7fe615c2beb3ad5ebdf432290
|
||||
|
||||
pkgname = basalt-monado-rifts
|
||||
@@ -0,0 +1,135 @@
|
||||
--- a/include/basalt/utils/time_utils.hpp
|
||||
+++ b/include/basalt/utils/time_utils.hpp
|
||||
@@ -124,6 +124,9 @@ class ExecutionStats {
|
||||
std::string format_;
|
||||
};
|
||||
|
||||
+ //! Live trackers never read the history, so a disabled instance keeps no values.
|
||||
+ bool enabled = true;
|
||||
+
|
||||
Meta& add(const std::string& name, double value);
|
||||
Meta& add(const std::string& name, const Eigen::VectorXd& value);
|
||||
Meta& add(const std::string& name, const Eigen::VectorXf& value);
|
||||
@@ -139,6 +142,7 @@ class ExecutionStats {
|
||||
private:
|
||||
std::unordered_map<std::string, Meta> stats_;
|
||||
std::vector<std::string> order_;
|
||||
+ Meta discarded_; // returned by add() while disabled
|
||||
};
|
||||
|
||||
} // namespace basalt
|
||||
--- a/include/basalt/vi_estimator/sqrt_keypoint_vio.h
|
||||
+++ b/include/basalt/vi_estimator/sqrt_keypoint_vio.h
|
||||
@@ -130,6 +130,7 @@ class SqrtKeypointVioEstimator : public VioEstimatorBase, public SqrtBundleAdjus
|
||||
bool show_uimat(UIMAT m) const;
|
||||
|
||||
void debug_finalize() override;
|
||||
+ void disable_stats() override { stats_all_.enabled = stats_sums_.enabled = false; }
|
||||
|
||||
void logMargNullspace();
|
||||
Eigen::VectorXd checkMargNullspace() const;
|
||||
--- a/include/basalt/vi_estimator/sqrt_keypoint_vo.h
|
||||
+++ b/include/basalt/vi_estimator/sqrt_keypoint_vo.h
|
||||
@@ -176,6 +176,7 @@ class SqrtKeypointVoEstimator : public VioEstimatorBase, public SqrtBundleAdjust
|
||||
Sophus::SE3d getT_w_i_init() override { return T_w_i_init.template cast<double>(); }
|
||||
|
||||
void debug_finalize() override;
|
||||
+ void disable_stats() override { stats_all_.enabled = stats_sums_.enabled = false; }
|
||||
|
||||
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
|
||||
|
||||
--- a/include/basalt/vi_estimator/vio_estimator.h
|
||||
+++ b/include/basalt/vi_estimator/vio_estimator.h
|
||||
@@ -136,6 +136,9 @@ class VioEstimatorBase {
|
||||
|
||||
virtual inline void debug_finalize() {}
|
||||
|
||||
+ //! Stop keeping the per-frame timing stats that only debug_finalize() reads.
|
||||
+ virtual void disable_stats() {}
|
||||
+
|
||||
virtual Sophus::SE3d getT_w_i_init() = 0;
|
||||
|
||||
// Legacy functions. Should not be used in the new code.
|
||||
--- a/src/utils/time_utils.cpp
|
||||
+++ b/src/utils/time_utils.cpp
|
||||
@@ -31,6 +31,7 @@ Scalar variance(const Eigen::Matrix<Scalar, N, 1>& vec) {
|
||||
}
|
||||
|
||||
ExecutionStats::Meta& ExecutionStats::add(const std::string& name, double value) {
|
||||
+ if (!enabled) return discarded_;
|
||||
auto [it, new_item] = stats_.try_emplace(name);
|
||||
if (new_item) {
|
||||
order_.push_back(name);
|
||||
@@ -41,6 +42,7 @@ ExecutionStats::Meta& ExecutionStats::add(const std::string& name, double value)
|
||||
}
|
||||
|
||||
ExecutionStats::Meta& ExecutionStats::add(const std::string& name, const Eigen::VectorXd& value) {
|
||||
+ if (!enabled) return discarded_;
|
||||
auto [it, new_item] = stats_.try_emplace(name);
|
||||
if (new_item) {
|
||||
order_.push_back(name);
|
||||
@@ -56,6 +58,7 @@ ExecutionStats::Meta& ExecutionStats::add(const std::string& name, const Eigen::
|
||||
}
|
||||
|
||||
void ExecutionStats::merge_all(const ExecutionStats& other) {
|
||||
+ if (!enabled) return;
|
||||
for (const auto& name : other.order_) {
|
||||
const auto& meta = other.stats_.at(name);
|
||||
std::visit(
|
||||
@@ -80,6 +83,7 @@ overload(Ts...) -> overload<Ts...>;
|
||||
} // namespace
|
||||
|
||||
void ExecutionStats::merge_sums(const ExecutionStats& other) {
|
||||
+ if (!enabled) return;
|
||||
for (const auto& name : other.order_) {
|
||||
const auto& meta = other.stats_.at(name);
|
||||
std::visit(overload{[&](const std::vector<double>& data) {
|
||||
--- a/src/vi_estimator/sqrt_keypoint_vio.cpp
|
||||
+++ b/src/vi_estimator/sqrt_keypoint_vio.cpp
|
||||
@@ -1224,6 +1224,7 @@ bool SqrtKeypointVioEstimator<Scalar_>::optimize() {
|
||||
|
||||
// timing
|
||||
ExecutionStats stats;
|
||||
+ stats.enabled = stats_all_.enabled;
|
||||
Timer timer_total;
|
||||
Timer timer_iteration;
|
||||
|
||||
@@ -1544,7 +1545,7 @@ bool SqrtKeypointVioEstimator<Scalar_>::optimize() {
|
||||
double cumulative_time = timer_total.elapsed();
|
||||
|
||||
stats.add("iteration", iteration_time).format("ms");
|
||||
- {
|
||||
+ if (stats.enabled) {
|
||||
basalt::MemoryInfo mi;
|
||||
if (get_memory_info(mi)) {
|
||||
stats.add("resident_memory", mi.resident_memory);
|
||||
--- a/src/vi_estimator/sqrt_keypoint_vo.cpp
|
||||
+++ b/src/vi_estimator/sqrt_keypoint_vo.cpp
|
||||
@@ -1108,6 +1108,7 @@ bool SqrtKeypointVoEstimator<Scalar_>::optimize() {
|
||||
|
||||
// timing
|
||||
ExecutionStats stats;
|
||||
+ stats.enabled = stats_all_.enabled;
|
||||
Timer timer_total;
|
||||
Timer timer_iteration;
|
||||
|
||||
@@ -1395,7 +1396,7 @@ bool SqrtKeypointVoEstimator<Scalar_>::optimize() {
|
||||
double cumulative_time = timer_total.elapsed();
|
||||
|
||||
stats.add("iteration", iteration_time).format("ms");
|
||||
- {
|
||||
+ if (stats.enabled) {
|
||||
basalt::MemoryInfo mi;
|
||||
if (get_memory_info(mi)) {
|
||||
stats.add("resident_memory", mi.resident_memory);
|
||||
--- a/src/vit/vit_tracker.cpp
|
||||
+++ b/src/vit/vit_tracker.cpp
|
||||
@@ -398,6 +398,8 @@ struct Tracker::Implementation {
|
||||
ASSERT_(image_data_queue != nullptr);
|
||||
|
||||
vio = VioEstimatorFactory::getVioEstimator(vio_config, calib, constants::g, true, use_double);
|
||||
+ // Nothing reads these stats in a live session; they would grow for as long as it runs.
|
||||
+ vio->disable_stats();
|
||||
imu_data_queue = &vio->imu_data_queue;
|
||||
ASSERT_(imu_data_queue != nullptr);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Maintainer: lbgos <285507185+lbgos@users.noreply.github.com>
|
||||
pkgname=basalt-monado-rifts
|
||||
pkgver=0.1.r544.gdf6e970
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
pkgdesc='Basalt tracking library with Rift S lifecycle and worker defaults'
|
||||
arch=('x86_64')
|
||||
url='https://gitlab.freedesktop.org/mateosss/basalt'
|
||||
@@ -20,9 +20,10 @@ source=('basalt::git+https://gitlab.freedesktop.org/mateosss/basalt.git#commit=d
|
||||
'cereal::git+https://github.com/USCiLab/cereal.git#commit=ebef1e929807629befafbb2918ea1a08c7194554'
|
||||
'0001-keep-imu-frequency-per-tracker.patch'
|
||||
'0002-limit-workers-with-driver-calibration.patch'
|
||||
'0003-safe-recreation-and-realtime-worker-budget.patch')
|
||||
'0003-safe-recreation-and-realtime-worker-budget.patch'
|
||||
'0004-skip-unread-execution-stats.patch')
|
||||
sha256sums=('SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP'
|
||||
'a0bc2a766ad6ae5fdfa33cd80d45d12798d43efa4c34605ee6cf20bae157d18d' '607d53dadbd8ca5b0978d10378e631539bc8047ca0fce317b90d68a52f785a52' 'a434e76c66c129aa3a900ed151dd2dd9bc98b671451dda26855a438cff08b2ac')
|
||||
'a0bc2a766ad6ae5fdfa33cd80d45d12798d43efa4c34605ee6cf20bae157d18d' '607d53dadbd8ca5b0978d10378e631539bc8047ca0fce317b90d68a52f785a52' 'a434e76c66c129aa3a900ed151dd2dd9bc98b671451dda26855a438cff08b2ac' '1f8848384b376addb471add3b9efea8c6effbfe7fe615c2beb3ad5ebdf432290')
|
||||
|
||||
prepare() {
|
||||
cd basalt
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pkgbase = rift-s-linux-bin
|
||||
pkgdesc = Oculus Rift S on Linux SteamVR: driver, Room Setup, Settings app and in-headset desktop (prebuilt)
|
||||
pkgver = 30
|
||||
pkgver = 31
|
||||
pkgrel = 1
|
||||
url = https://github.com/lbgos/rift-s-linux
|
||||
install = rift-s-linux.install
|
||||
@@ -72,7 +72,7 @@ pkgbase = rift-s-linux-bin
|
||||
conflicts = monado-rifts-git
|
||||
options = !strip
|
||||
options = !debug
|
||||
source = https://github.com/lbgos/rift-s-linux/releases/download/r30/rift-s-linux-r30-x86_64.tar.zst
|
||||
sha256sums = 6a4ff30493b8ee42acd4187fae2ea27bd01063b6c7e106e0c1494609302ef501
|
||||
source = https://github.com/lbgos/rift-s-linux/releases/download/r31/rift-s-linux-r31-x86_64.tar.zst
|
||||
sha256sums = b466138ac5f2bfa38d78335e4fdbee3d1b4c8efd3891cb0639d1eb28df86f47b
|
||||
|
||||
pkgname = rift-s-linux-bin
|
||||
@@ -1,6 +1,6 @@
|
||||
# Maintainer: lbgos <285507185+lbgos@users.noreply.github.com>
|
||||
pkgname=rift-s-linux-bin
|
||||
pkgver=30
|
||||
pkgver=31
|
||||
pkgrel=1
|
||||
pkgdesc='Oculus Rift S on Linux SteamVR: driver, Room Setup, Settings app and in-headset desktop (prebuilt)'
|
||||
arch=('x86_64')
|
||||
@@ -24,7 +24,7 @@ conflicts=('rift-s-linux' 'monado' 'monado-git' 'rifts-settings' 'basalt-monado-
|
||||
install=rift-s-linux.install
|
||||
options=('!strip' '!debug')
|
||||
source=("https://github.com/lbgos/rift-s-linux/releases/download/r$pkgver/rift-s-linux-r$pkgver-x86_64.tar.zst")
|
||||
sha256sums=('6a4ff30493b8ee42acd4187fae2ea27bd01063b6c7e106e0c1494609302ef501')
|
||||
sha256sums=('b466138ac5f2bfa38d78335e4fdbee3d1b4c8efd3891cb0639d1eb28df86f47b')
|
||||
|
||||
package() {
|
||||
cp -a usr "$pkgdir/"
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
--- a/include/basalt/utils/time_utils.hpp
|
||||
+++ b/include/basalt/utils/time_utils.hpp
|
||||
@@ -124,6 +124,9 @@ class ExecutionStats {
|
||||
std::string format_;
|
||||
};
|
||||
|
||||
+ //! Live trackers never read the history, so a disabled instance keeps no values.
|
||||
+ bool enabled = true;
|
||||
+
|
||||
Meta& add(const std::string& name, double value);
|
||||
Meta& add(const std::string& name, const Eigen::VectorXd& value);
|
||||
Meta& add(const std::string& name, const Eigen::VectorXf& value);
|
||||
@@ -139,6 +142,7 @@ class ExecutionStats {
|
||||
private:
|
||||
std::unordered_map<std::string, Meta> stats_;
|
||||
std::vector<std::string> order_;
|
||||
+ Meta discarded_; // returned by add() while disabled
|
||||
};
|
||||
|
||||
} // namespace basalt
|
||||
--- a/include/basalt/vi_estimator/sqrt_keypoint_vio.h
|
||||
+++ b/include/basalt/vi_estimator/sqrt_keypoint_vio.h
|
||||
@@ -130,6 +130,7 @@ class SqrtKeypointVioEstimator : public VioEstimatorBase, public SqrtBundleAdjus
|
||||
bool show_uimat(UIMAT m) const;
|
||||
|
||||
void debug_finalize() override;
|
||||
+ void disable_stats() override { stats_all_.enabled = stats_sums_.enabled = false; }
|
||||
|
||||
void logMargNullspace();
|
||||
Eigen::VectorXd checkMargNullspace() const;
|
||||
--- a/include/basalt/vi_estimator/sqrt_keypoint_vo.h
|
||||
+++ b/include/basalt/vi_estimator/sqrt_keypoint_vo.h
|
||||
@@ -176,6 +176,7 @@ class SqrtKeypointVoEstimator : public VioEstimatorBase, public SqrtBundleAdjust
|
||||
Sophus::SE3d getT_w_i_init() override { return T_w_i_init.template cast<double>(); }
|
||||
|
||||
void debug_finalize() override;
|
||||
+ void disable_stats() override { stats_all_.enabled = stats_sums_.enabled = false; }
|
||||
|
||||
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
|
||||
|
||||
--- a/include/basalt/vi_estimator/vio_estimator.h
|
||||
+++ b/include/basalt/vi_estimator/vio_estimator.h
|
||||
@@ -136,6 +136,9 @@ class VioEstimatorBase {
|
||||
|
||||
virtual inline void debug_finalize() {}
|
||||
|
||||
+ //! Stop keeping the per-frame timing stats that only debug_finalize() reads.
|
||||
+ virtual void disable_stats() {}
|
||||
+
|
||||
virtual Sophus::SE3d getT_w_i_init() = 0;
|
||||
|
||||
// Legacy functions. Should not be used in the new code.
|
||||
--- a/src/utils/time_utils.cpp
|
||||
+++ b/src/utils/time_utils.cpp
|
||||
@@ -31,6 +31,7 @@ Scalar variance(const Eigen::Matrix<Scalar, N, 1>& vec) {
|
||||
}
|
||||
|
||||
ExecutionStats::Meta& ExecutionStats::add(const std::string& name, double value) {
|
||||
+ if (!enabled) return discarded_;
|
||||
auto [it, new_item] = stats_.try_emplace(name);
|
||||
if (new_item) {
|
||||
order_.push_back(name);
|
||||
@@ -41,6 +42,7 @@ ExecutionStats::Meta& ExecutionStats::add(const std::string& name, double value)
|
||||
}
|
||||
|
||||
ExecutionStats::Meta& ExecutionStats::add(const std::string& name, const Eigen::VectorXd& value) {
|
||||
+ if (!enabled) return discarded_;
|
||||
auto [it, new_item] = stats_.try_emplace(name);
|
||||
if (new_item) {
|
||||
order_.push_back(name);
|
||||
@@ -56,6 +58,7 @@ ExecutionStats::Meta& ExecutionStats::add(const std::string& name, const Eigen::
|
||||
}
|
||||
|
||||
void ExecutionStats::merge_all(const ExecutionStats& other) {
|
||||
+ if (!enabled) return;
|
||||
for (const auto& name : other.order_) {
|
||||
const auto& meta = other.stats_.at(name);
|
||||
std::visit(
|
||||
@@ -80,6 +83,7 @@ overload(Ts...) -> overload<Ts...>;
|
||||
} // namespace
|
||||
|
||||
void ExecutionStats::merge_sums(const ExecutionStats& other) {
|
||||
+ if (!enabled) return;
|
||||
for (const auto& name : other.order_) {
|
||||
const auto& meta = other.stats_.at(name);
|
||||
std::visit(overload{[&](const std::vector<double>& data) {
|
||||
--- a/src/vi_estimator/sqrt_keypoint_vio.cpp
|
||||
+++ b/src/vi_estimator/sqrt_keypoint_vio.cpp
|
||||
@@ -1224,6 +1224,7 @@ bool SqrtKeypointVioEstimator<Scalar_>::optimize() {
|
||||
|
||||
// timing
|
||||
ExecutionStats stats;
|
||||
+ stats.enabled = stats_all_.enabled;
|
||||
Timer timer_total;
|
||||
Timer timer_iteration;
|
||||
|
||||
@@ -1544,7 +1545,7 @@ bool SqrtKeypointVioEstimator<Scalar_>::optimize() {
|
||||
double cumulative_time = timer_total.elapsed();
|
||||
|
||||
stats.add("iteration", iteration_time).format("ms");
|
||||
- {
|
||||
+ if (stats.enabled) {
|
||||
basalt::MemoryInfo mi;
|
||||
if (get_memory_info(mi)) {
|
||||
stats.add("resident_memory", mi.resident_memory);
|
||||
--- a/src/vi_estimator/sqrt_keypoint_vo.cpp
|
||||
+++ b/src/vi_estimator/sqrt_keypoint_vo.cpp
|
||||
@@ -1108,6 +1108,7 @@ bool SqrtKeypointVoEstimator<Scalar_>::optimize() {
|
||||
|
||||
// timing
|
||||
ExecutionStats stats;
|
||||
+ stats.enabled = stats_all_.enabled;
|
||||
Timer timer_total;
|
||||
Timer timer_iteration;
|
||||
|
||||
@@ -1395,7 +1396,7 @@ bool SqrtKeypointVoEstimator<Scalar_>::optimize() {
|
||||
double cumulative_time = timer_total.elapsed();
|
||||
|
||||
stats.add("iteration", iteration_time).format("ms");
|
||||
- {
|
||||
+ if (stats.enabled) {
|
||||
basalt::MemoryInfo mi;
|
||||
if (get_memory_info(mi)) {
|
||||
stats.add("resident_memory", mi.resident_memory);
|
||||
--- a/src/vit/vit_tracker.cpp
|
||||
+++ b/src/vit/vit_tracker.cpp
|
||||
@@ -398,6 +398,8 @@ struct Tracker::Implementation {
|
||||
ASSERT_(image_data_queue != nullptr);
|
||||
|
||||
vio = VioEstimatorFactory::getVioEstimator(vio_config, calib, constants::g, true, use_double);
|
||||
+ // Nothing reads these stats in a live session; they would grow for as long as it runs.
|
||||
+ vio->disable_stats();
|
||||
imu_data_queue = &vio->imu_data_queue;
|
||||
ASSERT_(imu_data_queue != nullptr);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "util/u_pretty_print.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/resource.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sched.h>
|
||||
@@ -104,6 +105,17 @@ u_linux_try_to_set_realtime_priority_on_thread(enum u_logging_level log_level, c
|
||||
name = str;
|
||||
}
|
||||
|
||||
/*
|
||||
* With a zero RLIMIT_RTTIME the kernel kills the whole process at the first
|
||||
* scheduler tick that lands while a realtime thread runs, even one that sleeps
|
||||
* every iteration. SteamVR's vrserver runs drivers with this limit.
|
||||
*/
|
||||
struct rlimit rttime;
|
||||
if (getrlimit(RLIMIT_RTTIME, &rttime) == 0 && (rttime.rlim_cur == 0 || rttime.rlim_max == 0)) {
|
||||
LOG_W("Keeping normal priority for thread '%s': RLIMIT_RTTIME is 0", name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (log_level <= U_LOGGING_DEBUG) {
|
||||
u_pp(dg, "Trying to raise priority on thread '%s'\n\t", name);
|
||||
u_pp(dg, "before: ");
|
||||
|
||||
@@ -42,23 +42,42 @@ bool rift_s_prepare_session_origin(const std::string &path)
|
||||
auto universes = cJSON_GetObjectItemCaseSensitive(document.get(), "universes");
|
||||
if (!cJSON_IsString(id) || std::strcmp(id->valuestring, "chaperone_info") || !cJSON_IsArray(universes))
|
||||
return false;
|
||||
Json origin(cJSON_Parse("{\"universeID\":\"3\",\"seated\":{\"translation\":[0,0,0],\"yaw\":0},\"standing\":{\"translation\":[0,0,0],\"yaw\":0}}"), cJSON_Delete);
|
||||
if (!origin) return false;
|
||||
// Each session re-bases tracking, so offsets saved in universe 3 (for example by
|
||||
// Quick Calibrate) would misplace the floor next time. Reset it to the plain origin.
|
||||
int index = 0;
|
||||
cJSON *entry;
|
||||
auto plain = [](const char *id) {
|
||||
return Json(cJSON_Parse((std::string("{\"universeID\":\"") + id +
|
||||
"\",\"seated\":{\"translation\":[0,0,0],\"yaw\":0},"
|
||||
"\"standing\":{\"translation\":[0,0,0],\"yaw\":0}}").c_str()),
|
||||
cJSON_Delete);
|
||||
};
|
||||
cJSON *session = nullptr, *room = nullptr, *entry;
|
||||
cJSON_ArrayForEach(entry, universes) {
|
||||
auto universe = cJSON_GetObjectItemCaseSensitive(entry, "universeID");
|
||||
if (cJSON_IsString(universe) && std::strcmp(universe->valuestring, "3") == 0) {
|
||||
if (cJSON_Compare(entry, origin.get(), true)) return true;
|
||||
if (!cJSON_ReplaceItemInArray(universes, index, origin.get())) return false;
|
||||
break;
|
||||
}
|
||||
++index;
|
||||
if (!cJSON_IsString(universe)) continue;
|
||||
if (std::strcmp(universe->valuestring, "3") == 0) session = entry;
|
||||
if (std::strcmp(universe->valuestring, "2") == 0) room = entry;
|
||||
}
|
||||
if (!entry && !cJSON_AddItemToArray(universes, origin.get())) return false;
|
||||
origin.release();
|
||||
bool changed = false;
|
||||
// Each session re-bases tracking, so offsets saved in universe 3 (for example by
|
||||
// Quick Calibrate) would misplace the floor next time. Reset it to the plain origin.
|
||||
auto origin = plain("3");
|
||||
if (!origin) return false;
|
||||
if (!session || !cJSON_Compare(session, origin.get(), true)) {
|
||||
if (session ? !cJSON_ReplaceItemViaPointer(universes, session, origin.get())
|
||||
: !cJSON_AddItemToArray(universes, origin.get()))
|
||||
return false;
|
||||
origin.release();
|
||||
changed = true;
|
||||
}
|
||||
// Room Setup moves to universe 2 once tracking is in setup. Without an entry SteamVR has no
|
||||
// calibration for the frame being set up ("Failed to update shared data"), and a first setup
|
||||
// shows no floor and takes no strokes. Seed the same plain origin so the switch keeps the
|
||||
// frame; Accept replaces it with the drawn room. A saved room is never touched.
|
||||
if (!room) {
|
||||
auto seed = plain("2");
|
||||
if (!seed || !cJSON_AddItemToArray(universes, seed.get())) return false;
|
||||
seed.release();
|
||||
changed = true;
|
||||
}
|
||||
if (!changed) return true;
|
||||
std::unique_ptr<char, decltype(&cJSON_free)> bytes(cJSON_PrintUnformatted(document.get()), cJSON_free);
|
||||
if (!bytes) return false;
|
||||
std::string temporary = path + ".monado-XXXXXX";
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <string>
|
||||
|
||||
// Write a parseable, unbounded session origin before publishing universe 3, replacing any
|
||||
// offsets left in universe 3 by an earlier session. Other universes remain unchanged.
|
||||
// offsets left in universe 3 by an earlier session. A missing universe 2 gets the same origin
|
||||
// so a first Room Setup has a calibrated frame; an existing universe 2 remains unchanged.
|
||||
bool rift_s_prepare_session_origin(const std::string &path);
|
||||
bool rift_s_prepare_session_origin();
|
||||
@@ -21,6 +21,8 @@
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "math/m_api.h"
|
||||
#include "ovrd_log.hpp"
|
||||
@@ -28,6 +30,7 @@
|
||||
#include "ovrd_display.hpp"
|
||||
#include "ovrd_camera.hpp"
|
||||
#include "ovrd_roomsetup_guard_process.hpp"
|
||||
#include "ovrd_realtime.hpp"
|
||||
#include "ovrd_world_universe.hpp"
|
||||
#include "ovrd_chaperone_origin.hpp"
|
||||
#include "../../drivers/rift_s/rift_s_world_control.h"
|
||||
@@ -1948,6 +1951,23 @@ CServerDriver_Monado g_serverDriverMonado;
|
||||
|
||||
#define NUM_XDEVS 16
|
||||
|
||||
#ifdef __linux__
|
||||
static void
|
||||
warn_realtime_compositor()
|
||||
{
|
||||
char server[4096];
|
||||
ssize_t length = readlink("/proc/self/exe", server, sizeof(server) - 1);
|
||||
if (length <= 0)
|
||||
return;
|
||||
server[length] = 0;
|
||||
std::string launcher = (std::filesystem::path(server).parent_path() / "vrcompositor-launcher").string();
|
||||
if (ovrd_realtime_compositor_killed(launcher.c_str()))
|
||||
ovrd_log("WARNING: %s has CAP_SYS_NICE while vrserver's RLIMIT_RTTIME is 0. The kernel kills the "
|
||||
"compositor as soon as its realtime threads run. Remove it: sudo setcap -r %s\n",
|
||||
launcher.c_str(), launcher.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
vr::EVRInitError
|
||||
CServerDriver_Monado::Init(vr::IVRDriverContext *pDriverContext)
|
||||
{
|
||||
@@ -1956,6 +1976,9 @@ CServerDriver_Monado::Init(vr::IVRDriverContext *pDriverContext)
|
||||
ovrd_log_init(vr::VRDriverLog());
|
||||
|
||||
ovrd_log("Initializing Monado driver\n");
|
||||
#ifdef __linux__
|
||||
warn_realtime_compositor();
|
||||
#endif
|
||||
if (debug_get_bool_option_steamvr_bench()) {
|
||||
vr::EVRSettingsError error = vr::VRSettingsError_None;
|
||||
m_savedBenchPause = vr::VRSettings()->GetBool("power", "pauseCompositorOnStandby", &error);
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2026, lbgos
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
#pragma once
|
||||
#ifdef __linux__
|
||||
#include <endian.h>
|
||||
#include <linux/capability.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/xattr.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/*!
|
||||
* vrcompositor inherits vrserver's RLIMIT_RTTIME. When this process has a zero limit and the
|
||||
* launcher at @p launcher carries CAP_SYS_NICE, the compositor's realtime threads are SIGKILLed
|
||||
* by the kernel at the first scheduler tick. SteamVR then only reports a compositor crash.
|
||||
*/
|
||||
static inline bool
|
||||
ovrd_realtime_compositor_killed(const char *launcher)
|
||||
{
|
||||
struct rlimit rttime;
|
||||
if (getrlimit(RLIMIT_RTTIME, &rttime) != 0 || (rttime.rlim_cur != 0 && rttime.rlim_max != 0))
|
||||
return false;
|
||||
struct vfs_ns_cap_data caps = {};
|
||||
ssize_t size = getxattr(launcher, "security.capability", &caps, sizeof(caps));
|
||||
if (size < (ssize_t)(sizeof(caps.magic_etc) + sizeof(caps.data[0])))
|
||||
return false;
|
||||
return (le32toh(caps.data[0].permitted) & (1u << CAP_SYS_NICE)) != 0;
|
||||
}
|
||||
#endif
|
||||
@@ -92,8 +92,9 @@ public:
|
||||
if (!transforms(standing, setup))
|
||||
return false;
|
||||
// An untrusted saved plane belongs to another tracking session. Offer this
|
||||
// session's nominal floor instead (raw Y=0, where the unanchored universe
|
||||
// puts it): without any result the native step shows no floor to adjust.
|
||||
// session's nominal floor instead (raw Y=0, the plain origin of universe 3 and
|
||||
// of a first-time universe 2): without any result the native step shows no
|
||||
// floor to adjust.
|
||||
const vr::HmdMatrix34_t session_origin{{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}}};
|
||||
auto y = saved_floor(saved_plane_trusted ? standing : session_origin, setup);
|
||||
if (!y)
|
||||
|
||||
@@ -240,6 +240,38 @@ TEST_CASE("Native session origin has no geometry and preserves the saved room")
|
||||
CHECK(read_file(fixture.path) == "broken json");
|
||||
}
|
||||
|
||||
TEST_CASE("First Room Setup switches into a universe 2 with the session origin")
|
||||
{
|
||||
LogFixture fixture;
|
||||
// An earlier release left only the session universe; a fresh install has no file at all.
|
||||
const std::string session = R"({"universeID":"3","seated":{"translation":[0,0,0],"yaw":0},"standing":{"translation":[0,0,0],"yaw":0}})";
|
||||
for (bool existing : {true, false}) {
|
||||
if (existing) fixture.write(R"({"jsonid":"chaperone_info","version":5,"universes":[)" + session + "]}");
|
||||
else fs::remove(fixture.path);
|
||||
REQUIRE(rift_s_prepare_session_origin(fixture.path.string()));
|
||||
auto bytes = read_file(fixture.path);
|
||||
std::unique_ptr<cJSON, decltype(&cJSON_Delete)> document(cJSON_Parse(bytes.c_str()), cJSON_Delete);
|
||||
auto universes = cJSON_GetObjectItemCaseSensitive(document.get(), "universes");
|
||||
REQUIRE(cJSON_GetArraySize(universes) == 2);
|
||||
cJSON *room = nullptr, *origin = nullptr, *entry;
|
||||
cJSON_ArrayForEach(entry, universes) {
|
||||
auto id = cJSON_GetObjectItemCaseSensitive(entry, "universeID")->valuestring;
|
||||
(std::string(id) == "2" ? room : origin) = entry;
|
||||
}
|
||||
REQUIRE(room);
|
||||
REQUIRE(origin);
|
||||
// Same frame on both sides of the switch, and no room geometry to bind a world map to.
|
||||
CHECK(cJSON_Compare(cJSON_GetObjectItemCaseSensitive(room, "standing"),
|
||||
cJSON_GetObjectItemCaseSensitive(origin, "standing"), true));
|
||||
CHECK(cJSON_Compare(cJSON_GetObjectItemCaseSensitive(room, "seated"),
|
||||
cJSON_GetObjectItemCaseSensitive(origin, "seated"), true));
|
||||
CHECK(cJSON_GetObjectItemCaseSensitive(room, "play_area") == nullptr);
|
||||
CHECK(cJSON_GetObjectItemCaseSensitive(room, "collision_bounds") == nullptr);
|
||||
REQUIRE(rift_s_prepare_session_origin(fixture.path.string()));
|
||||
CHECK(read_file(fixture.path) == bytes);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Display recovery requires one exact runtime process and the expected server PID")
|
||||
{
|
||||
auto executable = fs::read_symlink("/proc/self/exe");
|
||||
|
||||
Reference in new issue
Block a user