From b2b40ba95af0e1afbc608feb7e5d839433adf4af Mon Sep 17 00:00:00 2001 From: awtterpip Date: Thu, 25 Apr 2024 21:05:37 -0500 Subject: [PATCH] updated documentation and resources --- crates/bevy_openxr/Cargo.toml | 3 +- crates/bevy_openxr/src/openxr/features/mod.rs | 2 + .../src/openxr/features/passthrough.rs | 59 ++++++ .../bevy_openxr/src/openxr/layer_builder.rs | 41 ++++- crates/bevy_openxr/src/openxr/mod.rs | 1 + crates/bevy_openxr/src/openxr/resources.rs | 171 ++++++++++++++++-- 6 files changed, 256 insertions(+), 21 deletions(-) create mode 100644 crates/bevy_openxr/src/openxr/features/mod.rs create mode 100644 crates/bevy_openxr/src/openxr/features/passthrough.rs diff --git a/crates/bevy_openxr/Cargo.toml b/crates/bevy_openxr/Cargo.toml index b936891..350f251 100644 --- a/crates/bevy_openxr/Cargo.toml +++ b/crates/bevy_openxr/Cargo.toml @@ -4,8 +4,9 @@ version = "0.1.0" edition = "2021" [features] -default = ["vulkan"] +default = ["vulkan", "passthrough"] vulkan = ["dep:ash"] +passthrough = [] # bevy can't be placed behind target or proc macros won't work properly [dependencies] diff --git a/crates/bevy_openxr/src/openxr/features/mod.rs b/crates/bevy_openxr/src/openxr/features/mod.rs new file mode 100644 index 0000000..eb46003 --- /dev/null +++ b/crates/bevy_openxr/src/openxr/features/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "passthrough")] +pub mod passthrough; diff --git a/crates/bevy_openxr/src/openxr/features/passthrough.rs b/crates/bevy_openxr/src/openxr/features/passthrough.rs new file mode 100644 index 0000000..948c9d1 --- /dev/null +++ b/crates/bevy_openxr/src/openxr/features/passthrough.rs @@ -0,0 +1,59 @@ +use bevy::prelude::*; +use openxr::sys::SystemPassthroughProperties2FB; +use openxr::PassthroughCapabilityFlagsFB; + +use crate::resources::*; +use crate::types::*; + +pub struct OxrPassthroughPlugin; + +impl Plugin for OxrPassthroughPlugin { + fn build(&self, _app: &mut App) { + todo!() + } +} + +pub fn create_passthrough( + session: &OxrSession, + flags: openxr::PassthroughFlagsFB, + purpose: openxr::PassthroughLayerPurposeFB, +) -> Result<(OxrPassthrough, OxrPassthroughLayer)> { + let passthrough = session.create_passthrough(flags)?; + + let passthrough_layer = session.create_passthrough_layer(&passthrough, purpose)?; + + Ok((passthrough, passthrough_layer)) +} + +#[inline] +pub fn supports_passthrough(instance: &OxrInstance, system: OxrSystemId) -> Result { + unsafe { + let mut hand = openxr::sys::SystemPassthroughProperties2FB { + ty: SystemPassthroughProperties2FB::TYPE, + next: std::ptr::null(), + capabilities: PassthroughCapabilityFlagsFB::PASSTHROUGH_CAPABILITY, + }; + let mut p = openxr::sys::SystemProperties::out(&mut hand as *mut _ as _); + cvt((instance.fp().get_system_properties)( + instance.as_raw(), + system.0, + p.as_mut_ptr(), + ))?; + bevy::log::info!( + "From supports_passthrough: Passthrough capabilities: {:?}", + hand.capabilities + ); + Ok( + (hand.capabilities & PassthroughCapabilityFlagsFB::PASSTHROUGH_CAPABILITY) + == PassthroughCapabilityFlagsFB::PASSTHROUGH_CAPABILITY, + ) + } +} + +fn cvt(x: openxr::sys::Result) -> openxr::Result { + if x.into_raw() >= 0 { + Ok(x) + } else { + Err(x) + } +} diff --git a/crates/bevy_openxr/src/openxr/layer_builder.rs b/crates/bevy_openxr/src/openxr/layer_builder.rs index 5febf55..531b5fc 100644 --- a/crates/bevy_openxr/src/openxr/layer_builder.rs +++ b/crates/bevy_openxr/src/openxr/layer_builder.rs @@ -3,7 +3,7 @@ use std::mem; use openxr::{sys, CompositionLayerFlags, Fovf, Posef, Rect2Di, Space}; use crate::graphics::graphics_match; -use crate::resources::OxrSwapchain; +use crate::resources::{OxrPassthroughLayer, OxrSwapchain}; #[derive(Copy, Clone)] pub struct SwapchainSubImage<'a> { @@ -105,7 +105,7 @@ impl<'a> Default for CompositionLayerProjectionView<'a> { } pub unsafe trait CompositionLayer<'a> { fn swapchain(&self) -> Option<&'a OxrSwapchain>; - fn header(&self) -> &'a sys::CompositionLayerBaseHeader; + fn header(&self) -> &sys::CompositionLayerBaseHeader; } #[derive(Clone)] pub struct CompositionLayerProjection<'a> { @@ -158,8 +158,8 @@ unsafe impl<'a> CompositionLayer<'a> for CompositionLayerProjection<'a> { self.swapchain } - fn header(&self) -> &'a sys::CompositionLayerBaseHeader { - unsafe { std::mem::transmute(&self.inner) } + fn header(&self) -> &sys::CompositionLayerBaseHeader { + unsafe { mem::transmute(&self.inner) } } } impl<'a> Default for CompositionLayerProjection<'a> { @@ -167,3 +167,36 @@ impl<'a> Default for CompositionLayerProjection<'a> { Self::new() } } +pub struct CompositionLayerPassthrough { + inner: sys::CompositionLayerPassthroughFB, +} +impl CompositionLayerPassthrough { + #[inline] + pub fn new() -> Self { + Self { + inner: openxr::sys::CompositionLayerPassthroughFB { + ty: openxr::sys::CompositionLayerPassthroughFB::TYPE, + ..unsafe { mem::zeroed() } + }, + } + } + #[inline] + pub fn layer_handle(mut self, layer_handle: &OxrPassthroughLayer) -> Self { + self.inner.layer_handle = *layer_handle.inner(); + self + } + #[inline] + pub fn layer_flags(mut self, value: CompositionLayerFlags) -> Self { + self.inner.flags = value; + self + } +} +unsafe impl<'a> CompositionLayer<'a> for CompositionLayerPassthrough { + fn swapchain(&self) -> Option<&'a OxrSwapchain> { + None + } + + fn header(&self) -> &sys::CompositionLayerBaseHeader { + unsafe { mem::transmute(&self.inner) } + } +} diff --git a/crates/bevy_openxr/src/openxr/mod.rs b/crates/bevy_openxr/src/openxr/mod.rs index 6143c37..328c290 100644 --- a/crates/bevy_openxr/src/openxr/mod.rs +++ b/crates/bevy_openxr/src/openxr/mod.rs @@ -12,6 +12,7 @@ use render::OxrRenderPlugin; pub mod error; mod exts; +pub mod features; pub mod graphics; pub mod init; pub mod layer_builder; diff --git a/crates/bevy_openxr/src/openxr/resources.rs b/crates/bevy_openxr/src/openxr/resources.rs index cd55ae7..bbf814a 100644 --- a/crates/bevy_openxr/src/openxr/resources.rs +++ b/crates/bevy_openxr/src/openxr/resources.rs @@ -10,7 +10,9 @@ use crate::graphics::*; use crate::layer_builder::CompositionLayer; use crate::types::*; -/// Wrapper around the entry point to the OpenXR API +/// Wrapper around an [`Entry`](openxr::Entry) with some methods overridden to use bevy types. +/// +/// See [`openxr::Entry`] for other available methods. #[derive(Deref, Clone)] pub struct OxrEntry(pub openxr::Entry); @@ -20,6 +22,9 @@ impl OxrEntry { Ok(self.0.enumerate_extensions().map(Into::into)?) } + /// Creates an [`OxrInstance`]. + /// + /// Calls [`create_instance`](openxr::Entry::create_instance) internally. pub fn create_instance( &self, app_info: AppInfo, @@ -49,6 +54,7 @@ impl OxrEntry { Ok(OxrInstance(instance, backend, app_info)) } + /// Returns a list of all of the backends the OpenXR runtime supports. pub fn available_backends(&self) -> Result> { Ok(GraphicsBackend::available_backends( &self.enumerate_extensions()?, @@ -56,20 +62,48 @@ impl OxrEntry { } } -/// Wrapper around [openxr::Instance] with additional data for safety. +/// Wrapper around [`openxr::Instance`] with additional data for safety and some methods overriden to use bevy types. +/// +/// See [`openxr::Instance`] for other available methods. #[derive(Resource, Deref, Clone)] pub struct OxrInstance( - #[deref] pub openxr::Instance, + #[deref] pub(crate) openxr::Instance, + /// [`GraphicsBackend`] is stored here to let us know what graphics API the current instance wants to target. pub(crate) GraphicsBackend, pub(crate) AppInfo, ); impl OxrInstance { + /// Creates an [`OxrInstance`] from an [`openxr::Instance`] if needed. + /// In the majority of cases, you should use [`create_instance`](OxrEntry::create_instance) instead. + /// + /// # Safety + /// + /// The OpenXR instance passed in *must* have support for the backend specified. + pub unsafe fn from_inner( + instance: openxr::Instance, + backend: GraphicsBackend, + info: AppInfo, + ) -> Self { + Self(instance, backend, info) + } + + /// Consumes self and returns the inner [`openxr::Instance`] pub fn into_inner(self) -> openxr::Instance { self.0 } - /// Initialize graphics. This is used to create [WgpuGraphics] for the bevy app and to get the [SessionCreateInfo] to make an XR session. + /// Returns the current backend being used by this instance. + pub fn backend(&self) -> GraphicsBackend { + self.1 + } + + /// Returns the [`AppInfo`] being used by this instance. + pub fn app_info(&self) -> &AppInfo { + &self.2 + } + + /// Initialize graphics. This is used to create [WgpuGraphics] for the bevy app and to get the [SessionCreateInfo] needed to make an XR session. pub fn init_graphics( &self, system_id: openxr::SystemId, @@ -86,6 +120,8 @@ impl OxrInstance { /// Creates an [OxrSession] /// + /// Calls [`create_session`](openxr::Instance::create_session) internally. + /// /// # Safety /// /// `info` must contain valid handles for the graphics api @@ -111,11 +147,18 @@ impl OxrInstance { } } -/// Graphics agnostic wrapper around [openxr::Session] +/// Graphics agnostic wrapper around [openxr::Session]. +/// +/// See [`openxr::Session`] for other available methods. #[derive(Resource, Deref, Clone)] pub struct OxrSession( - #[deref] pub openxr::Session, - pub GraphicsWrap, + /// A session handle with [`AnyGraphics`]. + /// Having this here allows the majority of [`Session`](openxr::Session)'s methods to work without having to rewrite them. + #[deref] + pub(crate) openxr::Session, + /// A [`GraphicsWrap`] with [`openxr::Session`] as the inner type. + /// This is so that we can still operate on functions that don't take [`AnyGraphics`] as the generic. + pub(crate) GraphicsWrap, ); impl GraphicsType for OxrSession { @@ -124,16 +167,27 @@ impl GraphicsType for OxrSession { impl From> for OxrSession { fn from(session: openxr::Session) -> Self { - Self::new(session) + Self::from_inner(session) } } impl OxrSession { - pub fn new(session: openxr::Session) -> Self { + /// Creates a new [`OxrSession`] from an [`openxr::Session`]. + /// In the majority of cases, you should use [`create_session`](OxrInstance::create_session) instead. + pub fn from_inner(session: openxr::Session) -> Self { Self(session.clone().into_any_graphics(), G::wrap(session)) } - /// Enumerate all available swapchain formats. + /// Returns [`GraphicsWrap`] with [`openxr::Session`] as the inner type. + /// + /// This can be useful if you need access to the original [`openxr::Session`] with the graphics API still specified. + pub fn typed_session(&self) -> &GraphicsWrap { + &self.1 + } + + /// Enumerates all available swapchain formats and converts them to wgpu's [`TextureFormat`](wgpu::TextureFormat). + /// + /// Calls [`enumerate_swapchain_formats`](openxr::Session::enumerate_swapchain_formats) internally. pub fn enumerate_swapchain_formats(&self) -> Result> { graphics_match!( &self.1; @@ -142,14 +196,75 @@ impl OxrSession { } /// Creates an [OxrSwapchain]. + /// + /// Calls [`create_swapchain`](openxr::Session::create_swapchain) internally. pub fn create_swapchain(&self, info: SwapchainCreateInfo) -> Result { Ok(OxrSwapchain(graphics_match!( &self.1; session => session.create_swapchain(&info.try_into()?)? => OxrSwapchain ))) } + + /// Creates a passthrough. + /// + /// Requires [`XR_FB_passthrough`](https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XR_FB_passthrough). + /// + /// Calls [`create_passthrough`](openxr::Session::create_passthrough) internally. + pub fn create_passthrough(&self, flags: openxr::PassthroughFlagsFB) -> Result { + Ok(OxrPassthrough( + graphics_match! { + &self.1; + session => session.create_passthrough(flags)? + }, + flags, + )) + } + + /// Creates a passthrough layer that can be used to make a [`CompositionLayerPassthrough`](crate::layer_builder::CompositionLayerPassthrough) for frame submission. + /// + /// Requires [`XR_FB_passthrough`](https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XR_FB_passthrough). + /// + /// Calls [`create_passthrough_layer`](openxr::Session::create_passthrough_layer) internally. + pub fn create_passthrough_layer( + &self, + passthrough: &OxrPassthrough, + purpose: openxr::PassthroughLayerPurposeFB, + ) -> Result { + Ok(OxrPassthroughLayer(graphics_match! { + &self.1; + session => session.create_passthrough_layer(&passthrough.0, passthrough.1, purpose)? + })) + } } +/// Wrapper around [`openxr::Passthrough`]. +/// +/// Used to [`start`](openxr::Passthrough::start) or [`pause`](openxr::Passthrough::pause) passthrough on the physical device. +/// +/// See [`openxr::Passthrough`] for available methods. +#[derive(Resource, Deref, DerefMut)] +pub struct OxrPassthrough( + #[deref] pub openxr::Passthrough, + /// The flags are stored here so that they don't need to be passed in again when creating an [`OxrPassthroughLayer`]. + openxr::PassthroughFlagsFB, +); + +impl OxrPassthrough { + /// This function can create an [`OxrPassthrough`] from raw openxr types if needed. + /// In the majority of cases, you should use [`create_passthrough`](OxrSession::create_passthrough) instead. + pub fn from_inner(passthrough: openxr::Passthrough, flags: openxr::PassthroughFlagsFB) -> Self { + Self(passthrough, flags) + } +} + +/// Wrapper around [`openxr::Passthrough`]. +/// +/// Used to create a [`CompositionLayerPassthrough`](crate::layer_builder::CompositionLayerPassthrough), and to [`pause`](openxr::PassthroughLayer::pause) or [`resume`](openxr::PassthroughLayer::resume) rendering of the passthrough layer. +/// +/// See [`openxr::PassthroughLayer`] for available methods. +#[derive(Resource, Deref, DerefMut)] +pub struct OxrPassthroughLayer(pub openxr::PassthroughLayer); + /// Graphics agnostic wrapper around [openxr::FrameStream] #[derive(Resource)] pub struct OxrFrameStream(pub GraphicsWrap); @@ -159,7 +274,15 @@ impl GraphicsType for OxrFrameStream { } impl OxrFrameStream { + /// Creates a new [`OxrFrameStream`] from an [`openxr::FrameStream`]. + /// In the majority of cases, you should use [`create_session`](OxrInstance::create_session) instead. + pub fn from_inner(frame_stream: openxr::FrameStream) -> Self { + Self(G::wrap(frame_stream)) + } + /// Indicate that graphics device work is beginning. + /// + /// Calls [`begin`](openxr::FrameStream::begin) internally. pub fn begin(&mut self) -> openxr::Result<()> { graphics_match!( &mut self.0; @@ -169,8 +292,8 @@ impl OxrFrameStream { /// Indicate that all graphics work for the frame has been submitted /// - /// `layers` is an array of references to any type of composition layer, - /// e.g. [`CompositionLayerProjection`](crate::oxr::layer_builder::CompositionLayerProjection) + /// `layers` is an array of references to any type of composition layer that implements [`CompositionLayer`], + /// e.g. [`CompositionLayerProjection`](crate::layer_builder::CompositionLayerProjection) pub fn end( &mut self, display_time: openxr::Time, @@ -202,7 +325,9 @@ impl OxrFrameStream { } } -/// Handle for waiting to render a frame. Check [`FrameWaiter`](openxr::FrameWaiter) for available methods. +/// Handle for waiting to render a frame. +/// +/// See [`FrameWaiter`](openxr::FrameWaiter) for available methods. #[derive(Resource, Deref, DerefMut)] pub struct OxrFrameWaiter(pub openxr::FrameWaiter); @@ -215,7 +340,15 @@ impl GraphicsType for OxrSwapchain { } impl OxrSwapchain { - /// Determine the index of the next image to render to in the swapchain image array + /// Creates a new [`OxrSwapchain`] from an [`openxr::Swapchain`]. + /// In the majority of cases, you should use [`create_swapchain`](OxrSession::create_swapchain) instead. + pub fn from_inner(swapchain: openxr::Swapchain) -> Self { + Self(G::wrap(swapchain)) + } + + /// Determine the index of the next image to render to in the swapchain image array. + /// + /// Calls [`acquire_image`](openxr::Swapchain::acquire_image) internally. pub fn acquire_image(&mut self) -> Result { graphics_match!( &mut self.0; @@ -223,7 +356,9 @@ impl OxrSwapchain { ) } - /// Wait for the compositor to finish reading from the oldest unwaited acquired image + /// Wait for the compositor to finish reading from the oldest unwaited acquired image. + /// + /// Calls [`wait_image`](openxr::Swapchain::wait_image) internally. pub fn wait_image(&mut self, timeout: openxr::Duration) -> Result<()> { graphics_match!( &mut self.0; @@ -231,7 +366,9 @@ impl OxrSwapchain { ) } - /// Release the oldest acquired image + /// Release the oldest acquired image. + /// + /// Calls [`release_image`](openxr::Swapchain::release_image) internally. pub fn release_image(&mut self) -> Result<()> { graphics_match!( &mut self.0; @@ -240,6 +377,8 @@ impl OxrSwapchain { } /// Enumerates swapchain images and converts them to wgpu [`Texture`](wgpu::Texture)s. + /// + /// Calls [`enumerate_images`](openxr::Swapchain::enumerate_images) internally. pub fn enumerate_images( &self, device: &wgpu::Device,