XR is kinda working now
This commit is contained in:
@@ -9,7 +9,7 @@ use bevy::prelude::*;
|
||||
use bevy::render::renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue};
|
||||
use bevy::window::RawHandleWrapper;
|
||||
use openxr as xr;
|
||||
use wgpu::Instance;
|
||||
use wgpu::{Instance, Texture};
|
||||
|
||||
use crate::input::XrInput;
|
||||
use crate::resources::{
|
||||
@@ -352,7 +352,6 @@ pub fn initialize_xr_graphics(window: Option<RawHandleWrapper>) -> anyhow::Resul
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
||||
Ok((
|
||||
wgpu_device.into(),
|
||||
RenderQueue(Arc::new(wgpu_queue)),
|
||||
@@ -368,7 +367,9 @@ pub fn initialize_xr_graphics(window: Option<RawHandleWrapper>) -> anyhow::Resul
|
||||
stream: frame_stream,
|
||||
handle,
|
||||
resolution,
|
||||
format: swapchain_format,
|
||||
buffers,
|
||||
image_index: 0,
|
||||
})).into(),
|
||||
XrInput::new(xr_instance, session.into_any_graphics())?,
|
||||
Mutex::default().into(),
|
||||
|
||||
33
src/lib.rs
33
src/lib.rs
@@ -100,6 +100,24 @@ impl Plugin for OpenXrPlugin {
|
||||
.insert_resource(views.clone())
|
||||
.insert_resource(frame_state.clone());
|
||||
|
||||
let swapchain_mut = swapchain.lock().unwrap();
|
||||
let (left, right) = swapchain_mut.get_render_views();
|
||||
let format = swapchain_mut.format();
|
||||
let left = ManualTextureView {
|
||||
texture_view: left.into(),
|
||||
size: swapchain_mut.resolution(),
|
||||
format,
|
||||
};
|
||||
let right = ManualTextureView {
|
||||
texture_view: right.into(),
|
||||
size: swapchain_mut.resolution(),
|
||||
format,
|
||||
};
|
||||
let mut manual_texture_views = app.world.resource_mut::<ManualTextureViews>();
|
||||
manual_texture_views.insert(LEFT_XR_TEXTURE_HANDLE, left);
|
||||
manual_texture_views.insert(RIGHT_XR_TEXTURE_HANDLE, right);
|
||||
drop(manual_texture_views);
|
||||
drop(swapchain_mut);
|
||||
let render_app = app.sub_app_mut(RenderApp);
|
||||
|
||||
render_app.insert_resource(instance)
|
||||
@@ -111,6 +129,7 @@ impl Plugin for OpenXrPlugin {
|
||||
.insert_resource(input)
|
||||
.insert_resource(views)
|
||||
.insert_resource(frame_state);
|
||||
|
||||
render_app.add_systems(Render, (pre_frame.in_set(RenderSet::Prepare).before(post_frame), post_frame.in_set(RenderSet::Prepare), post_queue_submit.in_set(RenderSet::Cleanup)));
|
||||
}
|
||||
|
||||
@@ -178,9 +197,19 @@ pub fn pre_frame(
|
||||
let mut swapchain = swapchain.lock().unwrap();
|
||||
|
||||
swapchain.begin().unwrap();
|
||||
swapchain.update_render_views();
|
||||
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());
|
||||
let format = swapchain.format();
|
||||
let left = ManualTextureView {
|
||||
texture_view: left.into(),
|
||||
size: swapchain.resolution(),
|
||||
format,
|
||||
};
|
||||
let right = ManualTextureView {
|
||||
texture_view: right.into(),
|
||||
size: swapchain.resolution(),
|
||||
format,
|
||||
};
|
||||
manual_texture_views.insert(LEFT_XR_TEXTURE_HANDLE, left);
|
||||
manual_texture_views.insert(RIGHT_XR_TEXTURE_HANDLE, right);
|
||||
}
|
||||
|
||||
@@ -26,12 +26,24 @@ impl Swapchain {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_render_views(&mut self) -> (wgpu::TextureView, wgpu::TextureView) {
|
||||
pub(crate) fn update_render_views(&mut self) {
|
||||
match self {
|
||||
Swapchain::Vulkan(swap) => swap.update_render_views(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_render_views(&self) -> (wgpu::TextureView, wgpu::TextureView) {
|
||||
match self {
|
||||
Swapchain::Vulkan(swap) => swap.get_render_views(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn format(&self) -> wgpu::TextureFormat {
|
||||
match self {
|
||||
Swapchain::Vulkan(swap) => swap.format
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn resolution(&self) -> UVec2 {
|
||||
match self {
|
||||
Swapchain::Vulkan(swap) => swap.resolution,
|
||||
@@ -55,7 +67,9 @@ pub struct SwapchainInner<G: xr::Graphics> {
|
||||
pub(crate) stream: xr::FrameStream<G>,
|
||||
pub(crate) handle: xr::Swapchain<G>,
|
||||
pub(crate) resolution: UVec2,
|
||||
pub(crate) format: wgpu::TextureFormat,
|
||||
pub(crate) buffers: Vec<wgpu::Texture>,
|
||||
pub(crate) image_index: usize,
|
||||
}
|
||||
|
||||
impl<G: xr::Graphics> SwapchainInner<G> {
|
||||
@@ -63,11 +77,8 @@ impl<G: xr::Graphics> SwapchainInner<G> {
|
||||
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];
|
||||
fn get_render_views(&self) -> (wgpu::TextureView, wgpu::TextureView) {
|
||||
let texture = &self.buffers[self.image_index];
|
||||
|
||||
(
|
||||
texture.create_view(&wgpu::TextureViewDescriptor {
|
||||
@@ -84,6 +95,13 @@ impl<G: xr::Graphics> SwapchainInner<G> {
|
||||
)
|
||||
}
|
||||
|
||||
fn update_render_views(&mut self) {
|
||||
let image_index = self.handle.acquire_image().unwrap();
|
||||
self.handle.wait_image(xr::Duration::INFINITE).unwrap();
|
||||
|
||||
self.image_index = image_index as _;
|
||||
}
|
||||
|
||||
fn post_queue_submit(
|
||||
&mut self,
|
||||
xr_frame_state: xr::FrameState,
|
||||
|
||||
141
srcold/input.rs
141
srcold/input.rs
@@ -1,141 +0,0 @@
|
||||
use std::sync::Mutex;
|
||||
|
||||
use glam::{Quat, Vec3};
|
||||
use openxr as xr;
|
||||
|
||||
use crate::xr::{VIEW_TYPE, XrPose};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PostFrameData {
|
||||
pub views: Vec<xr::View>,
|
||||
pub left_hand: Option<XrPose>,
|
||||
pub right_hand: Option<XrPose>,
|
||||
}
|
||||
|
||||
pub(crate) struct XrInput {
|
||||
session: xr::Session<xr::Vulkan>,
|
||||
action_set: xr::ActionSet,
|
||||
right_action: xr::Action<xr::Posef>,
|
||||
left_action: xr::Action<xr::Posef>,
|
||||
right_space: xr::Space,
|
||||
left_space: xr::Space,
|
||||
stage: xr::Space,
|
||||
left_hand: Mutex<XrPose>,
|
||||
right_hand: Mutex<XrPose>,
|
||||
views: Mutex<Vec<XrPose>>,
|
||||
}
|
||||
|
||||
impl XrInput {
|
||||
pub(crate) fn new(
|
||||
instance: xr::Instance,
|
||||
session: xr::Session<xr::Vulkan>,
|
||||
) -> anyhow::Result<Self> {
|
||||
let action_set = instance.create_action_set("input", "input pose information", 0)?;
|
||||
let right_action =
|
||||
action_set.create_action::<xr::Posef>("right_hand", "Right Hand Controller", &[])?;
|
||||
let left_action =
|
||||
action_set.create_action::<xr::Posef>("left_hand", "Left Hand Controller", &[])?;
|
||||
instance.suggest_interaction_profile_bindings(
|
||||
instance.string_to_path("/interaction_profiles/khr/simple_controller")?,
|
||||
&[
|
||||
xr::Binding::new(
|
||||
&right_action,
|
||||
instance.string_to_path("/user/hand/right/input/grip/pose")?,
|
||||
),
|
||||
xr::Binding::new(
|
||||
&left_action,
|
||||
instance.string_to_path("/user/hand/left/input/grip/pose")?,
|
||||
),
|
||||
],
|
||||
)?;
|
||||
session.attach_action_sets(&[&action_set])?;
|
||||
let right_space =
|
||||
right_action.create_space(session.clone(), xr::Path::NULL, xr::Posef::IDENTITY)?;
|
||||
let left_space =
|
||||
left_action.create_space(session.clone(), xr::Path::NULL, xr::Posef::IDENTITY)?;
|
||||
let stage =
|
||||
session.create_reference_space(xr::ReferenceSpaceType::STAGE, xr::Posef::IDENTITY)?;
|
||||
Ok(Self {
|
||||
left_action,
|
||||
left_space,
|
||||
right_action,
|
||||
right_space,
|
||||
action_set,
|
||||
stage,
|
||||
session,
|
||||
left_hand: Default::default(),
|
||||
right_hand: Default::default(),
|
||||
views: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn post_frame(
|
||||
&self,
|
||||
xr_frame_state: xr::FrameState,
|
||||
) -> xr::Result<PostFrameData> {
|
||||
self.session.sync_actions(&[(&self.action_set).into()])?;
|
||||
let locate_hand_pose = |action: &xr::Action<xr::Posef>,
|
||||
space: &xr::Space|
|
||||
-> xr::Result<Option<(Vec3, Quat)>> {
|
||||
if action.is_active(&self.session, xr::Path::NULL)? {
|
||||
Ok(Some(openxr_pose_to_glam(
|
||||
&space
|
||||
.locate(&self.stage, xr_frame_state.predicted_display_time)?
|
||||
.pose,
|
||||
)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
};
|
||||
|
||||
let left_hand = locate_hand_pose(&self.left_action, &self.left_space)?;
|
||||
let right_hand = locate_hand_pose(&self.right_action, &self.right_space)?;
|
||||
let (_, views) = self.session.locate_views(
|
||||
VIEW_TYPE,
|
||||
xr_frame_state.predicted_display_time,
|
||||
&self.stage,
|
||||
)?;
|
||||
|
||||
if let Some(left_hand) = &left_hand {
|
||||
*self.left_hand.lock().unwrap() = left_hand.clone();
|
||||
}
|
||||
|
||||
if let Some(right_hand) = &left_hand {
|
||||
*self.right_hand.lock().unwrap() = right_hand.clone();
|
||||
}
|
||||
|
||||
*self.views.lock().unwrap() = views.iter().map(|f| openxr_pose_to_glam(&f.pose)).collect();
|
||||
|
||||
Ok(PostFrameData {
|
||||
views,
|
||||
left_hand,
|
||||
right_hand,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn stage(&self) -> &xr::Space {
|
||||
&self.stage
|
||||
}
|
||||
|
||||
pub fn left_hand(&self) -> XrPose {
|
||||
*self.left_hand.lock().unwrap()
|
||||
}
|
||||
|
||||
pub fn right_hand(&self) -> XrPose {
|
||||
*self.right_hand.lock().unwrap()
|
||||
}
|
||||
|
||||
pub fn views(&self) -> Vec<XrPose> {
|
||||
self.views.lock().unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn openxr_pose_to_glam(pose: &openxr::Posef) -> (Vec3, Quat) {
|
||||
// with enough sign errors anything is possible
|
||||
let rotation = {
|
||||
let o = pose.orientation;
|
||||
Quat::from_rotation_x(180.0f32.to_radians()) * glam::quat(o.w, o.z, o.y, o.x)
|
||||
};
|
||||
let translation = glam::vec3(-pose.position.x, pose.position.y, -pose.position.z);
|
||||
(translation, rotation)
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use bevy::ecs::system::SystemState;
|
||||
use bevy::prelude::*;
|
||||
use bevy::render::settings::WgpuSettings;
|
||||
use bevy::render::FutureRendererResources;
|
||||
use bevy::window::{PrimaryWindow, RawHandleWrapper};
|
||||
|
||||
mod xr;
|
||||
mod input;
|
||||
|
||||
pub struct OpenXrPlugin {
|
||||
pub wgpu_settings: WgpuSettings,
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct FutureXrResources (
|
||||
Arc<
|
||||
Mutex<
|
||||
Option<
|
||||
()
|
||||
>
|
||||
>
|
||||
>
|
||||
);
|
||||
|
||||
impl Plugin for OpenXrPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
if let Some(backends) = self.wgpu_settings.backends {
|
||||
let future_renderer_resources_wrapper = Arc::new(Mutex::new(None));
|
||||
let future_xr_resources_wrapper = Arc::new(Mutex::new(None));
|
||||
app.insert_resource(FutureRendererResources(
|
||||
future_renderer_resources_wrapper.clone(),
|
||||
));
|
||||
|
||||
app.insert_resource(FutureXrResources(
|
||||
future_xr_resources_wrapper.clone(),
|
||||
));
|
||||
|
||||
let mut system_state: SystemState<Query<&RawHandleWrapper, With<PrimaryWindow>>> =
|
||||
SystemState::new(&mut app.world);
|
||||
let primary_window = system_state.get(&app.world).get_single().ok().cloned();
|
||||
|
||||
let settings = self.wgpu_settings.clone();
|
||||
bevy::tasks::IoTaskPool::get()
|
||||
.spawn_local(async move {
|
||||
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
178
srcold/xr/mod.rs
178
srcold/xr/mod.rs
@@ -1,178 +0,0 @@
|
||||
use std::cell::UnsafeCell;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use glam::{Quat, Vec3};
|
||||
use openxr as xr;
|
||||
|
||||
use crate::input::{PostFrameData, XrInput};
|
||||
|
||||
pub type XrPose = (Vec3, Quat);
|
||||
pub const VIEW_TYPE: xr::ViewConfigurationType = xr::ViewConfigurationType::PRIMARY_STEREO;
|
||||
|
||||
pub enum XrState {
|
||||
Vulkan(XrStateInner<xr::Vulkan>),
|
||||
}
|
||||
|
||||
pub struct XrStateInner<G: xr::Graphics> {
|
||||
instance: xr::Instance,
|
||||
session: xr::Session<G>,
|
||||
session_running: AtomicBool,
|
||||
frame: Mutex<FrameInner<G>>,
|
||||
frame_state: Mutex<Option<xr::FrameState>>,
|
||||
post_frame_data: Mutex<Option<PostFrameData>>,
|
||||
event_buffer: UnsafeCell<xr::EventDataBuffer>,
|
||||
input: XrInput,
|
||||
}
|
||||
|
||||
unsafe impl<G: xr::Graphics> Sync for XrStateInner<G> {}
|
||||
unsafe impl<G: xr::Graphics> Send for XrStateInner<G> {}
|
||||
|
||||
impl<G: xr::Graphics> XrStateInner<G> {
|
||||
pub fn preframe(&self) -> xr::Result<()> {
|
||||
let event_buffer = unsafe { &mut *self.event_buffer.get() };
|
||||
while let Some(event) = self.instance.poll_event(event_buffer)? {
|
||||
use xr::Event::*;
|
||||
match event {
|
||||
SessionStateChanged(e) => {
|
||||
// Session state change is where we can begin and end sessions, as well as
|
||||
// find quit messages!
|
||||
match e.state() {
|
||||
xr::SessionState::READY => {
|
||||
self.session
|
||||
.begin(VIEW_TYPE)?; // TODO! support other view types
|
||||
self.session_running
|
||||
.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
xr::SessionState::STOPPING => {
|
||||
self.session.end()?;
|
||||
self.session_running
|
||||
.store(false, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
xr::SessionState::EXITING | xr::SessionState::LOSS_PENDING => {
|
||||
*self.frame_state.lock().unwrap() = None;
|
||||
return Ok(());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
InstanceLossPending(_) => {
|
||||
*self.frame_state.lock().unwrap() = None;
|
||||
return Ok(());
|
||||
}
|
||||
EventsLost(e) => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if !self
|
||||
.session_running
|
||||
.load(std::sync::atomic::Ordering::Relaxed)
|
||||
{
|
||||
// Don't grind up the CPU
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
*self.frame_state.lock().unwrap() = None;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
*self.frame_state.lock().unwrap() = Some(self.frame.lock().unwrap().begin()?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn post_frame(&self) -> xr::Result<(wgpu::TextureView, wgpu::TextureView)> {
|
||||
*self.post_frame_data.lock().unwrap() = Some(self.input.post_frame(self.frame_state.lock().unwrap().unwrap().clone())?);
|
||||
Ok(self.frame.lock().unwrap().get_render_views())
|
||||
}
|
||||
|
||||
pub fn post_queue_submit(&self) -> xr::Result<()> {
|
||||
let pfd = self.post_frame_data.lock().unwrap();
|
||||
self.frame.lock().unwrap().post_queue_submit(self.frame_state.lock().unwrap().unwrap().clone(), &(*pfd).clone().unwrap().views, self.input.stage())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FrameInner<G: xr::Graphics> {
|
||||
waiter: xr::FrameWaiter,
|
||||
stream: xr::FrameStream<G>,
|
||||
blend_mode: xr::EnvironmentBlendMode,
|
||||
views: Vec<xr::ViewConfigurationView>,
|
||||
swapchain: xr::Swapchain<G>,
|
||||
resolution: Extent2D,
|
||||
buffers: Vec<wgpu::Texture>,
|
||||
}
|
||||
|
||||
impl<G: xr::Graphics> FrameInner<G> {
|
||||
fn begin(&mut self) -> xr::Result<xr::FrameState> {
|
||||
let frame_state = self.waiter.wait()?;
|
||||
self.stream.begin()?;
|
||||
Ok(frame_state)
|
||||
}
|
||||
|
||||
fn get_render_views(&mut self) -> (wgpu::TextureView, wgpu::TextureView) {
|
||||
let image_index = self.swapchain.acquire_image().unwrap();
|
||||
self.swapchain.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),
|
||||
base_array_layer: 0,
|
||||
..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,
|
||||
) -> xr::Result<()> {
|
||||
self.swapchain.release_image()?;
|
||||
let rect = xr::Rect2Di {
|
||||
offset: xr::Offset2Di { x: 0, y: 0 },
|
||||
extent: xr::Extent2Di {
|
||||
width: self.resolution.width as _,
|
||||
height: self.resolution.height as _,
|
||||
},
|
||||
};
|
||||
self.stream.end(
|
||||
xr_frame_state.predicted_display_time,
|
||||
self.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.swapchain)
|
||||
.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.swapchain)
|
||||
.image_array_index(1)
|
||||
.image_rect(rect),
|
||||
),
|
||||
])],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Extent2D {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
Reference in New Issue
Block a user