diff --git a/Cargo.toml b/Cargo.toml index 847c1b6..1eacc55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,8 +8,10 @@ license = "MIT/Apache-2.0" [features] -default = [] +default = ["vulkan", "d3d12"] force-link = ["openxr/linked"] +vulkan = ["wgpu-core/vulkan"] +d3d12 = ["wgpu-core/dx12", "dep:winapi", "dep:d3d12"] [workspace] members = ["examples/android", "examples/demo"] @@ -21,7 +23,7 @@ bevy = "0.13" futures-lite = "2.0.1" mint = "0.5.9" wgpu = "0.19" -wgpu-core = { version = "0.19", features = ["vulkan"] } +wgpu-core = { version = "0.19" } wgpu-hal = "0.19" eyre = "0.6.11" @@ -31,6 +33,9 @@ openxr = { git = "https://github.com/Ralith/openxrs", rev = "0177d2d", features "static", "mint", ] } +winapi = { version = "0.3.9", optional = true } +d3d12 = { version = "0.19", features = ["libloading"], optional = true } + [target.'cfg(all(target_family = "unix", not(target_arch = "wasm32")) )'.dependencies] openxr = { git = "https://github.com/Ralith/openxrs", rev = "0177d2d", features = [ "mint", diff --git a/src/graphics/d3d12.rs b/src/graphics/d3d12.rs new file mode 100644 index 0000000..af7643e --- /dev/null +++ b/src/graphics/d3d12.rs @@ -0,0 +1,474 @@ +use std::sync::atomic::AtomicBool; +use std::sync::{Arc, Mutex}; + +// use anyhow::Context; +use bevy::math::uvec2; +use bevy::prelude::*; +use bevy::render::renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue}; +use bevy::window::RawHandleWrapper; +use eyre::{Context, ContextCompat}; +use openxr as xr; +use wgpu::Instance; +use wgpu_hal::{api::Dx12, Api}; +use wgpu_hal::{Adapter as HalAdapter, Instance as HalInstance}; +use winapi::shared::dxgiformat::{self, DXGI_FORMAT}; +use winapi::um::{d3d12 as winapi_d3d12, d3dcommon}; +use xr::EnvironmentBlendMode; + +use crate::graphics::extensions::XrExtensions; +use crate::input::XrInput; + +use crate::resources::{ + OXrSessionSetupInfo, Swapchain, SwapchainInner, XrEnvironmentBlendMode, XrFormat, XrFrameState, + XrFrameWaiter, XrInstance, XrResolution, XrSession, XrSessionRunning, XrSwapchain, XrViews, +}; + +#[cfg(all(feature = "d3d12", windows))] +use crate::resources::D3D12OXrSessionSetupInfo; +#[cfg(feature = "vulkan")] +use crate::resources::VulkanOXrSessionSetupInfo; + +use super::{XrAppInfo, XrPreferdBlendMode}; +use crate::VIEW_TYPE; + +pub fn initialize_xr_instance( + window: Option, + xr_entry: xr::Entry, + reqeusted_extensions: XrExtensions, + available_extensions: XrExtensions, + prefered_blend_mode: XrPreferdBlendMode, + app_info: XrAppInfo, +) -> eyre::Result<( + XrInstance, + OXrSessionSetupInfo, + XrEnvironmentBlendMode, + RenderDevice, + RenderQueue, + RenderAdapterInfo, + RenderAdapter, + Instance, +)> { + #[cfg(target_os = "android")] + xr_entry.initialize_android_loader()?; + + assert!(available_extensions.raw().khr_d3d12_enable); + //info!("available xr exts: {:#?}", available_extensions); + + let mut enabled_extensions: xr::ExtensionSet = + (available_extensions & reqeusted_extensions).into(); + enabled_extensions.khr_d3d12_enable = true; + + let available_layers = xr_entry.enumerate_layers()?; + //info!("available xr layers: {:#?}", available_layers); + + let xr_instance = xr_entry.create_instance( + &xr::ApplicationInfo { + application_name: &app_info.name, + engine_name: "Bevy", + ..Default::default() + }, + &enabled_extensions, + &[], + )?; + info!("created OpenXR instance"); + let instance_props = xr_instance.properties()?; + let xr_system_id = xr_instance.system(xr::FormFactor::HEAD_MOUNTED_DISPLAY)?; + info!("created OpenXR system"); + let system_props = xr_instance.system_properties(xr_system_id).unwrap(); + info!( + "loaded OpenXR runtime: {} {} {}", + instance_props.runtime_name, + instance_props.runtime_version, + if system_props.system_name.is_empty() { + "" + } else { + &system_props.system_name + } + ); + + let blend_modes = xr_instance.enumerate_environment_blend_modes(xr_system_id, VIEW_TYPE)?; + let blend_mode: EnvironmentBlendMode = match prefered_blend_mode { + XrPreferdBlendMode::Opaque if blend_modes.contains(&EnvironmentBlendMode::OPAQUE) => { + bevy::log::info!("Using Opaque"); + EnvironmentBlendMode::OPAQUE + } + XrPreferdBlendMode::Additive if blend_modes.contains(&EnvironmentBlendMode::ADDITIVE) => { + bevy::log::info!("Using Additive"); + EnvironmentBlendMode::ADDITIVE + } + XrPreferdBlendMode::AlphaBlend + if blend_modes.contains(&EnvironmentBlendMode::ALPHA_BLEND) => + { + bevy::log::info!("Using AlphaBlend"); + EnvironmentBlendMode::ALPHA_BLEND + } + _ => { + bevy::log::info!("Using Opaque"); + EnvironmentBlendMode::OPAQUE + } + }; + + let reqs = xr_instance.graphics_requirements::(xr_system_id)?; + + let instance_descriptor = &wgpu_hal::InstanceDescriptor { + name: &app_info.name, + dx12_shader_compiler: wgpu::util::dx12_shader_compiler_from_env().unwrap_or( + wgpu::Dx12Compiler::Dxc { + dxil_path: None, + dxc_path: None, + }, + ), + flags: wgpu::InstanceFlags::from_build_config().with_env(), + gles_minor_version: Default::default(), + }; + let wgpu_raw_instance: wgpu_hal::dx12::Instance = + unsafe { wgpu_hal::dx12::Instance::init(instance_descriptor)? }; + let wgpu_adapters: Vec> = + unsafe { wgpu_raw_instance.enumerate_adapters() }; + let wgpu_exposed_adapter = wgpu_adapters + .into_iter() + .find(|a| { + let mut desc = unsafe { std::mem::zeroed() }; + unsafe { a.adapter.raw_adapter().GetDesc1(&mut desc) }; + desc.AdapterLuid.HighPart == reqs.adapter_luid.HighPart + && desc.AdapterLuid.LowPart == reqs.adapter_luid.LowPart + }) + .context("failed to find DXGI adapter matching LUID provided by runtime")?; + + let wgpu_instance = + unsafe { wgpu::Instance::from_hal::(wgpu_raw_instance) }; + + let wgpu_features = wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES + | wgpu::Features::MULTIVIEW + | wgpu::Features::MULTI_DRAW_INDIRECT_COUNT + | wgpu::Features::MULTI_DRAW_INDIRECT; + + let wgpu_limits = wgpu::Limits { + max_bind_groups: 8, + max_storage_buffer_binding_size: wgpu_exposed_adapter + .capabilities + .limits + .max_storage_buffer_binding_size, + max_push_constant_size: 4, + ..Default::default() + }; + + let wgpu_open_device = unsafe { + wgpu_exposed_adapter + .adapter + .open(wgpu_features, &wgpu_limits)? + }; + + let device_supported_feature_level: d3d12::FeatureLevel = + get_device_feature_level(wgpu_open_device.device.raw_device()); + + if (device_supported_feature_level as u32) < (reqs.min_feature_level as u32) { + panic!( + "OpenXR runtime requires D3D12 feature level >= {}", + reqs.min_feature_level + ); + } + + let (session, frame_wait, frame_stream) = unsafe { + xr_instance.create_session::( + xr_system_id, + &xr::d3d::SessionCreateInfoD3D12 { + device: wgpu_open_device.device.raw_device().as_mut_ptr().cast(), + queue: wgpu_open_device.device.raw_queue().as_mut_ptr().cast(), + }, + ) + }?; + + let wgpu_adapter = unsafe { wgpu_instance.create_adapter_from_hal(wgpu_exposed_adapter) }; + let raw_device = wgpu_open_device.device.raw_device().as_mut_ptr(); + let raw_queue = wgpu_open_device.device.raw_queue().as_mut_ptr(); + let (wgpu_device, wgpu_queue) = unsafe { + wgpu_adapter.create_device_from_hal( + wgpu_open_device, + &wgpu::DeviceDescriptor { + label: Some("bevy_oxr device"), + required_features: wgpu_features, + required_limits: wgpu_limits, + }, + None, + )? + }; + + Ok(( + xr_instance.into(), + OXrSessionSetupInfo::D3D12(D3D12OXrSessionSetupInfo { + raw_device, + raw_queue, + xr_system_id, + }), + blend_mode.into(), + wgpu_device.into(), + RenderQueue(wgpu_queue.into()), + RenderAdapterInfo(wgpu_adapter.get_info()), + RenderAdapter(wgpu_adapter.into()), + wgpu_instance.into(), + )) +} + +pub fn start_xr_session( + window: Option, + ptrs: &OXrSessionSetupInfo, + xr_instance: &XrInstance, + render_device: &RenderDevice, + render_adapter: &RenderAdapter, + wgpu_instance: &Instance, +) -> eyre::Result<( + XrSession, + XrResolution, + XrFormat, + XrSessionRunning, + XrFrameWaiter, + XrSwapchain, + XrInput, + XrViews, + XrFrameState, +)> { + let wgpu_device = render_device.wgpu_device(); + let wgpu_adapter = &render_adapter.0; + + #[allow(unreachable_patterns)] + let setup_info = match ptrs { + OXrSessionSetupInfo::D3D12(v) => v, + _ => eyre::bail!("Wrong Graphics Api"), + }; + let (session, frame_wait, frame_stream) = unsafe { + xr_instance.create_session::( + setup_info.xr_system_id, + &xr::d3d::SessionCreateInfoD3D12 { + device: setup_info.raw_device.cast(), + queue: setup_info.raw_queue.cast(), + }, + ) + }?; + + let views = + xr_instance.enumerate_view_configuration_views(setup_info.xr_system_id, VIEW_TYPE)?; + let surface = window.map(|wrapper| unsafe { + // SAFETY: Plugins should be set up on the main thread. + let handle = wrapper.get_handle(); + wgpu_instance + .create_surface(handle) + .expect("Failed to create wgpu surface") + }); + let swapchain_format = surface + .as_ref() + .map(|surface| surface.get_capabilities(wgpu_adapter).formats[0]) + .unwrap_or(wgpu::TextureFormat::Rgba8UnormSrgb); + + // TODO: Log swapchain format + + let resolution = uvec2( + views[0].recommended_image_rect_width, + views[0].recommended_image_rect_height, + ); + + let handle = session + .create_swapchain(&xr::SwapchainCreateInfo { + create_flags: xr::SwapchainCreateFlags::EMPTY, + usage_flags: xr::SwapchainUsageFlags::COLOR_ATTACHMENT + | xr::SwapchainUsageFlags::SAMPLED, + format: wgpu_to_d3d12(swapchain_format).expect("Unsupported texture format"), + // The Vulkan graphics pipeline we create is not set up for multisampling, + // so we hardcode this to 1. If we used a proper multisampling setup, we + // could set this to `views[0].recommended_swapchain_sample_count`. + sample_count: 1, + width: resolution.x, + height: resolution.y, + face_count: 1, + array_size: 2, + mip_count: 1, + }) + .unwrap(); + + let images = handle.enumerate_images().unwrap(); + + let buffers = images + .into_iter() + .map(|color_image| { + info!("image map swapchain"); + let wgpu_hal_texture = unsafe { + ::Device::texture_from_raw( + d3d12::ComPtr::from_raw(color_image as *mut _), + swapchain_format, + wgpu::TextureDimension::D2, + wgpu::Extent3d { + width: resolution.x, + height: resolution.y, + depth_or_array_layers: 2, + }, + 1, + 1, + ) + }; + let texture = unsafe { + wgpu_device.create_texture_from_hal::( + wgpu_hal_texture, + &wgpu::TextureDescriptor { + label: Some("bevy_openxr swapchain"), + size: wgpu::Extent3d { + width: resolution.x, + height: resolution.y, + depth_or_array_layers: 2, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: swapchain_format, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT + | wgpu::TextureUsages::COPY_DST, + view_formats: &[], + }, + ) + }; + texture + }) + .collect(); + + Ok(( + XrSession::D3D12(session.clone()), + resolution.into(), + swapchain_format.into(), + // TODO: this shouldn't be in here + AtomicBool::new(false).into(), + frame_wait.into(), + Swapchain::D3D12(SwapchainInner { + stream: Mutex::new(frame_stream), + handle: Mutex::new(handle), + buffers, + image_index: Mutex::new(0), + }) + .into(), + XrInput::new(xr_instance, &session.into_any_graphics())?, + Vec::default().into(), + // TODO: Feels wrong to return a FrameState here, we probably should just wait for the next frame + xr::FrameState { + predicted_display_time: xr::Time::from_nanos(1), + predicted_display_period: xr::Duration::from_nanos(1), + should_render: true, + } + .into(), + )) +} + +// Extracted from https://github.com/gfx-rs/wgpu/blob/1161a22f4fbb4fc204eb06f2ac4243f83e0e980d/wgpu-hal/src/dx12/adapter.rs#L73-L94 +// license: MIT OR Apache-2.0 +fn get_device_feature_level( + device: &d3d12::ComPtr, +) -> d3d12::FeatureLevel { + // Detect the highest supported feature level. + let d3d_feature_level = [ + d3d12::FeatureLevel::L12_1, + d3d12::FeatureLevel::L12_0, + d3d12::FeatureLevel::L11_1, + d3d12::FeatureLevel::L11_0, + ]; + type FeatureLevelsInfo = winapi_d3d12::D3D12_FEATURE_DATA_FEATURE_LEVELS; + let mut device_levels: FeatureLevelsInfo = unsafe { std::mem::zeroed() }; + device_levels.NumFeatureLevels = d3d_feature_level.len() as u32; + device_levels.pFeatureLevelsRequested = d3d_feature_level.as_ptr().cast(); + unsafe { + device.CheckFeatureSupport( + winapi_d3d12::D3D12_FEATURE_FEATURE_LEVELS, + (&mut device_levels as *mut FeatureLevelsInfo).cast(), + std::mem::size_of::() as _, + ) + }; + // This cast should never fail because we only requested feature levels that are already in the enum. + let max_feature_level = d3d12::FeatureLevel::try_from(device_levels.MaxSupportedFeatureLevel) + .expect("Unexpected feature level"); + max_feature_level +} + +fn wgpu_to_d3d12(format: wgpu::TextureFormat) -> Option { + // Copied wholesale from: + // https://github.com/gfx-rs/wgpu/blob/v0.19/wgpu-hal/src/auxil/dxgi/conv.rs#L12-L94 + // license: MIT OR Apache-2.0 + use wgpu::TextureFormat as Tf; + use winapi::shared::dxgiformat::*; + + Some(match format { + Tf::R8Unorm => DXGI_FORMAT_R8_UNORM, + Tf::R8Snorm => DXGI_FORMAT_R8_SNORM, + Tf::R8Uint => DXGI_FORMAT_R8_UINT, + Tf::R8Sint => DXGI_FORMAT_R8_SINT, + Tf::R16Uint => DXGI_FORMAT_R16_UINT, + Tf::R16Sint => DXGI_FORMAT_R16_SINT, + Tf::R16Unorm => DXGI_FORMAT_R16_UNORM, + Tf::R16Snorm => DXGI_FORMAT_R16_SNORM, + Tf::R16Float => DXGI_FORMAT_R16_FLOAT, + Tf::Rg8Unorm => DXGI_FORMAT_R8G8_UNORM, + Tf::Rg8Snorm => DXGI_FORMAT_R8G8_SNORM, + Tf::Rg8Uint => DXGI_FORMAT_R8G8_UINT, + Tf::Rg8Sint => DXGI_FORMAT_R8G8_SINT, + Tf::Rg16Unorm => DXGI_FORMAT_R16G16_UNORM, + Tf::Rg16Snorm => DXGI_FORMAT_R16G16_SNORM, + Tf::R32Uint => DXGI_FORMAT_R32_UINT, + Tf::R32Sint => DXGI_FORMAT_R32_SINT, + Tf::R32Float => DXGI_FORMAT_R32_FLOAT, + Tf::Rg16Uint => DXGI_FORMAT_R16G16_UINT, + Tf::Rg16Sint => DXGI_FORMAT_R16G16_SINT, + Tf::Rg16Float => DXGI_FORMAT_R16G16_FLOAT, + Tf::Rgba8Unorm => DXGI_FORMAT_R8G8B8A8_UNORM, + Tf::Rgba8UnormSrgb => DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, + Tf::Bgra8UnormSrgb => DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, + Tf::Rgba8Snorm => DXGI_FORMAT_R8G8B8A8_SNORM, + Tf::Bgra8Unorm => DXGI_FORMAT_B8G8R8A8_UNORM, + Tf::Rgba8Uint => DXGI_FORMAT_R8G8B8A8_UINT, + Tf::Rgba8Sint => DXGI_FORMAT_R8G8B8A8_SINT, + Tf::Rgb9e5Ufloat => DXGI_FORMAT_R9G9B9E5_SHAREDEXP, + Tf::Rgb10a2Uint => DXGI_FORMAT_R10G10B10A2_UINT, + Tf::Rgb10a2Unorm => DXGI_FORMAT_R10G10B10A2_UNORM, + Tf::Rg11b10Float => DXGI_FORMAT_R11G11B10_FLOAT, + Tf::Rg32Uint => DXGI_FORMAT_R32G32_UINT, + Tf::Rg32Sint => DXGI_FORMAT_R32G32_SINT, + Tf::Rg32Float => DXGI_FORMAT_R32G32_FLOAT, + Tf::Rgba16Uint => DXGI_FORMAT_R16G16B16A16_UINT, + Tf::Rgba16Sint => DXGI_FORMAT_R16G16B16A16_SINT, + Tf::Rgba16Unorm => DXGI_FORMAT_R16G16B16A16_UNORM, + Tf::Rgba16Snorm => DXGI_FORMAT_R16G16B16A16_SNORM, + Tf::Rgba16Float => DXGI_FORMAT_R16G16B16A16_FLOAT, + Tf::Rgba32Uint => DXGI_FORMAT_R32G32B32A32_UINT, + Tf::Rgba32Sint => DXGI_FORMAT_R32G32B32A32_SINT, + Tf::Rgba32Float => DXGI_FORMAT_R32G32B32A32_FLOAT, + Tf::Stencil8 => DXGI_FORMAT_D24_UNORM_S8_UINT, + Tf::Depth16Unorm => DXGI_FORMAT_D16_UNORM, + Tf::Depth24Plus => DXGI_FORMAT_D24_UNORM_S8_UINT, + Tf::Depth24PlusStencil8 => DXGI_FORMAT_D24_UNORM_S8_UINT, + Tf::Depth32Float => DXGI_FORMAT_D32_FLOAT, + Tf::Depth32FloatStencil8 => DXGI_FORMAT_D32_FLOAT_S8X24_UINT, + Tf::NV12 => DXGI_FORMAT_NV12, + Tf::Bc1RgbaUnorm => DXGI_FORMAT_BC1_UNORM, + Tf::Bc1RgbaUnormSrgb => DXGI_FORMAT_BC1_UNORM_SRGB, + Tf::Bc2RgbaUnorm => DXGI_FORMAT_BC2_UNORM, + Tf::Bc2RgbaUnormSrgb => DXGI_FORMAT_BC2_UNORM_SRGB, + Tf::Bc3RgbaUnorm => DXGI_FORMAT_BC3_UNORM, + Tf::Bc3RgbaUnormSrgb => DXGI_FORMAT_BC3_UNORM_SRGB, + Tf::Bc4RUnorm => DXGI_FORMAT_BC4_UNORM, + Tf::Bc4RSnorm => DXGI_FORMAT_BC4_SNORM, + Tf::Bc5RgUnorm => DXGI_FORMAT_BC5_UNORM, + Tf::Bc5RgSnorm => DXGI_FORMAT_BC5_SNORM, + Tf::Bc6hRgbUfloat => DXGI_FORMAT_BC6H_UF16, + Tf::Bc6hRgbFloat => DXGI_FORMAT_BC6H_SF16, + Tf::Bc7RgbaUnorm => DXGI_FORMAT_BC7_UNORM, + Tf::Bc7RgbaUnormSrgb => DXGI_FORMAT_BC7_UNORM_SRGB, + Tf::Etc2Rgb8Unorm + | Tf::Etc2Rgb8UnormSrgb + | Tf::Etc2Rgb8A1Unorm + | Tf::Etc2Rgb8A1UnormSrgb + | Tf::Etc2Rgba8Unorm + | Tf::Etc2Rgba8UnormSrgb + | Tf::EacR11Unorm + | Tf::EacR11Snorm + | Tf::EacRg11Unorm + | Tf::EacRg11Snorm + | Tf::Astc { + block: _, + channel: _, + } => return None, + }) +} diff --git a/src/graphics/mod.rs b/src/graphics/mod.rs index 147fa1c..c897604 100644 --- a/src/graphics/mod.rs +++ b/src/graphics/mod.rs @@ -1,4 +1,8 @@ pub mod extensions; + +#[cfg(all(feature = "d3d12", windows))] +mod d3d12; +#[cfg(feature = "vulkan")] mod vulkan; use bevy::ecs::query::With; @@ -17,6 +21,8 @@ use crate::resources::{ }; use crate::OXrSessionSetupInfo; +use crate::Backend; + use openxr as xr; use self::extensions::XrExtensions; @@ -63,16 +69,29 @@ pub fn start_xr_session( XrViews, XrFrameState, )> { - vulkan::start_xr_session( - window, - session_setup_data, - xr_instance, - render_device, - render_adapter, - wgpu_instance, - ) + match session_setup_data { + #[cfg(feature = "vulkan")] + OXrSessionSetupInfo::Vulkan(_) => vulkan::start_xr_session( + window, + session_setup_data, + xr_instance, + render_device, + render_adapter, + wgpu_instance, + ), + #[cfg(all(feature = "d3d12", windows))] + OXrSessionSetupInfo::D3D12(_) => d3d12::start_xr_session( + window, + session_setup_data, + xr_instance, + render_device, + render_adapter, + wgpu_instance, + ), + } } pub fn initialize_xr_instance( + backend_preference: &[Backend], window: Option, reqeusted_extensions: XrExtensions, prefered_blend_mode: XrPreferdBlendMode, @@ -87,11 +106,57 @@ pub fn initialize_xr_instance( RenderAdapter, Instance, )> { - vulkan::initialize_xr_instance(window, reqeusted_extensions, prefered_blend_mode, app_info) + if backend_preference.is_empty() { + eyre::bail!("Cannot initialize with no backend selected"); + } + let xr_entry = xr_entry()?; + + #[cfg(target_os = "android")] + xr_entry.initialize_android_loader()?; + + let available_extensions: XrExtensions = xr_entry.enumerate_extensions()?.into(); + + for backend in backend_preference { + match backend { + #[cfg(feature = "vulkan")] + Backend::Vulkan => { + if !available_extensions.raw().khr_vulkan_enable2 { + continue; + } + return vulkan::initialize_xr_instance( + window, + xr_entry, + reqeusted_extensions, + available_extensions, + prefered_blend_mode, + app_info, + ); + } + #[cfg(all(feature = "d3d12", windows))] + Backend::D3D12 => { + if !available_extensions.raw().khr_d3d12_enable { + continue; + } + return d3d12::initialize_xr_instance( + window, + xr_entry, + reqeusted_extensions, + available_extensions, + prefered_blend_mode, + app_info, + ); + } + } + } + eyre::bail!( + "No selected backend was supported by the runtime. Selected: {:?}", + backend_preference + ); } pub fn try_full_init( world: &mut World, + backend_preference: &[Backend], reqeusted_extensions: XrExtensions, prefered_blend_mode: XrPreferdBlendMode, app_info: XrAppInfo, @@ -115,6 +180,7 @@ pub fn try_full_init( render_adapter, wgpu_instance, ) = initialize_xr_instance( + backend_preference, primary_window.clone(), reqeusted_extensions, prefered_blend_mode, diff --git a/src/graphics/vulkan.rs b/src/graphics/vulkan.rs index b53ed02..441cc1d 100644 --- a/src/graphics/vulkan.rs +++ b/src/graphics/vulkan.rs @@ -28,7 +28,9 @@ use super::{XrAppInfo, XrPreferdBlendMode}; pub fn initialize_xr_instance( window: Option, + xr_entry: xr::Entry, reqeusted_extensions: XrExtensions, + available_extensions: XrExtensions, prefered_blend_mode: XrPreferdBlendMode, app_info: XrAppInfo, ) -> eyre::Result<( @@ -41,14 +43,11 @@ pub fn initialize_xr_instance( RenderAdapter, Instance, )> { - let xr_entry = super::xr_entry()?; - #[cfg(target_os = "android")] xr_entry.initialize_android_loader()?; - let available_extensions: XrExtensions = xr_entry.enumerate_extensions()?.into(); assert!(available_extensions.raw().khr_vulkan_enable2); - //info!("available xr exts: {:#?}", available_extensions); + // info!("available OpenXR extensions: {:#?}", available_extensions); let mut enabled_extensions: xr::ExtensionSet = (available_extensions & reqeusted_extensions).into(); @@ -59,7 +58,7 @@ pub fn initialize_xr_instance( } let available_layers = xr_entry.enumerate_layers()?; - //info!("available xr layers: {:#?}", available_layers); + // info!("available OpenXR layers: {:#?}", available_layers); let xr_instance = xr_entry.create_instance( &xr::ApplicationInfo { @@ -70,10 +69,10 @@ pub fn initialize_xr_instance( &enabled_extensions, &[], )?; - info!("created instance"); + info!("created OpenXR instance"); let instance_props = xr_instance.properties()?; let xr_system_id = xr_instance.system(xr::FormFactor::HEAD_MOUNTED_DISPLAY)?; - info!("created system"); + info!("created OpenXR system"); let system_props = xr_instance.system_properties(xr_system_id).unwrap(); info!( "loaded OpenXR runtime: {} {} {}", @@ -123,7 +122,7 @@ pub fn initialize_xr_instance( || vk_target_version_xr.major() > reqs.max_api_version_supported.major() { panic!( - "OpenXR runtime requires Vulkan version > {}, < {}.0.0", + "OpenXR runtime requires Vulkan version >= {}, < {}.0.0", reqs.min_api_version_supported, reqs.max_api_version_supported.major() + 1 ); @@ -139,7 +138,7 @@ pub fn initialize_xr_instance( ash::extensions::khr::TimelineSemaphore::name(), ]; info!( - "creating vulkan instance with these extensions: {:#?}", + "creating Vulkan instance with these extensions: {:#?}", extensions ); @@ -163,7 +162,7 @@ pub fn initialize_xr_instance( .enabled_extension_names(&extensions_cchar) as *const _ as *const _, ) - .context("XR error creating Vulkan instance") + .context("OpenXR error creating Vulkan instance") .unwrap() .map_err(vk::Result::from_raw) .context("Vulkan error creating Vulkan instance") @@ -174,7 +173,7 @@ pub fn initialize_xr_instance( vk::Instance::from_raw(vk_instance as _), ) }; - info!("created vulkan instance"); + info!("created Vulkan instance"); let vk_instance_ptr = vk_instance.handle().as_raw() as *const c_void; @@ -247,7 +246,7 @@ pub fn initialize_xr_instance( vk_physical_device.as_raw() as _, &info as *const _ as *const _, ) - .context("XR error creating Vulkan device")? + .context("OpenXR error creating Vulkan device")? .map_err(vk::Result::from_raw) .context("Vulkan error creating Vulkan device")?; @@ -401,7 +400,7 @@ pub fn start_xr_session( ::Device::texture_from_raw( color_image, &wgpu_hal::TextureDescriptor { - label: Some("VR Swapchain"), + label: Some("bevy_openxr swapchain"), // unused internally size: wgpu::Extent3d { width: resolution.x, height: resolution.y, @@ -423,7 +422,7 @@ pub fn start_xr_session( wgpu_device.create_texture_from_hal::( wgpu_hal_texture, &wgpu::TextureDescriptor { - label: Some("VR Swapchain"), + label: Some("bevy_openxr swapchain"), size: wgpu::Extent3d { width: resolution.x, height: resolution.y, @@ -470,67 +469,136 @@ pub fn start_xr_session( } fn wgpu_to_vulkan(format: wgpu::TextureFormat) -> vk::Format { - use vk::Format; + // Copied with minor modification from: + // https://github.com/gfx-rs/wgpu/blob/v0.19/wgpu-hal/src/vulkan/conv.rs#L5C1-L153 + // license: MIT OR Apache-2.0 + use ash::vk::Format as F; + use wgpu::TextureFormat as Tf; + use wgpu::{AstcBlock, AstcChannel}; match format { - wgpu::TextureFormat::R8Unorm => Format::R8_UNORM, - wgpu::TextureFormat::R8Snorm => Format::R8_SNORM, - wgpu::TextureFormat::R8Uint => Format::R8_UINT, - wgpu::TextureFormat::R8Sint => Format::R8_SINT, - wgpu::TextureFormat::R16Uint => Format::R16_UINT, - wgpu::TextureFormat::R16Sint => Format::R16_SINT, - wgpu::TextureFormat::R16Unorm => Format::R16_UNORM, - wgpu::TextureFormat::R16Snorm => Format::R16_SNORM, - wgpu::TextureFormat::R16Float => Format::R16_SFLOAT, - wgpu::TextureFormat::Rg8Unorm => Format::R8G8_UNORM, - wgpu::TextureFormat::Rg8Snorm => Format::R8G8_SNORM, - wgpu::TextureFormat::Rg8Uint => Format::R8G8_UINT, - wgpu::TextureFormat::Rg8Sint => Format::R8G8_SINT, - wgpu::TextureFormat::R32Uint => Format::R32_UINT, - wgpu::TextureFormat::R32Sint => Format::R32_SINT, - wgpu::TextureFormat::R32Float => Format::R32_SFLOAT, - wgpu::TextureFormat::Rg16Uint => Format::R16G16_UINT, - wgpu::TextureFormat::Rg16Sint => Format::R16G16_SINT, - wgpu::TextureFormat::Rg16Unorm => Format::R16G16_UNORM, - wgpu::TextureFormat::Rg16Snorm => Format::R16G16_SNORM, - wgpu::TextureFormat::Rg16Float => Format::R16G16_SFLOAT, - wgpu::TextureFormat::Rgba8Unorm => Format::R8G8B8A8_UNORM, - wgpu::TextureFormat::Rgba8UnormSrgb => Format::R8G8B8A8_SRGB, - wgpu::TextureFormat::Rgba8Snorm => Format::R8G8B8A8_SNORM, - wgpu::TextureFormat::Rgba8Uint => Format::R8G8B8A8_UINT, - wgpu::TextureFormat::Rgba8Sint => Format::R8G8B8A8_SINT, - wgpu::TextureFormat::Bgra8Unorm => Format::B8G8R8A8_UNORM, - wgpu::TextureFormat::Bgra8UnormSrgb => Format::B8G8R8A8_SRGB, - wgpu::TextureFormat::Rgb9e5Ufloat => Format::E5B9G9R9_UFLOAT_PACK32, // this might be the wrong type??? i can't tell - wgpu::TextureFormat::Rgb10a2Unorm => Format::A2R10G10B10_UNORM_PACK32, - wgpu::TextureFormat::Rg11b10Float => panic!("this texture type invokes nothing but fear within my soul and i don't think vulkan has a proper type for this"), - wgpu::TextureFormat::Rg32Uint => Format::R32G32_UINT, - wgpu::TextureFormat::Rg32Sint => Format::R32G32_SINT, - wgpu::TextureFormat::Rg32Float => Format::R32G32_SFLOAT, - wgpu::TextureFormat::Rgba16Uint => Format::R16G16B16A16_UINT, - wgpu::TextureFormat::Rgba16Sint => Format::R16G16B16A16_SINT, - wgpu::TextureFormat::Rgba16Unorm => Format::R16G16B16A16_UNORM, - wgpu::TextureFormat::Rgba16Snorm => Format::R16G16B16A16_SNORM, - wgpu::TextureFormat::Rgba16Float => Format::R16G16B16A16_SFLOAT, - wgpu::TextureFormat::Rgba32Uint => Format::R32G32B32A32_UINT, - wgpu::TextureFormat::Rgba32Sint => Format::R32G32B32A32_SINT, - wgpu::TextureFormat::Rgba32Float => Format::R32G32B32A32_SFLOAT, - wgpu::TextureFormat::Stencil8 => Format::S8_UINT, - wgpu::TextureFormat::Depth16Unorm => Format::D16_UNORM, - wgpu::TextureFormat::Depth24Plus => Format::X8_D24_UNORM_PACK32, - wgpu::TextureFormat::Depth24PlusStencil8 => Format::D24_UNORM_S8_UINT, - wgpu::TextureFormat::Depth32Float => Format::D32_SFLOAT, - wgpu::TextureFormat::Depth32FloatStencil8 => Format::D32_SFLOAT_S8_UINT, - wgpu::TextureFormat::Etc2Rgb8Unorm => Format::ETC2_R8G8B8_UNORM_BLOCK, - wgpu::TextureFormat::Etc2Rgb8UnormSrgb => Format::ETC2_R8G8B8_SRGB_BLOCK, - wgpu::TextureFormat::Etc2Rgb8A1Unorm => Format::ETC2_R8G8B8A1_UNORM_BLOCK, - wgpu::TextureFormat::Etc2Rgb8A1UnormSrgb => Format::ETC2_R8G8B8A1_SRGB_BLOCK, - wgpu::TextureFormat::Etc2Rgba8Unorm => Format::ETC2_R8G8B8A8_UNORM_BLOCK, - wgpu::TextureFormat::Etc2Rgba8UnormSrgb => Format::ETC2_R8G8B8A8_SRGB_BLOCK, - wgpu::TextureFormat::EacR11Unorm => Format::EAC_R11_UNORM_BLOCK, - wgpu::TextureFormat::EacR11Snorm => Format::EAC_R11_SNORM_BLOCK, - wgpu::TextureFormat::EacRg11Unorm => Format::EAC_R11G11_UNORM_BLOCK, - wgpu::TextureFormat::EacRg11Snorm => Format::EAC_R11G11_SNORM_BLOCK, - wgpu::TextureFormat::Astc { .. } => panic!("please god kill me now"), - _ => panic!("fuck no") + Tf::R8Unorm => F::R8_UNORM, + Tf::R8Snorm => F::R8_SNORM, + Tf::R8Uint => F::R8_UINT, + Tf::R8Sint => F::R8_SINT, + Tf::R16Uint => F::R16_UINT, + Tf::R16Sint => F::R16_SINT, + Tf::R16Unorm => F::R16_UNORM, + Tf::R16Snorm => F::R16_SNORM, + Tf::R16Float => F::R16_SFLOAT, + Tf::Rg8Unorm => F::R8G8_UNORM, + Tf::Rg8Snorm => F::R8G8_SNORM, + Tf::Rg8Uint => F::R8G8_UINT, + Tf::Rg8Sint => F::R8G8_SINT, + Tf::Rg16Unorm => F::R16G16_UNORM, + Tf::Rg16Snorm => F::R16G16_SNORM, + Tf::R32Uint => F::R32_UINT, + Tf::R32Sint => F::R32_SINT, + Tf::R32Float => F::R32_SFLOAT, + Tf::Rg16Uint => F::R16G16_UINT, + Tf::Rg16Sint => F::R16G16_SINT, + Tf::Rg16Float => F::R16G16_SFLOAT, + Tf::Rgba8Unorm => F::R8G8B8A8_UNORM, + Tf::Rgba8UnormSrgb => F::R8G8B8A8_SRGB, + Tf::Bgra8UnormSrgb => F::B8G8R8A8_SRGB, + Tf::Rgba8Snorm => F::R8G8B8A8_SNORM, + Tf::Bgra8Unorm => F::B8G8R8A8_UNORM, + Tf::Rgba8Uint => F::R8G8B8A8_UINT, + Tf::Rgba8Sint => F::R8G8B8A8_SINT, + Tf::Rgb10a2Uint => F::A2B10G10R10_UINT_PACK32, + Tf::Rgb10a2Unorm => F::A2B10G10R10_UNORM_PACK32, + Tf::Rg11b10Float => F::B10G11R11_UFLOAT_PACK32, + Tf::Rg32Uint => F::R32G32_UINT, + Tf::Rg32Sint => F::R32G32_SINT, + Tf::Rg32Float => F::R32G32_SFLOAT, + Tf::Rgba16Uint => F::R16G16B16A16_UINT, + Tf::Rgba16Sint => F::R16G16B16A16_SINT, + Tf::Rgba16Unorm => F::R16G16B16A16_UNORM, + Tf::Rgba16Snorm => F::R16G16B16A16_SNORM, + Tf::Rgba16Float => F::R16G16B16A16_SFLOAT, + Tf::Rgba32Uint => F::R32G32B32A32_UINT, + Tf::Rgba32Sint => F::R32G32B32A32_SINT, + Tf::Rgba32Float => F::R32G32B32A32_SFLOAT, + Tf::Depth32Float => F::D32_SFLOAT, + Tf::Depth32FloatStencil8 => F::D32_SFLOAT_S8_UINT, + Tf::Depth24Plus | Tf::Depth24PlusStencil8 | Tf::Stencil8 => { + panic!("Cannot convert format that is dependent on device properties") + } + Tf::Depth16Unorm => F::D16_UNORM, + Tf::NV12 => F::G8_B8R8_2PLANE_420_UNORM, + Tf::Rgb9e5Ufloat => F::E5B9G9R9_UFLOAT_PACK32, + Tf::Bc1RgbaUnorm => F::BC1_RGBA_UNORM_BLOCK, + Tf::Bc1RgbaUnormSrgb => F::BC1_RGBA_SRGB_BLOCK, + Tf::Bc2RgbaUnorm => F::BC2_UNORM_BLOCK, + Tf::Bc2RgbaUnormSrgb => F::BC2_SRGB_BLOCK, + Tf::Bc3RgbaUnorm => F::BC3_UNORM_BLOCK, + Tf::Bc3RgbaUnormSrgb => F::BC3_SRGB_BLOCK, + Tf::Bc4RUnorm => F::BC4_UNORM_BLOCK, + Tf::Bc4RSnorm => F::BC4_SNORM_BLOCK, + Tf::Bc5RgUnorm => F::BC5_UNORM_BLOCK, + Tf::Bc5RgSnorm => F::BC5_SNORM_BLOCK, + Tf::Bc6hRgbUfloat => F::BC6H_UFLOAT_BLOCK, + Tf::Bc6hRgbFloat => F::BC6H_SFLOAT_BLOCK, + Tf::Bc7RgbaUnorm => F::BC7_UNORM_BLOCK, + Tf::Bc7RgbaUnormSrgb => F::BC7_SRGB_BLOCK, + Tf::Etc2Rgb8Unorm => F::ETC2_R8G8B8_UNORM_BLOCK, + Tf::Etc2Rgb8UnormSrgb => F::ETC2_R8G8B8_SRGB_BLOCK, + Tf::Etc2Rgb8A1Unorm => F::ETC2_R8G8B8A1_UNORM_BLOCK, + Tf::Etc2Rgb8A1UnormSrgb => F::ETC2_R8G8B8A1_SRGB_BLOCK, + Tf::Etc2Rgba8Unorm => F::ETC2_R8G8B8A8_UNORM_BLOCK, + Tf::Etc2Rgba8UnormSrgb => F::ETC2_R8G8B8A8_SRGB_BLOCK, + Tf::EacR11Unorm => F::EAC_R11_UNORM_BLOCK, + Tf::EacR11Snorm => F::EAC_R11_SNORM_BLOCK, + Tf::EacRg11Unorm => F::EAC_R11G11_UNORM_BLOCK, + Tf::EacRg11Snorm => F::EAC_R11G11_SNORM_BLOCK, + Tf::Astc { block, channel } => match channel { + AstcChannel::Unorm => match block { + AstcBlock::B4x4 => F::ASTC_4X4_UNORM_BLOCK, + AstcBlock::B5x4 => F::ASTC_5X4_UNORM_BLOCK, + AstcBlock::B5x5 => F::ASTC_5X5_UNORM_BLOCK, + AstcBlock::B6x5 => F::ASTC_6X5_UNORM_BLOCK, + AstcBlock::B6x6 => F::ASTC_6X6_UNORM_BLOCK, + AstcBlock::B8x5 => F::ASTC_8X5_UNORM_BLOCK, + AstcBlock::B8x6 => F::ASTC_8X6_UNORM_BLOCK, + AstcBlock::B8x8 => F::ASTC_8X8_UNORM_BLOCK, + AstcBlock::B10x5 => F::ASTC_10X5_UNORM_BLOCK, + AstcBlock::B10x6 => F::ASTC_10X6_UNORM_BLOCK, + AstcBlock::B10x8 => F::ASTC_10X8_UNORM_BLOCK, + AstcBlock::B10x10 => F::ASTC_10X10_UNORM_BLOCK, + AstcBlock::B12x10 => F::ASTC_12X10_UNORM_BLOCK, + AstcBlock::B12x12 => F::ASTC_12X12_UNORM_BLOCK, + }, + AstcChannel::UnormSrgb => match block { + AstcBlock::B4x4 => F::ASTC_4X4_SRGB_BLOCK, + AstcBlock::B5x4 => F::ASTC_5X4_SRGB_BLOCK, + AstcBlock::B5x5 => F::ASTC_5X5_SRGB_BLOCK, + AstcBlock::B6x5 => F::ASTC_6X5_SRGB_BLOCK, + AstcBlock::B6x6 => F::ASTC_6X6_SRGB_BLOCK, + AstcBlock::B8x5 => F::ASTC_8X5_SRGB_BLOCK, + AstcBlock::B8x6 => F::ASTC_8X6_SRGB_BLOCK, + AstcBlock::B8x8 => F::ASTC_8X8_SRGB_BLOCK, + AstcBlock::B10x5 => F::ASTC_10X5_SRGB_BLOCK, + AstcBlock::B10x6 => F::ASTC_10X6_SRGB_BLOCK, + AstcBlock::B10x8 => F::ASTC_10X8_SRGB_BLOCK, + AstcBlock::B10x10 => F::ASTC_10X10_SRGB_BLOCK, + AstcBlock::B12x10 => F::ASTC_12X10_SRGB_BLOCK, + AstcBlock::B12x12 => F::ASTC_12X12_SRGB_BLOCK, + }, + AstcChannel::Hdr => match block { + AstcBlock::B4x4 => F::ASTC_4X4_SFLOAT_BLOCK_EXT, + AstcBlock::B5x4 => F::ASTC_5X4_SFLOAT_BLOCK_EXT, + AstcBlock::B5x5 => F::ASTC_5X5_SFLOAT_BLOCK_EXT, + AstcBlock::B6x5 => F::ASTC_6X5_SFLOAT_BLOCK_EXT, + AstcBlock::B6x6 => F::ASTC_6X6_SFLOAT_BLOCK_EXT, + AstcBlock::B8x5 => F::ASTC_8X5_SFLOAT_BLOCK_EXT, + AstcBlock::B8x6 => F::ASTC_8X6_SFLOAT_BLOCK_EXT, + AstcBlock::B8x8 => F::ASTC_8X8_SFLOAT_BLOCK_EXT, + AstcBlock::B10x5 => F::ASTC_10X5_SFLOAT_BLOCK_EXT, + AstcBlock::B10x6 => F::ASTC_10X6_SFLOAT_BLOCK_EXT, + AstcBlock::B10x8 => F::ASTC_10X8_SFLOAT_BLOCK_EXT, + AstcBlock::B10x10 => F::ASTC_10X10_SFLOAT_BLOCK_EXT, + AstcBlock::B12x10 => F::ASTC_12X10_SFLOAT_BLOCK_EXT, + AstcBlock::B12x12 => F::ASTC_12X12_SFLOAT_BLOCK_EXT, + }, + }, } } diff --git a/src/lib.rs b/src/lib.rs index ca7f0a2..e35d09e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,6 +47,7 @@ pub const RIGHT_XR_TEXTURE_HANDLE: ManualTextureViewHandle = ManualTextureViewHa /// Adds OpenXR support to an App pub struct OpenXrPlugin { + pub backend_preference: Vec, pub reqeusted_extensions: XrExtensions, pub prefered_blend_mode: XrPreferdBlendMode, pub app_info: XrAppInfo, @@ -59,6 +60,7 @@ impl Plugin for OpenXrPlugin { app.insert_resource(ExitAppOnSessionExit::default()); #[cfg(not(target_arch = "wasm32"))] match graphics::initialize_xr_instance( + &self.backend_preference, SystemState::>>::new(&mut app.world) .get(&app.world) .get_single() @@ -169,6 +171,17 @@ impl Plugin for OpenXrPlugin { } } +#[cfg(all(not(feature = "vulkan"), not(all(feature = "d3d12", windows))))] +compile_error!("At least one platform-compatible backend feature must be enabled."); + +#[derive(Debug)] +pub enum Backend { + #[cfg(feature = "vulkan")] + Vulkan, + #[cfg(all(feature = "d3d12", windows))] + D3D12, +} + #[derive(Resource)] struct DoPipelinedRendering; @@ -213,21 +226,33 @@ fn xr_skip_frame( ) { let swapchain: &Swapchain = &xr_swapchain; match swapchain { - Swapchain::Vulkan(swap) => { - swap.stream - .lock() - .unwrap() - .end( - xr_frame_state.predicted_display_time, - **environment_blend_mode, - &[], - ) - .unwrap(); - } - } + #[cfg(feature = "vulkan")] + Swapchain::Vulkan(swap) => &swap + .stream + .lock() + .unwrap() + .end( + xr_frame_state.predicted_display_time, + **environment_blend_mode, + &[], + ) + .unwrap(), + #[cfg(all(feature = "d3d12", windows))] + Swapchain::D3D12(swap) => &swap + .stream + .lock() + .unwrap() + .end( + xr_frame_state.predicted_display_time, + **environment_blend_mode, + &[], + ) + .unwrap(), + }; } pub struct DefaultXrPlugins { + pub backend_preference: Vec, pub reqeusted_extensions: XrExtensions, pub prefered_blend_mode: XrPreferdBlendMode, pub app_info: XrAppInfo, @@ -236,6 +261,12 @@ pub struct DefaultXrPlugins { impl Default for DefaultXrPlugins { fn default() -> Self { Self { + backend_preference: vec![ + #[cfg(feature = "vulkan")] + Backend::Vulkan, + #[cfg(all(feature = "d3d12", windows))] + Backend::D3D12, + ], reqeusted_extensions: default(), prefered_blend_mode: default(), app_info: default(), @@ -261,6 +292,7 @@ impl PluginGroup for DefaultXrPlugins { }) .disable::() .add_before::(OpenXrPlugin { + backend_preference: self.backend_preference, prefered_blend_mode: self.prefered_blend_mode, reqeusted_extensions: self.reqeusted_extensions, app_info: self.app_info.clone(), diff --git a/src/passthrough.rs b/src/passthrough.rs index e6eb1e5..c6d67c8 100644 --- a/src/passthrough.rs +++ b/src/passthrough.rs @@ -195,17 +195,25 @@ pub fn supports_passthrough(instance: &XrInstance, system: xr::SystemId) -> xr:: pub fn create_passthrough( xr_session: &XrSession, ) -> xr::Result<(xr::Passthrough, xr::PassthroughLayer)> { + let flags = xr::PassthroughFlagsFB::IS_RUNNING_AT_CREATION; + let purpose = xr::PassthroughLayerPurposeFB::RECONSTRUCTION; let passthrough = match xr_session { + #[cfg(feature = "vulkan")] XrSession::Vulkan(session) => { session.create_passthrough(xr::PassthroughFlagsFB::IS_RUNNING_AT_CREATION) } + #[cfg(all(feature = "d3d12", windows))] + XrSession::D3D12(session) => { + session.create_passthrough(xr::PassthroughFlagsFB::IS_RUNNING_AT_CREATION) + } }?; let passthrough_layer = match xr_session { - XrSession::Vulkan(session) => session.create_passthrough_layer( - &passthrough, - xr::PassthroughFlagsFB::IS_RUNNING_AT_CREATION, - xr::PassthroughLayerPurposeFB::RECONSTRUCTION, - ), + #[cfg(feature = "vulkan")] + XrSession::Vulkan(session) => { + session.create_passthrough_layer(&passthrough, flags, purpose) + } + #[cfg(all(feature = "d3d12", windows))] + XrSession::D3D12(session) => session.create_passthrough_layer(&passthrough, flags, purpose), }?; Ok((passthrough, passthrough_layer)) } diff --git a/src/resources.rs b/src/resources.rs index 1dd64ad..1f1e2f9 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -14,6 +14,8 @@ use bevy::render::extract_component::ExtractComponent; use bevy::render::extract_resource::{ExtractResource, ExtractResourcePlugin}; use core::ptr; use openxr as xr; +#[cfg(all(feature = "d3d12", windows))] +use winapi::um::d3d12::{ID3D12CommandQueue, ID3D12Device}; xr_resource_wrapper!(XrInstance, xr::Instance); xr_resource_wrapper_copy!(XrEnvironmentBlendMode, xr::EnvironmentBlendMode); @@ -27,7 +29,10 @@ xr_no_clone_resource_wrapper!(XrFrameWaiter, xr::FrameWaiter); #[derive(Clone, Resource, ExtractResource)] pub enum XrSession { + #[cfg(feature = "vulkan")] Vulkan(xr::Session), + #[cfg(all(feature = "d3d12", windows))] + D3D12(xr::Session), } impl std::ops::Deref for XrSession { @@ -37,12 +42,16 @@ impl std::ops::Deref for XrSession { // SAFTEY: should be fine i think -Schmarni unsafe { match self { + #[cfg(feature = "vulkan")] XrSession::Vulkan(sess) => std::mem::transmute(sess), + #[cfg(all(feature = "d3d12", windows))] + XrSession::D3D12(sess) => std::mem::transmute(sess), } } } } +#[cfg(feature = "vulkan")] pub struct VulkanOXrSessionSetupInfo { pub(crate) device_ptr: *const c_void, pub(crate) physical_device_ptr: *const c_void, @@ -51,8 +60,18 @@ pub struct VulkanOXrSessionSetupInfo { pub(crate) xr_system_id: xr::SystemId, } +#[cfg(all(feature = "d3d12", windows))] +pub struct D3D12OXrSessionSetupInfo { + pub(crate) raw_device: *mut ID3D12Device, + pub(crate) raw_queue: *mut ID3D12CommandQueue, + pub(crate) xr_system_id: xr::SystemId, +} + pub enum OXrSessionSetupInfo { + #[cfg(feature = "vulkan")] Vulkan(VulkanOXrSessionSetupInfo), + #[cfg(all(feature = "d3d12", windows))] + D3D12(D3D12OXrSessionSetupInfo), } pub struct XrResourcePlugin; @@ -72,37 +91,55 @@ impl Plugin for XrResourcePlugin { } pub enum Swapchain { + #[cfg(feature = "vulkan")] Vulkan(SwapchainInner), + #[cfg(all(feature = "d3d12", windows))] + D3D12(SwapchainInner), } impl Swapchain { pub(crate) fn begin(&self) -> xr::Result<()> { match self { + #[cfg(feature = "vulkan")] Swapchain::Vulkan(swapchain) => swapchain.begin(), + #[cfg(all(feature = "d3d12", windows))] + Swapchain::D3D12(swapchain) => swapchain.begin(), } } pub(crate) fn get_render_views(&self) -> (wgpu::TextureView, wgpu::TextureView) { match self { + #[cfg(feature = "vulkan")] Swapchain::Vulkan(swapchain) => swapchain.get_render_views(), + #[cfg(all(feature = "d3d12", windows))] + Swapchain::D3D12(swapchain) => swapchain.get_render_views(), } } pub(crate) fn acquire_image(&self) -> xr::Result<()> { match self { + #[cfg(feature = "vulkan")] Swapchain::Vulkan(swapchain) => swapchain.acquire_image(), + #[cfg(all(feature = "d3d12", windows))] + Swapchain::D3D12(swapchain) => swapchain.acquire_image(), } } pub(crate) fn wait_image(&self) -> xr::Result<()> { match self { + #[cfg(feature = "vulkan")] Swapchain::Vulkan(swapchain) => swapchain.wait_image(), + #[cfg(all(feature = "d3d12", windows))] + Swapchain::D3D12(swapchain) => swapchain.wait_image(), } } pub(crate) fn release_image(&self) -> xr::Result<()> { match self { + #[cfg(feature = "vulkan")] Swapchain::Vulkan(swapchain) => swapchain.release_image(), + #[cfg(all(feature = "d3d12", windows))] + Swapchain::D3D12(swapchain) => swapchain.release_image(), } } @@ -116,6 +153,7 @@ impl Swapchain { passthrough_layer: Option<&XrPassthroughLayer>, ) -> xr::Result<()> { match self { + #[cfg(feature = "vulkan")] Swapchain::Vulkan(swapchain) => swapchain.end( predicted_display_time, views, @@ -124,6 +162,15 @@ impl Swapchain { environment_blend_mode, passthrough_layer, ), + #[cfg(all(feature = "d3d12", windows))] + Swapchain::D3D12(swapchain) => swapchain.end( + predicted_display_time, + views, + stage, + resolution, + environment_blend_mode, + passthrough_layer, + ), } } }