Begin new OpenVR driver

This commit is contained in:
Riccardo Zaglia 2021-09-04 21:16:20 +02:00
parent 9133e2328d
commit 95b14028d9
26 changed files with 8547 additions and 3291 deletions

4
.clang-format Normal file
View File

@ -0,0 +1,4 @@
IndentWidth: 4
ColumnLimit: 100
BinPackArguments: false
BinPackParameters: false

6
Cargo.lock generated
View File

@ -263,9 +263,13 @@ dependencies = [
name = "alvr_openvr_driver"
version = "0.1.0"
dependencies = [
"alvr_common",
"alvr_ipc",
"bindgen 0.59.1",
"cc",
"interprocess",
"lazy_static",
"parking_lot",
"regex",
]
[[package]]

View File

@ -15,3 +15,14 @@ pub struct Fov {
#[schema(min = 0., max = 90., step = 0.1, gui = "UpDown")]
pub bottom: f32,
}
#[derive(SettingsSchema, Serialize, Deserialize, Clone)]
pub enum OpenvrPropValue {
Bool(bool),
Float(f32),
Int32(i32),
Uint64(u64),
Vector3([f32; 3]),
Double(f64),
String(String),
}

View File

@ -10,5 +10,5 @@ alvr_common = { path = "../../common" }
bincode = "1"
interprocess = "1"
nalgebra = "0.29"
nalgebra = { version = "0.29", features = ["serde-serialize"] }
serde = { version = "1", features = ["derive"] }

View File

@ -5,75 +5,122 @@ pub use packets::*;
use alvr_common::prelude::*;
use interprocess::local_socket::{LocalSocketListener, LocalSocketStream};
use serde::{de::DeserializeOwned, Serialize};
use std::{marker::PhantomData, thread, time::Duration};
use std::{io::ErrorKind, marker::PhantomData, thread, time::Duration};
pub struct IpcSender<S> {
fn deserialize_non_blocking<R: DeserializeOwned>(
mut socket: &mut LocalSocketStream,
) -> StrResult<Option<R>> {
match bincode::deserialize_from(&mut socket) {
Ok(message) => Ok(Some(message)),
Err(e) => match *e {
bincode::ErrorKind::Io(e) if e.kind() == ErrorKind::WouldBlock => Ok(None),
_ => fmt_e!("IPC Error"),
},
}
}
pub struct IpcClient<S, R> {
socket: LocalSocketStream,
_phantom: PhantomData<(S, R)>,
}
impl<S: Serialize, R: DeserializeOwned> IpcClient<S, R> {
pub fn request(&mut self, message: &S) -> StrResult<R> {
trace_err!(bincode::serialize_into(&mut self.socket, message))?;
trace_err!(bincode::deserialize_from(&mut self.socket))
}
}
pub struct IpcSseReceiver<R> {
socket: LocalSocketStream,
_phantom: PhantomData<R>,
}
impl<R: DeserializeOwned> IpcSseReceiver<R> {
pub fn receive_non_blocking(&mut self) -> StrResult<Option<R>> {
deserialize_non_blocking(&mut self.socket)
}
}
pub fn ipc_connect<CS, CR, SR>(name: &str) -> StrResult<(IpcClient<CS, CR>, IpcSseReceiver<SR>)> {
let request_socket = trace_err!(LocalSocketStream::connect(format!(
"/tmp/alvr_{}_request.sock",
name
)))?;
let sse_socket = trace_err!(trace_err!(LocalSocketListener::bind(format!(
"/tmp/alvr_{}_sse.sock",
name
)))?
.accept())?;
sse_socket.set_nonblocking(true).unwrap();
Ok((
IpcClient {
socket: request_socket,
_phantom: PhantomData,
},
IpcSseReceiver {
socket: sse_socket,
_phantom: PhantomData,
},
))
}
pub struct IpcServer<S, R> {
socket: LocalSocketStream,
_phantom: PhantomData<(S, R)>,
}
impl<S: Serialize, R: DeserializeOwned> IpcServer<S, R> {
// Ok: try again, Err: connection closed
pub fn serve_non_blocking(&mut self, mut request_callback: impl FnMut(R) -> S) -> StrResult {
while let Some(request) = deserialize_non_blocking(&mut self.socket)? {
let response = request_callback(request);
// Note: the socket is shared, so even the sending part is non blocking. Despite
// this, WouldBlock should never happen and this call should never fail.
trace_err!(bincode::serialize_into(&mut self.socket, &response))?;
}
Ok(())
}
}
pub struct IpcSseSender<S> {
socket: LocalSocketStream,
_phantom: PhantomData<S>,
}
impl<S: Serialize> IpcSender<S> {
impl<S: Serialize> IpcSseSender<S> {
pub fn send(&mut self, message: &S) -> StrResult {
trace_err!(bincode::serialize_into(&mut self.socket, message))
}
}
pub struct IpcReceiver<R> {
socket: LocalSocketStream,
_phantom: PhantomData<R>,
}
impl<R: DeserializeOwned> IpcReceiver<R> {
pub fn receive(&mut self) -> StrResult<R> {
trace_err!(bincode::deserialize_from(&mut self.socket))
}
}
pub fn ipc_connect<S, R>(name: &str) -> StrResult<(IpcSender<S>, IpcReceiver<R>)> {
let sender = trace_err!(LocalSocketStream::connect(format!(
"/tmp/alvr_{}_out.sock",
name
)))?;
let receiver = trace_err!(trace_err!(LocalSocketListener::bind(format!(
"/tmp/alvr_{}_in.sock",
pub fn ipc_listen<CS, CR, SR>(name: &str) -> StrResult<(IpcServer<CS, CR>, IpcSseSender<SR>)> {
let request_socket = trace_err!(trace_err!(LocalSocketListener::bind(format!(
"/tmp/alvr_{}_request.sock",
name
)))?
.accept())?;
Ok((
IpcSender {
socket: sender,
_phantom: PhantomData,
},
IpcReceiver {
socket: receiver,
_phantom: PhantomData,
},
))
}
request_socket.set_nonblocking(true).unwrap();
pub fn ipc_listen<S, R>(name: &str) -> StrResult<(IpcSender<S>, IpcReceiver<R>)> {
let receiver = trace_err!(trace_err!(LocalSocketListener::bind(format!(
"/tmp/alvr_{}_out.sock",
name
)))?
.accept())?;
// Wait for the client to setup the listener
// Wait for the client to setup the sse socket listener
thread::sleep(Duration::from_millis(100));
let sender = trace_err!(LocalSocketStream::connect(format!(
"/tmp/alvr_{}_in.sock",
let sse_sender = trace_err!(LocalSocketStream::connect(format!(
"/tmp/alvr_{}_sse.sock",
name
)))?;
Ok((
IpcSender {
socket: sender,
IpcServer {
socket: request_socket,
_phantom: PhantomData,
},
IpcReceiver {
socket: receiver,
IpcSseSender {
socket: sse_sender,
_phantom: PhantomData,
},
))

View File

@ -1,48 +1,117 @@
use alvr_common::Fov;
use alvr_common::{Fov, OpenvrPropValue};
use nalgebra::{UnitQuaternion, Vector3};
use serde::{Deserialize, Serialize};
use std::time::Duration;
pub enum TrackerType {
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TrackedDeviceType {
Hmd,
LeftHand,
RightHand,
Generic(usize),
GenericTracker,
}
pub enum ServerPacket {
Settings {
// This packet groups all data that is updated rarely
#[derive(Serialize, Deserialize)]
pub struct DriverConfigUpdate {
pub preferred_view_size: (u32, u32),
pub fov: [Fov; 2],
pub ipd_m: f32,
pub fps: f32,
pub battery: Vec<(TrackedDeviceType, f32)>,
}
#[derive(Serialize, Deserialize)]
pub struct DisplayConfig {
pub presentation: bool,
pub config: DriverConfigUpdate,
}
#[derive(Serialize, Deserialize)]
pub struct Layer {
pub views: Vec<(u64, Fov)>, // IDs of the swapchains
pub orientation: UnitQuaternion<f32>,
}
#[derive(Serialize, Deserialize)]
pub enum DriverRequest {
GetInitializationConfig,
GetExtraProperties(u64), // device index
CreateSwapchain {
images_count: usize,
width: u32,
height: u32,
fov: [Fov; 2],
ipd_m: f32,
fps: f32,
format: u32, // interpreted as Directx or Vulkan
sample_count: u32,
},
HeadTrackingData {
position: Vector3<f32>,
orientation: UnitQuaternion<f32>,
target_time_offset: Duration, // controls black pull
phase_shift: Duration, // adjusts latency, always positive
DestroySwapchain {
id: u64,
},
TrackerData {
tracker_type: TrackerType,
position: Vector3<f32>,
orientation: UnitQuaternion<f32>,
linear_velocity: Vector3<f32>,
angular_velocity: Vector3<f32>,
target_time_offset: Duration,
GetNextSwapchainIndex {
id: u64,
},
LayersConsumed,
PresentLayers(Vec<Layer>),
Haptics {
duration: Duration,
frequency: f32,
amplitude: f32,
},
}
#[derive(Serialize, Deserialize)]
pub enum ButtonType {
Boolean,
Scalar,
}
#[derive(Serialize, Deserialize)]
pub enum ButtonValue {
Boolean(bool),
Scalar(f32),
}
#[derive(Serialize, Deserialize)]
pub struct TrackedDeviceConfig {
pub serial_number: String,
pub device_type: TrackedDeviceType,
pub available_buttons: Vec<(String, ButtonType)>,
}
#[derive(Serialize, Deserialize)]
pub enum ResponseForDriver {
Ok,
InitializationConfig {
tracked_devices: Vec<TrackedDeviceConfig>,
display_config: Option<DisplayConfig>, // None if there is no Hmd tracked device
},
ExtraProperties(Vec<(String, OpenvrPropValue)>),
Swapchain {
id: u64,
textures: Vec<u64>, // HANDLEs or file descriptors
},
SwapchainIndex(usize),
}
#[derive(Serialize, Deserialize)]
pub struct MotionData {
pub position: Vector3<f32>,
pub orientation: UnitQuaternion<f32>,
pub linear_velocity: Option<Vector3<f32>>,
pub angular_velocity: Option<Vector3<f32>>,
}
#[derive(Serialize, Deserialize)]
pub enum SsePacket {
UpdateConfig(DriverConfigUpdate),
PropertyChanged {
name: String,
value: OpenvrPropValue,
},
TrackingData {
trackers_data: Vec<Option<MotionData>>,
hand_skeleton_motions: [Option<Box<[MotionData; 25]>>; 2],
target_time_offset: Duration, // controls black pull and controller jitter
},
ButtonsData(Vec<(String, ButtonValue)>),
Restart,
}
// Note: this can be reused by the vulkan layer
#[derive(Serialize, Deserialize)]
pub struct Layer {
views: Vec<u64>, // Windows HANDLEs or file descriptors
orientation: UnitQuaternion<f32>,
}
#[derive(Serialize, Deserialize)]
pub enum DriverPacket {
Layers(Vec<Layer>),
}

View File

@ -0,0 +1,21 @@
[package]
name = "alvr_openvr_driver"
version = "0.1.0"
authors = ["alvr-org"]
license = "MIT"
edition = "2018"
[lib]
crate-type = ["cdylib"]
[dependencies]
alvr_common = { path = "../../common" }
alvr_ipc = { path = "../ipc" }
lazy_static = "1"
parking_lot = "0.11"
[build-dependencies]
cc = "1"
bindgen = "0.59"
regex = "1"

View File

@ -0,0 +1,3 @@
# alvr_openvr_driver
OpenVR driver written mostly in C++. C++ is used because `openvr_driver.h` header makes extensive use of classes which are expected to be used with subclassing (that maps poorly to Rust). The scope of this driver is reduced at the point that error handling is not needed. Communication with the server happens through IPC.

View File

@ -0,0 +1,68 @@
use regex::Regex;
use std::{env, fs, path::PathBuf};
fn main() {
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let mut build = cc::Build::new();
build
.cpp(true)
.flag_if_supported("-std=c++17")
.flag_if_supported("-Wno-unused-parameter")
.files([
"cpp/tracked_device.cpp",
"cpp/hmd.cpp",
"cpp/controller.cpp",
"cpp/driver.cpp",
])
.include("cpp")
.compile("bindings");
bindgen::builder()
.clang_arg("-xc++")
.clang_arg("-std=c++17")
.clang_arg("-Icpp/bindgen_workaround")
.header("cpp/bindings.h")
.derive_default(true)
.enable_cxx_namespaces()
.prepend_enum_name(false)
.generate()
.unwrap()
.write_to_file(out_dir.join("bindings.rs"))
.unwrap();
let openvr_driver_header_string = fs::read_to_string("cpp/openvr_driver.h").unwrap();
let property_finder = Regex::new(
r"\t(Prop_[A-Za-z\d_]*_(?:Bool|Int32|Uint64|Float|String|Vector3))[ \t]*= (\d*),",
)
.unwrap();
let mut mappings_fn_string: String = String::from(
r"
pub fn tracked_device_property_name_to_key(prop_name: &str) -> Result<vr::ETrackedDeviceProperty, String> {
match prop_name {
",
);
for entry in property_finder.captures_iter(&openvr_driver_header_string) {
mappings_fn_string.push_str(&format!(
r#"
"{}" => Ok({}),
"#,
&entry[1], &entry[2]
));
}
mappings_fn_string.push_str(
r#"
_ => Err(format!("{} property not found or not supported", prop_name)),
}
}
"#,
);
fs::write(out_dir.join("properties_mappings.rs"), mappings_fn_string).unwrap();
println!("cargo:rerun-if-changed=cpp");
}

View File

@ -0,0 +1,12 @@
// dummy string
#pragma once
namespace std {
class string {
public:
string();
string(const char *);
const char *c_str() const;
};
} // namespace std

View File

@ -0,0 +1,7 @@
// dummy vector
#pragma once
namespace std {
template <typename T> class vector {};
} // namespace std

View File

@ -0,0 +1,68 @@
#pragma once
#include "openvr_driver.h"
#include <stdint.h>
struct Fov {
float left;
float right;
float top;
float bottom;
};
struct DriverConfigUpdate {
uint32_t preferred_view_width;
uint32_t preferred_view_height;
Fov fov[2];
float ipd_m;
float fps;
};
struct TrackerConfig {
unsigned int type_and_index; // 0: left, 1: right, >1: generic - 2
};
struct InitializationConfig {
const char tracked_device_serial_numbers[10][20];
vr::ETrackedDeviceClass tracked_device_classes[10];
vr::ETrackedControllerRole controller_role[10];
uint64_t tracked_devices_count;
bool presentation;
DriverConfigUpdate config;
};
struct MotionData {
bool connected;
double position[3];
vr::HmdQuaternion_t orientation;
double linear_velocity[3];
double angular_velocity[3];
bool has_linear_velocity;
bool has_angular_velocity;
};
// Fuctions provided by Rust
extern "C" bool (*spawn_sse_receiver_loop)();
extern "C" InitializationConfig (*get_initialization_config)();
extern "C" void (*set_extra_properties)(uint64_t device_index);
// This is our only way of logging. OpenVR does not have severity levels
extern "C" void log(const char *message);
extern "C" void
set_bool_property(uint64_t device_index, vr::ETrackedDeviceProperty prop, bool value);
extern "C" void
set_float_property(uint64_t device_index, vr::ETrackedDeviceProperty prop, float value);
extern "C" void
set_int32_property(uint64_t device_index, vr::ETrackedDeviceProperty prop, int32_t value);
extern "C" void
set_uint64_property(uint64_t device_index, vr::ETrackedDeviceProperty prop, uint64_t value);
extern "C" void set_vec3_property(uint64_t device_index,
vr::ETrackedDeviceProperty prop,
const vr::HmdVector3_t &value);
extern "C" void
set_double_property(uint64_t device_index, vr::ETrackedDeviceProperty prop, double value);
extern "C" void
set_string_property(uint64_t device_index, vr::ETrackedDeviceProperty prop, const char *value);
extern "C" void *entry_point(const char *interface_name, int *return_code);

View File

@ -0,0 +1 @@
#include "controller.h"

View File

@ -0,0 +1,13 @@
#pragma once
#include "bindings.h"
#include "openvr_driver.h"
#include "tracked_device.h"
class Controller : public TrackedDevice {
vr::ETrackedControllerRole role;
public:
Controller(uint64_t device_index, vr::ETrackedControllerRole role)
: TrackedDevice(device_index), role(role) {}
};

View File

@ -0,0 +1,130 @@
#include "bindings.h"
#include "controller.h"
#include "generic_tracker.h"
#include "hmd.h"
#include "tracked_device.h"
#include <optional>
#include <string>
#include <vector>
// Fuctions provided by Rust
bool (*spawn_sse_receiver_loop)();
InitializationConfig (*get_initialization_config)();
void (*set_extra_properties)(uint64_t);
void log(const char *message) {
auto message_string = std::string("[ALVR] ") + message;
vr::VRDriverLog()->Log(message_string.c_str());
}
class DriverProvider : vr::IServerTrackedDeviceProvider {
virtual vr::EVRInitError Init(vr::IVRDriverContext *pContext) override {
VR_INIT_SERVER_DRIVER_CONTEXT(pContext);
if (!spawn_sse_receiver_loop()) {
return vr::VRInitError_IPC_ServerInitFailed;
}
auto config = get_initialization_config();
for (uint64_t idx = 0; idx < config.tracked_devices_count; idx++) {
TrackedDevice *device;
if (config.tracked_device_classes[idx] == vr::TrackedDeviceClass_HMD) {
this->hmd = Hmd(idx, config.presentation, config.config);
device = &*this->hmd;
} else if (config.tracked_device_classes[idx] == vr::TrackedDeviceClass_Controller &&
config.controller_role[idx] == vr::TrackedControllerRole_LeftHand) {
this->left_controller = Controller(idx, vr::TrackedControllerRole_LeftHand);
device = &*this->left_controller;
} else if (config.tracked_device_classes[idx] == vr::TrackedDeviceClass_Controller &&
config.controller_role[idx] == vr::TrackedControllerRole_RightHand) {
this->right_controller = Controller(idx, vr::TrackedControllerRole_RightHand);
device = &*this->right_controller;
} else if (config.tracked_device_classes[idx] ==
vr::TrackedDeviceClass_GenericTracker) {
this->generic_trackers.push_back(GenericTracker(idx));
device = &this->generic_trackers[this->generic_trackers.size() - 1];
} else {
continue;
}
vr::VRServerDriverHost()->TrackedDeviceAdded(config.tracked_device_serial_numbers[idx],
config.tracked_device_classes[idx],
device);
this->tracked_devices.push_back(device);
}
return vr::VRInitError_None;
}
virtual void Cleanup() override { VR_CLEANUP_SERVER_DRIVER_CONTEXT(); }
virtual const char *const *GetInterfaceVersions() override { return vr::k_InterfaceVersions; }
virtual const char *GetTrackedDeviceDriverVersion() {
return vr::ITrackedDeviceServerDriver_Version;
}
virtual void RunFrame() override {}
virtual bool ShouldBlockStandbyMode() override { return false; }
virtual void EnterStandby() override {}
virtual void LeaveStandby() override {}
public:
std::optional<Hmd> hmd;
std::optional<Controller> left_controller, right_controller;
std::vector<GenericTracker> generic_trackers;
std::vector<TrackedDevice *> tracked_devices;
DriverProvider() : hmd(std::nullopt) {}
} g_driver_provider;
void *entry_point(const char *interface_name, int *return_code) {
if (std::string(vr::IServerTrackedDeviceProvider_Version) == std::string(interface_name)) {
*return_code = vr::VRInitError_None;
return &g_driver_provider;
} else {
*return_code = vr::VRInitError_Init_InterfaceNotFound;
return nullptr;
}
}
void handle_prop_error(vr::ETrackedPropertyError error) {
log(vr::VRPropertiesRaw()->GetPropErrorNameFromEnum(error));
}
void set_bool_property(uint64_t device_index, vr::ETrackedDeviceProperty prop, bool value) {
auto container = g_driver_provider.tracked_devices[device_index]->get_container();
handle_prop_error(vr::VRProperties()->SetBoolProperty(container, prop, value));
}
void set_float_property(uint64_t device_index, vr::ETrackedDeviceProperty prop, float value) {
auto container = g_driver_provider.tracked_devices[device_index]->get_container();
handle_prop_error(vr::VRProperties()->SetFloatProperty(container, prop, value));
}
void set_int32_property(uint64_t device_index, vr::ETrackedDeviceProperty prop, int32_t value) {
auto container = g_driver_provider.tracked_devices[device_index]->get_container();
handle_prop_error(vr::VRProperties()->SetInt32Property(container, prop, value));
}
void set_uint64_property(uint64_t device_index, vr::ETrackedDeviceProperty prop, uint64_t value) {
auto container = g_driver_provider.tracked_devices[device_index]->get_container();
handle_prop_error(vr::VRProperties()->SetUint64Property(container, prop, value));
}
void set_vec3_property(uint64_t device_index,
vr::ETrackedDeviceProperty prop,
const vr::HmdVector3_t &value) {
auto container = g_driver_provider.tracked_devices[device_index]->get_container();
handle_prop_error(vr::VRProperties()->SetVec3Property(container, prop, value));
}
void set_double_property(uint64_t device_index, vr::ETrackedDeviceProperty prop, double value) {
auto container = g_driver_provider.tracked_devices[device_index]->get_container();
handle_prop_error(vr::VRProperties()->SetDoubleProperty(container, prop, value));
}
void set_string_property(uint64_t device_index,
vr::ETrackedDeviceProperty prop,
const char *value) {
auto container = g_driver_provider.tracked_devices[device_index]->get_container();
handle_prop_error(vr::VRProperties()->SetStringProperty(container, prop, value));
}
void set_motion_data(MotionData *data, size_t count, double time_offset_s) {
for (size_t idx = 0; idx < count; idx++) {
g_driver_provider.tracked_devices[idx]->set_motion(data[idx], time_offset_s);
}
}

View File

@ -0,0 +1,8 @@
#pragma once
#include "tracked_device.h"
class GenericTracker : public TrackedDevice {
public:
GenericTracker(uint64_t device_index) : TrackedDevice(device_index){};
};

View File

@ -0,0 +1,45 @@
#include "hmd.h"
#include <string>
void Hmd::activate_inner() {}
void *Hmd::GetComponent(const char *component_name_and_version) {
if (std::string(component_name_and_version) == vr::IVRDisplayComponent_Version ||
(std::string(component_name_and_version) == vr::IVRDriverDirectModeComponent_Version &&
this->presentation)) {
return this;
}
}
void Hmd::GetWindowBounds(int32_t *x, int32_t *y, uint32_t *width, uint32_t *height) {
*x = 0;
*y = 0;
*width = this->config.preferred_view_width * 2;
*height = this->config.preferred_view_height;
}
void Hmd::GetRecommendedRenderTargetSize(uint32_t *width, uint32_t *height) {
*width = this->config.preferred_view_width;
*height = this->config.preferred_view_height;
}
void Hmd::GetEyeOutputViewport(
vr::EVREye eye, uint32_t *x, uint32_t *y, uint32_t *width, uint32_t *height) {
*x = (eye == vr::Eye_Left ? 0 : this->config.preferred_view_width);
*y = 0;
*width = this->config.preferred_view_width;
*height = this->config.preferred_view_height;
}
void Hmd::GetProjectionRaw(vr::EVREye eye, float *left, float *right, float *top, float *bottom) {
auto fov = this->config.fov[eye];
*left = fov.left;
*right = fov.right;
*top = fov.top;
*bottom = fov.bottom;
}
vr::DistortionCoordinates_t Hmd::ComputeDistortion(vr::EVREye, float u, float v) {
return {{u, v}, {u, v}, {u, v}};
}

View File

@ -0,0 +1,45 @@
#pragma once
#include "bindings.h"
#include "tracked_device.h"
class Hmd : public TrackedDevice, vr::IVRDisplayComponent, vr::IVRDriverDirectModeComponent {
bool presentation;
DriverConfigUpdate config;
// TrackedDevice
virtual void activate_inner() override;
virtual void *GetComponent(const char *component_name_and_version) override;
// IVRDisplayComponent
virtual void
GetWindowBounds(int32_t *x, int32_t *y, uint32_t *width, uint32_t *height) override;
virtual bool IsDisplayOnDesktop() override { return false; }
virtual bool IsDisplayRealDisplay() override { return true; }
virtual void GetRecommendedRenderTargetSize(uint32_t *width, uint32_t *height) override;
virtual void GetEyeOutputViewport(
vr::EVREye eye, uint32_t *x, uint32_t *y, uint32_t *width, uint32_t *height) override;
virtual void
GetProjectionRaw(vr::EVREye eye, float *left, float *right, float *top, float *bottom) override;
virtual vr::DistortionCoordinates_t
ComputeDistortion(vr::EVREye eye, float u, float v) override;
// IVRDriverDirectModeComponent
virtual void CreateSwapTextureSet(
uint32_t pid,
const vr::IVRDriverDirectModeComponent::SwapTextureSetDesc_t *swap_texture_set_desc,
vr::IVRDriverDirectModeComponent::SwapTextureSet_t *swap_texture_set) override {}
virtual void DestroySwapTextureSet(vr::SharedTextureHandle_t shared_texture_handle) override {}
virtual void DestroyAllSwapTextureSets(uint32_t pid) override {}
virtual void GetNextSwapTextureSetIndex(vr::SharedTextureHandle_t shared_texture_handles[2],
uint32_t (*indices)[2]) override {}
virtual void
SubmitLayer(const vr::IVRDriverDirectModeComponent::SubmitLayerPerEye_t (&eye)[2]) override {}
virtual void Present(vr::SharedTextureHandle_t syncTexture) override {}
virtual void PostPresent() override {}
virtual void GetFrameTiming(vr::DriverDirectMode_FrameTiming *pFrameTiming) override {}
public:
Hmd(uint64_t device_index, bool presentation, DriverConfigUpdate config)
: TrackedDevice(device_index), presentation(presentation), config(config) {}
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
#include "tracked_device.h"
vr::EVRInitError TrackedDevice::Activate(uint32_t object_id) {
this->object_id = object_id;
this->property_container = vr::VRProperties()->TrackedDeviceToPropertyContainer(object_id);
this->activate_inner();
set_extra_properties(this->device_index);
return vr::VRInitError_None;
};
void TrackedDevice::set_motion(MotionData motion, double time_offset_s) {
this->pose = {};
if (motion.connected) {
this->pose.result = vr::TrackingResult_Running_OK;
this->pose.poseIsValid = true;
this->pose.deviceIsConnected = true;
this->pose.poseTimeOffset = time_offset_s;
this->pose.vecPosition[0] = motion.position[0];
this->pose.vecPosition[1] = motion.position[1];
this->pose.vecPosition[2] = motion.position[2];
if (motion.has_linear_velocity) {
this->pose.vecVelocity[0] = motion.linear_velocity[0];
this->pose.vecVelocity[1] = motion.linear_velocity[1];
this->pose.vecVelocity[2] = motion.linear_velocity[2];
}
this->pose.qRotation = motion.orientation;
if (motion.has_angular_velocity) {
this->pose.vecAngularVelocity[0] = motion.angular_velocity[0];
this->pose.vecAngularVelocity[1] = motion.angular_velocity[1];
this->pose.vecAngularVelocity[2] = motion.angular_velocity[2];
}
}
vr::VRServerDriverHost()->TrackedDevicePoseUpdated(
this->object_id, this->pose, sizeof(vr::DriverPose_t));
}

View File

@ -0,0 +1,31 @@
#pragma once
#include "bindings.h"
#include "openvr_driver.h"
class TrackedDevice : public vr::ITrackedDeviceServerDriver {
virtual vr::EVRInitError Activate(uint32_t object_id) override;
virtual void *GetComponent(const char *component_name_and_version) override { return nullptr; }
virtual void Deactivate() override {}
virtual void EnterStandby() override {}
virtual void DebugRequest(const char *request,
char *response_buffer,
uint32_t response_buffer_size) override {}
virtual vr::DriverPose_t GetPose() override { return this->pose; }
protected:
uint64_t device_index;
vr::TrackedDeviceIndex_t object_id;
vr::PropertyContainerHandle_t property_container;
vr::DriverPose_t pose;
virtual void activate_inner() {}
TrackedDevice(uint64_t device_index) : device_index(device_index) {
this->pose.result = vr::TrackingResult_Uninitialized;
}
public:
void set_motion(MotionData motion, double time_offset_s);
vr::PropertyContainerHandle_t get_container() { return this->property_container; }
};

View File

@ -0,0 +1,216 @@
#![allow(
non_camel_case_types,
non_upper_case_globals,
dead_code,
clippy::missing_safety_doc
)]
use alvr_common::OpenvrPropValue;
use alvr_ipc::{
DriverConfigUpdate, DriverRequest, IpcClient, IpcSseReceiver, ResponseForDriver, SsePacket,
TrackedDeviceType,
};
use parking_lot::Mutex;
use std::ffi::CString;
use std::ptr;
use std::{ffi::c_void, os::raw::c_char, sync::Arc, thread, time::Duration};
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
use root as drv;
use root::vr;
include!(concat!(env!("OUT_DIR"), "/properties_mappings.rs"));
struct IpcConnections {
client: Option<IpcClient<DriverRequest, ResponseForDriver>>,
sse_receiver: Option<IpcSseReceiver<SsePacket>>,
}
lazy_static::lazy_static! {
static ref IPC_CONNECTIONS: Arc<Mutex<IpcConnections>> = {
let (client, sse_receiver) = if let Ok((client, sse_receiver)) = alvr_ipc::ipc_connect("driver") {
(Some(client), Some(sse_receiver))
} else {
(None, None)
};
Arc::new(Mutex::new(IpcConnections {
client,
sse_receiver,
}))
};
}
fn log(message: &str) {
let c_string = CString::new(message).unwrap();
unsafe { drv::log(c_string.as_ptr()) };
}
extern "C" fn spawn_sse_receiver_loop() -> bool {
if let Some(mut receiver) = IPC_CONNECTIONS.lock().sse_receiver.take() {
thread::spawn(move || {
while let Ok(message) = receiver.receive_non_blocking() {
match message {
Some(message) => match message {
SsePacket::UpdateConfig(_) => todo!(),
SsePacket::PropertyChanged { name, value } => todo!(),
SsePacket::TrackingData {
trackers_data,
hand_skeleton_motions,
target_time_offset,
} => todo!(),
SsePacket::ButtonsData(data) => todo!(),
SsePacket::Restart => todo!(),
},
None => {
thread::sleep(Duration::from_millis(2));
}
}
}
});
true
} else {
false
}
}
fn ipc_driver_config_to_driver(config: DriverConfigUpdate) -> drv::DriverConfigUpdate {
drv::DriverConfigUpdate {
preferred_view_width: config.preferred_view_size.0,
preferred_view_height: config.preferred_view_size.1,
fov: [
drv::Fov {
left: config.fov[0].left,
right: config.fov[0].right,
top: config.fov[0].top,
bottom: config.fov[0].bottom,
},
drv::Fov {
left: config.fov[1].left,
right: config.fov[1].right,
top: config.fov[1].top,
bottom: config.fov[1].bottom,
},
],
ipd_m: config.ipd_m,
fps: config.fps,
}
}
extern "C" fn get_initialization_config() -> drv::InitializationConfig {
if let Some(client) = &mut IPC_CONNECTIONS.lock().client {
let response = client.request(&DriverRequest::GetInitializationConfig);
if let Ok(ResponseForDriver::InitializationConfig {
tracked_devices,
display_config,
}) = response
{
let mut tracked_device_serial_numbers = [[0; 20]; 10];
let mut tracked_device_classes = [vr::TrackedDeviceClass_Invalid; 10];
let mut controller_role = [vr::TrackedControllerRole_Invalid; 10];
for idx in 0..tracked_devices.len() {
let config = &tracked_devices[idx];
let serial_number_cstring = CString::new(config.serial_number.clone()).unwrap();
unsafe {
ptr::copy_nonoverlapping(
serial_number_cstring.as_ptr(),
tracked_device_serial_numbers[idx].as_mut_ptr(),
serial_number_cstring.as_bytes_with_nul().len(),
)
};
tracked_device_classes[idx] = match config.device_type {
TrackedDeviceType::Hmd => vr::TrackedDeviceClass_HMD,
TrackedDeviceType::LeftHand | TrackedDeviceType::RightHand => {
vr::TrackedDeviceClass_Controller
}
TrackedDeviceType::GenericTracker => vr::TrackedDeviceClass_GenericTracker,
};
controller_role[idx] = match config.device_type {
TrackedDeviceType::Hmd | TrackedDeviceType::GenericTracker => {
vr::TrackedControllerRole_Invalid
}
TrackedDeviceType::LeftHand => vr::TrackedControllerRole_LeftHand,
TrackedDeviceType::RightHand => vr::TrackedControllerRole_RightHand,
}
}
let (presentation, config) = if let Some(display_config) = display_config {
(
display_config.presentation,
ipc_driver_config_to_driver(display_config.config),
)
} else {
(false, drv::DriverConfigUpdate::default())
};
return drv::InitializationConfig {
tracked_device_serial_numbers,
tracked_device_classes,
controller_role,
tracked_devices_count: tracked_devices.len() as _,
presentation,
config,
};
}
}
drv::InitializationConfig::default()
}
fn set_property(device_index: u64, name: &str, value: OpenvrPropValue) {
let key = match tracked_device_property_name_to_key(name) {
Ok(key) => key,
Err(e) => {
log(&e);
return;
}
};
unsafe {
match value {
OpenvrPropValue::Bool(value) => drv::set_bool_property(device_index, key, value),
OpenvrPropValue::Float(value) => drv::set_float_property(device_index, key, value),
OpenvrPropValue::Int32(value) => drv::set_int32_property(device_index, key, value),
OpenvrPropValue::Uint64(value) => drv::set_uint64_property(device_index, key, value),
OpenvrPropValue::Vector3(value) => {
drv::set_vec3_property(device_index, key, &vr::HmdVector3_t { v: value })
}
OpenvrPropValue::Double(value) => drv::set_double_property(device_index, key, value),
OpenvrPropValue::String(value) => {
let c_string = CString::new(value).unwrap();
drv::set_string_property(device_index, key, c_string.as_ptr())
}
}
};
}
extern "C" fn set_extra_properties(device_index: u64) {
if let Some(client) = &mut IPC_CONNECTIONS.lock().client {
let response = client.request(&DriverRequest::GetExtraProperties(device_index));
if let Ok(ResponseForDriver::ExtraProperties(props)) = response {
for (name, value) in props {
set_property(device_index, &name, value);
}
}
}
}
// Entry point. The entry point must live on the Rust side, since C symbols are not exported
#[no_mangle]
pub unsafe extern "C" fn HmdDriverFactory(
interface_name: *const c_char,
return_code: *mut i32,
) -> *mut c_void {
// Initialize funtion pointers
drv::spawn_sse_receiver_loop = Some(spawn_sse_receiver_loop);
drv::get_initialization_config = Some(get_initialization_config);
drv::set_extra_properties = Some(set_extra_properties);
drv::entry_point(interface_name, return_code)
}

View File

@ -139,4 +139,4 @@ impl Compositor {
Ok(self.inner_create_swapchain(textures, array_size))
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,4 @@
use alvr_common::prelude::*;
use alvr_session::OpenvrPropValue;
use alvr_common::{prelude::*, OpenvrPropValue};
use openvr_driver_sys as vr;
use std::ffi::CString;

View File

@ -210,17 +210,6 @@ pub struct AudioSection {
pub microphone: Switch<MicrophoneDesc>,
}
#[derive(SettingsSchema, Serialize, Deserialize, Clone)]
pub enum OpenvrPropValue {
Bool(bool),
Float(f32),
Int32(i32),
Uint64(u64),
Vector3([f32; 3]),
Double(f64),
String(String),
}
#[derive(SettingsSchema, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ControllersDesc {