diff --git a/bin/driver-proxy/src/driver/hmd.rs b/bin/driver-proxy/src/driver/hmd.rs index 67bfcb0..b22fdbd 100644 --- a/bin/driver-proxy/src/driver/hmd.rs +++ b/bin/driver-proxy/src/driver/hmd.rs @@ -4,12 +4,19 @@ use std::{ rc::Rc, }; -use crate::Result; +use crate::{ + driver_context::{self, DRIVER_CONTEXT}, + settings::{set_properties, Property, PropertyValue, PROPERTIES}, + Result, +}; use cppvtbl::{impl_vtables, HasVtable, VtableRef, WithVtables}; use lens_protocol::{Eye, LensClient}; -use openvr::HmdVector2_t; +use openvr::{ + k_unFloatPropertyTag, EPropertyWriteType, ETrackedDeviceProperty, ETrackedPropertyError, + HmdVector2_t, IVRProperties, PropertyWrite_t, +}; use tracing::{error, info, instrument}; -use vive_hid::Mode; +use vive_hid::{Mode, ViveConfig, ViveDevice}; use crate::openvr::{ DistortionCoordinates_t, DriverPose_t, EVREye, EVRInitError, ITrackedDeviceServerDriver, @@ -150,7 +157,8 @@ impl IVRDisplayComponent for HmdDisplay { #[impl_vtables(ITrackedDeviceServerDriver)] pub struct HmdDriver { - // pub steam: Rc, + pub vive: Rc, + pub vive_config: ViveConfig, pub lens: Rc, pub real: &'static VtableRef, pub mode: Mode, @@ -158,7 +166,58 @@ pub struct HmdDriver { impl ITrackedDeviceServerDriver for HmdDriver { fn Activate(&self, unObjectId: u32) -> EVRInitError { - self.real.Activate(unObjectId) + let res = self.real.Activate(unObjectId); + if res != EVRInitError::VRInitError_None { + return res; + } + let container = PROPERTIES.TrackedDeviceToPropertyContainer(unObjectId); + + set_properties( + container, + vec![ + Property::new( + ETrackedDeviceProperty::Prop_DisplayFrequency_Float, + PropertyValue::Float(self.mode.frame_rate), + ), + Property::new( + ETrackedDeviceProperty::Prop_DisplaySupportsMultipleFramerates_Bool, + PropertyValue::Bool(true), + ), + Property::new( + ETrackedDeviceProperty::Prop_SecondsFromVsyncToPhotons_Float, + PropertyValue::Float( + (1.0 / self.mode.frame_rate) + self.mode.extra_photon_vsync, + ), + ), + // Property::new( + // ETrackedDeviceProperty::Prop_MinimumIpdStepMeters_Float, + // PropertyValue::Float(0.0005), + // ), + // Property::new( + // ETrackedDeviceProperty::Prop_UserIpdMeters_Float, + // // TODO + // PropertyValue::Float(0.0005), + // ), + Property::new( + ETrackedDeviceProperty::Prop_UserHeadToEyeDepthMeters_Float, + PropertyValue::Float(0.015), + ), + Property::new( + ETrackedDeviceProperty::Prop_DisplayAvailableFrameRates_Float_Array, + PropertyValue::FloatArray(if self.mode.frame_rate == 90.0 { + vec![90.0, 120.0] + } else { + vec![120.0, 90.0] + }), + ), + Property::new( + ETrackedDeviceProperty::Prop_DisplaySupportsRuntimeFramerateChange_Bool, + PropertyValue::Bool(true), + ), + ], + ); + + EVRInitError::VRInitError_None } fn Deactivate(&self) { diff --git a/bin/driver-proxy/src/server/driver_host.rs b/bin/driver-proxy/src/server/driver_host.rs index ecee5d5..2bb18c9 100644 --- a/bin/driver-proxy/src/server/driver_host.rs +++ b/bin/driver-proxy/src/server/driver_host.rs @@ -51,15 +51,11 @@ impl IVRServerDriverHost for DriverHost { let mode = { let res = HMD_RESOLUTION.get(); let modes = vive.query_modes(); - let mode = modes - .iter() - .find(|m| m.id == res as u8) - .unwrap_or( - modes - .first() - .expect("device has at least one mode if opened"), - ) - .clone(); + let mode = *modes.iter().find(|m| m.id == res as u8).unwrap_or( + modes + .first() + .expect("device has at least one mode if opened"), + ); HMD_RESOLUTION.set(mode.id as i32); vive.set_mode(mode.id)?; @@ -81,9 +77,9 @@ impl IVRServerDriverHost for DriverHost { vive.set_brightness(brightness as u8)?; } - let config = vive.read_config()?; + let vive_config = vive.read_config()?; - let lens = start_lens_server(config.inhouse_lens_correction) + let lens = start_lens_server(vive_config.inhouse_lens_correction.clone()) .map(|v| Rc::new(v) as Rc) .unwrap_or_else(|e| { let zenity = var_os("STEAM_ZENITY").unwrap_or_else(|| OsString::from("zenity")); @@ -104,7 +100,8 @@ impl IVRServerDriverHost for DriverHost { let hmd = Box::leak(Box::new(WithVtables::new(HmdDriver { // steam, - // vive, + vive, + vive_config, lens, real, mode, diff --git a/bin/driver-proxy/src/settings.rs b/bin/driver-proxy/src/settings.rs index 525840b..3df7bf5 100644 --- a/bin/driver-proxy/src/settings.rs +++ b/bin/driver-proxy/src/settings.rs @@ -1,8 +1,13 @@ -use std::ffi::CString; +use std::ffi::{c_void, CString}; use std::{marker::PhantomData, os::raw::c_char}; use cppvtbl::VtableRef; use once_cell::sync::Lazy; +use openvr::{ + k_unBoolPropertyTag, k_unFloatPropertyTag, EPropertyWriteType, ETrackedDeviceProperty, + ETrackedPropertyError, IVRProperties, IVRPropertiesVtable, IVRProperties_Version, + PropertyWrite_t, +}; use tracing::{error, instrument}; use crate::driver_context::DRIVER_CONTEXT; @@ -102,3 +107,66 @@ pub static SETTINGS: Lazy<&'static VtableRef> = Lazy::new(|| .expect("there should be settings interface"); unsafe { VtableRef::from_raw(raw as *const VtableRef) } }); + +pub enum PropertyValue { + Float(f32), + FloatArray(Vec), + Bool(bool), +} +impl PropertyValue { + fn tag(&self) -> u32 { + match self { + Self::Float(_) | Self::FloatArray(_) => k_unFloatPropertyTag, + Self::Bool(_) => k_unBoolPropertyTag, + } + } + fn size(&self) -> u32 { + match self { + PropertyValue::Float(_) => 4, + PropertyValue::FloatArray(v) => 4 * v.len() as u32, + PropertyValue::Bool(_) => 1, + } + } + fn buf(&mut self) -> *mut c_void { + match self { + PropertyValue::Float(f) => (f as *mut f32).cast(), + PropertyValue::FloatArray(f) => f.as_mut_ptr().cast(), + PropertyValue::Bool(v) => (v as *mut bool).cast(), + } + } +} +pub struct Property { + name: ETrackedDeviceProperty, + value: PropertyValue, +} +impl Property { + pub fn new(name: ETrackedDeviceProperty, value: PropertyValue) -> Self { + Self { name, value } + } +} +pub fn set_properties(container: u64, mut props: Vec) { + let mut batch = Vec::with_capacity(props.len()); + for prop in props.iter_mut() { + batch.push(PropertyWrite_t { + writeType: EPropertyWriteType::PropertyWrite_Set, + prop: prop.name, + unTag: prop.value.tag(), + unBufferSize: prop.value.size(), + pvBuffer: prop.value.buf(), + + eError: ETrackedPropertyError::TrackedProp_Success, + eSetError: ETrackedPropertyError::TrackedProp_Success, + }); + } + PROPERTIES.WritePropertyBatch(container, batch.as_mut_ptr(), batch.len() as u32); +} + +pub static PROPERTIES: Lazy<&'static VtableRef> = Lazy::new(|| { + let ctx = DRIVER_CONTEXT + .get() + .expect("context should be initialized at this point"); + let raw = ctx + .get_generic_interface(IVRProperties_Version) + .expect("there should be properties interface"); + unsafe { VtableRef::from_raw(raw as *const VtableRef) } +}); diff --git a/crates/vive-hid/src/lib.rs b/crates/vive-hid/src/lib.rs index 25d70b5..060e7d8 100644 --- a/crates/vive-hid/src/lib.rs +++ b/crates/vive-hid/src/lib.rs @@ -27,7 +27,7 @@ type Result = result::Result; static HIDAPI: OnceCell = OnceCell::new(); pub fn get_hidapi() -> Result<&'static HidApi> { - HIDAPI.get_or_try_init(|| HidApi::new()).map_err(From::from) + HIDAPI.get_or_try_init(HidApi::new).map_err(From::from) } const STEAM_VID: u16 = 0x28de; @@ -154,25 +154,33 @@ pub struct Mode { pub width: u32, pub height: u32, pub frame_rate: f32, + pub extra_photon_vsync: f32, } impl Mode { - const fn new(id: u8, width: u32, height: u32, frame_rate: f32) -> Self { + const fn new( + id: u8, + width: u32, + height: u32, + frame_rate: f32, + extra_photon_vsync: f32, + ) -> Self { Self { id, width, height, frame_rate, + extra_photon_vsync, } } } const VIVE_PRO_2_MODES: [Mode; 6] = [ - Mode::new(0, 2448, 1224, 90.0), - Mode::new(1, 2448, 1224, 120.0), - Mode::new(2, 3264, 1632, 90.0), - Mode::new(3, 3680, 1836, 90.0), - Mode::new(4, 4896, 2448, 90.0), - Mode::new(5, 4896, 2448, 120.0), + Mode::new(0, 2448, 1224, 90.0, 0.0), + Mode::new(1, 2448, 1224, 120.0, 0.0), + Mode::new(2, 3264, 1632, 90.0, 0.00297), + Mode::new(3, 3672, 1836, 90.0, 0.00332), + Mode::new(4, 4896, 2448, 90.0, 0.0), + Mode::new(5, 4896, 2448, 120.0, 0.0), ]; pub struct ViveDevice(HidDevice); @@ -240,12 +248,12 @@ impl ViveDevice { .map_err(|_| Error::ProtocolError("devsn is not a string"))? .to_string()) } - pub fn read_ipd(&self) -> Result { - self.write(0x02, b"mfg-r-ipdadc")?; + pub fn read_reg(&self, reg: &str) -> Result { + self.write(0x02, reg.as_bytes())?; let mut out = [0u8; 62]; let size = self.read(0x02, &[], &mut out)?; Ok(std::str::from_utf8(&out[..size]) - .map_err(|_| Error::ProtocolError("ipd is not a string"))? + .map_err(|_| Error::ProtocolError("result is not a string"))? .to_string()) } pub fn read_config(&self) -> Result { @@ -283,7 +291,7 @@ impl ViveDevice { let string = std::str::from_utf8(&out[128..]) .map_err(|_| Error::ProtocolError("config is not utf-8"))?; - serde_json::from_str(&string).map_err(|_| Error::ConfigReadFailed) + serde_json::from_str(string).map_err(|_| Error::ConfigReadFailed) } /// Always returns at least one mode pub fn query_modes(&self) -> Vec { @@ -292,6 +300,7 @@ impl ViveDevice { pub fn set_mode(&self, resolution: u8) -> Result<(), Error> { self.write_feature(0x04, 0x2970, b"wireless,0")?; self.write_feature(0x04, 0x2970, format!("dtd,{}", resolution).as_bytes())?; + self.write_feature(0x04, 0x2970, b"chipreset")?; // TODO: wait for reconnection Ok(()) } @@ -328,3 +337,11 @@ impl ViveDevice { Ok(()) } } + +#[test] +fn test() -> Result<()> { + let dev = ViveDevice::open_first()?; + dbg!(dev.read_config()?); + dev.set_mode(1)?; + Ok(()) +}