move graphics initialization to instance
This commit is contained in:
@@ -7,33 +7,66 @@ pub use resources::*;
|
||||
pub use types::*;
|
||||
|
||||
use bevy::app::{App, Plugin};
|
||||
use bevy::log::error;
|
||||
|
||||
pub fn xr_entry() -> Result<XrEntry> {
|
||||
#[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<Vec<GraphicsBackend>>,
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -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<std::borrow::Cow<'static, str>> {
|
||||
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);
|
||||
|
||||
@@ -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<T: GraphicsType>(item: T::Inner<Self>) -> GraphicsWrap<T>;
|
||||
/// Convert from wgpu format to the graphics format
|
||||
fn from_wgpu_format(format: wgpu::TextureFormat) -> Option<Self::Format>;
|
||||
/// Convert from the graphics format to wgpu format
|
||||
fn to_wgpu_format(format: Self::Format) -> Option<wgpu::TextureFormat>;
|
||||
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<T: GraphicsType> GraphicsWrap<T> {
|
||||
)
|
||||
}
|
||||
|
||||
fn graphics_type(&self) -> std::any::TypeId {
|
||||
fn graphics_type(&self) -> TypeId {
|
||||
graphics_match!(
|
||||
self;
|
||||
_ => std::any::TypeId::of::<Api>()
|
||||
_ => TypeId::of::<Api>()
|
||||
)
|
||||
}
|
||||
|
||||
/// Checks if this struct is using the wanted graphics api.
|
||||
pub fn using_graphics<G: GraphicsExt + 'static>(&self) -> bool {
|
||||
self.graphics_type() == std::any::TypeId::of::<G>()
|
||||
self.graphics_type() == TypeId::of::<G>()
|
||||
}
|
||||
|
||||
/// Checks if the two values are both using the same graphics backend
|
||||
pub fn using_graphics_of_val<V: GraphicsType>(&self, other: &GraphicsWrap<V>) -> bool {
|
||||
self.graphics_type() == other.graphics_type()
|
||||
}
|
||||
|
||||
pub fn as_type<G: GraphicsExt>(&self) -> Result<&T::Inner<G>> {
|
||||
// graphics_match!(
|
||||
// self;
|
||||
// inner => if TypeId::of::<Api> == TypeId::of::<G> {
|
||||
// return Ok(inner)
|
||||
// }
|
||||
// );
|
||||
|
||||
return Err(XrError::FailedGraphicsRequirements);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Self::Format> {
|
||||
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::<openxr::Vulkan>(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::<openxr::Vulkan>(
|
||||
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<T: super::GraphicsType>(item: T::Inner<Self>) -> super::GraphicsWrap<T> {
|
||||
super::GraphicsWrap::Vulkan(item)
|
||||
}
|
||||
}
|
||||
|
||||
fn vulkan_to_wgpu(format: ash::vk::Format) -> Option<wgpu::TextureFormat> {
|
||||
|
||||
@@ -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<XrExtensions> {
|
||||
@@ -48,37 +50,57 @@ impl XrEntry {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<openxr::Entry> 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::<SessionCreateInfo>(),
|
||||
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::<Api>(system_id, &info)? };
|
||||
Ok((session.into(), XrFrameWaiter(frame_waiter), XrFrameStream(Api::wrap(frame_stream))))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SessionCreateInfo(pub(crate) GraphicsWrap<Self>);
|
||||
|
||||
impl GraphicsType for SessionCreateInfo {
|
||||
type Inner<G: GraphicsExt> = G::SessionCreateInfo;
|
||||
}
|
||||
|
||||
impl GraphicsType for XrSession {
|
||||
@@ -91,6 +113,12 @@ pub struct XrSession(
|
||||
pub(crate) GraphicsWrap<Self>,
|
||||
);
|
||||
|
||||
impl<G: GraphicsExt> From<openxr::Session<G>> for XrSession {
|
||||
fn from(value: openxr::Session<G>) -> Self {
|
||||
Self(value.clone().into_any_graphics(), G::wrap(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl XrSession {
|
||||
pub fn enumerate_swapchain_formats(&self) -> Result<Vec<wgpu::TextureFormat>> {
|
||||
graphics_match!(
|
||||
@@ -107,6 +135,7 @@ impl XrSession {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct XrFrameStream(pub(crate) GraphicsWrap<Self>);
|
||||
|
||||
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::<Api>() {
|
||||
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::<Api>(),
|
||||
);
|
||||
@@ -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<Self>);
|
||||
|
||||
impl GraphicsType for XrSwapchain {
|
||||
|
||||
@@ -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<T> = std::result::Result<T, XrError>;
|
||||
|
||||
@@ -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<Vec<Cow<'static, str>>> for XrError {
|
||||
fn from(value: Vec<Cow<'static, str>>) -> Self {
|
||||
Self::UnavailableExtensions(UnavailableExts(value))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UnavailableExts(Vec<Cow<'static, str>>);
|
||||
|
||||
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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user