diff --git a/src/openxr.rs b/src/openxr.rs index a43f34e..1d1ffcb 100644 --- a/src/openxr.rs +++ b/src/openxr.rs @@ -7,33 +7,66 @@ pub use resources::*; pub use types::*; use bevy::app::{App, Plugin}; +use bevy::log::error; pub fn xr_entry() -> Result { #[cfg(windows)] let entry = openxr::Entry::linked(); #[cfg(not(windows))] let entry = unsafe { openxr::Entry::load()? }; - Ok(entry.into()) + Ok(XrEntry(entry)) } -fn init_xr() -> Result<()> { - let entry = xr_entry()?; - let instance = entry.create_instance( - AppInfo::default(), - XrExtensions::default(), - GraphicsBackend::Vulkan(()), - )?; - let system_id = instance.system(openxr::FormFactor::HEAD_MOUNTED_DISPLAY)?; - - instance.create_session(system_id)?; - Ok(()) +pub struct XrInitPlugin { + /// Information about the app this is being used to build. + pub app_info: AppInfo, + /// Extensions wanted for this session. + // This should preferably be changed into a simpler list of features wanted that this crate supports. i.e. hand tracking + pub exts: XrExtensions, + /// List of backends the openxr session can use. If [None], pick the first available backend. + pub backends: Option>, } -pub struct XrInitPlugin; - impl Plugin for XrInitPlugin { fn build(&self, app: &mut App) { - let entry = xr_entry(); + init_xr(self, app).unwrap(); todo!() } } + +fn init_xr(config: &XrInitPlugin, _app: &mut App) -> Result<()> { + let entry = xr_entry()?; + + let available_exts = entry.enumerate_extensions()?; + + for ext in available_exts.unavailable_exts(&config.exts) { + error!( + "Extension \"{ext}\" not available in the current openxr runtime. Disabling extension." + ); + } + + let available_backends = GraphicsBackend::available_backends(&available_exts); + + // Backend selection + let backend = if let Some(wanted_backends) = &config.backends { + let mut backend = None; + for wanted_backend in wanted_backends { + if available_backends.contains(wanted_backend) { + backend = Some(*wanted_backend); + break; + } + } + backend + } else { + available_backends.first().copied() + } + .ok_or(XrError::NoAvailableBackend)?; + + let exts = config.exts.clone() & available_exts; + + let instance = entry.create_instance(config.app_info.clone(), exts, backend)?; + let _system_id = instance.system(openxr::FormFactor::HEAD_MOUNTED_DISPLAY)?; + + //instance.create_session(system_id)?; + Ok(()) +} diff --git a/src/openxr/extensions.rs b/src/openxr/extensions.rs index 2c376ec..b5d8f09 100644 --- a/src/openxr/extensions.rs +++ b/src/openxr/extensions.rs @@ -49,6 +49,40 @@ impl Default for XrExtensions { } } +macro_rules! unavailable_exts { + ( + $exts:ty; + $( + $( + #[$meta:meta] + )* + $ident:ident + ),* + $(,)? + ) => { + impl $exts { + /// Returns any extensions needed by `required_exts` that aren't available in `self` + pub(crate) fn unavailable_exts(&self, required_exts: &Self) -> Vec> { + let mut exts = vec![]; + $( + $( + #[$meta] + )* + if required_exts.0.$ident && !self.0.$ident { + exts.push(std::borrow::Cow::Borrowed(stringify!($ident))) + } + )* + for ext in required_exts.0.other.iter() { + if !self.0.other.contains(ext) { + exts.push(std::borrow::Cow::Owned(ext.clone())) + } + } + exts + } + } + }; +} + macro_rules! bitor { ( $exts:ty; @@ -254,4 +288,4 @@ macro_rules! impl_ext { }; } -impl_ext!(bitor, bitand); +impl_ext!(bitor, bitand, unavailable_exts); diff --git a/src/openxr/graphics.rs b/src/openxr/graphics.rs index ab0b756..74cf9ee 100644 --- a/src/openxr/graphics.rs +++ b/src/openxr/graphics.rs @@ -1,15 +1,21 @@ pub mod vulkan; +use std::any::TypeId; + use bevy::math::UVec2; -use crate::openxr::resources::*; use crate::openxr::types::{AppInfo, Result, XrError}; use crate::types::BlendMode; -pub trait GraphicsExt: openxr::Graphics { +pub unsafe trait GraphicsExt: openxr::Graphics { + /// Wrap the graphics specific type into the [GraphicsWrap] enum + fn wrap(item: T::Inner) -> GraphicsWrap; + /// Convert from wgpu format to the graphics format fn from_wgpu_format(format: wgpu::TextureFormat) -> Option; + /// Convert from the graphics format to wgpu format fn to_wgpu_format(format: Self::Format) -> Option; - fn create_session( + /// Initialize graphics for this backend + fn init_graphics( app_info: &AppInfo, instance: &openxr::Instance, system_id: openxr::SystemId, @@ -18,10 +24,9 @@ pub trait GraphicsExt: openxr::Graphics { wgpu::Queue, wgpu::Adapter, wgpu::Instance, - XrSession, - XrFrameWaiter, - XrFrameStream, + Self::SessionCreateInfo, )>; + /// Convert a swapchain function unsafe fn to_wgpu_img( image: Self::SwapchainImage, device: &wgpu::Device, @@ -45,16 +50,32 @@ impl GraphicsWrap { ) } - fn graphics_type(&self) -> std::any::TypeId { + fn graphics_type(&self) -> TypeId { graphics_match!( self; - _ => std::any::TypeId::of::() + _ => TypeId::of::() ) } /// Checks if this struct is using the wanted graphics api. pub fn using_graphics(&self) -> bool { - self.graphics_type() == std::any::TypeId::of::() + self.graphics_type() == TypeId::of::() + } + + /// Checks if the two values are both using the same graphics backend + pub fn using_graphics_of_val(&self, other: &GraphicsWrap) -> bool { + self.graphics_type() == other.graphics_type() + } + + pub fn as_type(&self) -> Result<&T::Inner> { + // graphics_match!( + // self; + // inner => if TypeId::of:: == TypeId::of:: { + // return Ok(inner) + // } + // ); + + return Err(XrError::FailedGraphicsRequirements); } } diff --git a/src/openxr/graphics/vulkan.rs b/src/openxr/graphics/vulkan.rs index 2fd09f4..4bc1889 100644 --- a/src/openxr/graphics/vulkan.rs +++ b/src/openxr/graphics/vulkan.rs @@ -8,7 +8,6 @@ use wgpu_hal::api::Vulkan; use wgpu_hal::Api; use crate::openxr::extensions::XrExtensions; -use crate::openxr::resources::*; use crate::openxr::types::Result; use super::{AppInfo, GraphicsExt, XrError}; @@ -25,7 +24,7 @@ const VK_TARGET_VERSION_ASH: u32 = ash::vk::make_api_version( VK_TARGET_VERSION.patch() as u32, ); -impl GraphicsExt for openxr::Vulkan { +unsafe impl GraphicsExt for openxr::Vulkan { fn from_wgpu_format(format: wgpu::TextureFormat) -> Option { wgpu_to_vulkan(format).map(|f| f.as_raw() as _) } @@ -34,7 +33,7 @@ impl GraphicsExt for openxr::Vulkan { vulkan_to_wgpu(ash::vk::Format::from_raw(format as _)) } - fn create_session( + fn init_graphics( app_info: &AppInfo, instance: &openxr::Instance, system_id: openxr::SystemId, @@ -43,9 +42,7 @@ impl GraphicsExt for openxr::Vulkan { wgpu::Queue, wgpu::Adapter, wgpu::Instance, - XrSession, - XrFrameWaiter, - XrFrameStream, + Self::SessionCreateInfo, )> { let reqs = instance.graphics_requirements::(system_id)?; if VK_TARGET_VERSION < reqs.min_api_version_supported @@ -228,30 +225,18 @@ impl GraphicsExt for openxr::Vulkan { ) }?; - let (session, frame_wait, frame_stream) = unsafe { - instance.create_session::( - system_id, - &openxr::vulkan::SessionCreateInfo { - instance: vk_instance_ptr, - physical_device: vk_physical_device_ptr, - device: vk_device_ptr, - queue_family_index, - queue_index: 0, - }, - ) - }?; - Ok(( wgpu_device, wgpu_queue, wgpu_adapter, wgpu_instance, - XrSession( - session.clone().into_any_graphics(), - super::GraphicsWrap::Vulkan(session), - ), - XrFrameWaiter(frame_wait), - XrFrameStream(super::GraphicsWrap::Vulkan(frame_stream)), + openxr::vulkan::SessionCreateInfo { + instance: vk_instance_ptr, + physical_device: vk_physical_device_ptr, + device: vk_device_ptr, + queue_family_index, + queue_index: 0, + }, )) } @@ -310,6 +295,10 @@ impl GraphicsExt for openxr::Vulkan { extensions.khr_vulkan_enable2 = true; extensions.into() } + + fn wrap(item: T::Inner) -> super::GraphicsWrap { + super::GraphicsWrap::Vulkan(item) + } } fn vulkan_to_wgpu(format: ash::vk::Format) -> Option { diff --git a/src/openxr/resources.rs b/src/openxr/resources.rs index b4356be..07e0bc4 100644 --- a/src/openxr/resources.rs +++ b/src/openxr/resources.rs @@ -1,12 +1,14 @@ use bevy::prelude::*; +use bevy::render::renderer::{RenderAdapter, RenderAdapterInfo, RenderInstance, RenderQueue}; +use bevy::render::settings::RenderCreation; use openxr::AnyGraphics; use super::graphics::{graphics_match, GraphicsExt, GraphicsType, GraphicsWrap}; use super::types::*; #[derive(Deref, Clone)] -pub struct XrEntry(openxr::Entry); +pub struct XrEntry(pub openxr::Entry); impl XrEntry { pub fn enumerate_extensions(&self) -> Result { @@ -48,37 +50,57 @@ impl XrEntry { } } -impl From for XrEntry { - fn from(value: openxr::Entry) -> Self { - Self(value) - } -} - #[derive(Resource, Deref, Clone)] pub struct XrInstance( - #[deref] pub(crate) openxr::Instance, + #[deref] pub openxr::Instance, pub(crate) GraphicsBackend, pub(crate) AppInfo, ); impl XrInstance { - pub fn create_session( + pub fn init_graphics( &self, system_id: openxr::SystemId, - ) -> Result<( - wgpu::Device, - wgpu::Queue, - wgpu::Adapter, - wgpu::Instance, - XrSession, - XrFrameWaiter, - XrFrameStream, - )> { + ) -> Result<(RenderCreation, SessionCreateInfo)> { graphics_match!( self.1; - _ => Api::create_session(&self.2, &self.0, system_id) + _ => { + let (device, queue, adapter, instance, session_info) = Api::init_graphics(&self.2, &self, system_id)?; + + Ok((RenderCreation::manual(device.into(), RenderQueue(queue.into()), RenderAdapterInfo(adapter.get_info()), RenderAdapter(adapter.into()), RenderInstance(instance.into())), SessionCreateInfo(Api::wrap(session_info)))) + } ) } + + /// # Safety + /// + /// `info` must contain valid handles for the graphics api + pub unsafe fn create_session( + &self, + system_id: openxr::SystemId, + info: SessionCreateInfo, + ) -> Result<(XrSession, XrFrameWaiter, XrFrameStream)> { + if !info.0.using_graphics_of_val(&self.1) { + return Err(XrError::GraphicsBackendMismatch { + item: std::any::type_name::(), + backend: info.0.graphics_name(), + expected_backend: self.1.graphics_name(), + }); + } + graphics_match!( + info.0; + info => { + let (session, frame_waiter, frame_stream) = unsafe { self.0.create_session::(system_id, &info)? }; + Ok((session.into(), XrFrameWaiter(frame_waiter), XrFrameStream(Api::wrap(frame_stream)))) + } + ) + } +} + +pub struct SessionCreateInfo(pub(crate) GraphicsWrap); + +impl GraphicsType for SessionCreateInfo { + type Inner = G::SessionCreateInfo; } impl GraphicsType for XrSession { @@ -91,6 +113,12 @@ pub struct XrSession( pub(crate) GraphicsWrap, ); +impl From> for XrSession { + fn from(value: openxr::Session) -> Self { + Self(value.clone().into_any_graphics(), G::wrap(value)) + } +} + impl XrSession { pub fn enumerate_swapchain_formats(&self) -> Result> { graphics_match!( @@ -107,6 +135,7 @@ impl XrSession { } } +#[derive(Resource)] pub struct XrFrameStream(pub(crate) GraphicsWrap); impl GraphicsType for XrFrameStream { @@ -135,8 +164,8 @@ impl XrFrameStream { for (i, layer) in layers.into_iter().enumerate() { if let Some(swapchain) = layer.swapchain() { if !swapchain.0.using_graphics::() { - warn!( - "composition layer {i} is using graphics api '{}', expected graphics api '{}'. Excluding layer from frame submission.", + error!( + "Composition layer {i} is using graphics api '{}', expected graphics api '{}'. Excluding layer from frame submission.", swapchain.0.graphics_name(), std::any::type_name::(), ); @@ -152,9 +181,10 @@ impl XrFrameStream { } } -#[derive(Deref, DerefMut)] +#[derive(Resource, Deref, DerefMut)] pub struct XrFrameWaiter(pub openxr::FrameWaiter); +#[derive(Resource)] pub struct XrSwapchain(pub(crate) GraphicsWrap); impl GraphicsType for XrSwapchain { diff --git a/src/openxr/types.rs b/src/openxr/types.rs index 0043667..8a6ec45 100644 --- a/src/openxr/types.rs +++ b/src/openxr/types.rs @@ -1,11 +1,10 @@ use std::borrow::Cow; -use thiserror::Error; use super::graphics::{graphics_match, GraphicsExt, GraphicsWrap}; pub use super::extensions::XrExtensions; pub use openxr::{ - Extent2Di, Offset2Di, Rect2Di, SwapchainCreateFlags, SwapchainUsageFlags, SystemId, + Extent2Di, Graphics, Offset2Di, Rect2Di, SwapchainCreateFlags, SwapchainUsageFlags, SystemId, }; pub type Result = std::result::Result; @@ -53,32 +52,71 @@ impl GraphicsBackend { } } -#[derive(Error, Debug)] -pub enum XrError { - #[error("OpenXR error: {0}")] - OpenXrError(#[from] openxr::sys::Result), - #[error("OpenXR loading error: {0}")] - OpenXrLoadingError(#[from] openxr::LoadError), - #[error("WGPU instance error: {0}")] - WgpuInstanceError(#[from] wgpu_hal::InstanceError), - #[error("WGPU device error: {0}")] - WgpuDeviceError(#[from] wgpu_hal::DeviceError), - #[error("WGPU request device error: {0}")] - WgpuRequestDeviceError(#[from] wgpu::RequestDeviceError), - #[error("Unsupported texture format: {0:?}")] - UnsupportedTextureFormat(wgpu::TextureFormat), - #[error("Vulkan error: {0}")] - VulkanError(#[from] ash::vk::Result), - #[error("Vulkan loading error: {0}")] - VulkanLoadingError(#[from] ash::LoadingError), - #[error("Graphics backend '{0:?}' is not available")] - UnavailableBackend(GraphicsBackend), - #[error("Could not meet graphics requirements for platform. See console for details")] - FailedGraphicsRequirements, - #[error("Failed to create CString: {0}")] - NulError(#[from] std::ffi::NulError), +mod error { + use super::GraphicsBackend; + use std::borrow::Cow; + use std::fmt; + use thiserror::Error; + + #[derive(Error, Debug)] + pub enum XrError { + #[error("OpenXR error: {0}")] + OpenXrError(#[from] openxr::sys::Result), + #[error("OpenXR loading error: {0}")] + OpenXrLoadingError(#[from] openxr::LoadError), + #[error("WGPU instance error: {0}")] + WgpuInstanceError(#[from] wgpu_hal::InstanceError), + #[error("WGPU device error: {0}")] + WgpuDeviceError(#[from] wgpu_hal::DeviceError), + #[error("WGPU request device error: {0}")] + WgpuRequestDeviceError(#[from] wgpu::RequestDeviceError), + #[error("Unsupported texture format: {0:?}")] + UnsupportedTextureFormat(wgpu::TextureFormat), + #[error("Vulkan error: {0}")] + VulkanError(#[from] ash::vk::Result), + #[error("Vulkan loading error: {0}")] + VulkanLoadingError(#[from] ash::LoadingError), + #[error("Graphics backend '{0:?}' is not available")] + UnavailableBackend(GraphicsBackend), + #[error("No available backend")] + NoAvailableBackend, + #[error("OpenXR runtime does not support these extensions: {0}")] + UnavailableExtensions(UnavailableExts), + #[error("Could not meet graphics requirements for platform. See console for details")] + FailedGraphicsRequirements, + #[error( + "Tried to use item {item} with backend {backend}. Expected backend {expected_backend}" + )] + GraphicsBackendMismatch { + item: &'static str, + backend: &'static str, + expected_backend: &'static str, + }, + #[error("Failed to create CString: {0}")] + NulError(#[from] std::ffi::NulError), + } + + impl From>> for XrError { + fn from(value: Vec>) -> Self { + Self::UnavailableExtensions(UnavailableExts(value)) + } + } + + #[derive(Debug)] + pub struct UnavailableExts(Vec>); + + impl fmt::Display for UnavailableExts { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for s in &self.0 { + write!(f, "\t{s}")?; + } + Ok(()) + } + } } +pub use error::XrError; + #[derive(Debug, Copy, Clone)] pub struct SwapchainCreateInfo { pub create_flags: SwapchainCreateFlags, diff --git a/src/render.rs b/src/render.rs index d79c6f8..5e63cfb 100644 --- a/src/render.rs +++ b/src/render.rs @@ -5,7 +5,7 @@ use bevy::render::camera::{RenderTarget, Viewport}; use crate::types::Pose; -pub(crate) const XR_TEXTURE_VIEW_INDEX: u32 = 1208214591; +pub const XR_TEXTURE_VIEW_INDEX: u32 = 1208214591; #[derive(Debug, Clone)] pub struct XrView { diff --git a/xr_api/Cargo.toml b/xr_api/Cargo.toml deleted file mode 100644 index 09bdf8c..0000000 --- a/xr_api/Cargo.toml +++ /dev/null @@ -1,78 +0,0 @@ -[package] -name = "xr_api" -version = "0.1.0" -edition = "2021" - -[features] -default = ["linked"] -linked = ["openxr/linked"] - -[dependencies] -futures = "0.3.29" -glam = "0.24.1" -hashbrown = "0.14" -paste = "1.0.14" -thiserror = "1.0.51" -tracing = "0.1.40" -wgpu = "0.17.1" -wgpu-hal = "0.17.1" - -[target.'cfg(not(target_family = "wasm"))'.dependencies] -openxr = "0.17.1" -ash = "0.37.3" - -[target.'cfg(target_family = "wasm")'.dependencies] -wasm-bindgen = "0.2.87" -web-sys = { version = "0.3.61", features = [ - # STANDARD - 'console', - 'Document', - 'Element', - 'Headers', - 'Navigator', - 'Window', - # IO - # 'Url', - # WEBGL - 'Gpu', - 'HtmlCanvasElement', - 'WebGl2RenderingContext', - 'WebGlFramebuffer', - ## XR - 'DomPointReadOnly', - 'XrWebGlLayer', - 'XrBoundedReferenceSpace', - 'XrEye', - 'XrFrame', - 'XrHandedness', - 'XrInputSource', - 'XrInputSourceArray', - 'XrInputSourceEvent', - 'XrInputSourceEventInit', - 'XrInputSourcesChangeEvent', - 'XrJointPose', - 'XrJointSpace', - 'XrPose', - 'XrReferenceSpace', - 'XrReferenceSpaceEvent', - 'XrReferenceSpaceEventInit', - 'XrReferenceSpaceType', - 'XrRenderState', - 'XrRenderStateInit', - 'XrRigidTransform', - 'XrSession', - 'XrSessionEvent', - 'XrSessionEventInit', - 'XrSessionInit', - 'XrSessionMode', - 'XrSpace', - 'XrTargetRayMode', - 'XrView', - 'XrViewerPose', - 'XrViewport', - 'XrVisibilityState', - 'XrWebGlLayer', - 'XrWebGlLayerInit', - 'XrSystem', -] } -wasm-bindgen-futures = "0.4" diff --git a/xr_api/src/api.rs b/xr_api/src/api.rs deleted file mode 100644 index 4733b8f..0000000 --- a/xr_api/src/api.rs +++ /dev/null @@ -1,117 +0,0 @@ -use std::ops::Deref; -use std::rc::Rc; - -use glam::Vec2; - -use crate::prelude::*; - -/// Entry point to the API -/// -/// To see methods available for this struct, refer to [EntryTrait] -#[derive(Clone)] -pub struct Entry(Rc); - -impl Entry { - /// Constructs a new Xr entry - pub fn new() -> Self { - todo!() - } -} - -/// Represents an intent to start a session with requested extensions. -/// -/// To see methods available for this struct, refer to [InstanceTrait] -#[derive(Clone)] -pub struct Instance(Rc); - -/// Represents a running XR application. -/// -/// To see methods available for this struct, refer to [SessionTrait] -#[derive(Clone)] -pub struct Session(Rc); - -/// A view of one eye. Used to retrieve render data such as texture views and projection matrices. -/// -/// To see methods available for this struct, refer to [ViewTrait] -#[derive(Clone)] -pub struct View(Rc); - -/// Represents all XR input sources. -/// -/// To see methods available for this struct, refer to [InputTrait] -#[derive(Clone)] -pub struct Input(Rc); - -/// Represents an XR Action. Can be used to retrieve input values or trigger output devices such as haptics. -/// -/// The methods available to this struct are dependent upon the action type. For input values, use `.get()` to retrieve the values. -/// For haptics, please refer to [HapticTrait] -#[derive(Clone)] -pub struct Action(Rc); - -macro_rules! impl_api { - ($($t:ty, $trait:ident; )*) => { - $( - impl std::ops::Deref for $t { - type Target = dyn $trait; - - fn deref(&self) -> &Self::Target { - &*self.0 - } - } - - impl From for $t { - fn from(value: T) -> Self { - Self(Rc::new(value)) - } - } - )* - - }; -} - -impl_api! { - Entry, EntryTrait; - Instance, InstanceTrait; - Session, SessionTrait; - View, ViewTrait; - Input, InputTrait; -} - -impl Deref for Action { - type Target = A::Inner; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl From for Action { - fn from(value: T) -> Self { - Self(Rc::new(value)) - } -} - -impl + 'static> From for Action { - fn from(value: T) -> Self { - Self(Rc::new(value)) - } -} - -impl + 'static> From for Action { - fn from(value: T) -> Self { - Self(Rc::new(value)) - } -} - -impl + 'static> From for Action { - fn from(value: T) -> Self { - Self(Rc::new(value)) - } -} - -impl + 'static> From for Action { - fn from(value: T) -> Self { - Self(Rc::new(value)) - } -} diff --git a/xr_api/src/api_traits.rs b/xr_api/src/api_traits.rs deleted file mode 100644 index ec29aee..0000000 --- a/xr_api/src/api_traits.rs +++ /dev/null @@ -1,117 +0,0 @@ -use glam::{UVec2, Vec2, Vec3A}; -use wgpu::{Adapter, AdapterInfo, Device, Queue, TextureView}; - -use crate::prelude::*; - -use crate::path::{InputComponent, UntypedActionPath}; - -pub trait EntryTrait { - /// Return currently available extensions - fn available_extensions(&self) -> Result; - /// Create an [Instance] with the enabled extensions. - fn create_instance(&self, exts: ExtensionSet) -> Result; -} - -pub trait InstanceTrait { - /// Returns the [Entry] used to create this. - fn entry(&self) -> Entry; - /// Returns an [ExtensionSet] listing all enabled extensions. - fn enabled_extensions(&self) -> ExtensionSet; - /// Creates a [Session] with the requested properties - fn create_session(&self, info: SessionCreateInfo) -> Result; -} - -pub trait SessionTrait { - /// Returns the [Instance] used to create this. - fn instance(&self) -> &Instance; - /// Get render resources compatible with this session. - fn get_render_resources(&self) - -> Option<(Device, Queue, AdapterInfo, Adapter, wgpu::Instance)>; - /// Returns the position of the headset. - fn headset_location(&self) -> Result; - /// Request input modules with the specified bindings. - fn create_input(&self, bindings: Bindings) -> Result; - /// Wait until a frame is ready to render to. - fn wait_frame(&self) -> Result; - /// Begin rendering work for the frame. - fn begin_frame(&self) -> Result<()>; - /// Locate the views of each eye. - fn locate_views(&self) -> Result<(View, View)>; - /// Submits rendering work for this frame. - fn end_frame(&self, data: FrameData) -> Result<()>; - /// Gets the resolution of a single eye. - fn resolution(&self) -> UVec2; - /// Gets the texture format for the session. - fn format(&self) -> wgpu::TextureFormat; -} - -pub trait ViewTrait { - /// Returns the [TextureView] used to render this view. - fn texture_view(&self) -> Option; - /// Returns the [Pose] representing the current position of this view. - fn pose(&self) -> Pose; - /// Returns the projection matrix for the current view. - fn projection_matrix(&self, near: f32, far: f32) -> glam::Mat4; - /// Gets the fov of the camera. - fn fov(&self) -> Fov; - /// Gets the resolution for this view. - fn resolution(&self) -> UVec2; - /// Gets the texture format for the view. - fn format(&self) -> wgpu::TextureFormat; -} - -pub trait InputTrait { - /// Get the haptic action at the specified path. - fn create_action_haptics(&self, path: UntypedActionPath) -> Result>; - /// Get the pose action at the specified path. - fn create_action_pose(&self, path: UntypedActionPath) -> Result>; - /// Get the float action at the specified path. - fn create_action_float(&self, path: UntypedActionPath) -> Result>; - /// Get the boolean action at the specified path. - fn create_action_bool(&self, path: UntypedActionPath) -> Result>; - /// Get the Vec2 action at the specified path. - fn create_action_vec2(&self, path: UntypedActionPath) -> Result>; -} - -// This impl is moved outside of the trait to ensure that InputTrait stays object safe. -impl dyn InputTrait { - /// Get the action at the specified path. - pub fn create_action( - &self, - path: ActionPath

, - ) -> Result> { - P::PathType::get(self, path.untyped()) - } -} - -/// Represents input actions, such as bools, floats, and poses -pub trait ActionInputTrait { - fn get(&self) -> Result; -} - -/// Represents haptic actions. -pub trait HapticTrait {} - -impl EntryTrait for T { - fn available_extensions(&self) -> Result { - self.entry().available_extensions() - } - - fn create_instance(&self, exts: ExtensionSet) -> Result { - self.entry().create_instance(exts) - } -} - -impl InstanceTrait for T { - fn entry(&self) -> Entry { - self.instance().entry() - } - - fn enabled_extensions(&self) -> ExtensionSet { - self.instance().enabled_extensions() - } - - fn create_session(&self, info: SessionCreateInfo) -> Result { - self.instance().create_session(info) - } -} diff --git a/xr_api/src/backend/mod.rs b/xr_api/src/backend/mod.rs deleted file mode 100644 index 407ca25..0000000 --- a/xr_api/src/backend/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[cfg(not(target_family = "wasm"))] -pub mod oxr; -#[cfg(target_family = "wasm")] -pub mod webxr; diff --git a/xr_api/src/backend/oxr.rs b/xr_api/src/backend/oxr.rs deleted file mode 100644 index c793047..0000000 --- a/xr_api/src/backend/oxr.rs +++ /dev/null @@ -1,617 +0,0 @@ -mod graphics; -mod utils; - -use utils::UntypedOXrAction; - -use std::{marker::PhantomData, rc::Rc, sync::Mutex}; - -use glam::{Mat4, UVec2, Vec2, Vec3A}; -use hashbrown::{hash_map, HashMap}; -use openxr::{EnvironmentBlendMode, Vector2f}; -use tracing::{info, info_span, warn}; - -use crate::{backend::oxr::graphics::VIEW_TYPE, path, prelude::*}; - -pub struct OXrEntry(openxr::Entry); - -impl OXrEntry { - pub fn new() -> Self { - #[cfg(feature = "linked")] - return OXrEntry(openxr::Entry::linked()); - #[cfg(not(feature = "linked"))] - return OXrEntry(unsafe { openxr::Entry::load().expect("Failed to load OpenXR runtime") }); - } -} - -impl Into for ExtensionSet { - fn into(self) -> openxr::ExtensionSet { - let mut set = openxr::ExtensionSet::default(); - set.khr_vulkan_enable2 = self.vulkan; - set - } -} - -impl EntryTrait for OXrEntry { - fn available_extensions(&self) -> Result { - // self.0.enumerate_extensions(); - Ok(ExtensionSet::default()) - } - - fn create_instance(&self, exts: ExtensionSet) -> Result { - #[allow(unused_mut)] - let mut enabled_extensions: openxr::ExtensionSet = exts.into(); - #[cfg(target_os = "android")] - { - enabled_extensions.khr_android_create_instance = true; - } - let xr_instance = self.0.create_instance( - &openxr::ApplicationInfo { - application_name: "bevy", - ..Default::default() - }, - &enabled_extensions, - &[], - )?; - Ok(OXrInstance(xr_instance, exts).into()) - } -} - -#[derive(Clone)] -pub struct OXrInstance(openxr::Instance, ExtensionSet); - -impl InstanceTrait for OXrInstance { - fn entry(&self) -> Entry { - OXrEntry(self.0.entry().clone()).into() - } - - fn enabled_extensions(&self) -> ExtensionSet { - self.1 - } - - fn create_session(&self, info: SessionCreateInfo) -> Result { - graphics::init_oxr_graphics(self.clone(), self.1, info.texture_format).map(Into::into) - } -} - -#[derive(Default)] -pub(crate) struct BindingState { - sessions_attached: bool, - bindings: Option, - map: HashMap, -} - -pub struct OXrSession { - pub(crate) instance: Instance, - // this could definitely be done better - pub(crate) inner_instance: openxr::Instance, - pub(crate) session: openxr::Session, - pub(crate) action_sets: Mutex>, - pub(crate) render_resources: Mutex< - Option<( - wgpu::Device, - wgpu::Queue, - wgpu::AdapterInfo, - wgpu::Adapter, - wgpu::Instance, - )>, - >, - pub(crate) bindings: Rc>, - pub(crate) frame_state: Rc>, - pub(crate) views: Mutex<[openxr::View; 2]>, - pub(crate) frame_waiter: Mutex, - pub(crate) swapchain: graphics::Swapchain, - pub(crate) stage: Rc, - pub(crate) head: openxr::Space, - pub(crate) resolution: UVec2, - pub(crate) blend_mode: EnvironmentBlendMode, - pub(crate) format: wgpu::TextureFormat, -} - -impl SessionTrait for OXrSession { - fn instance(&self) -> &Instance { - &self.instance - } - fn get_render_resources( - &self, - ) -> Option<( - wgpu::Device, - wgpu::Queue, - wgpu::AdapterInfo, - wgpu::Adapter, - wgpu::Instance, - )> { - std::mem::take(&mut self.render_resources.lock().unwrap()) - } - - fn create_input(&self, bindings: Bindings) -> Result { - let mut binding_state = self.bindings.lock().unwrap(); - if binding_state.bindings.is_none() { - binding_state.bindings = Some(bindings); - } else { - Err(XrError::Placeholder)? - } - - let action_set = self - .inner_instance - .create_action_set("xr_input", "XR Input", 0)?; - self.action_sets.lock().unwrap().push(action_set.clone()); - Ok(OXrInput { - inner_session: self.session.clone().into_any_graphics(), - action_set, - bindings: self.bindings.clone(), - stage: self.stage.clone(), - frame_state: self.frame_state.clone(), - } - .into()) - } - - fn wait_frame(&self) -> Result { - { - let mut bindings = self.bindings.lock().unwrap(); - if !bindings.sessions_attached { - if let Some(interaction_profile) = bindings.bindings { - let _span = info_span!("xr_attach_actions"); - let controller_bindings: Vec = bindings - .map - .iter() - .filter_map(|(path, action)| { - let path = match self.inner_instance.string_to_path(path) { - Ok(path) => path, - Err(e) => { - warn!("{e}"); - return None; - } - }; - Some(match action { - UntypedOXrAction::Haptics(action) => { - openxr::Binding::new(action, path) - } - UntypedOXrAction::Pose(action) => { - openxr::Binding::new(action, path) - } - UntypedOXrAction::Float(action) => { - openxr::Binding::new(action, path) - } - UntypedOXrAction::Bool(action) => { - openxr::Binding::new(action, path) - } - UntypedOXrAction::Vec2(action) => { - openxr::Binding::new(action, path) - } - }) - }) - .collect(); - self.inner_instance.suggest_interaction_profile_bindings( - self.inner_instance - .string_to_path(interaction_profile.get_interaction_profile())?, - &controller_bindings, - )?; - - let action_sets = self.action_sets.lock().unwrap(); - self.session - .attach_action_sets(&action_sets.iter().collect::>())?; - } - - bindings.sessions_attached = true; - } - } - { - let _span = info_span!("xr_poll_events"); - while let Some(event) = self - .inner_instance - .poll_event(&mut Default::default()) - .unwrap() - { - use openxr::Event::*; - match event { - SessionStateChanged(e) => { - // Session state change is where we can begin and end sessions, as well as - // find quit messages! - info!("entered XR state {:?}", e.state()); - match e.state() { - openxr::SessionState::READY => { - self.session.begin(VIEW_TYPE).unwrap(); - } - openxr::SessionState::STOPPING => { - self.session.end().unwrap(); - } - openxr::SessionState::EXITING | openxr::SessionState::LOSS_PENDING => { - return Err(XrError::Placeholder); - } - _ => {} - } - } - InstanceLossPending(_) => return Err(XrError::Placeholder), - EventsLost(e) => { - warn!("lost {} XR events", e.lost_event_count()); - } - _ => {} - } - } - } - { - let _span = info_span!("xr_wait_frame").entered(); - *self.frame_state.lock().unwrap() = match self.frame_waiter.lock().unwrap().wait() { - Ok(a) => a, - Err(e) => { - warn!("error: {}", e); - return Err(XrError::Placeholder); - } - }; - } - Ok(FrameData) - } - - fn begin_frame(&self) -> Result<()> { - { - let _span = info_span!("xr_begin_frame").entered(); - self.swapchain.begin().unwrap() - } - - { - let _span = info_span!("xr_acquire_image").entered(); - self.swapchain.acquire_image().unwrap() - } - { - let _span = info_span!("xr_wait_image").entered(); - self.swapchain.wait_image().unwrap(); - } - { - let action_sets = self.action_sets.lock().unwrap(); - self.session - .sync_actions(&action_sets.iter().map(Into::into).collect::>())?; - } - Ok(()) - } - - fn locate_views(&self) -> Result<(View, View)> { - let views = { - let _span = info_span!("xr_locate_views").entered(); - self.session - .locate_views( - VIEW_TYPE, - self.frame_state.lock().unwrap().predicted_display_time, - &self.stage, - ) - .unwrap() - .1 - }; - *self.views.lock().unwrap() = [views[0].clone(), views[1].clone()]; - { - let _span = info_span!("xr_update_manual_texture_views").entered(); - let (left, right) = self.swapchain.get_render_views(); - let left = OXrView { - texture: Mutex::new(Some(left)), - view: views[0], - resolution: self.resolution, - format: self.format, - }; - let right = OXrView { - texture: Mutex::new(Some(right)), - view: views[1], - resolution: self.resolution, - format: self.format, - }; - Ok((left.into(), right.into())) - } - } - - fn end_frame(&self, data: FrameData) -> Result<()> { - { - let _span = info_span!("xr_release_image").entered(); - self.swapchain.release_image().unwrap(); - } - { - let _span = info_span!("xr_end_frame").entered(); - let frame_state = self.frame_state.lock().unwrap().clone(); - let result = self.swapchain.end( - frame_state.predicted_display_time, - &*self.views.lock().unwrap(), - &self.stage, - self.resolution, - self.blend_mode, - frame_state.should_render, - // passthrough_layer.map(|p| p.into_inner()), - ); - match result { - Ok(_) => {} - Err(e) => warn!("error: {}", e), - } - } - Ok(()) - } - - fn resolution(&self) -> UVec2 { - self.resolution - } - - fn format(&self) -> wgpu::TextureFormat { - self.format - } - - fn headset_location(&self) -> Result { - let location = self.head.locate( - &self.stage, - self.frame_state.lock().unwrap().predicted_display_time, - )?; - Ok(location.pose.into()) - } -} - -pub struct OXrInput { - inner_session: openxr::Session, - action_set: openxr::ActionSet, - bindings: Rc>, - stage: Rc, - frame_state: Rc>, -} - -impl InputTrait for OXrInput { - fn create_action_haptics(&self, path: path::UntypedActionPath) -> Result> { - let mut bindings = self.bindings.lock().unwrap(); - let action = match bindings.map.entry(path.into_xr_path()) { - hash_map::Entry::Occupied(entry) => match entry.get() { - UntypedOXrAction::Haptics(action) => action.clone(), - _ => Err(XrError::Placeholder)?, - }, - hash_map::Entry::Vacant(entry) => { - let name = &path.into_name(); - let action = self - .action_set - .create_action::(name, name, &[])?; - entry.insert(UntypedOXrAction::Haptics(action.clone())); - action - } - }; - Ok(OXrHaptics(action, self.inner_session.clone()).into()) - } - - fn create_action_pose(&self, path: path::UntypedActionPath) -> Result> { - let mut bindings = self.bindings.lock().unwrap(); - let action = match bindings.map.entry(path.into_xr_path()) { - hash_map::Entry::Occupied(entry) => match entry.get() { - UntypedOXrAction::Pose(action) => action.clone(), - _ => Err(XrError::Placeholder)?, - }, - hash_map::Entry::Vacant(entry) => { - let name = &path.into_name(); - let action = self - .action_set - .create_action::(name, name, &[])?; - entry.insert(UntypedOXrAction::Pose(action.clone())); - action - } - }; - Ok(OXrPoseAction { - pose_space: action.create_space( - self.inner_session.clone(), - openxr::Path::NULL, - openxr::Posef::IDENTITY, - )?, - stage: self.stage.clone(), - frame_state: self.frame_state.clone(), - } - .into()) - } - - fn create_action_float(&self, path: path::UntypedActionPath) -> Result> { - let mut bindings = self.bindings.lock().unwrap(); - let action = match bindings.map.entry(path.into_xr_path()) { - hash_map::Entry::Occupied(entry) => match entry.get() { - UntypedOXrAction::Float(action) => action.clone(), - _ => Err(XrError::Placeholder)?, - }, - hash_map::Entry::Vacant(entry) => { - let name = &path.into_name(); - let action = self.action_set.create_action::(name, name, &[])?; - entry.insert(UntypedOXrAction::Float(action.clone())); - action - } - }; - Ok(OXrAction(action, self.inner_session.clone(), PhantomData).into()) - } - - fn create_action_bool(&self, path: path::UntypedActionPath) -> Result> { - let mut bindings = self.bindings.lock().unwrap(); - let action = match bindings.map.entry(path.into_xr_path()) { - hash_map::Entry::Occupied(entry) => match entry.get() { - UntypedOXrAction::Bool(action) => action.clone(), - _ => Err(XrError::Placeholder)?, - }, - hash_map::Entry::Vacant(entry) => { - let name = &path.into_name(); - let action = self.action_set.create_action::(name, name, &[])?; - entry.insert(UntypedOXrAction::Bool(action.clone())); - action - } - }; - Ok(OXrAction(action, self.inner_session.clone(), PhantomData).into()) - } - - fn create_action_vec2(&self, path: path::UntypedActionPath) -> Result> { - let mut bindings = self.bindings.lock().unwrap(); - let action = match bindings.map.entry(path.into_xr_path()) { - hash_map::Entry::Occupied(entry) => match entry.get() { - UntypedOXrAction::Vec2(action) => action.clone(), - _ => Err(XrError::Placeholder)?, - }, - hash_map::Entry::Vacant(entry) => { - let name = &path.into_name(); - let action = self.action_set.create_action::(name, name, &[])?; - entry.insert(UntypedOXrAction::Vec2(action.clone())); - action - } - }; - Ok(OXrAction::(action, self.inner_session.clone(), PhantomData).into()) - } -} - -pub struct OXrHaptics( - openxr::Action, - openxr::Session, -); - -impl HapticTrait for OXrHaptics {} - -pub struct OXrPoseAction { - pose_space: openxr::Space, - stage: Rc, - frame_state: Rc>, -} - -impl ActionInputTrait for OXrPoseAction { - fn get(&self) -> Result { - let pose = self - .pose_space - .locate( - &self.stage, - self.frame_state.lock().unwrap().predicted_display_time, - )? - .pose; - - Ok(pose.into()) - } -} - -pub struct OXrAction( - openxr::Action, - openxr::Session, - PhantomData, -); - -impl ActionInputTrait for OXrAction { - fn get(&self) -> Result { - Ok(self.0.state(&self.1, openxr::Path::NULL)?.current_state) - } -} - -impl ActionInputTrait for OXrAction { - fn get(&self) -> Result { - let Vector2f { x, y } = self.0.state(&self.1, openxr::Path::NULL)?.current_state; - Ok(Vec2 { x, y }) - } -} - -pub struct OXrView { - texture: Mutex>, - view: openxr::View, - resolution: UVec2, - format: wgpu::TextureFormat, -} - -impl ViewTrait for OXrView { - fn texture_view(&self) -> Option { - std::mem::take(&mut *self.texture.lock().unwrap()) - } - - fn pose(&self) -> Pose { - self.view.pose.clone().into() - } - - fn projection_matrix(&self, near: f32, far: f32) -> glam::Mat4 { - // symmetric perspective for debugging - // let x_fov = (self.fov.angle_left.abs() + self.fov.angle_right.abs()); - // let y_fov = (self.fov.angle_up.abs() + self.fov.angle_down.abs()); - // return Mat4::perspective_infinite_reverse_rh(y_fov, x_fov / y_fov, self.near); - - let fov = self.view.fov; - let is_vulkan_api = false; // FIXME wgpu probably abstracts this - - let tan_angle_left = fov.angle_left.tan(); - let tan_angle_right = fov.angle_right.tan(); - - let tan_angle_down = fov.angle_down.tan(); - let tan_angle_up = fov.angle_up.tan(); - - let tan_angle_width = tan_angle_right - tan_angle_left; - - // Set to tanAngleDown - tanAngleUp for a clip space with positive Y - // down (Vulkan). Set to tanAngleUp - tanAngleDown for a clip space with - // positive Y up (OpenGL / D3D / Metal). - // const float tanAngleHeight = - // graphicsApi == GRAPHICS_VULKAN ? (tanAngleDown - tanAngleUp) : (tanAngleUp - tanAngleDown); - let tan_angle_height = if is_vulkan_api { - tan_angle_down - tan_angle_up - } else { - tan_angle_up - tan_angle_down - }; - - // Set to nearZ for a [-1,1] Z clip space (OpenGL / OpenGL ES). - // Set to zero for a [0,1] Z clip space (Vulkan / D3D / Metal). - // const float offsetZ = - // (graphicsApi == GRAPHICS_OPENGL || graphicsApi == GRAPHICS_OPENGL_ES) ? nearZ : 0; - // FIXME handle enum of graphics apis - let offset_z = 0.; - - let mut cols: [f32; 16] = [0.0; 16]; - - if far <= near { - // place the far plane at infinity - cols[0] = 2. / tan_angle_width; - cols[4] = 0.; - cols[8] = (tan_angle_right + tan_angle_left) / tan_angle_width; - cols[12] = 0.; - - cols[1] = 0.; - cols[5] = 2. / tan_angle_height; - cols[9] = (tan_angle_up + tan_angle_down) / tan_angle_height; - cols[13] = 0.; - - cols[2] = 0.; - cols[6] = 0.; - cols[10] = -1.; - cols[14] = -(near + offset_z); - - cols[3] = 0.; - cols[7] = 0.; - cols[11] = -1.; - cols[15] = 0.; - - // bevy uses the _reverse_ infinite projection - // https://dev.theomader.com/depth-precision/ - let z_reversal = Mat4::from_cols_array_2d(&[ - [1f32, 0., 0., 0.], - [0., 1., 0., 0.], - [0., 0., -1., 0.], - [0., 0., 1., 1.], - ]); - - return z_reversal * Mat4::from_cols_array(&cols); - } else { - // normal projection - cols[0] = 2. / tan_angle_width; - cols[4] = 0.; - cols[8] = (tan_angle_right + tan_angle_left) / tan_angle_width; - cols[12] = 0.; - - cols[1] = 0.; - cols[5] = 2. / tan_angle_height; - cols[9] = (tan_angle_up + tan_angle_down) / tan_angle_height; - cols[13] = 0.; - - cols[2] = 0.; - cols[6] = 0.; - cols[10] = -(far + offset_z) / (far - near); - cols[14] = -(far * (near + offset_z)) / (far - near); - - cols[3] = 0.; - cols[7] = 0.; - cols[11] = -1.; - cols[15] = 0.; - } - - Mat4::from_cols_array(&cols) - } - - fn resolution(&self) -> UVec2 { - self.resolution - } - - fn format(&self) -> wgpu::TextureFormat { - self.format - } - - fn fov(&self) -> Fov { - self.view.fov.into() - } -} diff --git a/xr_api/src/backend/oxr/graphics.rs b/xr_api/src/backend/oxr/graphics.rs deleted file mode 100644 index d5bf110..0000000 --- a/xr_api/src/backend/oxr/graphics.rs +++ /dev/null @@ -1,183 +0,0 @@ -mod vulkan; - -use std::sync::Mutex; - -use glam::UVec2; -use tracing::warn; - -use super::{OXrInstance, OXrSession}; -use crate::error::{Result, XrError}; -use crate::types::ExtensionSet; - -pub const VIEW_TYPE: openxr::ViewConfigurationType = openxr::ViewConfigurationType::PRIMARY_STEREO; - -pub fn init_oxr_graphics( - instance: OXrInstance, - extensions: ExtensionSet, - format: wgpu::TextureFormat, -) -> Result { - if extensions.vulkan { - return vulkan::init_oxr_graphics(instance, format, String::new()); - } - - Err(XrError::Placeholder) -} - -pub enum Swapchain { - Vulkan(SwapchainInner), -} - -impl Swapchain { - pub(crate) fn begin(&self) -> Result<()> { - Ok(match self { - Swapchain::Vulkan(swapchain) => swapchain.begin()?, - }) - } - - pub(crate) fn get_render_views(&self) -> (wgpu::TextureView, wgpu::TextureView) { - match self { - Swapchain::Vulkan(swapchain) => swapchain.get_render_views(), - } - } - - pub(crate) fn acquire_image(&self) -> Result<()> { - Ok(match self { - Swapchain::Vulkan(swapchain) => swapchain.acquire_image()?, - }) - } - - pub(crate) fn wait_image(&self) -> Result<()> { - Ok(match self { - Swapchain::Vulkan(swapchain) => swapchain.wait_image()?, - }) - } - - pub(crate) fn release_image(&self) -> Result<()> { - Ok(match self { - Swapchain::Vulkan(swapchain) => swapchain.release_image()?, - }) - } - - pub(crate) fn end( - &self, - predicted_display_time: openxr::Time, - views: &[openxr::View], - stage: &openxr::Space, - resolution: UVec2, - environment_blend_mode: openxr::EnvironmentBlendMode, - should_render: bool, - ) -> Result<()> { - Ok(match self { - Swapchain::Vulkan(swapchain) => swapchain.end( - predicted_display_time, - views, - stage, - resolution, - environment_blend_mode, - should_render, - )?, - }) - } -} - -pub struct SwapchainInner { - stream: Mutex>, - swapchain: Mutex>, - buffers: Vec, - image_index: Mutex, -} - -impl SwapchainInner { - fn begin(&self) -> openxr::Result<()> { - self.stream.lock().unwrap().begin() - } - - fn get_render_views(&self) -> (wgpu::TextureView, wgpu::TextureView) { - let texture = &self.buffers[*self.image_index.lock().unwrap()]; - - ( - texture.create_view(&wgpu::TextureViewDescriptor { - dimension: Some(wgpu::TextureViewDimension::D2), - array_layer_count: Some(1), - ..Default::default() - }), - texture.create_view(&wgpu::TextureViewDescriptor { - dimension: Some(wgpu::TextureViewDimension::D2), - array_layer_count: Some(1), - base_array_layer: 1, - ..Default::default() - }), - ) - } - - fn acquire_image(&self) -> openxr::Result<()> { - let image_index = self.swapchain.lock().unwrap().acquire_image()?; - *self.image_index.lock().unwrap() = image_index as _; - Ok(()) - } - - fn wait_image(&self) -> openxr::Result<()> { - self.swapchain - .lock() - .unwrap() - .wait_image(openxr::Duration::INFINITE) - } - - fn release_image(&self) -> openxr::Result<()> { - self.swapchain.lock().unwrap().release_image() - } - - fn end( - &self, - predicted_display_time: openxr::Time, - views: &[openxr::View], - stage: &openxr::Space, - resolution: UVec2, - environment_blend_mode: openxr::EnvironmentBlendMode, - should_render: bool, - ) -> openxr::Result<()> { - let rect = openxr::Rect2Di { - offset: openxr::Offset2Di { x: 0, y: 0 }, - extent: openxr::Extent2Di { - width: resolution.x as _, - height: resolution.y as _, - }, - }; - let swapchain = self.swapchain.lock().unwrap(); - if views.len() == 0 { - warn!("views are len of 0"); - return Ok(()); - } - let mut stream = self.stream.lock().unwrap(); - if should_render { - stream.end( - predicted_display_time, - environment_blend_mode, - &[&openxr::CompositionLayerProjection::new() - .space(stage) - .views(&[ - openxr::CompositionLayerProjectionView::new() - .pose(views[0].pose) - .fov(views[0].fov) - .sub_image( - openxr::SwapchainSubImage::new() - .swapchain(&swapchain) - .image_array_index(0) - .image_rect(rect), - ), - openxr::CompositionLayerProjectionView::new() - .pose(views[1].pose) - .fov(views[1].fov) - .sub_image( - openxr::SwapchainSubImage::new() - .swapchain(&swapchain) - .image_array_index(1) - .image_rect(rect), - ), - ])], - ) - } else { - stream.end(predicted_display_time, environment_blend_mode, &[]) - } - } -} diff --git a/xr_api/src/backend/oxr/graphics/vulkan.rs b/xr_api/src/backend/oxr/graphics/vulkan.rs deleted file mode 100644 index dcf749d..0000000 --- a/xr_api/src/backend/oxr/graphics/vulkan.rs +++ /dev/null @@ -1,461 +0,0 @@ -use std::ffi::{c_void, CString}; -use std::rc::Rc; -use std::sync::Mutex; - -use super::{Swapchain, SwapchainInner, VIEW_TYPE}; -use crate::backend::oxr::{OXrInstance, OXrSession}; -use crate::error::{Result, XrError}; -use ash::vk::{self, Handle}; -use glam::uvec2; - -pub fn init_oxr_graphics( - instance: OXrInstance, - swapchain_format: wgpu::TextureFormat, - xr_app_name: String, -) -> Result { - use wgpu_hal::{api::Vulkan as V, Api}; - let xr_instance = &instance.0; - let xr_system_id = xr_instance.system(openxr::FormFactor::HEAD_MOUNTED_DISPLAY)?; - // TODO! allow selecting specific blend mode. - let blend_mode = openxr::EnvironmentBlendMode::OPAQUE; - - #[cfg(not(target_os = "android"))] - let vk_target_version = vk::make_api_version(0, 1, 2, 0); - #[cfg(not(target_os = "android"))] - let vk_target_version_xr = openxr::Version::new(1, 2, 0); - - #[cfg(target_os = "android")] - let vk_target_version = vk::make_api_version(0, 1, 1, 0); - #[cfg(target_os = "android")] - let vk_target_version_xr = openxr::Version::new(1, 1, 0); - - let reqs = xr_instance.graphics_requirements::(xr_system_id)?; - if vk_target_version_xr < reqs.min_api_version_supported - || vk_target_version_xr.major() > reqs.max_api_version_supported.major() - { - return Err(XrError::Placeholder); - } - - let vk_entry = unsafe { ash::Entry::load() }.map_err(|_| XrError::Placeholder)?; - let flags = wgpu_hal::InstanceFlags::empty(); - let extensions = ::Instance::required_extensions(&vk_entry, vk_target_version, flags) - .map_err(|_| XrError::Placeholder)?; - let device_extensions = vec![ - ash::extensions::khr::Swapchain::name(), - ash::extensions::khr::DrawIndirectCount::name(), - #[cfg(target_os = "android")] - ash::extensions::khr::TimelineSemaphore::name(), - ]; - - let vk_instance = unsafe { - let extensions_cchar: Vec<_> = extensions.iter().map(|s| s.as_ptr()).collect(); - - let app_name = CString::new(xr_app_name).map_err(|_| XrError::Placeholder)?; - let vk_app_info = vk::ApplicationInfo::builder() - .application_name(&app_name) - .application_version(1) - .engine_name(&app_name) - .engine_version(1) - .api_version(vk_target_version); - - let vk_instance = xr_instance - .create_vulkan_instance( - xr_system_id, - std::mem::transmute(vk_entry.static_fn().get_instance_proc_addr), - &vk::InstanceCreateInfo::builder() - .application_info(&vk_app_info) - .enabled_extension_names(&extensions_cchar) as *const _ - as *const _, - ) - .unwrap() - .map_err(vk::Result::from_raw) - .unwrap(); - - ash::Instance::load( - vk_entry.static_fn(), - vk::Instance::from_raw(vk_instance as _), - ) - }; - - let vk_instance_ptr = vk_instance.handle().as_raw() as *const c_void; - - let vk_physical_device = vk::PhysicalDevice::from_raw(unsafe { - xr_instance.vulkan_graphics_device(xr_system_id, vk_instance.handle().as_raw() as _)? as _ - }); - let vk_physical_device_ptr = vk_physical_device.as_raw() as *const c_void; - - let vk_device_properties = - unsafe { vk_instance.get_physical_device_properties(vk_physical_device) }; - if vk_device_properties.api_version < vk_target_version { - unsafe { vk_instance.destroy_instance(None) } - panic!("Vulkan physical device doesn't support version 1.1"); - } - - let wgpu_vk_instance = unsafe { - ::Instance::from_raw( - vk_entry.clone(), - vk_instance.clone(), - vk_target_version, - 0, - None, - extensions, - flags, - false, - Some(Box::new(())), - ) - .map_err(|_| XrError::Placeholder)? - }; - - let wgpu_features = wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES - | wgpu::Features::MULTIVIEW - | wgpu::Features::MULTI_DRAW_INDIRECT_COUNT - | wgpu::Features::MULTI_DRAW_INDIRECT; - - let wgpu_exposed_adapter = wgpu_vk_instance - .expose_adapter(vk_physical_device) - .ok_or(XrError::Placeholder)?; - - let enabled_extensions = wgpu_exposed_adapter - .adapter - .required_device_extensions(wgpu_features); - - let (wgpu_open_device, vk_device_ptr, queue_family_index) = { - let extensions_cchar: Vec<_> = device_extensions.iter().map(|s| s.as_ptr()).collect(); - let mut enabled_phd_features = wgpu_exposed_adapter - .adapter - .physical_device_features(&enabled_extensions, wgpu_features); - let family_index = 0; - let family_info = vk::DeviceQueueCreateInfo::builder() - .queue_family_index(family_index) - .queue_priorities(&[1.0]) - .build(); - let family_infos = [family_info]; - let info = enabled_phd_features - .add_to_device_create_builder( - vk::DeviceCreateInfo::builder() - .queue_create_infos(&family_infos) - .push_next(&mut vk::PhysicalDeviceMultiviewFeatures { - multiview: vk::TRUE, - ..Default::default() - }), - ) - .enabled_extension_names(&extensions_cchar) - .build(); - let vk_device = unsafe { - let vk_device = xr_instance - .create_vulkan_device( - xr_system_id, - std::mem::transmute(vk_entry.static_fn().get_instance_proc_addr), - vk_physical_device.as_raw() as _, - &info as *const _ as *const _, - )? - .map_err(|_| XrError::Placeholder)?; - - ash::Device::load(vk_instance.fp_v1_0(), vk::Device::from_raw(vk_device as _)) - }; - let vk_device_ptr = vk_device.handle().as_raw() as *const c_void; - - let wgpu_open_device = unsafe { - wgpu_exposed_adapter.adapter.device_from_raw( - vk_device, - true, - &enabled_extensions, - wgpu_features, - family_info.queue_family_index, - 0, - ) - } - .map_err(|_| XrError::Placeholder)?; - - ( - wgpu_open_device, - vk_device_ptr, - family_info.queue_family_index, - ) - }; - - let wgpu_instance = - unsafe { wgpu::Instance::from_hal::(wgpu_vk_instance) }; - let wgpu_adapter = unsafe { wgpu_instance.create_adapter_from_hal(wgpu_exposed_adapter) }; - let (wgpu_device, wgpu_queue) = unsafe { - wgpu_adapter.create_device_from_hal( - wgpu_open_device, - &wgpu::DeviceDescriptor { - label: None, - features: wgpu_features, - limits: wgpu::Limits { - max_bind_groups: 8, - max_storage_buffer_binding_size: wgpu_adapter - .limits() - .max_storage_buffer_binding_size, - max_push_constant_size: 4, - ..Default::default() - }, - }, - None, - ) - } - .map_err(|_| XrError::Placeholder)?; - - let (session, frame_wait, frame_stream) = unsafe { - xr_instance.create_session::( - xr_system_id, - &openxr::vulkan::SessionCreateInfo { - instance: vk_instance_ptr, - physical_device: vk_physical_device_ptr, - device: vk_device_ptr, - queue_family_index, - queue_index: 0, - }, - ) - }?; - - let views = xr_instance.enumerate_view_configuration_views(xr_system_id, VIEW_TYPE)?; - - let resolution = uvec2( - views[0].recommended_image_rect_width, - views[0].recommended_image_rect_height, - ); - - let swapchain = session - .create_swapchain(&openxr::SwapchainCreateInfo { - create_flags: openxr::SwapchainCreateFlags::EMPTY, - usage_flags: openxr::SwapchainUsageFlags::COLOR_ATTACHMENT - | openxr::SwapchainUsageFlags::SAMPLED, - format: wgpu_to_vulkan(swapchain_format) - .ok_or(XrError::Placeholder)? - .as_raw() as _, - // The Vulkan graphics pipeline we create is not set up for multisampling, - // so we hardcode this to 1. If we used a proper multisampling setup, we - // could set this to `views[0].recommended_swapchain_sample_count`. - sample_count: 1, - width: resolution.x, - height: resolution.y, - face_count: 1, - array_size: 2, - mip_count: 1, - }) - .unwrap(); - let images = swapchain.enumerate_images().unwrap(); - - let buffers: Vec<_> = images - .into_iter() - .map(|color_image| { - let color_image = vk::Image::from_raw(color_image); - let wgpu_hal_texture = unsafe { - ::Device::texture_from_raw( - color_image, - &wgpu_hal::TextureDescriptor { - label: Some("VR Swapchain"), - size: wgpu::Extent3d { - width: resolution.x, - height: resolution.y, - depth_or_array_layers: 2, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: swapchain_format, - usage: wgpu_hal::TextureUses::COLOR_TARGET - | wgpu_hal::TextureUses::COPY_DST, - memory_flags: wgpu_hal::MemoryFlags::empty(), - view_formats: vec![], - }, - None, - ) - }; - let texture = unsafe { - wgpu_device.create_texture_from_hal::( - wgpu_hal_texture, - &wgpu::TextureDescriptor { - label: Some("VR Swapchain"), - size: wgpu::Extent3d { - width: resolution.x, - height: resolution.y, - depth_or_array_layers: 2, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: swapchain_format, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT - | wgpu::TextureUsages::COPY_DST, - view_formats: &[], - }, - ) - }; - texture - }) - .collect(); - - Ok(OXrSession { - inner_instance: instance.0.clone(), - instance: instance.into(), - session: session.clone().into_any_graphics(), - render_resources: Mutex::new(Some(( - wgpu_device, - wgpu_queue, - wgpu_adapter.get_info(), - wgpu_adapter, - wgpu_instance, - ))), - resolution, - views: Mutex::new([openxr::View::default(), openxr::View::default()]), - swapchain: Swapchain::Vulkan(SwapchainInner { - stream: Mutex::new(frame_stream), - swapchain: Mutex::new(swapchain), - buffers, - image_index: Mutex::new(0), - }), - frame_state: Rc::new(Mutex::new(openxr::FrameState { - predicted_display_time: openxr::Time::from_nanos(1), - predicted_display_period: openxr::Duration::from_nanos(1), - should_render: true, - })), - blend_mode, - frame_waiter: Mutex::new(frame_wait), - stage: Rc::new( - session.create_reference_space( - openxr::ReferenceSpaceType::STAGE, - openxr::Posef::IDENTITY, - )?, - ), - head: session - .create_reference_space(openxr::ReferenceSpaceType::VIEW, openxr::Posef::IDENTITY)?, - format: swapchain_format, - action_sets: Default::default(), - bindings: Default::default(), - }) -} - -fn wgpu_to_vulkan(format: wgpu::TextureFormat) -> Option { - // Copied with minor modification from: - // https://github.com/gfx-rs/wgpu/blob/a7defb723f856d946d6d220e9897d20dbb7b8f61/wgpu-hal/src/vulkan/conv.rs#L5-L151 - // license: MIT OR Apache-2.0 - use ash::vk::Format as F; - use wgpu::TextureFormat as Tf; - use wgpu::{AstcBlock, AstcChannel}; - Some(match format { - Tf::R8Unorm => F::R8_UNORM, - Tf::R8Snorm => F::R8_SNORM, - Tf::R8Uint => F::R8_UINT, - Tf::R8Sint => F::R8_SINT, - Tf::R16Uint => F::R16_UINT, - Tf::R16Sint => F::R16_SINT, - Tf::R16Unorm => F::R16_UNORM, - Tf::R16Snorm => F::R16_SNORM, - Tf::R16Float => F::R16_SFLOAT, - Tf::Rg8Unorm => F::R8G8_UNORM, - Tf::Rg8Snorm => F::R8G8_SNORM, - Tf::Rg8Uint => F::R8G8_UINT, - Tf::Rg8Sint => F::R8G8_SINT, - Tf::Rg16Unorm => F::R16G16_UNORM, - Tf::Rg16Snorm => F::R16G16_SNORM, - Tf::R32Uint => F::R32_UINT, - Tf::R32Sint => F::R32_SINT, - Tf::R32Float => F::R32_SFLOAT, - Tf::Rg16Uint => F::R16G16_UINT, - Tf::Rg16Sint => F::R16G16_SINT, - Tf::Rg16Float => F::R16G16_SFLOAT, - Tf::Rgba8Unorm => F::R8G8B8A8_UNORM, - Tf::Rgba8UnormSrgb => F::R8G8B8A8_SRGB, - Tf::Bgra8UnormSrgb => F::B8G8R8A8_SRGB, - Tf::Rgba8Snorm => F::R8G8B8A8_SNORM, - Tf::Bgra8Unorm => F::B8G8R8A8_UNORM, - Tf::Rgba8Uint => F::R8G8B8A8_UINT, - Tf::Rgba8Sint => F::R8G8B8A8_SINT, - // Tf::Rgb10a2Uint => F::A2B10G10R10_UINT_PACK32, - Tf::Rgb10a2Unorm => F::A2B10G10R10_UNORM_PACK32, - Tf::Rg11b10Float => F::B10G11R11_UFLOAT_PACK32, - Tf::Rg32Uint => F::R32G32_UINT, - Tf::Rg32Sint => F::R32G32_SINT, - Tf::Rg32Float => F::R32G32_SFLOAT, - Tf::Rgba16Uint => F::R16G16B16A16_UINT, - Tf::Rgba16Sint => F::R16G16B16A16_SINT, - Tf::Rgba16Unorm => F::R16G16B16A16_UNORM, - Tf::Rgba16Snorm => F::R16G16B16A16_SNORM, - Tf::Rgba16Float => F::R16G16B16A16_SFLOAT, - Tf::Rgba32Uint => F::R32G32B32A32_UINT, - Tf::Rgba32Sint => F::R32G32B32A32_SINT, - Tf::Rgba32Float => F::R32G32B32A32_SFLOAT, - Tf::Depth32Float => F::D32_SFLOAT, - Tf::Depth32FloatStencil8 => F::D32_SFLOAT_S8_UINT, - Tf::Depth24Plus | Tf::Depth24PlusStencil8 | Tf::Stencil8 => return None, // Dependent on device properties - Tf::Depth16Unorm => F::D16_UNORM, - Tf::Rgb9e5Ufloat => F::E5B9G9R9_UFLOAT_PACK32, - Tf::Bc1RgbaUnorm => F::BC1_RGBA_UNORM_BLOCK, - Tf::Bc1RgbaUnormSrgb => F::BC1_RGBA_SRGB_BLOCK, - Tf::Bc2RgbaUnorm => F::BC2_UNORM_BLOCK, - Tf::Bc2RgbaUnormSrgb => F::BC2_SRGB_BLOCK, - Tf::Bc3RgbaUnorm => F::BC3_UNORM_BLOCK, - Tf::Bc3RgbaUnormSrgb => F::BC3_SRGB_BLOCK, - Tf::Bc4RUnorm => F::BC4_UNORM_BLOCK, - Tf::Bc4RSnorm => F::BC4_SNORM_BLOCK, - Tf::Bc5RgUnorm => F::BC5_UNORM_BLOCK, - Tf::Bc5RgSnorm => F::BC5_SNORM_BLOCK, - Tf::Bc6hRgbUfloat => F::BC6H_UFLOAT_BLOCK, - Tf::Bc6hRgbFloat => F::BC6H_SFLOAT_BLOCK, - Tf::Bc7RgbaUnorm => F::BC7_UNORM_BLOCK, - Tf::Bc7RgbaUnormSrgb => F::BC7_SRGB_BLOCK, - Tf::Etc2Rgb8Unorm => F::ETC2_R8G8B8_UNORM_BLOCK, - Tf::Etc2Rgb8UnormSrgb => F::ETC2_R8G8B8_SRGB_BLOCK, - Tf::Etc2Rgb8A1Unorm => F::ETC2_R8G8B8A1_UNORM_BLOCK, - Tf::Etc2Rgb8A1UnormSrgb => F::ETC2_R8G8B8A1_SRGB_BLOCK, - Tf::Etc2Rgba8Unorm => F::ETC2_R8G8B8A8_UNORM_BLOCK, - Tf::Etc2Rgba8UnormSrgb => F::ETC2_R8G8B8A8_SRGB_BLOCK, - Tf::EacR11Unorm => F::EAC_R11_UNORM_BLOCK, - Tf::EacR11Snorm => F::EAC_R11_SNORM_BLOCK, - Tf::EacRg11Unorm => F::EAC_R11G11_UNORM_BLOCK, - Tf::EacRg11Snorm => F::EAC_R11G11_SNORM_BLOCK, - Tf::Astc { block, channel } => match channel { - AstcChannel::Unorm => match block { - AstcBlock::B4x4 => F::ASTC_4X4_UNORM_BLOCK, - AstcBlock::B5x4 => F::ASTC_5X4_UNORM_BLOCK, - AstcBlock::B5x5 => F::ASTC_5X5_UNORM_BLOCK, - AstcBlock::B6x5 => F::ASTC_6X5_UNORM_BLOCK, - AstcBlock::B6x6 => F::ASTC_6X6_UNORM_BLOCK, - AstcBlock::B8x5 => F::ASTC_8X5_UNORM_BLOCK, - AstcBlock::B8x6 => F::ASTC_8X6_UNORM_BLOCK, - AstcBlock::B8x8 => F::ASTC_8X8_UNORM_BLOCK, - AstcBlock::B10x5 => F::ASTC_10X5_UNORM_BLOCK, - AstcBlock::B10x6 => F::ASTC_10X6_UNORM_BLOCK, - AstcBlock::B10x8 => F::ASTC_10X8_UNORM_BLOCK, - AstcBlock::B10x10 => F::ASTC_10X10_UNORM_BLOCK, - AstcBlock::B12x10 => F::ASTC_12X10_UNORM_BLOCK, - AstcBlock::B12x12 => F::ASTC_12X12_UNORM_BLOCK, - }, - AstcChannel::UnormSrgb => match block { - AstcBlock::B4x4 => F::ASTC_4X4_SRGB_BLOCK, - AstcBlock::B5x4 => F::ASTC_5X4_SRGB_BLOCK, - AstcBlock::B5x5 => F::ASTC_5X5_SRGB_BLOCK, - AstcBlock::B6x5 => F::ASTC_6X5_SRGB_BLOCK, - AstcBlock::B6x6 => F::ASTC_6X6_SRGB_BLOCK, - AstcBlock::B8x5 => F::ASTC_8X5_SRGB_BLOCK, - AstcBlock::B8x6 => F::ASTC_8X6_SRGB_BLOCK, - AstcBlock::B8x8 => F::ASTC_8X8_SRGB_BLOCK, - AstcBlock::B10x5 => F::ASTC_10X5_SRGB_BLOCK, - AstcBlock::B10x6 => F::ASTC_10X6_SRGB_BLOCK, - AstcBlock::B10x8 => F::ASTC_10X8_SRGB_BLOCK, - AstcBlock::B10x10 => F::ASTC_10X10_SRGB_BLOCK, - AstcBlock::B12x10 => F::ASTC_12X10_SRGB_BLOCK, - AstcBlock::B12x12 => F::ASTC_12X12_SRGB_BLOCK, - }, - AstcChannel::Hdr => match block { - AstcBlock::B4x4 => F::ASTC_4X4_SFLOAT_BLOCK_EXT, - AstcBlock::B5x4 => F::ASTC_5X4_SFLOAT_BLOCK_EXT, - AstcBlock::B5x5 => F::ASTC_5X5_SFLOAT_BLOCK_EXT, - AstcBlock::B6x5 => F::ASTC_6X5_SFLOAT_BLOCK_EXT, - AstcBlock::B6x6 => F::ASTC_6X6_SFLOAT_BLOCK_EXT, - AstcBlock::B8x5 => F::ASTC_8X5_SFLOAT_BLOCK_EXT, - AstcBlock::B8x6 => F::ASTC_8X6_SFLOAT_BLOCK_EXT, - AstcBlock::B8x8 => F::ASTC_8X8_SFLOAT_BLOCK_EXT, - AstcBlock::B10x5 => F::ASTC_10X5_SFLOAT_BLOCK_EXT, - AstcBlock::B10x6 => F::ASTC_10X6_SFLOAT_BLOCK_EXT, - AstcBlock::B10x8 => F::ASTC_10X8_SFLOAT_BLOCK_EXT, - AstcBlock::B10x10 => F::ASTC_10X10_SFLOAT_BLOCK_EXT, - AstcBlock::B12x10 => F::ASTC_12X10_SFLOAT_BLOCK_EXT, - AstcBlock::B12x12 => F::ASTC_12X12_SFLOAT_BLOCK_EXT, - }, - }, - }) -} diff --git a/xr_api/src/backend/oxr/utils.rs b/xr_api/src/backend/oxr/utils.rs deleted file mode 100644 index c08797d..0000000 --- a/xr_api/src/backend/oxr/utils.rs +++ /dev/null @@ -1,208 +0,0 @@ -use glam::Quat; -use openxr::{Action, Fovf, Posef}; - -use crate::{ - error::XrError, - path::{input, Handed, InputId, PathComponent, UntypedActionPath}, - prelude::Pose, -}; - -use super::{Bindings, Fov}; - -impl From for XrError { - fn from(_: openxr::sys::Result) -> Self { - XrError::Placeholder - } -} - -impl From for Pose { - fn from(pose: Posef) -> Self { - // with enough sign errors anything is possible - let rotation = { - let o = pose.orientation; - Quat::from_xyzw(o.x, o.y, o.z, o.w) - }; - let translation = glam::vec3(pose.position.x, pose.position.y, pose.position.z); - - Pose { - translation, - rotation, - } - } -} - -impl From for Fov { - fn from(fov: Fovf) -> Self { - let Fovf { - angle_left, - angle_right, - angle_down, - angle_up, - } = fov; - Self { - angle_down, - angle_left, - angle_right, - angle_up, - } - } -} - -macro_rules! untyped_oxr_actions { - ( - $id:ident { - $( - $inner:ident($inner_ty:ty) - ),* - $(,)? - } - ) => { - pub(crate) enum $id { - $( - $inner($inner_ty), - )* - } - - $( - impl TryInto<$inner_ty> for $id { - type Error = (); - - fn try_into(self) -> std::prelude::v1::Result<$inner_ty, Self::Error> { - match self { - Self::$inner(action) => Ok(action), - _ => Err(()), - } - } - } - - impl From<$inner_ty> for $id { - fn from(value: $inner_ty) -> Self { - Self::$inner(value) - } - } - )* - }; -} - -untyped_oxr_actions! { - UntypedOXrAction { - Haptics(Action), - Pose(Action), - Float(Action), - Bool(Action), - Vec2(Action), - } -} - -impl UntypedActionPath { - pub(crate) fn into_xr_path(self) -> String { - let dev_path; - let sub_path; - let comp_path = match self.comp { - PathComponent::Click => "/click", - PathComponent::Touch => "/touch", - PathComponent::Value => "/value", - PathComponent::X => "/x", - PathComponent::Y => "/y", - PathComponent::Pose => "/pose", - PathComponent::Haptic => "/haptic", - }; - match self.input { - InputId::Left(hand) => { - dev_path = "/user/hand/left"; - sub_path = match hand { - Handed::PrimaryButton => "/input/x", - Handed::SecondaryButton => "/input/y", - Handed::Select => "/input/select", - Handed::Menu => "/input/menu", - Handed::Thumbstick => "/input/thumbstick", - Handed::Trigger => "/input/trigger", - Handed::Grip if matches!(self.comp, PathComponent::Pose) => "/input/grip", - Handed::Grip => "/input/squeeze", - Handed::Output => "/output", - }; - } - InputId::Right(hand) => { - dev_path = "/user/hand/right"; - sub_path = match hand { - Handed::PrimaryButton => "/input/a", - Handed::SecondaryButton => "/input/b", - Handed::Select => "/input/select", - Handed::Menu => "/input/menu", - Handed::Thumbstick => "/input/thumbstick", - Handed::Trigger => "/input/trigger", - Handed::Grip if matches!(self.comp, PathComponent::Pose) => "/input/grip", - Handed::Grip => "/input/squeeze", - Handed::Output => "/output", - }; - } - InputId::Head(head) => { - use input::head::Head; - dev_path = "/user/head"; - sub_path = match head { - Head::VolumeUp => "/input/volume_up", - Head::VolumeDown => "/input/volume_down", - Head::MuteMic => "/input/mute_mic", - }; - } - }; - - let mut path = dev_path.to_owned(); - path.push_str(sub_path); - path.push_str(comp_path); - path - } - - pub(crate) fn into_name(&self) -> String { - let comp_path = match self.comp { - PathComponent::Click => "_click", - PathComponent::Touch => "_touch", - PathComponent::Value => "_value", - PathComponent::X => "_x", - PathComponent::Y => "_y", - PathComponent::Pose => "_pose", - PathComponent::Haptic => "", - }; - let dev_path = match self.input { - InputId::Left(hand) => match hand { - Handed::PrimaryButton => "left_primary_button", - Handed::SecondaryButton => "left_secondary_button", - Handed::Select => "left_select", - Handed::Menu => "left_menu", - Handed::Thumbstick => "left_thumbstick", - Handed::Trigger => "left_trigger", - Handed::Grip => "left_grip", - Handed::Output => "left_output", - }, - InputId::Right(hand) => match hand { - Handed::PrimaryButton => "right_primary_button", - Handed::SecondaryButton => "right_secondary_button", - Handed::Select => "right_select", - Handed::Menu => "right_menu", - Handed::Thumbstick => "right_thumbstick", - Handed::Trigger => "right_trigger", - Handed::Grip => "right_grip", - Handed::Output => "right_output", - }, - InputId::Head(head) => { - use input::head::Head; - match head { - Head::VolumeUp => "volume_up", - Head::VolumeDown => "volume_down", - Head::MuteMic => "mute_mic", - } - } - }; - let mut path = dev_path.to_string(); - path.push_str(comp_path); - path - } -} - -impl Bindings { - pub(crate) fn get_interaction_profile(&self) -> &'static str { - match self { - Bindings::OculusTouch => "/interaction_profiles/oculus/touch_controller", - } - } -} diff --git a/xr_api/src/backend/webxr.rs b/xr_api/src/backend/webxr.rs deleted file mode 100644 index 8b13789..0000000 --- a/xr_api/src/backend/webxr.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/xr_api/src/backend/webxr/utils.rs b/xr_api/src/backend/webxr/utils.rs deleted file mode 100644 index fbbe473..0000000 --- a/xr_api/src/backend/webxr/utils.rs +++ /dev/null @@ -1,17 +0,0 @@ -use wasm_bindgen::JsValue; -use wasm_bindgen_futures::js_sys::Promise; -use wasm_bindgen_futures::JsFuture; - -pub trait PromiseRes { - fn resolve>(self) -> Result; -} - -impl PromiseRes for Promise { - fn resolve>(self) -> Result { - resolve_promise(self) - } -} - -pub fn resolve_promise>(promise: Promise) -> Result { - futures::executor::block_on(async move { JsFuture::from(promise).await.map(Into::into) }) -} diff --git a/xr_api/src/error.rs b/xr_api/src/error.rs deleted file mode 100644 index 696194e..0000000 --- a/xr_api/src/error.rs +++ /dev/null @@ -1,16 +0,0 @@ -use std::fmt::Display; - -use thiserror::Error; - -pub type Result = std::result::Result; - -#[derive(Error, Debug)] -pub enum XrError { - Placeholder, -} - -impl Display for XrError { - fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - Ok(()) - } -} diff --git a/xr_api/src/lib.rs b/xr_api/src/lib.rs deleted file mode 100644 index d548c2a..0000000 --- a/xr_api/src/lib.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! Abstracted API over WebXR and OpenXR -//! -//! This crate is intended to be used as a common API for cross platform projects. It was primarily -//! made for use in Bevy, but can be used elsewhere. -//! -//! To get started, create an [Entry] with [Entry](Entry#method.new) - -mod api; -pub mod api_traits; -pub mod backend; -pub mod error; -pub mod path; -pub mod types; - -pub mod prelude { - pub use super::api::*; - pub use super::api_traits::*; - pub use super::error::*; - pub use super::path::{input, ActionPath}; - pub use super::types::*; -} - -pub use api::*; diff --git a/xr_api/src/path.rs b/xr_api/src/path.rs deleted file mode 100644 index 970d152..0000000 --- a/xr_api/src/path.rs +++ /dev/null @@ -1,332 +0,0 @@ -use std::marker::PhantomData; - -use crate::prelude::ActionType; - -#[derive(Clone, Copy)] -pub struct ActionPath { - pub(crate) input: InputId, - pub(crate) comp: PathComponent, - _data: PhantomData

