maybe got xr working?
This commit is contained in:
@@ -7,6 +7,7 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.75"
|
anyhow = "1.0.75"
|
||||||
|
ash = "0.37.3"
|
||||||
bevy = { git = "https://github.com/awtterpip/bevy", default-features = false, features = ["bevy_render"] }
|
bevy = { git = "https://github.com/awtterpip/bevy", default-features = false, features = ["bevy_render"] }
|
||||||
openxr = "0.17.1"
|
openxr = "0.17.1"
|
||||||
wgpu = "0.16.0"
|
wgpu = "0.16.0"
|
||||||
@@ -18,4 +19,4 @@ bevy = { git = "https://github.com/awtterpip/bevy" }
|
|||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "xr"
|
name = "xr"
|
||||||
path = "examples/xr.rs"
|
path = "examples/xr.rs"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! A simple 3D scene with light shining over a cube sitting on a plane.
|
//! A simple 3D scene with light shining over a cube sitting on a plane.
|
||||||
use bevy_openxr::DefaultXrPlugins;
|
use bevy_openxr::{DefaultXrPlugins, LEFT_XR_TEXTURE_HANDLE, RIGHT_XR_TEXTURE_HANDLE};
|
||||||
use bevy::prelude::*;
|
use bevy::{prelude::*, render::camera::RenderTarget};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
@@ -43,4 +43,23 @@ fn setup(
|
|||||||
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
..default()
|
..default()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
commands.spawn(Camera3dBundle {
|
||||||
|
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
|
camera: Camera {
|
||||||
|
order: -1,
|
||||||
|
target: RenderTarget::TextureView(LEFT_XR_TEXTURE_HANDLE),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
commands.spawn(Camera3dBundle {
|
||||||
|
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
|
camera: Camera {
|
||||||
|
order: -1,
|
||||||
|
target: RenderTarget::TextureView(RIGHT_XR_TEXTURE_HANDLE),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
..default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/graphics/mod.rs
Normal file
12
src/graphics/mod.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
mod vulkan;
|
||||||
|
|
||||||
|
use bevy::render::renderer::{RenderDevice, RenderQueue, RenderAdapterInfo, RenderAdapter};
|
||||||
|
use bevy::window::RawHandleWrapper;
|
||||||
|
use wgpu::Instance;
|
||||||
|
|
||||||
|
use crate::input::XrInput;
|
||||||
|
use crate::resources::{XrInstance, XrSession, XrEnvironmentBlendMode, XrSessionRunning, XrFrameWaiter, XrSwapchain, XrViews};
|
||||||
|
|
||||||
|
pub fn initialize_xr_graphics(window: Option<RawHandleWrapper>) -> anyhow::Result<(RenderDevice, RenderQueue, RenderAdapterInfo, RenderAdapter, Instance, XrInstance, XrSession, XrEnvironmentBlendMode, XrSessionRunning, XrFrameWaiter, XrSwapchain, XrInput, XrViews)>{
|
||||||
|
vulkan::initialize_xr_graphics(window)
|
||||||
|
}
|
||||||
441
src/graphics/vulkan.rs
Normal file
441
src/graphics/vulkan.rs
Normal file
@@ -0,0 +1,441 @@
|
|||||||
|
use std::ffi::{c_void, CString};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
use ash::vk::{self, Handle};
|
||||||
|
use bevy::math::uvec2;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy::render::renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue};
|
||||||
|
use bevy::window::RawHandleWrapper;
|
||||||
|
use openxr as xr;
|
||||||
|
use wgpu::Instance;
|
||||||
|
|
||||||
|
use crate::input::XrInput;
|
||||||
|
use crate::resources::{
|
||||||
|
XrEnvironmentBlendMode, XrFrameWaiter, XrInstance, XrSession, XrSessionRunning, XrSwapchain, Swapchain, SwapchainInner, XrViews,
|
||||||
|
};
|
||||||
|
use crate::VIEW_TYPE;
|
||||||
|
|
||||||
|
pub fn initialize_xr_graphics(window: Option<RawHandleWrapper>) -> anyhow::Result<(
|
||||||
|
RenderDevice,
|
||||||
|
RenderQueue,
|
||||||
|
RenderAdapterInfo,
|
||||||
|
RenderAdapter,
|
||||||
|
Instance,
|
||||||
|
XrInstance,
|
||||||
|
XrSession,
|
||||||
|
XrEnvironmentBlendMode,
|
||||||
|
XrSessionRunning,
|
||||||
|
XrFrameWaiter,
|
||||||
|
XrSwapchain,
|
||||||
|
XrInput,
|
||||||
|
XrViews,
|
||||||
|
)> {
|
||||||
|
use wgpu_hal::{api::Vulkan as V, Api};
|
||||||
|
|
||||||
|
let xr_entry = unsafe { xr::Entry::load() }?;
|
||||||
|
|
||||||
|
let available_extensions = xr_entry.enumerate_extensions()?;
|
||||||
|
assert!(available_extensions.khr_vulkan_enable2);
|
||||||
|
info!("available xr exts: {:#?}", available_extensions);
|
||||||
|
|
||||||
|
let mut enabled_extensions = xr::ExtensionSet::default();
|
||||||
|
enabled_extensions.khr_vulkan_enable2 = true;
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
{
|
||||||
|
enabled_extensions.khr_android_create_instance = 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: "Ambient",
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
&enabled_extensions,
|
||||||
|
&[],
|
||||||
|
)?;
|
||||||
|
info!("created instance");
|
||||||
|
let instance_props = xr_instance.properties()?;
|
||||||
|
let xr_system_id = xr_instance.system(xr::FormFactor::HEAD_MOUNTED_DISPLAY)?;
|
||||||
|
info!("created 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() {
|
||||||
|
"<unnamed>"
|
||||||
|
} else {
|
||||||
|
&system_props.system_name
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let blend_mode = xr_instance.enumerate_environment_blend_modes(xr_system_id, VIEW_TYPE)?[0];
|
||||||
|
|
||||||
|
let vk_target_version = vk::make_api_version(0, 1, 1, 0);
|
||||||
|
let vk_target_version_xr = xr::Version::new(1, 1, 0);
|
||||||
|
let reqs = xr_instance.graphics_requirements::<xr::Vulkan>(xr_system_id)?;
|
||||||
|
if vk_target_version_xr < reqs.min_api_version_supported
|
||||||
|
|| vk_target_version_xr.major() > reqs.max_api_version_supported.major()
|
||||||
|
{
|
||||||
|
panic!(
|
||||||
|
"OpenXR runtime requires Vulkan version > {}, < {}.0.0",
|
||||||
|
reqs.min_api_version_supported,
|
||||||
|
reqs.max_api_version_supported.major() + 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let vk_entry = unsafe { ash::Entry::load() }?;
|
||||||
|
let flags = wgpu_hal::InstanceFlags::empty();
|
||||||
|
let extensions =
|
||||||
|
<V as Api>::Instance::required_extensions(&vk_entry, vk_target_version, flags)?;
|
||||||
|
let device_extensions = vec![
|
||||||
|
ash::extensions::khr::Swapchain::name(),
|
||||||
|
ash::extensions::khr::DrawIndirectCount::name(),
|
||||||
|
];
|
||||||
|
info!(
|
||||||
|
"creating vulkan instance with these extensions: {:#?}",
|
||||||
|
extensions
|
||||||
|
);
|
||||||
|
|
||||||
|
let vk_instance = unsafe {
|
||||||
|
let extensions_cchar: Vec<_> = extensions.iter().map(|s| s.as_ptr()).collect();
|
||||||
|
|
||||||
|
let app_name = CString::new("Ambient")?;
|
||||||
|
let vk_app_info = vk::ApplicationInfo::builder()
|
||||||
|
.application_name(&app_name)
|
||||||
|
.application_version(1)
|
||||||
|
.engine_name(&app_name)
|
||||||
|
.engine_version(1)
|
||||||
|
.api_version(vk_target_version);
|
||||||
|
|
||||||
|
let vk_instance = xr_instance
|
||||||
|
.create_vulkan_instance(
|
||||||
|
xr_system_id,
|
||||||
|
std::mem::transmute(vk_entry.static_fn().get_instance_proc_addr),
|
||||||
|
&vk::InstanceCreateInfo::builder()
|
||||||
|
.application_info(&vk_app_info)
|
||||||
|
.enabled_extension_names(&extensions_cchar) as *const _
|
||||||
|
as *const _,
|
||||||
|
)
|
||||||
|
.context("XR error creating Vulkan instance")
|
||||||
|
.unwrap()
|
||||||
|
.map_err(vk::Result::from_raw)
|
||||||
|
.context("Vulkan error creating Vulkan instance")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
ash::Instance::load(
|
||||||
|
vk_entry.static_fn(),
|
||||||
|
vk::Instance::from_raw(vk_instance as _),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
info!("created vulkan instance");
|
||||||
|
|
||||||
|
let vk_instance_ptr = vk_instance.handle().as_raw() as *const c_void;
|
||||||
|
|
||||||
|
let vk_physical_device = vk::PhysicalDevice::from_raw(unsafe {
|
||||||
|
xr_instance.vulkan_graphics_device(xr_system_id, vk_instance.handle().as_raw() as _)? as _
|
||||||
|
});
|
||||||
|
let vk_physical_device_ptr = vk_physical_device.as_raw() as *const c_void;
|
||||||
|
|
||||||
|
let vk_device_properties =
|
||||||
|
unsafe { vk_instance.get_physical_device_properties(vk_physical_device) };
|
||||||
|
if vk_device_properties.api_version < vk_target_version {
|
||||||
|
unsafe { vk_instance.destroy_instance(None) }
|
||||||
|
panic!("Vulkan physical device doesn't support version 1.1");
|
||||||
|
}
|
||||||
|
|
||||||
|
let wgpu_vk_instance = unsafe {
|
||||||
|
<V as Api>::Instance::from_raw(
|
||||||
|
vk_entry.clone(),
|
||||||
|
vk_instance.clone(),
|
||||||
|
vk_target_version,
|
||||||
|
0,
|
||||||
|
extensions,
|
||||||
|
flags,
|
||||||
|
false,
|
||||||
|
Some(Box::new(())),
|
||||||
|
)?
|
||||||
|
};
|
||||||
|
|
||||||
|
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_exposed_adapter = wgpu_vk_instance
|
||||||
|
.expose_adapter(vk_physical_device)
|
||||||
|
.context("failed to expose adapter")?;
|
||||||
|
|
||||||
|
let enabled_extensions = wgpu_exposed_adapter
|
||||||
|
.adapter
|
||||||
|
.required_device_extensions(wgpu_features);
|
||||||
|
|
||||||
|
let (wgpu_open_device, vk_device_ptr, queue_family_index) = {
|
||||||
|
let extensions_cchar: Vec<_> = device_extensions.iter().map(|s| s.as_ptr()).collect();
|
||||||
|
let mut enabled_phd_features = wgpu_exposed_adapter
|
||||||
|
.adapter
|
||||||
|
.physical_device_features(&enabled_extensions, wgpu_features);
|
||||||
|
let family_index = 0;
|
||||||
|
let family_info = vk::DeviceQueueCreateInfo::builder()
|
||||||
|
.queue_family_index(family_index)
|
||||||
|
.queue_priorities(&[1.0])
|
||||||
|
.build();
|
||||||
|
let family_infos = [family_info];
|
||||||
|
let info = enabled_phd_features
|
||||||
|
.add_to_device_create_builder(
|
||||||
|
vk::DeviceCreateInfo::builder()
|
||||||
|
.queue_create_infos(&family_infos)
|
||||||
|
.push_next(&mut vk::PhysicalDeviceMultiviewFeatures {
|
||||||
|
multiview: vk::TRUE,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.enabled_extension_names(&extensions_cchar)
|
||||||
|
.build();
|
||||||
|
let vk_device = unsafe {
|
||||||
|
let vk_device = xr_instance
|
||||||
|
.create_vulkan_device(
|
||||||
|
xr_system_id,
|
||||||
|
std::mem::transmute(vk_entry.static_fn().get_instance_proc_addr),
|
||||||
|
vk_physical_device.as_raw() as _,
|
||||||
|
&info as *const _ as *const _,
|
||||||
|
)
|
||||||
|
.context("XR error creating Vulkan device")?
|
||||||
|
.map_err(vk::Result::from_raw)
|
||||||
|
.context("Vulkan error creating Vulkan device")?;
|
||||||
|
|
||||||
|
ash::Device::load(vk_instance.fp_v1_0(), vk::Device::from_raw(vk_device as _))
|
||||||
|
};
|
||||||
|
let vk_device_ptr = vk_device.handle().as_raw() as *const c_void;
|
||||||
|
|
||||||
|
let wgpu_open_device = unsafe {
|
||||||
|
wgpu_exposed_adapter.adapter.device_from_raw(
|
||||||
|
vk_device,
|
||||||
|
true,
|
||||||
|
&enabled_extensions,
|
||||||
|
wgpu_features,
|
||||||
|
family_info.queue_family_index,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
}?;
|
||||||
|
|
||||||
|
(
|
||||||
|
wgpu_open_device,
|
||||||
|
vk_device_ptr,
|
||||||
|
family_info.queue_family_index,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let wgpu_instance =
|
||||||
|
unsafe { wgpu::Instance::from_hal::<wgpu_hal::api::Vulkan>(wgpu_vk_instance) };
|
||||||
|
let wgpu_adapter = unsafe { wgpu_instance.create_adapter_from_hal(wgpu_exposed_adapter) };
|
||||||
|
let (wgpu_device, wgpu_queue) = unsafe {
|
||||||
|
wgpu_adapter.create_device_from_hal(
|
||||||
|
wgpu_open_device,
|
||||||
|
&wgpu::DeviceDescriptor {
|
||||||
|
label: None,
|
||||||
|
features: wgpu_features,
|
||||||
|
limits: wgpu::Limits {
|
||||||
|
max_bind_groups: 8,
|
||||||
|
max_storage_buffer_binding_size: wgpu_adapter
|
||||||
|
.limits()
|
||||||
|
.max_storage_buffer_binding_size,
|
||||||
|
max_push_constant_size: 4,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let (session, frame_wait, frame_stream) = unsafe {
|
||||||
|
xr_instance.create_session::<xr::Vulkan>(
|
||||||
|
xr_system_id,
|
||||||
|
&xr::vulkan::SessionCreateInfo {
|
||||||
|
instance: vk_instance_ptr,
|
||||||
|
physical_device: vk_physical_device_ptr,
|
||||||
|
device: vk_device_ptr,
|
||||||
|
queue_family_index,
|
||||||
|
queue_index: 0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let views = xr_instance.enumerate_view_configuration_views(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);
|
||||||
|
|
||||||
|
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_vulkan(swapchain_format).as_raw() as _,
|
||||||
|
// 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| {
|
||||||
|
let color_image = vk::Image::from_raw(color_image);
|
||||||
|
let wgpu_hal_texture = unsafe {
|
||||||
|
<V as 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: swapchain_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 {
|
||||||
|
wgpu_device.create_texture_from_hal::<V>(
|
||||||
|
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: swapchain_format,
|
||||||
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
|
||||||
|
| wgpu::TextureUsages::COPY_DST,
|
||||||
|
view_formats: &[],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
};
|
||||||
|
texture
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
wgpu_device.into(),
|
||||||
|
RenderQueue(Arc::new(wgpu_queue)),
|
||||||
|
RenderAdapterInfo(wgpu_adapter.get_info()),
|
||||||
|
RenderAdapter(Arc::new(wgpu_adapter)),
|
||||||
|
wgpu_instance,
|
||||||
|
xr_instance.clone().into(),
|
||||||
|
session.clone().into_any_graphics().into(),
|
||||||
|
blend_mode.into(),
|
||||||
|
AtomicBool::new(false).into(),
|
||||||
|
Mutex::new(frame_wait).into(),
|
||||||
|
Mutex::new(Swapchain::Vulkan(SwapchainInner {
|
||||||
|
stream: frame_stream,
|
||||||
|
handle,
|
||||||
|
resolution,
|
||||||
|
buffers,
|
||||||
|
})).into(),
|
||||||
|
XrInput::new(xr_instance, session.into_any_graphics())?,
|
||||||
|
Mutex::default().into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wgpu_to_vulkan(format: wgpu::TextureFormat) -> vk::Format {
|
||||||
|
use vk::Format;
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/input.rs
16
src/input.rs
@@ -1,16 +1,18 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use openxr as xr;
|
use openxr as xr;
|
||||||
|
|
||||||
type XrPose = (Vec3, Quat);
|
type XrPose = (Vec3, Quat);
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Clone, Resource)]
|
||||||
pub struct XrInput {
|
pub struct XrInput {
|
||||||
action_set: xr::ActionSet,
|
action_set: xr::ActionSet,
|
||||||
right_action: xr::Action<xr::Posef>,
|
right_action: xr::Action<xr::Posef>,
|
||||||
left_action: xr::Action<xr::Posef>,
|
left_action: xr::Action<xr::Posef>,
|
||||||
right_space: xr::Space,
|
right_space: Arc<xr::Space>,
|
||||||
left_space: xr::Space,
|
left_space: Arc<xr::Space>,
|
||||||
stage: xr::Space,
|
pub stage: Arc<xr::Space>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XrInput {
|
impl XrInput {
|
||||||
@@ -47,9 +49,9 @@ impl XrInput {
|
|||||||
action_set,
|
action_set,
|
||||||
right_action,
|
right_action,
|
||||||
left_action,
|
left_action,
|
||||||
right_space,
|
right_space: Arc::new(right_space),
|
||||||
left_space,
|
left_space: Arc::new(left_space),
|
||||||
stage,
|
stage: Arc::new(stage),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
144
src/lib.rs
144
src/lib.rs
@@ -1,16 +1,23 @@
|
|||||||
pub mod input;
|
pub mod input;
|
||||||
pub mod resource_macros;
|
pub mod resource_macros;
|
||||||
pub mod resources;
|
pub mod resources;
|
||||||
|
mod graphics;
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use bevy::ecs::system::SystemState;
|
use bevy::ecs::system::SystemState;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::render::settings::WgpuSettings;
|
use bevy::render::camera::{ManualTextureViews, ManualTextureView, ManualTextureViewHandle};
|
||||||
use bevy::render::{FutureRendererResources, RenderPlugin, renderer};
|
use bevy::render::{FutureRendererResources, RenderPlugin, RenderApp, Render, RenderSet};
|
||||||
use bevy::window::{PrimaryWindow, RawHandleWrapper};
|
use bevy::window::{PrimaryWindow, RawHandleWrapper};
|
||||||
use input::XrInput;
|
use input::XrInput;
|
||||||
use resources::*;
|
use resources::*;
|
||||||
|
use openxr as xr;
|
||||||
|
|
||||||
|
const VIEW_TYPE: xr::ViewConfigurationType = xr::ViewConfigurationType::PRIMARY_STEREO;
|
||||||
|
|
||||||
|
pub const LEFT_XR_TEXTURE_HANDLE: ManualTextureViewHandle = ManualTextureViewHandle(1208214591);
|
||||||
|
pub const RIGHT_XR_TEXTURE_HANDLE: ManualTextureViewHandle = ManualTextureViewHandle(3383858418);
|
||||||
|
|
||||||
/// Adds OpenXR support to an App
|
/// Adds OpenXR support to an App
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -28,6 +35,7 @@ pub struct FutureXrResources(
|
|||||||
XrFrameWaiter,
|
XrFrameWaiter,
|
||||||
XrSwapchain,
|
XrSwapchain,
|
||||||
XrInput,
|
XrInput,
|
||||||
|
XrViews,
|
||||||
)>,
|
)>,
|
||||||
>,
|
>,
|
||||||
>,
|
>,
|
||||||
@@ -49,38 +57,25 @@ impl Plugin for OpenXrPlugin {
|
|||||||
SystemState::new(&mut app.world);
|
SystemState::new(&mut app.world);
|
||||||
let primary_window = system_state.get(&app.world).get_single().ok().cloned();
|
let primary_window = system_state.get(&app.world).get_single().ok().cloned();
|
||||||
|
|
||||||
let settings = WgpuSettings::default();
|
|
||||||
bevy::tasks::IoTaskPool::get()
|
bevy::tasks::IoTaskPool::get()
|
||||||
.spawn_local(async move {
|
.spawn_local(async move {
|
||||||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
let (device, queue, adapter_info, render_adapter, instance, xr_instance, session, blend_mode, session_running, frame_waiter, swapchain, input, views) = graphics::initialize_xr_graphics(primary_window).unwrap();
|
||||||
backends: settings.backends.unwrap(),
|
|
||||||
dx12_shader_compiler: settings.dx12_shader_compiler.clone(),
|
|
||||||
});
|
|
||||||
let surface = primary_window.map(|wrapper| unsafe {
|
|
||||||
// SAFETY: Plugins should be set up on the main thread.
|
|
||||||
let handle = wrapper.get_handle();
|
|
||||||
instance
|
|
||||||
.create_surface(&handle)
|
|
||||||
.expect("Failed to create wgpu surface")
|
|
||||||
});
|
|
||||||
|
|
||||||
let request_adapter_options = wgpu::RequestAdapterOptions {
|
|
||||||
power_preference: settings.power_preference,
|
|
||||||
compatible_surface: surface.as_ref(),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let (device, queue, adapter_info, render_adapter) =
|
|
||||||
renderer::initialize_renderer(&instance, &settings, &request_adapter_options)
|
|
||||||
.await;
|
|
||||||
debug!("Configured wgpu adapter Limits: {:#?}", device.limits());
|
debug!("Configured wgpu adapter Limits: {:#?}", device.limits());
|
||||||
debug!("Configured wgpu adapter Features: {:#?}", device.features());
|
debug!("Configured wgpu adapter Features: {:#?}", device.features());
|
||||||
let mut future_renderer_resources_inner =
|
let mut future_renderer_resources_inner =
|
||||||
future_renderer_resources_wrapper.lock().unwrap();
|
future_renderer_resources_wrapper.lock().unwrap();
|
||||||
*future_renderer_resources_inner =
|
*future_renderer_resources_inner =
|
||||||
Some((device, queue, adapter_info, render_adapter, instance));
|
Some((device, queue, adapter_info, render_adapter, instance));
|
||||||
|
let mut future_xr_resources_inner = future_xr_resources_wrapper.lock().unwrap();
|
||||||
|
*future_xr_resources_inner =
|
||||||
|
Some((xr_instance, session, blend_mode, session_running, frame_waiter, swapchain, input, views));
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
|
app.add_systems(Last, pre_frame);
|
||||||
|
|
||||||
|
let render_app = app.sub_app_mut(RenderApp);
|
||||||
|
render_app.add_systems(Render, (post_frame.in_set(RenderSet::Prepare), post_queue_submit.in_set(RenderSet::Cleanup)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ready(&self, app: &App) -> bool {
|
fn ready(&self, app: &App) -> bool {
|
||||||
@@ -94,7 +89,7 @@ impl Plugin for OpenXrPlugin {
|
|||||||
if let Some(future_renderer_resources) =
|
if let Some(future_renderer_resources) =
|
||||||
app.world.remove_resource::<FutureXrResources>()
|
app.world.remove_resource::<FutureXrResources>()
|
||||||
{
|
{
|
||||||
let (instance, session, blend_mode, session_running, frame_waiter, swapchain, input) =
|
let (instance, session, blend_mode, session_running, frame_waiter, swapchain, input, views) =
|
||||||
future_renderer_resources.0.lock().unwrap().take().unwrap();
|
future_renderer_resources.0.lock().unwrap().take().unwrap();
|
||||||
|
|
||||||
app.insert_resource(instance.clone())
|
app.insert_resource(instance.clone())
|
||||||
@@ -102,7 +97,20 @@ impl Plugin for OpenXrPlugin {
|
|||||||
.insert_resource(blend_mode.clone())
|
.insert_resource(blend_mode.clone())
|
||||||
.insert_resource(session_running.clone())
|
.insert_resource(session_running.clone())
|
||||||
.insert_resource(frame_waiter.clone())
|
.insert_resource(frame_waiter.clone())
|
||||||
.insert_resource(swapchain.clone());
|
.insert_resource(swapchain.clone())
|
||||||
|
.insert_resource(input.clone())
|
||||||
|
.insert_resource(views.clone());
|
||||||
|
|
||||||
|
let render_app = app.sub_app_mut(RenderApp);
|
||||||
|
|
||||||
|
render_app.insert_resource(instance)
|
||||||
|
.insert_resource(session)
|
||||||
|
.insert_resource(blend_mode)
|
||||||
|
.insert_resource(session_running)
|
||||||
|
.insert_resource(frame_waiter)
|
||||||
|
.insert_resource(swapchain)
|
||||||
|
.insert_resource(input)
|
||||||
|
.insert_resource(views);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,3 +124,87 @@ impl PluginGroup for DefaultXrPlugins {
|
|||||||
.add_before::<RenderPlugin, _>(OpenXrPlugin)
|
.add_before::<RenderPlugin, _>(OpenXrPlugin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pre_frame(
|
||||||
|
instance: Res<XrInstance>,
|
||||||
|
session: Res<XrSession>,
|
||||||
|
session_running: Res<XrSessionRunning>,
|
||||||
|
frame_state: Res<XrFrameState>,
|
||||||
|
frame_waiter: Res<XrFrameWaiter>,
|
||||||
|
swapchain: Res<XrSwapchain>,
|
||||||
|
mut manual_texture_views: ResMut<ManualTextureViews>,
|
||||||
|
){
|
||||||
|
while let Some(event) = instance.poll_event(&mut Default::default()).unwrap() {
|
||||||
|
use xr::Event::*;
|
||||||
|
match event {
|
||||||
|
SessionStateChanged(e) => {
|
||||||
|
// Session state change is where we can begin and end sessions, as well as
|
||||||
|
// find quit messages!
|
||||||
|
info!("entered XR state {:?}", e.state());
|
||||||
|
match e.state() {
|
||||||
|
xr::SessionState::READY => {
|
||||||
|
session.begin(VIEW_TYPE).unwrap();
|
||||||
|
session_running.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
xr::SessionState::STOPPING => {
|
||||||
|
session.end().unwrap();
|
||||||
|
session_running.store(false, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
xr::SessionState::EXITING | xr::SessionState::LOSS_PENDING => {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InstanceLossPending(_) => {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
EventsLost(e) => {
|
||||||
|
warn!("lost {} XR events", e.lost_event_count());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !session_running.load(std::sync::atomic::Ordering::Relaxed) {
|
||||||
|
// Don't grind up the CPU
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
*frame_state.lock().unwrap() = Some(frame_waiter.lock().unwrap().wait().unwrap());
|
||||||
|
|
||||||
|
let mut swapchain = swapchain.lock().unwrap();
|
||||||
|
|
||||||
|
swapchain.begin().unwrap();
|
||||||
|
let (left, right) = swapchain.get_render_views();
|
||||||
|
let left = ManualTextureView::with_default_format(left.into(), swapchain.resolution());
|
||||||
|
let right = ManualTextureView::with_default_format(right.into(), swapchain.resolution());
|
||||||
|
manual_texture_views.insert(LEFT_XR_TEXTURE_HANDLE, left);
|
||||||
|
manual_texture_views.insert(RIGHT_XR_TEXTURE_HANDLE, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn post_frame(
|
||||||
|
views: Res<XrViews>,
|
||||||
|
input: Res<XrInput>,
|
||||||
|
session: Res<XrSession>,
|
||||||
|
xr_frame_state: Res<XrFrameState>,
|
||||||
|
) {
|
||||||
|
*views.lock().unwrap() = session.locate_views(
|
||||||
|
VIEW_TYPE,
|
||||||
|
xr_frame_state.lock().unwrap().unwrap().predicted_display_time,
|
||||||
|
&input.stage,
|
||||||
|
).unwrap().1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn post_queue_submit(
|
||||||
|
xr_frame_state: Res<XrFrameState>,
|
||||||
|
views: Res<XrViews>,
|
||||||
|
input: Res<XrInput>,
|
||||||
|
swapchain: Res<XrSwapchain>,
|
||||||
|
environment_blend_mode: Res<XrEnvironmentBlendMode>,
|
||||||
|
) {
|
||||||
|
let xr_frame_state = xr_frame_state.lock().unwrap().unwrap();
|
||||||
|
let views = &*views.lock().unwrap();
|
||||||
|
let stage = &input.stage;
|
||||||
|
swapchain.lock().unwrap().post_queue_submit(xr_frame_state, views, stage, **environment_blend_mode).unwrap();
|
||||||
|
}
|
||||||
120
src/resources.rs
120
src/resources.rs
@@ -1,23 +1,127 @@
|
|||||||
use std::sync::Mutex;
|
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use crate::resource_macros::*;
|
use crate::resource_macros::*;
|
||||||
|
use bevy::prelude::*;
|
||||||
use openxr as xr;
|
use openxr as xr;
|
||||||
|
|
||||||
xr_resource_wrapper!(XrInstance, xr::Instance);
|
xr_resource_wrapper!(XrInstance, xr::Instance);
|
||||||
xr_resource_wrapper!(XrSession, xr::Session<xr::AnyGraphics>);
|
xr_resource_wrapper!(XrSession, xr::Session<xr::AnyGraphics>);
|
||||||
xr_resource_wrapper!(XrEnvironmentBlendMode, xr::EnvironmentBlendMode);
|
xr_resource_wrapper!(XrEnvironmentBlendMode, xr::EnvironmentBlendMode);
|
||||||
|
xr_resource_wrapper!(XrViewConfigurationViews, Vec<xr::ViewConfigurationView>);
|
||||||
xr_arc_resource_wrapper!(XrSessionRunning, AtomicBool);
|
xr_arc_resource_wrapper!(XrSessionRunning, AtomicBool);
|
||||||
xr_arc_resource_wrapper!(XrFrameWaiter, Mutex<XrFrameWaiter>);
|
xr_arc_resource_wrapper!(XrFrameWaiter, Mutex<xr::FrameWaiter>);
|
||||||
xr_arc_resource_wrapper!(XrSwapchain, Mutex<Swapchain>);
|
xr_arc_resource_wrapper!(XrSwapchain, Mutex<Swapchain>);
|
||||||
|
xr_arc_resource_wrapper!(XrFrameState, Mutex<Option<xr::FrameState>>);
|
||||||
|
xr_arc_resource_wrapper!(XrViews, Mutex<Vec<xr::View>>);
|
||||||
|
|
||||||
pub enum Swapchain {
|
pub enum Swapchain {
|
||||||
Vulkan(SwapchainInner<xr::Vulkan>)
|
Vulkan(SwapchainInner<xr::Vulkan>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Swapchain {
|
||||||
|
pub(crate) fn begin(&mut self) -> xr::Result<()> {
|
||||||
|
match self {
|
||||||
|
Swapchain::Vulkan(swap) => swap.begin(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_render_views(&mut self) -> (wgpu::TextureView, wgpu::TextureView) {
|
||||||
|
match self {
|
||||||
|
Swapchain::Vulkan(swap) => swap.get_render_views(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn resolution(&self) -> UVec2 {
|
||||||
|
match self {
|
||||||
|
Swapchain::Vulkan(swap) => swap.resolution,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn post_queue_submit(
|
||||||
|
&mut self,
|
||||||
|
xr_frame_state: xr::FrameState,
|
||||||
|
views: &[openxr::View],
|
||||||
|
stage: &xr::Space,
|
||||||
|
environment_blend_mode: xr::EnvironmentBlendMode,
|
||||||
|
) -> xr::Result<()> {
|
||||||
|
match self {
|
||||||
|
Swapchain::Vulkan(swap) => swap.post_queue_submit(xr_frame_state, views, stage, environment_blend_mode),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SwapchainInner<G: xr::Graphics> {
|
pub struct SwapchainInner<G: xr::Graphics> {
|
||||||
stream: xr::FrameStream<G>,
|
pub(crate) stream: xr::FrameStream<G>,
|
||||||
handle: xr::Swapchain<G>,
|
pub(crate) handle: xr::Swapchain<G>,
|
||||||
resolution: (u32, u32),
|
pub(crate) resolution: UVec2,
|
||||||
buffers: Vec<wgpu::Texture>,
|
pub(crate) buffers: Vec<wgpu::Texture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<G: xr::Graphics> SwapchainInner<G> {
|
||||||
|
fn begin(&mut self) -> xr::Result<()> {
|
||||||
|
self.stream.begin()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_render_views(&mut self) -> (wgpu::TextureView, wgpu::TextureView) {
|
||||||
|
let image_index = self.handle.acquire_image().unwrap();
|
||||||
|
self.handle.wait_image(xr::Duration::INFINITE).unwrap();
|
||||||
|
|
||||||
|
let texture = &self.buffers[image_index as usize];
|
||||||
|
|
||||||
|
(
|
||||||
|
texture.create_view(&wgpu::TextureViewDescriptor {
|
||||||
|
dimension: Some(wgpu::TextureViewDimension::D2),
|
||||||
|
array_layer_count: Some(1),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
texture.create_view(&wgpu::TextureViewDescriptor {
|
||||||
|
dimension: Some(wgpu::TextureViewDimension::D2),
|
||||||
|
array_layer_count: Some(1),
|
||||||
|
base_array_layer: 1,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_queue_submit(
|
||||||
|
&mut self,
|
||||||
|
xr_frame_state: xr::FrameState,
|
||||||
|
views: &[openxr::View],
|
||||||
|
stage: &xr::Space,
|
||||||
|
environment_blend_mode: xr::EnvironmentBlendMode,
|
||||||
|
) -> xr::Result<()> {
|
||||||
|
self.handle.release_image().unwrap();
|
||||||
|
let rect = xr::Rect2Di {
|
||||||
|
offset: xr::Offset2Di { x: 0, y: 0 },
|
||||||
|
extent: xr::Extent2Di {
|
||||||
|
width: self.resolution.x as _,
|
||||||
|
height: self.resolution.y as _,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
self.stream.end(
|
||||||
|
xr_frame_state.predicted_display_time,
|
||||||
|
environment_blend_mode,
|
||||||
|
&[&xr::CompositionLayerProjection::new().space(stage).views(&[
|
||||||
|
xr::CompositionLayerProjectionView::new()
|
||||||
|
.pose(views[0].pose)
|
||||||
|
.fov(views[0].fov)
|
||||||
|
.sub_image(
|
||||||
|
xr::SwapchainSubImage::new()
|
||||||
|
.swapchain(&self.handle)
|
||||||
|
.image_array_index(0)
|
||||||
|
.image_rect(rect),
|
||||||
|
),
|
||||||
|
xr::CompositionLayerProjectionView::new()
|
||||||
|
.pose(views[1].pose)
|
||||||
|
.fov(views[1].fov)
|
||||||
|
.sub_image(
|
||||||
|
xr::SwapchainSubImage::new()
|
||||||
|
.swapchain(&self.handle)
|
||||||
|
.image_array_index(1)
|
||||||
|
.image_rect(rect),
|
||||||
|
),
|
||||||
|
])],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user