openxr changes
This commit is contained in:
@@ -1,16 +1,39 @@
|
|||||||
pub mod extensions;
|
mod extensions;
|
||||||
pub mod init;
|
pub mod graphics;
|
||||||
mod resources;
|
mod resources;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
pub use resources::*;
|
pub use resources::*;
|
||||||
|
pub use types::*;
|
||||||
|
|
||||||
use bevy::app::{App, Plugin};
|
use bevy::app::{App, Plugin};
|
||||||
|
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
pub struct XrInitPlugin;
|
||||||
|
|
||||||
impl Plugin for XrInitPlugin {
|
impl Plugin for XrInitPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
|
let entry = xr_entry();
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,40 +49,6 @@ 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 {
|
macro_rules! bitor {
|
||||||
(
|
(
|
||||||
$exts:ty;
|
$exts:ty;
|
||||||
@@ -288,4 +254,4 @@ macro_rules! impl_ext {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_ext!(bitor, bitand, unavailable_exts);
|
impl_ext!(bitor, bitand);
|
||||||
|
|||||||
133
src/openxr/graphics.rs
Normal file
133
src/openxr/graphics.rs
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
pub mod vulkan;
|
||||||
|
|
||||||
|
use bevy::math::UVec2;
|
||||||
|
|
||||||
|
use crate::openxr::resources::*;
|
||||||
|
use crate::openxr::types::{AppInfo, Result, XrError};
|
||||||
|
use crate::types::BlendMode;
|
||||||
|
|
||||||
|
trait GraphicWrapper {
|
||||||
|
type Inner<T: GraphicsExt>;
|
||||||
|
type Func: Fn();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait GraphicsExt: openxr::Graphics {
|
||||||
|
fn from_wgpu_format(format: wgpu::TextureFormat) -> Option<Self::Format>;
|
||||||
|
fn to_wgpu_format(format: Self::Format) -> Option<wgpu::TextureFormat>;
|
||||||
|
fn create_session(
|
||||||
|
app_info: &AppInfo,
|
||||||
|
instance: &openxr::Instance,
|
||||||
|
system_id: openxr::SystemId,
|
||||||
|
) -> Result<(
|
||||||
|
wgpu::Device,
|
||||||
|
wgpu::Queue,
|
||||||
|
wgpu::Adapter,
|
||||||
|
wgpu::Instance,
|
||||||
|
XrSession,
|
||||||
|
XrFrameWaiter,
|
||||||
|
XrFrameStream,
|
||||||
|
)>;
|
||||||
|
unsafe fn to_wgpu_img(
|
||||||
|
image: Self::SwapchainImage,
|
||||||
|
device: &wgpu::Device,
|
||||||
|
format: wgpu::TextureFormat,
|
||||||
|
resolution: UVec2,
|
||||||
|
) -> Result<wgpu::Texture>;
|
||||||
|
fn required_exts() -> XrExtensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum GraphicsWrap<T: GraphicsType> {
|
||||||
|
Vulkan(T::Inner<openxr::Vulkan>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: GraphicsType> GraphicsWrap<T> {
|
||||||
|
/// Returns the name of the graphics api this struct is using.
|
||||||
|
pub fn graphics_name(&self) -> &'static str {
|
||||||
|
graphics_match!(
|
||||||
|
self;
|
||||||
|
_ => std::any::type_name::<Api>()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn graphics_type(&self) -> std::any::TypeId {
|
||||||
|
graphics_match!(
|
||||||
|
self;
|
||||||
|
_ => std::any::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>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait GraphicsType {
|
||||||
|
type Inner<G: GraphicsExt>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GraphicsType for () {
|
||||||
|
type Inner<G: GraphicsExt> = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! graphics_match {
|
||||||
|
(
|
||||||
|
$field:expr;
|
||||||
|
$var:pat => $expr:expr $(=> $($return:tt)*)?
|
||||||
|
) => {
|
||||||
|
match $field {
|
||||||
|
$crate::openxr::graphics::GraphicsWrap::Vulkan($var) => {
|
||||||
|
#[allow(unused)]
|
||||||
|
type Api = openxr::Vulkan;
|
||||||
|
graphics_match!(@arm_impl Vulkan; $expr $(=> $($return)*)?)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(
|
||||||
|
@arm_impl
|
||||||
|
$variant:ident;
|
||||||
|
$expr:expr => $wrap_ty:ty
|
||||||
|
) => {
|
||||||
|
GraphicsWrap::<$wrap_ty>::$variant($expr)
|
||||||
|
};
|
||||||
|
|
||||||
|
(
|
||||||
|
@arm_impl
|
||||||
|
$variant:ident;
|
||||||
|
$expr:expr
|
||||||
|
) => {
|
||||||
|
$expr
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use graphics_match;
|
||||||
|
|
||||||
|
use super::XrExtensions;
|
||||||
|
|
||||||
|
impl From<openxr::EnvironmentBlendMode> for BlendMode {
|
||||||
|
fn from(value: openxr::EnvironmentBlendMode) -> Self {
|
||||||
|
use openxr::EnvironmentBlendMode;
|
||||||
|
if value == EnvironmentBlendMode::OPAQUE {
|
||||||
|
BlendMode::Opaque
|
||||||
|
} else if value == EnvironmentBlendMode::ADDITIVE {
|
||||||
|
BlendMode::Additive
|
||||||
|
} else if value == EnvironmentBlendMode::ALPHA_BLEND {
|
||||||
|
BlendMode::AlphaBlend
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BlendMode> for openxr::EnvironmentBlendMode {
|
||||||
|
fn from(value: BlendMode) -> Self {
|
||||||
|
use openxr::EnvironmentBlendMode;
|
||||||
|
match value {
|
||||||
|
BlendMode::Opaque => EnvironmentBlendMode::OPAQUE,
|
||||||
|
BlendMode::Additive => EnvironmentBlendMode::ADDITIVE,
|
||||||
|
BlendMode::AlphaBlend => EnvironmentBlendMode::ALPHA_BLEND,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,14 +9,9 @@ use wgpu_hal::Api;
|
|||||||
|
|
||||||
use crate::openxr::extensions::XrExtensions;
|
use crate::openxr::extensions::XrExtensions;
|
||||||
use crate::openxr::resources::*;
|
use crate::openxr::resources::*;
|
||||||
|
use crate::openxr::types::Result;
|
||||||
|
|
||||||
use super::{AppInfo, GraphicsExt, XrInitError};
|
use super::{AppInfo, GraphicsExt, XrError};
|
||||||
|
|
||||||
pub fn required_exts() -> XrExtensions {
|
|
||||||
let mut extensions = openxr::ExtensionSet::default();
|
|
||||||
extensions.khr_vulkan_enable2 = true;
|
|
||||||
extensions.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "android"))]
|
#[cfg(not(target_os = "android"))]
|
||||||
const VK_TARGET_VERSION: Version = Version::new(1, 2, 0);
|
const VK_TARGET_VERSION: Version = Version::new(1, 2, 0);
|
||||||
@@ -30,35 +25,28 @@ const VK_TARGET_VERSION_ASH: u32 = ash::vk::make_api_version(
|
|||||||
VK_TARGET_VERSION.patch() as u32,
|
VK_TARGET_VERSION.patch() as u32,
|
||||||
);
|
);
|
||||||
|
|
||||||
pub fn enumerate_swapchain_formats(
|
impl GraphicsExt for openxr::Vulkan {
|
||||||
session: &openxr::Session<openxr::Vulkan>,
|
fn from_wgpu_format(format: wgpu::TextureFormat) -> Option<Self::Format> {
|
||||||
) -> openxr::Result<Vec<wgpu::TextureFormat>> {
|
wgpu_to_vulkan(format).map(|f| f.as_raw() as _)
|
||||||
let formats = session
|
|
||||||
.enumerate_swapchain_formats()?
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|f| vulkan_to_wgpu(ash::vk::Format::from_raw(f as _)))
|
|
||||||
.collect();
|
|
||||||
Ok(formats)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_session(
|
fn to_wgpu_format(format: Self::Format) -> Option<wgpu::TextureFormat> {
|
||||||
app_info: AppInfo,
|
vulkan_to_wgpu(ash::vk::Format::from_raw(format as _))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_session(
|
||||||
|
app_info: &AppInfo,
|
||||||
instance: &openxr::Instance,
|
instance: &openxr::Instance,
|
||||||
system_id: openxr::SystemId,
|
system_id: openxr::SystemId,
|
||||||
format: wgpu::TextureFormat,
|
) -> Result<(
|
||||||
resolution: UVec2,
|
|
||||||
) -> Result<
|
|
||||||
(
|
|
||||||
wgpu::Device,
|
wgpu::Device,
|
||||||
wgpu::Queue,
|
wgpu::Queue,
|
||||||
wgpu::Adapter,
|
wgpu::Adapter,
|
||||||
wgpu::Instance,
|
wgpu::Instance,
|
||||||
openxr::Session<openxr::AnyGraphics>,
|
XrSession,
|
||||||
openxr::FrameWaiter,
|
XrFrameWaiter,
|
||||||
FrameStreamInner,
|
XrFrameStream,
|
||||||
),
|
)> {
|
||||||
XrInitError,
|
|
||||||
> {
|
|
||||||
let reqs = instance.graphics_requirements::<openxr::Vulkan>(system_id)?;
|
let reqs = instance.graphics_requirements::<openxr::Vulkan>(system_id)?;
|
||||||
if VK_TARGET_VERSION < reqs.min_api_version_supported
|
if VK_TARGET_VERSION < reqs.min_api_version_supported
|
||||||
|| VK_TARGET_VERSION.major() > reqs.max_api_version_supported.major()
|
|| VK_TARGET_VERSION.major() > reqs.max_api_version_supported.major()
|
||||||
@@ -68,12 +56,15 @@ pub fn create_session(
|
|||||||
reqs.min_api_version_supported,
|
reqs.min_api_version_supported,
|
||||||
reqs.max_api_version_supported.major() + 1
|
reqs.max_api_version_supported.major() + 1
|
||||||
);
|
);
|
||||||
return Err(XrInitError::FailedGraphicsRequirements);
|
return Err(XrError::FailedGraphicsRequirements);
|
||||||
};
|
};
|
||||||
let vk_entry = unsafe { ash::Entry::load() }?;
|
let vk_entry = unsafe { ash::Entry::load() }?;
|
||||||
let flags = wgpu_hal::InstanceFlags::empty();
|
let flags = wgpu_hal::InstanceFlags::empty();
|
||||||
let extensions =
|
let extensions = <Vulkan as Api>::Instance::required_extensions(
|
||||||
<Vulkan as Api>::Instance::required_extensions(&vk_entry, VK_TARGET_VERSION_ASH, flags)?;
|
&vk_entry,
|
||||||
|
VK_TARGET_VERSION_ASH,
|
||||||
|
flags,
|
||||||
|
)?;
|
||||||
let device_extensions = vec![
|
let device_extensions = vec![
|
||||||
ash::extensions::khr::Swapchain::name(),
|
ash::extensions::khr::Swapchain::name(),
|
||||||
ash::extensions::khr::DrawIndirectCount::name(),
|
ash::extensions::khr::DrawIndirectCount::name(),
|
||||||
@@ -84,7 +75,7 @@ pub fn create_session(
|
|||||||
let vk_instance = unsafe {
|
let vk_instance = unsafe {
|
||||||
let extensions_cchar: Vec<_> = extensions.iter().map(|s| s.as_ptr()).collect();
|
let extensions_cchar: Vec<_> = extensions.iter().map(|s| s.as_ptr()).collect();
|
||||||
|
|
||||||
let app_name = CString::new(app_info.name)?;
|
let app_name = CString::new(app_info.name.clone().into_owned())?;
|
||||||
let vk_app_info = ash::vk::ApplicationInfo::builder()
|
let vk_app_info = ash::vk::ApplicationInfo::builder()
|
||||||
.application_name(&app_name)
|
.application_name(&app_name)
|
||||||
.application_version(1)
|
.application_version(1)
|
||||||
@@ -127,7 +118,7 @@ pub fn create_session(
|
|||||||
VK_TARGET_VERSION.minor(),
|
VK_TARGET_VERSION.minor(),
|
||||||
VK_TARGET_VERSION.patch()
|
VK_TARGET_VERSION.patch()
|
||||||
);
|
);
|
||||||
return Err(XrInitError::FailedGraphicsRequirements);
|
return Err(XrError::FailedGraphicsRequirements);
|
||||||
}
|
}
|
||||||
|
|
||||||
let wgpu_vk_instance = unsafe {
|
let wgpu_vk_instance = unsafe {
|
||||||
@@ -151,7 +142,7 @@ pub fn create_session(
|
|||||||
|
|
||||||
let Some(wgpu_exposed_adapter) = wgpu_vk_instance.expose_adapter(vk_physical_device) else {
|
let Some(wgpu_exposed_adapter) = wgpu_vk_instance.expose_adapter(vk_physical_device) else {
|
||||||
error!("WGPU failed to provide an adapter");
|
error!("WGPU failed to provide an adapter");
|
||||||
return Err(XrInitError::FailedGraphicsRequirements);
|
return Err(XrError::FailedGraphicsRequirements);
|
||||||
};
|
};
|
||||||
|
|
||||||
let enabled_extensions = wgpu_exposed_adapter
|
let enabled_extensions = wgpu_exposed_adapter
|
||||||
@@ -255,40 +246,69 @@ pub fn create_session(
|
|||||||
wgpu_queue,
|
wgpu_queue,
|
||||||
wgpu_adapter,
|
wgpu_adapter,
|
||||||
wgpu_instance,
|
wgpu_instance,
|
||||||
session.into_any_graphics(),
|
XrSession(
|
||||||
frame_wait,
|
session.clone().into_any_graphics(),
|
||||||
FrameStreamInner::Vulkan(frame_stream),
|
super::GraphicsWrap::Vulkan(session),
|
||||||
|
),
|
||||||
|
XrFrameWaiter(frame_wait),
|
||||||
|
XrFrameStream(super::GraphicsWrap::Vulkan(frame_stream)),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GraphicsExt for openxr::Vulkan {
|
unsafe fn to_wgpu_img(
|
||||||
fn from_wgpu_format(format: wgpu::TextureFormat) -> Option<Self::Format> {
|
color_image: Self::SwapchainImage,
|
||||||
wgpu_to_vulkan(format).map(|f| f.as_raw() as _)
|
device: &wgpu::Device,
|
||||||
}
|
|
||||||
|
|
||||||
fn to_wgpu_format(format: Self::Format) -> Option<wgpu::TextureFormat> {
|
|
||||||
vulkan_to_wgpu(ash::vk::Format::from_raw(format as _))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_session(
|
|
||||||
app_info: AppInfo,
|
|
||||||
instance: &openxr::Instance,
|
|
||||||
system_id: openxr::SystemId,
|
|
||||||
format: wgpu::TextureFormat,
|
format: wgpu::TextureFormat,
|
||||||
resolution: UVec2,
|
resolution: UVec2,
|
||||||
) -> Result<
|
) -> Result<wgpu::Texture> {
|
||||||
(
|
let color_image = ash::vk::Image::from_raw(color_image);
|
||||||
wgpu::Device,
|
let wgpu_hal_texture = unsafe {
|
||||||
wgpu::Queue,
|
<wgpu_hal::vulkan::Api as wgpu_hal::Api>::Device::texture_from_raw(
|
||||||
wgpu::Adapter,
|
color_image,
|
||||||
wgpu::Instance,
|
&wgpu_hal::TextureDescriptor {
|
||||||
openxr::Session<openxr::AnyGraphics>,
|
label: Some("VR Swapchain"),
|
||||||
openxr::FrameWaiter,
|
size: wgpu::Extent3d {
|
||||||
FrameStreamInner,
|
width: resolution.x,
|
||||||
),
|
height: resolution.y,
|
||||||
XrInitError,
|
depth_or_array_layers: 2,
|
||||||
> {
|
},
|
||||||
create_session(app_info, instance, system_id, format, resolution)
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: 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 {
|
||||||
|
device.create_texture_from_hal::<wgpu_hal::vulkan::Api>(
|
||||||
|
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: format,
|
||||||
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST,
|
||||||
|
view_formats: &[],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
};
|
||||||
|
Ok(texture)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn required_exts() -> XrExtensions {
|
||||||
|
let mut extensions = openxr::ExtensionSet::default();
|
||||||
|
extensions.khr_vulkan_enable2 = true;
|
||||||
|
extensions.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
pub mod vulkan;
|
|
||||||
|
|
||||||
use bevy::log::{info, warn};
|
|
||||||
use bevy::math::{uvec2, UVec2};
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
use crate::openxr::resources::*;
|
|
||||||
use crate::types::BlendMode;
|
|
||||||
|
|
||||||
use super::extensions::XrExtensions;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
|
||||||
pub struct Version(pub u8, pub u8, pub u16);
|
|
||||||
|
|
||||||
impl Version {
|
|
||||||
pub const BEVY: Self = Self(0, 12, 1);
|
|
||||||
|
|
||||||
pub const fn to_u32(self) -> u32 {
|
|
||||||
let major = (self.0 as u32) << 24;
|
|
||||||
let minor = (self.1 as u32) << 16;
|
|
||||||
self.2 as u32 | major | minor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
|
||||||
pub struct AppInfo<'a> {
|
|
||||||
pub name: &'a str,
|
|
||||||
pub version: Version,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub enum GraphicsBackend {
|
|
||||||
Vulkan,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GraphicsBackend {
|
|
||||||
const ALL: &'static [Self] = &[Self::Vulkan];
|
|
||||||
|
|
||||||
pub fn available_backends(exts: &XrExtensions) -> Vec<Self> {
|
|
||||||
Self::ALL
|
|
||||||
.iter()
|
|
||||||
.copied()
|
|
||||||
.filter(|backend| backend.is_available(exts))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_available(&self, exts: &XrExtensions) -> bool {
|
|
||||||
self.required_exts().is_available(exts)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn required_exts(&self) -> XrExtensions {
|
|
||||||
match self {
|
|
||||||
GraphicsBackend::Vulkan => vulkan::required_exts(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum XrInitError {
|
|
||||||
#[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),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait GraphicsExt: openxr::Graphics {
|
|
||||||
fn from_wgpu_format(format: wgpu::TextureFormat) -> Option<Self::Format>;
|
|
||||||
fn to_wgpu_format(format: Self::Format) -> Option<wgpu::TextureFormat>;
|
|
||||||
fn create_session(
|
|
||||||
app_info: AppInfo,
|
|
||||||
instance: &openxr::Instance,
|
|
||||||
system_id: openxr::SystemId,
|
|
||||||
format: wgpu::TextureFormat,
|
|
||||||
resolution: UVec2,
|
|
||||||
) -> Result<
|
|
||||||
(
|
|
||||||
wgpu::Device,
|
|
||||||
wgpu::Queue,
|
|
||||||
wgpu::Adapter,
|
|
||||||
wgpu::Instance,
|
|
||||||
openxr::Session<openxr::AnyGraphics>,
|
|
||||||
openxr::FrameWaiter,
|
|
||||||
FrameStreamInner,
|
|
||||||
),
|
|
||||||
XrInitError,
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn xr_entry() -> Result<openxr::Entry, XrInitError> {
|
|
||||||
#[cfg(windows)]
|
|
||||||
let entry = Some(openxr::Entry::linked());
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let entry = unsafe { openxr::Entry::load()? };
|
|
||||||
Ok(entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub fn init_xr(
|
|
||||||
// app_info: AppInfo,
|
|
||||||
// requested_exts: XrExtensions,
|
|
||||||
// format: wgpu::TextureFormat,
|
|
||||||
// preferred_blend_mode: BlendMode,
|
|
||||||
// ) -> Result<
|
|
||||||
// (
|
|
||||||
// wgpu::Device,
|
|
||||||
// wgpu::Queue,
|
|
||||||
// wgpu::Adapter,
|
|
||||||
// wgpu::Instance,
|
|
||||||
// openxr::Instance,
|
|
||||||
// openxr::Session<openxr::AnyGraphics>,
|
|
||||||
// openxr::FrameWaiter,
|
|
||||||
// FrameStreamInner,
|
|
||||||
// XrSwapchain,
|
|
||||||
// ),
|
|
||||||
// XrInitError,
|
|
||||||
// > {
|
|
||||||
// let entry = xr_entry().unwrap();
|
|
||||||
|
|
||||||
// let required_exts = vulkan::required_exts() | requested_exts;
|
|
||||||
// let available_exts: XrExtensions = entry.enumerate_extensions()?.into();
|
|
||||||
// for ext in available_exts.unavailable_exts(&required_exts) {
|
|
||||||
// warn!("OpenXR extension '{ext}' is not supported by the current OpenXR runtime")
|
|
||||||
// }
|
|
||||||
// let enabled_exts = required_exts & available_exts;
|
|
||||||
|
|
||||||
// let instance = entry.create_instance(
|
|
||||||
// &openxr::ApplicationInfo {
|
|
||||||
// application_name: app_info.name,
|
|
||||||
// application_version: app_info.version.to_u32(),
|
|
||||||
// engine_name: "Bevy",
|
|
||||||
// engine_version: Version::BEVY.to_u32(),
|
|
||||||
// },
|
|
||||||
// &enabled_exts.into(),
|
|
||||||
// &[],
|
|
||||||
// )?;
|
|
||||||
// info!("Created OpenXR Instance: {:#?}", instance.properties()?);
|
|
||||||
|
|
||||||
// let system_id = instance.system(openxr::FormFactor::HEAD_MOUNTED_DISPLAY)?;
|
|
||||||
// info!(
|
|
||||||
// "Using system: {:#?}",
|
|
||||||
// instance.system_properties(system_id)?
|
|
||||||
// );
|
|
||||||
|
|
||||||
// let view = instance
|
|
||||||
// .enumerate_view_configurations(system_id)?
|
|
||||||
// .first()
|
|
||||||
// .copied()
|
|
||||||
// .unwrap_or(openxr::ViewConfigurationType::PRIMARY_STEREO);
|
|
||||||
|
|
||||||
// let resolution = instance
|
|
||||||
// .enumerate_view_configuration_views(system_id, view)?
|
|
||||||
// .first()
|
|
||||||
// .map(|c| {
|
|
||||||
// uvec2(
|
|
||||||
// c.recommended_image_rect_width,
|
|
||||||
// c.recommended_image_rect_height,
|
|
||||||
// )
|
|
||||||
// })
|
|
||||||
// .unwrap();
|
|
||||||
|
|
||||||
// let (wgpu_device, wgpu_queue, wgpu_adapter, wgpu_instance, session, frame_waiter, frame_stream) =
|
|
||||||
// vulkan::create_session(app_info, &instance, system_id, format, resolution)?;
|
|
||||||
|
|
||||||
// let blend_modes = instance.enumerate_environment_blend_modes(system_id, view)?;
|
|
||||||
|
|
||||||
// let blend_mode = if blend_modes.contains(&preferred_blend_mode.into()) {
|
|
||||||
// preferred_blend_mode.into()
|
|
||||||
// } else {
|
|
||||||
// warn!(
|
|
||||||
// "Runtime does not support blend mode '{:?}'",
|
|
||||||
// preferred_blend_mode
|
|
||||||
// );
|
|
||||||
// blend_modes
|
|
||||||
// .first()
|
|
||||||
// .copied()
|
|
||||||
// .unwrap_or(openxr::EnvironmentBlendMode::OPAQUE)
|
|
||||||
// };
|
|
||||||
// info!("Using blend mode '{:?}'", blend_mode);
|
|
||||||
|
|
||||||
// todo!()
|
|
||||||
// }
|
|
||||||
|
|
||||||
impl From<openxr::EnvironmentBlendMode> for BlendMode {
|
|
||||||
fn from(value: openxr::EnvironmentBlendMode) -> Self {
|
|
||||||
use openxr::EnvironmentBlendMode;
|
|
||||||
if value == EnvironmentBlendMode::OPAQUE {
|
|
||||||
BlendMode::Opaque
|
|
||||||
} else if value == EnvironmentBlendMode::ADDITIVE {
|
|
||||||
BlendMode::Additive
|
|
||||||
} else if value == EnvironmentBlendMode::ALPHA_BLEND {
|
|
||||||
BlendMode::AlphaBlend
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<BlendMode> for openxr::EnvironmentBlendMode {
|
|
||||||
fn from(value: BlendMode) -> Self {
|
|
||||||
use openxr::EnvironmentBlendMode;
|
|
||||||
match value {
|
|
||||||
BlendMode::Opaque => EnvironmentBlendMode::OPAQUE,
|
|
||||||
BlendMode::Additive => EnvironmentBlendMode::ADDITIVE,
|
|
||||||
BlendMode::AlphaBlend => EnvironmentBlendMode::ALPHA_BLEND,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,8 @@
|
|||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use ash::vk::Handle;
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
use openxr::{AnyGraphics, Vulkan};
|
use openxr::AnyGraphics;
|
||||||
|
|
||||||
use crate::openxr::init::Version;
|
use super::graphics::{graphics_match, GraphicsExt, GraphicsType, GraphicsWrap};
|
||||||
|
|
||||||
use super::extensions::XrExtensions;
|
|
||||||
use super::init::{self, AppInfo, GraphicsBackend, GraphicsExt, XrInitError};
|
|
||||||
type Result<T> = std::result::Result<T, XrInitError>;
|
|
||||||
use super::types::*;
|
use super::types::*;
|
||||||
|
|
||||||
#[derive(Deref, Clone)]
|
#[derive(Deref, Clone)]
|
||||||
@@ -21,22 +14,22 @@ impl XrEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_instance(
|
pub fn create_instance(
|
||||||
entry: XrEntry,
|
&self,
|
||||||
app_info: AppInfo,
|
app_info: AppInfo,
|
||||||
exts: XrExtensions,
|
exts: XrExtensions,
|
||||||
backend: GraphicsBackend,
|
backend: GraphicsBackend,
|
||||||
) -> Result<XrInstance> {
|
) -> Result<XrInstance> {
|
||||||
let available_exts = entry.enumerate_extensions()?;
|
let available_exts = self.enumerate_extensions()?;
|
||||||
|
|
||||||
if !backend.is_available(&available_exts) {
|
if !backend.is_available(&available_exts) {
|
||||||
return Err(XrInitError::UnavailableBackend(backend));
|
return Err(XrError::UnavailableBackend(backend));
|
||||||
}
|
}
|
||||||
|
|
||||||
let required_exts = exts | backend.required_exts();
|
let required_exts = exts | backend.required_exts();
|
||||||
|
|
||||||
let instance = entry.create_instance(
|
let instance = self.0.create_instance(
|
||||||
&openxr::ApplicationInfo {
|
&openxr::ApplicationInfo {
|
||||||
application_name: app_info.name,
|
application_name: &app_info.name,
|
||||||
application_version: app_info.version.to_u32(),
|
application_version: app_info.version.to_u32(),
|
||||||
engine_name: "Bevy",
|
engine_name: "Bevy",
|
||||||
engine_version: Version::BEVY.to_u32(),
|
engine_version: Version::BEVY.to_u32(),
|
||||||
@@ -45,7 +38,7 @@ impl XrEntry {
|
|||||||
&[],
|
&[],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(XrInstance(instance, backend))
|
Ok(XrInstance(instance, backend, app_info))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn available_backends(&self) -> Result<Vec<GraphicsBackend>> {
|
pub fn available_backends(&self) -> Result<Vec<GraphicsBackend>> {
|
||||||
@@ -65,187 +58,148 @@ impl From<openxr::Entry> for XrEntry {
|
|||||||
pub struct XrInstance(
|
pub struct XrInstance(
|
||||||
#[deref] pub(crate) openxr::Instance,
|
#[deref] pub(crate) openxr::Instance,
|
||||||
pub(crate) GraphicsBackend,
|
pub(crate) GraphicsBackend,
|
||||||
|
pub(crate) AppInfo,
|
||||||
);
|
);
|
||||||
|
|
||||||
impl XrInstance {
|
impl XrInstance {
|
||||||
pub fn create_session(
|
pub fn create_session(
|
||||||
&self,
|
&self,
|
||||||
app_info: AppInfo,
|
|
||||||
system_id: openxr::SystemId,
|
system_id: openxr::SystemId,
|
||||||
format: wgpu::TextureFormat,
|
|
||||||
resolution: UVec2,
|
|
||||||
) -> Result<(
|
) -> Result<(
|
||||||
wgpu::Device,
|
wgpu::Device,
|
||||||
wgpu::Queue,
|
wgpu::Queue,
|
||||||
wgpu::Adapter,
|
wgpu::Adapter,
|
||||||
wgpu::Instance,
|
wgpu::Instance,
|
||||||
openxr::Session<openxr::AnyGraphics>,
|
XrSession,
|
||||||
openxr::FrameWaiter,
|
XrFrameWaiter,
|
||||||
FrameStreamInner,
|
XrFrameStream,
|
||||||
)> {
|
)> {
|
||||||
match self.1 {
|
graphics_match!(
|
||||||
GraphicsBackend::Vulkan => {
|
self.1;
|
||||||
openxr::Vulkan::create_session(app_info, &self.0, system_id, format, resolution)
|
_ => Api::create_session(&self.2, &self.0, system_id)
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GraphicsType for XrSession {
|
||||||
|
type Inner<G: GraphicsExt> = openxr::Session<G>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource, Deref, Clone)]
|
#[derive(Resource, Deref, Clone)]
|
||||||
pub struct XrSession(
|
pub struct XrSession(
|
||||||
#[deref] pub(crate) openxr::Session<AnyGraphics>,
|
#[deref] pub(crate) openxr::Session<AnyGraphics>,
|
||||||
pub(crate) TypedSession,
|
pub(crate) GraphicsWrap<Self>,
|
||||||
);
|
);
|
||||||
|
|
||||||
impl XrSession {
|
impl XrSession {
|
||||||
pub fn enumerate_swapchain_formats(&self) -> Result<Vec<wgpu::TextureFormat>> {
|
pub fn enumerate_swapchain_formats(&self) -> Result<Vec<wgpu::TextureFormat>> {
|
||||||
self.1.enumerate_swapchain_formats()
|
graphics_match!(
|
||||||
|
&self.1;
|
||||||
|
session => Ok(session.enumerate_swapchain_formats()?.into_iter().filter_map(Api::to_wgpu_format).collect())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_swapchain(&self, info: SwapchainCreateInfo) -> Result<XrSwapchain> {
|
pub fn create_swapchain(&self, info: SwapchainCreateInfo) -> Result<XrSwapchain> {
|
||||||
self.1.create_swapchain(info)
|
Ok(XrSwapchain(graphics_match!(
|
||||||
|
&self.1;
|
||||||
|
session => session.create_swapchain(&info.try_into()?)? => XrSwapchain
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
pub struct XrFrameStream(pub(crate) GraphicsWrap<Self>);
|
||||||
pub enum TypedSession {
|
|
||||||
Vulkan(openxr::Session<Vulkan>),
|
impl GraphicsType for XrFrameStream {
|
||||||
|
type Inner<G: GraphicsExt> = openxr::FrameStream<G>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypedSession {
|
impl XrFrameStream {
|
||||||
pub fn into_any_graphics(&self) -> openxr::Session<AnyGraphics> {
|
pub fn begin(&mut self) -> openxr::Result<()> {
|
||||||
match self {
|
graphics_match!(
|
||||||
TypedSession::Vulkan(session) => session.clone().into_any_graphics(),
|
&mut self.0;
|
||||||
|
stream => stream.begin()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end(
|
||||||
|
&mut self,
|
||||||
|
display_time: openxr::Time,
|
||||||
|
environment_blend_mode: openxr::EnvironmentBlendMode,
|
||||||
|
layers: &[&dyn CompositionLayer],
|
||||||
|
) -> Result<()> {
|
||||||
|
graphics_match!(
|
||||||
|
&mut self.0;
|
||||||
|
stream => {
|
||||||
|
let mut new_layers = vec![];
|
||||||
|
|
||||||
|
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.",
|
||||||
|
swapchain.0.graphics_name(),
|
||||||
|
std::any::type_name::<Api>(),
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new_layers.push(unsafe { std::mem::transmute(layer.header()) });
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(stream.end(display_time, environment_blend_mode, new_layers.as_slice())?)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enumerate_swapchain_formats(&self) -> Result<Vec<wgpu::TextureFormat>> {
|
#[derive(Deref, DerefMut)]
|
||||||
Ok(match self {
|
pub struct XrFrameWaiter(pub openxr::FrameWaiter);
|
||||||
TypedSession::Vulkan(session) => init::vulkan::enumerate_swapchain_formats(session),
|
|
||||||
}?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_swapchain(&self, info: SwapchainCreateInfo) -> Result<XrSwapchain> {
|
pub struct XrSwapchain(pub(crate) GraphicsWrap<Self>);
|
||||||
Ok(match self {
|
|
||||||
TypedSession::Vulkan(session) => {
|
|
||||||
XrSwapchain::Vulkan(session.create_swapchain(&info.try_into()?)?)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource, Default, Deref)]
|
impl GraphicsType for XrSwapchain {
|
||||||
pub struct Framebuffers(pub Vec<wgpu::Texture>);
|
type Inner<G: GraphicsExt> = openxr::Swapchain<G>;
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Swapchain {
|
|
||||||
pub inner: Arc<Mutex<XrSwapchain>>,
|
|
||||||
pub format: wgpu::TextureFormat,
|
|
||||||
pub resolution: UVec2,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum FrameStreamInner {
|
|
||||||
Vulkan(openxr::FrameStream<openxr::Vulkan>),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum XrSwapchain {
|
|
||||||
Vulkan(openxr::Swapchain<openxr::Vulkan>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XrSwapchain {
|
impl XrSwapchain {
|
||||||
pub fn acquire_image(&mut self) -> Result<u32> {
|
pub fn acquire_image(&mut self) -> Result<u32> {
|
||||||
Ok(match self {
|
graphics_match!(
|
||||||
XrSwapchain::Vulkan(swap) => swap.acquire_image()?,
|
&mut self.0;
|
||||||
})
|
swap => Ok(swap.acquire_image()?)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait_image(&mut self, timeout: openxr::Duration) -> Result<()> {
|
pub fn wait_image(&mut self, timeout: openxr::Duration) -> Result<()> {
|
||||||
Ok(match self {
|
graphics_match!(
|
||||||
XrSwapchain::Vulkan(swap) => swap.wait_image(timeout)?,
|
&mut self.0;
|
||||||
})
|
swap => Ok(swap.wait_image(timeout)?)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn release_image(&mut self) -> Result<()> {
|
pub fn release_image(&mut self) -> Result<()> {
|
||||||
Ok(match self {
|
graphics_match!(
|
||||||
XrSwapchain::Vulkan(swap) => swap.release_image()?,
|
&mut self.0;
|
||||||
})
|
swap => Ok(swap.release_image()?)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enumerate_images(
|
pub fn enumerate_images(
|
||||||
&mut self,
|
&mut self,
|
||||||
device: wgpu::Device,
|
device: &wgpu::Device,
|
||||||
format: wgpu::TextureFormat,
|
format: wgpu::TextureFormat,
|
||||||
resolution: UVec2,
|
resolution: UVec2,
|
||||||
) -> Result<Vec<wgpu::Texture>> {
|
) -> Result<Vec<wgpu::Texture>> {
|
||||||
match self {
|
graphics_match!(
|
||||||
XrSwapchain::Vulkan(swap) => swap.enumerate_imgs(device, format, resolution),
|
&mut self.0;
|
||||||
|
swap => {
|
||||||
|
let mut images = vec![];
|
||||||
|
for image in swap.enumerate_images()? {
|
||||||
|
unsafe {
|
||||||
|
images.push(Api::to_wgpu_img(image, device, format, resolution)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(images)
|
||||||
}
|
}
|
||||||
|
|
||||||
trait EnumerateImages {
|
|
||||||
fn enumerate_imgs(
|
|
||||||
&mut self,
|
|
||||||
device: wgpu::Device,
|
|
||||||
format: wgpu::TextureFormat,
|
|
||||||
resolution: UVec2,
|
|
||||||
) -> Result<Vec<wgpu::Texture>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EnumerateImages for openxr::Swapchain<openxr::Vulkan> {
|
|
||||||
fn enumerate_imgs(
|
|
||||||
&mut self,
|
|
||||||
device: wgpu::Device,
|
|
||||||
format: wgpu::TextureFormat,
|
|
||||||
resolution: UVec2,
|
|
||||||
) -> Result<Vec<wgpu::Texture>> {
|
|
||||||
let images = self.enumerate_images()?;
|
|
||||||
let images = images.into_iter().map(|color_image| {
|
|
||||||
let color_image = ash::vk::Image::from_raw(color_image);
|
|
||||||
let wgpu_hal_texture = unsafe {
|
|
||||||
<wgpu_hal::vulkan::Api as wgpu_hal::Api>::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: 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 {
|
|
||||||
device.create_texture_from_hal::<wgpu_hal::vulkan::Api>(
|
|
||||||
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: format,
|
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
|
|
||||||
| wgpu::TextureUsages::COPY_DST,
|
|
||||||
view_formats: &[],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
};
|
|
||||||
texture
|
|
||||||
});
|
|
||||||
Ok(images.collect())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,83 @@
|
|||||||
pub use openxr::{SwapchainCreateFlags, SwapchainUsageFlags, SystemId};
|
use std::borrow::Cow;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
use super::init::{GraphicsExt, XrInitError};
|
use super::graphics::{graphics_match, GraphicsExt, GraphicsWrap};
|
||||||
|
|
||||||
|
pub use super::extensions::XrExtensions;
|
||||||
|
pub use openxr::{
|
||||||
|
Extent2Di, Offset2Di, Rect2Di, SwapchainCreateFlags, SwapchainUsageFlags, SystemId,
|
||||||
|
};
|
||||||
|
pub type Result<T> = std::result::Result<T, XrError>;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
|
pub struct Version(pub u8, pub u8, pub u16);
|
||||||
|
|
||||||
|
impl Version {
|
||||||
|
pub const BEVY: Self = Self(0, 12, 1);
|
||||||
|
|
||||||
|
pub const fn to_u32(self) -> u32 {
|
||||||
|
let major = (self.0 as u32) << 24;
|
||||||
|
let minor = (self.1 as u32) << 16;
|
||||||
|
self.2 as u32 | major | minor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
|
pub struct AppInfo {
|
||||||
|
pub name: Cow<'static, str>,
|
||||||
|
pub version: Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type GraphicsBackend = GraphicsWrap<()>;
|
||||||
|
|
||||||
|
impl GraphicsBackend {
|
||||||
|
const ALL: &'static [Self] = &[Self::Vulkan(())];
|
||||||
|
|
||||||
|
pub fn available_backends(exts: &XrExtensions) -> Vec<Self> {
|
||||||
|
Self::ALL
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.filter(|backend| backend.is_available(exts))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_available(&self, exts: &XrExtensions) -> bool {
|
||||||
|
self.required_exts().is_available(exts)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn required_exts(&self) -> XrExtensions {
|
||||||
|
graphics_match!(
|
||||||
|
self;
|
||||||
|
_ => Api::required_exts()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct SwapchainCreateInfo {
|
pub struct SwapchainCreateInfo {
|
||||||
@@ -16,14 +93,14 @@ pub struct SwapchainCreateInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<G: GraphicsExt> TryFrom<SwapchainCreateInfo> for openxr::SwapchainCreateInfo<G> {
|
impl<G: GraphicsExt> TryFrom<SwapchainCreateInfo> for openxr::SwapchainCreateInfo<G> {
|
||||||
type Error = XrInitError;
|
type Error = XrError;
|
||||||
|
|
||||||
fn try_from(value: SwapchainCreateInfo) -> Result<Self, Self::Error> {
|
fn try_from(value: SwapchainCreateInfo) -> Result<Self> {
|
||||||
Ok(openxr::SwapchainCreateInfo {
|
Ok(openxr::SwapchainCreateInfo {
|
||||||
create_flags: value.create_flags,
|
create_flags: value.create_flags,
|
||||||
usage_flags: value.usage_flags,
|
usage_flags: value.usage_flags,
|
||||||
format: G::from_wgpu_format(value.format)
|
format: G::from_wgpu_format(value.format)
|
||||||
.ok_or(XrInitError::UnsupportedTextureFormat(value.format))?,
|
.ok_or(XrError::UnsupportedTextureFormat(value.format))?,
|
||||||
sample_count: value.sample_count,
|
sample_count: value.sample_count,
|
||||||
width: value.width,
|
width: value.width,
|
||||||
height: value.height,
|
height: value.height,
|
||||||
@@ -33,3 +110,177 @@ impl<G: GraphicsExt> TryFrom<SwapchainCreateInfo> for openxr::SwapchainCreateInf
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub use builder::*;
|
||||||
|
|
||||||
|
/// Copied with modification from the openxr crate to allow for a safe, graphics agnostic api to work with Bevy.
|
||||||
|
mod builder {
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
use openxr::{sys, CompositionLayerFlags, Fovf, Posef, Rect2Di, Space};
|
||||||
|
|
||||||
|
use crate::openxr::{graphics::graphics_match, XrSwapchain};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct SwapchainSubImage<'a> {
|
||||||
|
inner: sys::SwapchainSubImage,
|
||||||
|
swapchain: Option<&'a XrSwapchain>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SwapchainSubImage<'a> {
|
||||||
|
#[inline]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
inner: sys::SwapchainSubImage {
|
||||||
|
..unsafe { mem::zeroed() }
|
||||||
|
},
|
||||||
|
swapchain: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn into_raw(self) -> sys::SwapchainSubImage {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn as_raw(&self) -> &sys::SwapchainSubImage {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn swapchain(mut self, value: &'a XrSwapchain) -> Self {
|
||||||
|
graphics_match!(
|
||||||
|
&value.0;
|
||||||
|
swap => self.inner.swapchain = swap.as_raw()
|
||||||
|
);
|
||||||
|
self.swapchain = Some(value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn image_rect(mut self, value: Rect2Di) -> Self {
|
||||||
|
self.inner.image_rect = value;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn image_array_index(mut self, value: u32) -> Self {
|
||||||
|
self.inner.image_array_index = value;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Default for SwapchainSubImage<'a> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct CompositionLayerProjectionView<'a> {
|
||||||
|
inner: sys::CompositionLayerProjectionView,
|
||||||
|
swapchain: Option<&'a XrSwapchain>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CompositionLayerProjectionView<'a> {
|
||||||
|
#[inline]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
inner: sys::CompositionLayerProjectionView {
|
||||||
|
ty: sys::StructureType::COMPOSITION_LAYER_PROJECTION_VIEW,
|
||||||
|
..unsafe { mem::zeroed() }
|
||||||
|
},
|
||||||
|
swapchain: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn into_raw(self) -> sys::CompositionLayerProjectionView {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn as_raw(&self) -> &sys::CompositionLayerProjectionView {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn pose(mut self, value: Posef) -> Self {
|
||||||
|
self.inner.pose = value;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn fov(mut self, value: Fovf) -> Self {
|
||||||
|
self.inner.fov = value;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn sub_image(mut self, value: SwapchainSubImage<'a>) -> Self {
|
||||||
|
self.inner.sub_image = value.inner;
|
||||||
|
self.swapchain = value.swapchain;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> Default for CompositionLayerProjectionView<'a> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub unsafe trait CompositionLayer<'a> {
|
||||||
|
fn swapchain(&self) -> Option<&'a XrSwapchain>;
|
||||||
|
fn header(&self) -> &'a sys::CompositionLayerBaseHeader;
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct CompositionLayerProjection<'a> {
|
||||||
|
inner: sys::CompositionLayerProjection,
|
||||||
|
swapchain: Option<&'a XrSwapchain>,
|
||||||
|
views: Vec<sys::CompositionLayerProjectionView>,
|
||||||
|
}
|
||||||
|
impl<'a> CompositionLayerProjection<'a> {
|
||||||
|
#[inline]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
inner: sys::CompositionLayerProjection {
|
||||||
|
ty: sys::StructureType::COMPOSITION_LAYER_PROJECTION,
|
||||||
|
..unsafe { mem::zeroed() }
|
||||||
|
},
|
||||||
|
swapchain: None,
|
||||||
|
views: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn into_raw(self) -> sys::CompositionLayerProjection {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn as_raw(&self) -> &sys::CompositionLayerProjection {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn layer_flags(mut self, value: CompositionLayerFlags) -> Self {
|
||||||
|
self.inner.layer_flags = value;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn space(mut self, value: &'a Space) -> Self {
|
||||||
|
self.inner.space = value.as_raw();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn views(mut self, value: &'a [CompositionLayerProjectionView<'a>]) -> Self {
|
||||||
|
for view in value {
|
||||||
|
self.views.push(view.inner.clone());
|
||||||
|
}
|
||||||
|
self.inner.views = self.views.as_slice().as_ptr() as *const _ as _;
|
||||||
|
self.inner.view_count = value.len() as u32;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe impl<'a> CompositionLayer<'a> for CompositionLayerProjection<'a> {
|
||||||
|
fn swapchain(&self) -> Option<&'a XrSwapchain> {
|
||||||
|
self.swapchain
|
||||||
|
}
|
||||||
|
|
||||||
|
fn header(&self) -> &'a sys::CompositionLayerBaseHeader {
|
||||||
|
unsafe { std::mem::transmute(&self.inner) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> Default for CompositionLayerProjection<'a> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user