updated documentation and resources
This commit is contained in:
@@ -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]
|
||||
|
||||
2
crates/bevy_openxr/src/openxr/features/mod.rs
Normal file
2
crates/bevy_openxr/src/openxr/features/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
#[cfg(feature = "passthrough")]
|
||||
pub mod passthrough;
|
||||
59
crates/bevy_openxr/src/openxr/features/passthrough.rs
Normal file
59
crates/bevy_openxr/src/openxr/features/passthrough.rs
Normal file
@@ -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<bool> {
|
||||
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<openxr::sys::Result> {
|
||||
if x.into_raw() >= 0 {
|
||||
Ok(x)
|
||||
} else {
|
||||
Err(x)
|
||||
}
|
||||
}
|
||||
@@ -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) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Vec<GraphicsBackend>> {
|
||||
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<AnyGraphics>,
|
||||
pub GraphicsWrap<Self>,
|
||||
/// 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<AnyGraphics>,
|
||||
/// A [`GraphicsWrap`] with [`openxr::Session<G>`] 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<Self>,
|
||||
);
|
||||
|
||||
impl GraphicsType for OxrSession {
|
||||
@@ -124,16 +167,27 @@ impl GraphicsType for OxrSession {
|
||||
|
||||
impl<G: GraphicsExt> From<openxr::Session<G>> for OxrSession {
|
||||
fn from(session: openxr::Session<G>) -> Self {
|
||||
Self::new(session)
|
||||
Self::from_inner(session)
|
||||
}
|
||||
}
|
||||
|
||||
impl OxrSession {
|
||||
pub fn new<G: GraphicsExt>(session: openxr::Session<G>) -> 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<G: GraphicsExt>(session: openxr::Session<G>) -> Self {
|
||||
Self(session.clone().into_any_graphics(), G::wrap(session))
|
||||
}
|
||||
|
||||
/// Enumerate all available swapchain formats.
|
||||
/// Returns [`GraphicsWrap`] with [`openxr::Session<G>`] 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> {
|
||||
&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<Vec<wgpu::TextureFormat>> {
|
||||
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<OxrSwapchain> {
|
||||
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<OxrPassthrough> {
|
||||
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<OxrPassthroughLayer> {
|
||||
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<Self>);
|
||||
@@ -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<G: GraphicsExt>(frame_stream: openxr::FrameStream<G>) -> 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<G: GraphicsExt>(swapchain: openxr::Swapchain<G>) -> 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<u32> {
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user