, -} - -#[derive(Clone, Copy)] -pub struct UntypedActionPath { - pub(crate) input: InputId, - pub(crate) comp: PathComponent, -} - -impl From> for UntypedActionPath { - fn from(value: ActionPath

) -> Self { - value.untyped() - } -} - -impl ActionPath

{ - const fn new(input: InputId, comp: PathComponent) -> Self { - Self { - input, - comp, - // hand, - _data: PhantomData, - } - } - - pub fn untyped(self) -> UntypedActionPath { - UntypedActionPath { - input: self.input, - comp: self.comp, - // hand: self.hand, - } - } -} - -#[derive(Clone, Copy)] -pub(crate) enum PathComponent { - Click, - Touch, - Value, - X, - Y, - Pose, - Haptic, -} - -pub struct Click; - -impl Click { - const COMP: PathComponent = PathComponent::Click; -} - -pub struct Touch; - -impl Touch { - const COMP: PathComponent = PathComponent::Touch; -} - -pub struct Value; - -impl Value { - const COMP: PathComponent = PathComponent::Value; -} - -pub struct X; - -impl X { - const COMP: PathComponent = PathComponent::X; -} - -pub struct Y; - -impl Y { - const COMP: PathComponent = PathComponent::Y; -} - -pub struct Pose; - -impl Pose { - const COMP: PathComponent = PathComponent::Pose; -} - -pub struct Haptic; - -impl Haptic { - const COMP: PathComponent = PathComponent::Haptic; -} - -pub trait InputComponent { - type PathType: ActionType; -} - -impl InputComponent for Click { - type PathType = bool; -} - -impl InputComponent for Touch { - type PathType = bool; -} - -impl InputComponent for Value { - type PathType = f32; -} - -impl InputComponent for X { - type PathType = f32; -} - -impl InputComponent for Y { - type PathType = f32; -} - -impl InputComponent for Pose { - type PathType = crate::types::Pose; -} - -impl InputComponent for Haptic { - type PathType = crate::types::Haptic; -} - -macro_rules! input_ids { - ( - $(#[$id_meta:meta])* - $id:ident; - handed { - $( - $(#[$inner_handed_meta:meta])* - $inner_handed:ident { - $( - $(#[$comp_name_handed_meta:meta])* - $comp_name_handed:ident - ),* - $(,)? - } - )* - } - $( - $(#[$dev_path_meta:meta])* - $dev_path:ident { - $( - $(#[$inner_meta:meta])* - $inner:ident { - $( - $(#[$comp_name_meta:meta])* - $comp_name:ident - ),* - $(,)? - } - )* - } - )* - ) => { - paste::paste! { - const LEFT: bool = true; - const RIGHT: bool = false; - - $( - #[$id_meta] - )* - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - pub(crate) enum $id { - Left(Handed), - Right(Handed), - $( - $( - #[$dev_path_meta] - )* - [<$dev_path:camel>](input::$dev_path::[<$dev_path:camel>]), - )* - } - - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - pub(crate) enum Handed { - $( - $( - #[$inner_handed_meta] - )* - $inner_handed, - )* - } - - pub mod input { - use super::*; - - pub(crate) mod private { - $( - $( - #[$inner_handed_meta] - )* - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - pub struct $inner_handed; - )* - } - - pub mod hand_left { - use super::*; - - $( - $( - #[$inner_handed_meta] - )* - pub type $inner_handed = private::$inner_handed; - - impl $inner_handed { - $( - $( - #[$comp_name_handed_meta] - )* - pub const [<$comp_name_handed:snake:upper>]: ActionPath<$comp_name_handed> = ActionPath::<$comp_name_handed>::new($id::Left(Handed::$inner_handed), $comp_name_handed::COMP); - )* - } - )* - } - - pub mod hand_right { - use super::*; - - $( - $( - #[$inner_handed_meta] - )* - pub type $inner_handed = private::$inner_handed; - - impl $inner_handed { - $( - $( - #[$comp_name_handed_meta] - )* - pub const [<$comp_name_handed:snake:upper>]: ActionPath<$comp_name_handed> = ActionPath::<$comp_name_handed>::new($id::Right(Handed::$inner_handed), $comp_name_handed::COMP); - )* - } - )* - } - - $( - $( - #[$dev_path_meta] - )* - pub mod $dev_path { - use super::*; - - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - pub(crate) enum [<$dev_path:camel>] { - $( - $( - #[$inner_meta] - )* - $inner, - )* - } - - $( - $( - #[$inner_meta] - )* - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - pub struct $inner; - - $( - #[$inner_meta] - )* - impl $inner { - $( - $( - #[$comp_name_meta] - )* - pub const [<$comp_name:snake:upper>]: ActionPath<$comp_name> = ActionPath::<$comp_name>::new($id::[<$dev_path:camel>]([<$dev_path:camel>]::$inner), $comp_name::COMP); - )* - } - )* - } - )* - } - } - }; -} - -input_ids! { - InputId; - handed { - PrimaryButton { - Click, - Touch, - } - SecondaryButton { - Click, - Touch, - } - Select { - Click, - } - Menu { - Click, - } - Thumbstick { - X, - Y, - Click, - Touch, - } - Trigger { - Touch, - Click, - } - Grip { - Click, - Value, - Pose, - } - Output { - Haptic, - } - } - head { - VolumeUp { - Click, - } - VolumeDown { - Click, - } - MuteMic { - Click, - } - } -} diff --git a/xr_api/src/types.rs b/xr_api/src/types.rs deleted file mode 100644 index ea6600a..0000000 --- a/xr_api/src/types.rs +++ /dev/null @@ -1,85 +0,0 @@ -use glam::{Quat, Vec2, Vec3}; - -use crate::api::Action; -use crate::api_traits::{ActionInputTrait, HapticTrait, InputTrait}; -use crate::error::Result; -use crate::path::UntypedActionPath; - -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub struct ExtensionSet { - pub vulkan: bool, -} - -pub struct FrameData; - -pub struct SessionCreateInfo { - /// preferred texture format - pub texture_format: wgpu::TextureFormat, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum Bindings { - OculusTouch, -} - -pub struct Haptic; - -#[derive(Clone, Copy, Debug, Default)] -pub struct Pose { - pub translation: Vec3, - pub rotation: Quat, -} - -#[derive(Clone, Copy, Debug, Default)] -pub struct Fov { - pub angle_left: f32, - pub angle_right: f32, - pub angle_down: f32, - pub angle_up: f32, -} - -pub trait ActionType: Sized { - type Inner: ?Sized; - - fn get(input: &dyn InputTrait, path: UntypedActionPath) -> Result>; -} - -impl ActionType for Haptic { - type Inner = dyn HapticTrait; - - fn get(input: &dyn InputTrait, path: UntypedActionPath) -> Result> { - input.create_action_haptics(path) - } -} - -impl ActionType for Pose { - type Inner = dyn ActionInputTrait; - - fn get(input: &dyn InputTrait, path: UntypedActionPath) -> Result> { - input.create_action_pose(path) - } -} - -impl ActionType for f32 { - type Inner = dyn ActionInputTrait; - - fn get(input: &dyn InputTrait, path: UntypedActionPath) -> Result> { - input.create_action_float(path) - } -} - -impl ActionType for bool { - type Inner = dyn ActionInputTrait; - - fn get(input: &dyn InputTrait, path: UntypedActionPath) -> Result> { - input.create_action_bool(path) - } -} - -impl ActionType for Vec2 { - type Inner = dyn ActionInputTrait; - - fn get(input: &dyn InputTrait, path: UntypedActionPath) -> Result> { - input.create_action_vec2(path) - } -}