From cf6b27215b91d9d0465ee34b7b44a36deee30f6c Mon Sep 17 00:00:00 2001
From: galister <22305755+galister@users.noreply.github.com>
Date: Sat, 1 Aug 2026 22:19:37 +0900
Subject: [PATCH] openvr: stereo mode
---
Cargo.lock | 18 ++++-----
wayvr/Cargo.toml | 2 +-
wayvr/src/assets/gui/edit.xml | 4 +-
wayvr/src/backend/openvr/mod.rs | 1 +
wayvr/src/backend/openvr/overlay.rs | 38 ++++++++++++++++++-
wayvr/src/overlays/edit/mod.rs | 26 ++++++++-----
wayvr/src/overlays/screen/backend.rs | 55 +++++++++++----------------
wayvr/src/overlays/screen/capture.rs | 10 +++--
wayvr/src/overlays/screen/mirror.rs | 5 +--
wayvr/src/overlays/screen/pw.rs | 5 +--
wayvr/src/overlays/screen/wl.rs | 1 -
wayvr/src/overlays/screen/x11.rs | 8 +---
wayvr/src/overlays/wayvr.rs | 56 ++++++++++++----------------
13 files changed, 124 insertions(+), 105 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 83fc4995..ea44c1c1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1734,7 +1734,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
- "windows-sys 0.61.2",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -3148,7 +3148,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
- "windows-sys 0.61.2",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -3650,7 +3650,7 @@ dependencies = [
[[package]]
name = "ovr_overlay"
version = "0.0.0"
-source = "git+https://github.com/galister/ovr_overlay_oyasumi?rev=e477bd2a9e04293ea68c1e7529ef2cb131f32acc#e477bd2a9e04293ea68c1e7529ef2cb131f32acc"
+source = "git+https://github.com/galister/ovr_overlay_oyasumi?rev=0ad843c793aec71f37e4efd43942607c94cff34c#0ad843c793aec71f37e4efd43942607c94cff34c"
dependencies = [
"byteorder",
"derive_more",
@@ -3665,7 +3665,7 @@ dependencies = [
[[package]]
name = "ovr_overlay_sys"
version = "0.0.0"
-source = "git+https://github.com/galister/ovr_overlay_oyasumi?rev=e477bd2a9e04293ea68c1e7529ef2cb131f32acc#e477bd2a9e04293ea68c1e7529ef2cb131f32acc"
+source = "git+https://github.com/galister/ovr_overlay_oyasumi?rev=0ad843c793aec71f37e4efd43942607c94cff34c#0ad843c793aec71f37e4efd43942607c94cff34c"
dependencies = [
"autocxx",
"autocxx-build",
@@ -4053,7 +4053,7 @@ version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042"
dependencies = [
- "heck 0.5.0",
+ "heck 0.4.1",
"itertools 0.14.0",
"log",
"multimap",
@@ -4666,7 +4666,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.12.1",
- "windows-sys 0.61.2",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -5400,10 +5400,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [
"fastrand",
- "getrandom 0.4.3",
+ "getrandom 0.3.4",
"once_cell",
"rustix 1.1.4",
- "windows-sys 0.61.2",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -6424,7 +6424,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
- "windows-sys 0.61.2",
+ "windows-sys 0.48.0",
]
[[package]]
diff --git a/wayvr/Cargo.toml b/wayvr/Cargo.toml
index 8f608ce4..30d45b23 100644
--- a/wayvr/Cargo.toml
+++ b/wayvr/Cargo.toml
@@ -86,7 +86,7 @@ mint = "0.5.9"
openxr = { version = "0.21.1", features = ["linked", "mint"], optional = true }
ovr_overlay = {
git = "https://github.com/galister/ovr_overlay_oyasumi",
- rev = "e477bd2a9e04293ea68c1e7529ef2cb131f32acc",
+ rev = "0ad843c793aec71f37e4efd43942607c94cff34c",
features = [
"ovr_input",
"ovr_system",
diff --git a/wayvr/src/assets/gui/edit.xml b/wayvr/src/assets/gui/edit.xml
index 6efc8632..a77bcfed 100644
--- a/wayvr/src/assets/gui/edit.xml
+++ b/wayvr/src/assets/gui/edit.xml
@@ -79,8 +79,8 @@
-
-
+
+
diff --git a/wayvr/src/backend/openvr/mod.rs b/wayvr/src/backend/openvr/mod.rs
index add2c4dc..f2bbf4dc 100644
--- a/wayvr/src/backend/openvr/mod.rs
+++ b/wayvr/src/backend/openvr/mod.rs
@@ -331,6 +331,7 @@ pub fn openvr_run(args: &Args) -> Result<(), BackendError> {
views: smallvec![o.ensure_staging_image(&mut app, meta.extent)?],
};
let mut rdr = RenderResources::new(app.gfx.clone(), tgt, &meta)?;
+ o.set_stereo_mode(meta.stereo);
o.render(&mut app, &mut rdr)?;
o.data.image_dirty = true;
futures.execute_results(rdr.end()?)?;
diff --git a/wayvr/src/backend/openvr/overlay.rs b/wayvr/src/backend/openvr/overlay.rs
index ba769f44..b368d973 100644
--- a/wayvr/src/backend/openvr/overlay.rs
+++ b/wayvr/src/backend/openvr/overlay.rs
@@ -5,13 +5,14 @@ use glam::Vec4;
use ovr_overlay::{
overlay::{OverlayHandle, OverlayManager},
pose::Matrix3x4,
- sys::{ETrackingUniverseOrigin, VRVulkanTextureData_t},
+ sys::{ETrackingUniverseOrigin, VROverlayFlags, VRVulkanTextureData_t},
};
use vulkano::{
Handle, VulkanObject,
image::{ImageUsage, view::ImageView},
};
use wgui::gfx::WGfx;
+use wlx_common::overlays::StereoMode;
use crate::{graphics::ExtentExt, state::AppState, windowing::window::OverlayWindowData};
@@ -26,6 +27,8 @@ pub(super) struct OpenVrOverlayData {
pub(super) override_width: bool,
pub(super) image_view: Option
>,
pub(super) image_dirty: bool,
+ pub(super) cur_stereo_mode: StereoMode,
+ pub(super) pending_stereo_mode: Option,
}
impl OverlayWindowData {
@@ -56,6 +59,7 @@ impl OverlayWindowData {
self.upload_width(overlay);
self.upload_color(overlay);
self.upload_alpha(overlay);
+ self.upload_flags(overlay);
self.upload_curvature(overlay);
self.upload_sort_order(overlay);
@@ -122,12 +126,19 @@ impl OverlayWindowData {
self.upload_transform(universe, overlay);
self.upload_alpha(overlay);
+ self.upload_flags(overlay);
self.config.dirty = false;
}
self.upload_texture(overlay, graphics);
}
}
+ pub(super) fn set_stereo_mode(&mut self, stereo_mode: StereoMode) {
+ if self.data.cur_stereo_mode != stereo_mode {
+ self.data.pending_stereo_mode = Some(stereo_mode);
+ }
+ }
+
fn show_internal(
&mut self,
overlay: &mut OverlayManager,
@@ -161,6 +172,31 @@ impl OverlayWindowData {
self.config.backend.pause(app)
}
+ const fn stereo_mode_to_flag(stereo_mode: StereoMode) -> Option {
+ match stereo_mode {
+ StereoMode::LeftRight => Some(VROverlayFlags::VROverlayFlags_SideBySide_Parallel),
+ StereoMode::RightLeft => Some(VROverlayFlags::VROverlayFlags_SideBySide_Crossed),
+ _ => None,
+ }
+ }
+
+ fn upload_flags(&mut self, overlay: &mut OverlayManager) {
+ let Some(handle) = self.data.handle else {
+ log::debug!("{}: No overlay handle", self.config.name);
+ return;
+ };
+
+ if let Some(pending) = self.data.pending_stereo_mode.take() {
+ if let Some(flag) = Self::stereo_mode_to_flag(self.data.cur_stereo_mode) {
+ let _ = overlay.set_overlay_flag(handle, flag, false);
+ }
+ if let Some(flag) = Self::stereo_mode_to_flag(pending) {
+ let _ = overlay.set_overlay_flag(handle, flag, true);
+ }
+ self.data.cur_stereo_mode = pending;
+ }
+ }
+
pub(super) fn upload_alpha(&self, overlay: &mut OverlayManager) {
let Some(handle) = self.data.handle else {
log::debug!("{}: No overlay handle", self.config.name);
diff --git a/wayvr/src/overlays/edit/mod.rs b/wayvr/src/overlays/edit/mod.rs
index 7600fc95..041c24d4 100644
--- a/wayvr/src/overlays/edit/mod.rs
+++ b/wayvr/src/overlays/edit/mod.rs
@@ -528,16 +528,22 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result {
}
});
- let mut panel = GuiPanel::new_from_template(
- app,
- "gui/edit.xml",
- state,
- NewGuiPanelParams {
- on_custom_attrib: Some(on_custom_attrib),
- resize_to_parent: true,
- ..Default::default()
- },
- )?;
+ let mut params = NewGuiPanelParams {
+ on_custom_attrib: Some(on_custom_attrib),
+ resize_to_parent: true,
+ ..Default::default()
+ };
+ params.extra_vars.insert(
+ "openvr".into(),
+ (if app.xr_backend.is_open_vr() {
+ "1"
+ } else {
+ "0"
+ })
+ .into(),
+ );
+
+ let mut panel = GuiPanel::new_from_template(app, "gui/edit.xml", state, params)?;
panel.state.pos = new_pos_tab_handler(&mut panel)?;
panel.state.stereo = new_stereo_tab_handler(&mut panel)?;
diff --git a/wayvr/src/overlays/screen/backend.rs b/wayvr/src/overlays/screen/backend.rs
index 0485e593..497894ff 100644
--- a/wayvr/src/overlays/screen/backend.rs
+++ b/wayvr/src/overlays/screen/backend.rs
@@ -7,7 +7,6 @@ use glam::{Affine2, DVec2, Vec2, vec2};
use wlx_capture::{WlxCapture, frame::Transform};
use crate::{
- XrBackend,
backend::input::{HoverResult, PointerHit, PointerMode},
overlays::screen::capture::MyFirstDmaExporter,
state::AppState,
@@ -55,7 +54,7 @@ pub struct ScreenBackend {
meta: Option,
mouse_transform: Affine2,
interaction_transform: Option,
- stereo: Option,
+ stereo: StereoMode,
stereo_full_frame: bool,
stereo_adjust_mouse: bool,
pub(super) logical_pos: Vec2,
@@ -67,7 +66,6 @@ pub struct ScreenBackend {
impl ScreenBackend {
pub(super) fn new_raw(
name: Arc,
- xr_backend: XrBackend,
capture_type: CaptureType,
capture: Box>,
) -> Self {
@@ -80,7 +78,7 @@ impl ScreenBackend {
meta: None,
mouse_transform: Affine2::ZERO,
interaction_transform: None,
- stereo: xr_backend.is_open_xr().then_some(StereoMode::None),
+ stereo: StereoMode::None,
stereo_full_frame: false,
stereo_adjust_mouse: false,
logical_pos: Vec2::ZERO,
@@ -94,10 +92,8 @@ impl ScreenBackend {
let mut size = self.logical_size;
let pos = self.logical_pos;
- if self.stereo_adjust_mouse
- && let Some(stereo) = self.stereo.as_ref()
- {
- match stereo {
+ if self.stereo_adjust_mouse {
+ match self.stereo {
StereoMode::LeftRight | StereoMode::RightLeft => size.x *= 0.5,
StereoMode::TopBottom | StereoMode::BottomTop => size.y *= 0.5,
_ => {}
@@ -209,21 +205,18 @@ impl OverlayBackend for ScreenBackend {
}
if let Some(frame) = self.capture.receive() {
- let stereo = self.stereo.unwrap_or(StereoMode::None);
- let mut meta = frame.get_frame_meta(&app.session.config, stereo);
+ let mut meta = frame.get_frame_meta(&app.session.config, self.stereo);
- if let Some(stereo) = self.stereo {
- // Apply stereo full frame logic
- if self.stereo_full_frame {
- match stereo {
- StereoMode::LeftRight | StereoMode::RightLeft => {
- meta.extent[0] /= 2;
- }
- StereoMode::TopBottom | StereoMode::BottomTop => {
- meta.extent[1] /= 2;
- }
- _ => {}
+ // Apply stereo full frame logic
+ if self.stereo_full_frame {
+ match self.stereo {
+ StereoMode::LeftRight | StereoMode::RightLeft => {
+ meta.extent[0] /= 2;
}
+ StereoMode::TopBottom | StereoMode::BottomTop => {
+ meta.extent[1] /= 2;
+ }
+ _ => {}
}
}
@@ -241,7 +234,7 @@ impl OverlayBackend for ScreenBackend {
}
} else {
let pipeline =
- ScreenPipeline::new(&meta, app, stereo, [0., 0.], frame.format.transform)?;
+ ScreenPipeline::new(&meta, app, self.stereo, [0., 0.], frame.format.transform)?;
self.pipeline = Some(pipeline);
self.interaction_transform = Some(ui_transform(meta.extent));
}
@@ -360,7 +353,7 @@ impl OverlayBackend for ScreenBackend {
}
fn get_attrib(&self, attrib: BackendAttrib) -> Option {
match attrib {
- BackendAttrib::Stereo => self.stereo.map(BackendAttribValue::Stereo),
+ BackendAttrib::Stereo => Some(BackendAttribValue::Stereo(self.stereo)),
BackendAttrib::MouseTransform => Some(BackendAttribValue::MouseTransform(
self.mouse_transform_override,
)),
@@ -373,19 +366,15 @@ impl OverlayBackend for ScreenBackend {
_ => None,
}
}
- fn set_attrib(&mut self, _app: &mut AppState, value: BackendAttribValue) -> bool {
+ fn set_attrib(&mut self, app: &mut AppState, value: BackendAttribValue) -> bool {
match value {
BackendAttribValue::Stereo(new) => {
- if let Some(stereo) = self.stereo.as_mut() {
- log::debug!("{}: stereo: {stereo:?} → {new:?}", self.name);
- *stereo = new;
- if let Some(pipeline) = self.pipeline.as_mut() {
- pipeline.ensure_stereo(new);
- }
- true
- } else {
- false
+ log::debug!("{}: stereo: {:?} → {new:?}", self.name, self.stereo);
+ self.stereo = new;
+ if let Some(pipeline) = self.pipeline.as_mut() {
+ pipeline.ensure_stereo(app.xr_backend, new);
}
+ true
}
BackendAttribValue::MouseTransform(new) => {
self.mouse_transform_override = new;
diff --git a/wayvr/src/overlays/screen/capture.rs b/wayvr/src/overlays/screen/capture.rs
index e670b32f..1489a497 100644
--- a/wayvr/src/overlays/screen/capture.rs
+++ b/wayvr/src/overlays/screen/capture.rs
@@ -28,7 +28,7 @@ use wlx_capture::{
DrmFormat, DrmFourcc, DrmModifier, WlxCapture,
frame::{self as wlx_frame, DmaExporter, FrameFormat, MouseMeta, WlxFrame},
};
-use wlx_common::{config::GeneralConfig, overlays::StereoMode};
+use wlx_common::{XrBackend, config::GeneralConfig, overlays::StereoMode};
use crate::{
graphics::{
@@ -87,13 +87,17 @@ impl ScreenPipeline {
stereo,
stereo_adjust_mouse: false,
};
- me.ensure_stereo(stereo);
+ me.ensure_stereo(app.xr_backend, stereo);
Ok(me)
}
- pub fn ensure_stereo(&mut self, stereo: StereoMode) {
+ pub fn ensure_stereo(&mut self, xr_backend: XrBackend, stereo: StereoMode) {
if self.stereo == stereo {
return;
+ } else if xr_backend.is_open_vr() {
+ // OpenVR takes raw SBS texture
+ self.stereo = StereoMode::None;
+ return;
}
self.stereo = stereo;
diff --git a/wayvr/src/overlays/screen/mirror.rs b/wayvr/src/overlays/screen/mirror.rs
index 3e7801b1..fe6e5b88 100644
--- a/wayvr/src/overlays/screen/mirror.rs
+++ b/wayvr/src/overlays/screen/mirror.rs
@@ -83,10 +83,9 @@ pub fn new_mirror(name: Arc, app: &mut AppState) -> anyhow::Result>,
- app: &mut AppState,
+ _app: &mut AppState,
) -> Box {
- let renderer =
- ScreenBackend::new_raw(name.clone(), app.xr_backend, CaptureType::PipeWire, capture);
+ let renderer = ScreenBackend::new_raw(name.clone(), CaptureType::PipeWire, capture);
let backend = MirrorBackend(renderer);
diff --git a/wayvr/src/overlays/screen/pw.rs b/wayvr/src/overlays/screen/pw.rs
index 782953d8..e100def0 100644
--- a/wayvr/src/overlays/screen/pw.rs
+++ b/wayvr/src/overlays/screen/pw.rs
@@ -76,12 +76,11 @@ impl ScreenCastBackend {
logical_pos: Vec2,
logical_size: Vec2,
capture: Box>,
- app: &mut AppState,
+ _app: &mut AppState,
) -> Box {
use wlx_capture::frame::Transform;
- let mut backend =
- ScreenBackend::new_raw(name, app.xr_backend, CaptureType::PipeWire, capture);
+ let mut backend = ScreenBackend::new_raw(name, CaptureType::PipeWire, capture);
backend.logical_pos = logical_pos;
backend.logical_size = logical_size;
diff --git a/wayvr/src/overlays/screen/wl.rs b/wayvr/src/overlays/screen/wl.rs
index ba1692a6..ea7624a8 100644
--- a/wayvr/src/overlays/screen/wl.rs
+++ b/wayvr/src/overlays/screen/wl.rs
@@ -29,7 +29,6 @@ impl ScreenBackend {
);
Some(Self::new_raw(
output.name.clone(),
- app.xr_backend,
CaptureType::ScreenCopy,
capture,
))
diff --git a/wayvr/src/overlays/screen/x11.rs b/wayvr/src/overlays/screen/x11.rs
index 783f518e..e45c9672 100644
--- a/wayvr/src/overlays/screen/x11.rs
+++ b/wayvr/src/overlays/screen/x11.rs
@@ -31,12 +31,7 @@ impl ScreenBackend {
app.gfx_extras.queue_capture,
XshmCapture::new(screen.clone())
);
- Self::new_raw(
- screen.name.clone(),
- app.xr_backend,
- CaptureType::Xshm,
- capture,
- )
+ Self::new_raw(screen.name.clone(), CaptureType::Xshm, capture)
}
}
@@ -103,7 +98,6 @@ pub fn create_screens_x11pw(app: &mut AppState) -> anyhow::Result,
mouse: Option,
- stereo: Option,
+ stereo: StereoMode,
stereo_full_frame: bool,
stereo_adjust_mouse: bool,
cur_image: Option>,
@@ -248,7 +248,7 @@ impl WvrWindowBackend {
just_resumed: false,
meta: None,
mouse: None,
- stereo: app.xr_backend.is_open_xr().then_some(StereoMode::None),
+ stereo: StereoMode::None,
stereo_full_frame: false,
stereo_adjust_mouse: false,
cur_image: None,
@@ -316,10 +316,8 @@ impl WvrWindowBackend {
-((BORDER_SIZE + BAR_SIZE) as f32) / meta.extent[1] as f32,
);
- if self.stereo_adjust_mouse
- && let Some(stereo) = self.stereo
- {
- match stereo {
+ if self.stereo_adjust_mouse {
+ match self.stereo {
StereoMode::LeftRight | StereoMode::RightLeft => {
scale.x *= 0.5;
translation.x *= 0.5;
@@ -591,22 +589,20 @@ impl OverlayBackend for WvrWindowBackend {
extent: surf.image.extent_u32arr(),
format: surf.image.format(),
clear: WGfxClearMode::Clear([0.0, 0.0, 0.0, 0.0]),
- stereo: self.stereo.unwrap_or(StereoMode::None),
+ stereo: self.stereo,
..Default::default()
};
- if let Some(stereo) = self.stereo {
- // Apply stereo full frame logic
- if self.stereo_full_frame {
- match stereo {
- StereoMode::LeftRight | StereoMode::RightLeft => {
- meta.extent[0] /= 2;
- }
- StereoMode::TopBottom | StereoMode::BottomTop => {
- meta.extent[1] /= 2;
- }
- _ => {}
+ // Apply stereo full frame logic
+ if self.stereo_full_frame {
+ match self.stereo {
+ StereoMode::LeftRight | StereoMode::RightLeft => {
+ meta.extent[0] /= 2;
}
+ StereoMode::TopBottom | StereoMode::BottomTop => {
+ meta.extent[1] /= 2;
+ }
+ _ => {}
}
}
@@ -641,7 +637,7 @@ impl OverlayBackend for WvrWindowBackend {
let pipeline = ScreenPipeline::new(
&meta,
app,
- self.stereo.unwrap_or(StereoMode::None),
+ self.stereo,
[BORDER_SIZE as _, (BAR_SIZE + BORDER_SIZE) as _],
Transform::Normal,
)?;
@@ -1025,7 +1021,7 @@ impl OverlayBackend for WvrWindowBackend {
fn get_attrib(&self, attrib: BackendAttrib) -> Option {
match attrib {
- BackendAttrib::Stereo => self.stereo.map(BackendAttribValue::Stereo),
+ BackendAttrib::Stereo => Some(BackendAttribValue::Stereo(self.stereo)),
BackendAttrib::Icon => Some(BackendAttribValue::Icon(self.icon.clone())),
BackendAttrib::StereoFullFrame => {
Some(BackendAttribValue::StereoFullFrame(self.stereo_full_frame))
@@ -1041,19 +1037,15 @@ impl OverlayBackend for WvrWindowBackend {
fn set_attrib(&mut self, app: &mut AppState, value: BackendAttribValue) -> bool {
match value {
BackendAttribValue::Stereo(new) => {
- if let Some(stereo) = self.stereo.as_mut() {
- log::debug!("{}: stereo: {stereo:?} → {new:?}", self.name);
- *stereo = new;
- if let Some(meta) = self.meta.clone() {
- let _ = self.apply_extent(app, &meta);
- }
- if let Some(pipeline) = self.pipeline.as_mut() {
- pipeline.ensure_stereo(new);
- }
- true
- } else {
- false
+ log::debug!("{}: stereo: {:?} → {new:?}", self.name, self.stereo);
+ self.stereo = new;
+ if let Some(meta) = self.meta.clone() {
+ let _ = self.apply_extent(app, &meta);
}
+ if let Some(pipeline) = self.pipeline.as_mut() {
+ pipeline.ensure_stereo(app.xr_backend, new);
+ }
+ true
}
BackendAttribValue::StereoFullFrame(new) => {
self.stereo_full_frame = new;