Skip to content

Commit

Permalink
Merge pull request #31 from CertainLach/feature/provide-explicit-fram…
Browse files Browse the repository at this point in the history
…erate

Explicitly provide framerate to SteamVR
  • Loading branch information
CertainLach authored Jan 25, 2024
2 parents 25b3a19 + 93bc507 commit 8982075
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 30 deletions.
69 changes: 64 additions & 5 deletions bin/driver-proxy/src/driver/hmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -150,15 +157,67 @@ impl IVRDisplayComponent for HmdDisplay {

#[impl_vtables(ITrackedDeviceServerDriver)]
pub struct HmdDriver {
// pub steam: Rc<SteamDevice>,
pub vive: Rc<ViveDevice>,
pub vive_config: ViveConfig,
pub lens: Rc<dyn LensClient>,
pub real: &'static VtableRef<ITrackedDeviceServerDriverVtable>,
pub mode: Mode,
}

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) {
Expand Down
21 changes: 9 additions & 12 deletions bin/driver-proxy/src/server/driver_host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
Expand All @@ -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<dyn LensClient>)
.unwrap_or_else(|e| {
let zenity = var_os("STEAM_ZENITY").unwrap_or_else(|| OsString::from("zenity"));
Expand All @@ -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,
Expand Down
70 changes: 69 additions & 1 deletion bin/driver-proxy/src/settings.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -102,3 +107,66 @@ pub static SETTINGS: Lazy<&'static VtableRef<IVRSettingsVtable>> = Lazy::new(||
.expect("there should be settings interface");
unsafe { VtableRef::from_raw(raw as *const VtableRef<IVRSettingsVtable>) }
});

pub enum PropertyValue {
Float(f32),
FloatArray(Vec<f32>),
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<Property>) {
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<IVRPropertiesVtable>> = 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<IVRPropertiesVtable>) }
});
41 changes: 29 additions & 12 deletions crates/vive-hid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type Result<T, E = Error> = result::Result<T, E>;

static HIDAPI: OnceCell<HidApi> = 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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -240,12 +248,12 @@ impl ViveDevice {
.map_err(|_| Error::ProtocolError("devsn is not a string"))?
.to_string())
}
pub fn read_ipd(&self) -> Result<String> {
self.write(0x02, b"mfg-r-ipdadc")?;
pub fn read_reg(&self, reg: &str) -> Result<String> {
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<ViveConfig> {
Expand Down Expand Up @@ -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<Mode> {
Expand All @@ -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(())
}
Expand Down Expand Up @@ -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(())
}

0 comments on commit 8982075

Please sign in to comment.