openvr: stereo mode

This commit is contained in:
galister 2026-08-01 22:19:37 +09:00
parent 98d8a3138d
commit cf6b27215b
13 changed files with 124 additions and 105 deletions

18
Cargo.lock generated
View File

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

View File

@ -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",

View File

@ -79,8 +79,8 @@
<PosButton id="stereo_none" src="edit/close.svg" tooltip="DISABLED" press="::EditModeSetStereo none" />
<PosButton id="stereo_leftright" src="edit/3d_leftright.svg" tooltip="EDIT_MODE.STEREO_3D_MODE.SPLIT_LEFT_RIGHT" press="::EditModeSetStereo leftright" />
<PosButton id="stereo_rightleft" src="edit/3d_rightleft.svg" tooltip="EDIT_MODE.STEREO_3D_MODE.SPLIT_RIGHT_LEFT" press="::EditModeSetStereo rightleft" />
<PosButton id="stereo_topbottom" src="edit/3d_topbottom.svg" tooltip="EDIT_MODE.STEREO_3D_MODE.SPLIT_TOP_BOTTOM" press="::EditModeSetStereo topbottom" />
<PosButton id="stereo_bottomtop" src="edit/3d_bottomtop.svg" tooltip="EDIT_MODE.STEREO_3D_MODE.SPLIT_BOTTOM_TOP" press="::EditModeSetStereo bottomtop" />
<PosButton id="stereo_topbottom" src="edit/3d_topbottom.svg" tooltip="EDIT_MODE.STEREO_3D_MODE.SPLIT_TOP_BOTTOM" press="::EditModeSetStereo topbottom" skip="${openvr}" />
<PosButton id="stereo_bottomtop" src="edit/3d_bottomtop.svg" tooltip="EDIT_MODE.STEREO_3D_MODE.SPLIT_BOTTOM_TOP" press="::EditModeSetStereo bottomtop" skip="${openvr}" />
</div>
<div justify_content="center" align_items="center" flex_direction="row" gap="16">
<CheckBox id="stereo_full_frame_box" translation="EDIT_MODE.STEREO_3D_MODE.FULL_FRAME" tooltip_side="top" />

View File

@ -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()?)?;

View File

@ -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<Arc<ImageView>>,
pub(super) image_dirty: bool,
pub(super) cur_stereo_mode: StereoMode,
pub(super) pending_stereo_mode: Option<StereoMode>,
}
impl OverlayWindowData<OpenVrOverlayData> {
@ -56,6 +59,7 @@ impl OverlayWindowData<OpenVrOverlayData> {
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<OpenVrOverlayData> {
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<OpenVrOverlayData> {
self.config.backend.pause(app)
}
const fn stereo_mode_to_flag(stereo_mode: StereoMode) -> Option<VROverlayFlags> {
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);

View File

@ -528,16 +528,22 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
}
});
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)?;

View File

@ -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<FrameMeta>,
mouse_transform: Affine2,
interaction_transform: Option<Affine2>,
stereo: Option<StereoMode>,
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<str>,
xr_backend: XrBackend,
capture_type: CaptureType,
capture: Box<dyn WlxCapture<WlxCaptureIn, WlxCaptureOut>>,
) -> 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<BackendAttribValue> {
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;

View File

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

View File

@ -83,10 +83,9 @@ pub fn new_mirror(name: Arc<str>, app: &mut AppState) -> anyhow::Result<OverlayW
_: Vec2,
_: Vec2,
capture: Box<dyn WlxCapture<WlxCaptureIn, WlxCaptureOut>>,
app: &mut AppState,
_app: &mut AppState,
) -> Box<dyn OverlayBackend> {
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);

View File

@ -76,12 +76,11 @@ impl ScreenCastBackend {
logical_pos: Vec2,
logical_size: Vec2,
capture: Box<dyn WlxCapture<WlxCaptureIn, WlxCaptureOut>>,
app: &mut AppState,
_app: &mut AppState,
) -> Box<dyn OverlayBackend> {
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;

View File

@ -29,7 +29,6 @@ impl ScreenBackend {
);
Some(Self::new_raw(
output.name.clone(),
app.xr_backend,
CaptureType::ScreenCopy,
capture,
))

View File

@ -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<ScreenCreateDa
let mut backend = ScreenBackend::new_raw(
m.name.clone(),
app.xr_backend,
CaptureType::PipeWire,
new_wlx_capture!(
app.gfx_extras.queue_capture,

View File

@ -147,7 +147,7 @@ pub struct WvrWindowBackend {
just_resumed: bool,
meta: Option<FrameMeta>,
mouse: Option<MouseMeta>,
stereo: Option<StereoMode>,
stereo: StereoMode,
stereo_full_frame: bool,
stereo_adjust_mouse: bool,
cur_image: Option<Arc<ImageView>>,
@ -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<BackendAttribValue> {
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;