From 747aff2fd4b2aa30b2985cd7a56fd97500f40a92 Mon Sep 17 00:00:00 2001 From: awtterpip Date: Fri, 15 Mar 2024 23:28:05 -0500 Subject: [PATCH] change xr status --- crates/bevy_openxr/src/init.rs | 116 +++++++++---------------- crates/bevy_openxr/src/render.rs | 9 +- crates/bevy_openxr/src/resources.rs | 20 +---- crates/bevy_openxr/src/types.rs | 4 +- crates/bevy_xr/src/session.rs | 129 +++++++++++++++------------- 5 files changed, 118 insertions(+), 160 deletions(-) diff --git a/crates/bevy_openxr/src/init.rs b/crates/bevy_openxr/src/init.rs index cfdcb3a..18cb0f5 100644 --- a/crates/bevy_openxr/src/init.rs +++ b/crates/bevy_openxr/src/init.rs @@ -1,7 +1,7 @@ -use bevy::app::{App, First, Plugin, PostUpdate}; +use bevy::app::{App, First, Plugin, PostUpdate, PreUpdate}; +use bevy::ecs::change_detection::DetectChangesMut; use bevy::ecs::component::Component; use bevy::ecs::entity::Entity; -use bevy::ecs::event::EventWriter; use bevy::ecs::query::{With, Without}; use bevy::ecs::schedule::common_conditions::{not, on_event}; use bevy::ecs::schedule::IntoSystemConfigs; @@ -21,7 +21,8 @@ use bevy::render::{ExtractSchedule, MainWorld, RenderApp, RenderPlugin}; use bevy::transform::components::GlobalTransform; use bevy::transform::{TransformBundle, TransformSystem}; use bevy_xr::session::{ - BeginXrSession, CreateXrSession, XrInstanceCreated, XrInstanceDestroyed, XrSessionState, + handle_session, session_available, session_running, status_equals, BeginXrSession, + CreateXrSession, XrStatus, }; use crate::error::XrError; @@ -47,28 +48,12 @@ pub struct XrInitPlugin { pub synchronous_pipeline_compilation: bool, } -pub fn instance_created(status: Option>) -> bool { - status.is_some_and(|status| status.instance_created) -} - -pub fn session_created(status: Option>) -> bool { - status.is_some_and(|status| status.session_created) -} - -pub fn session_ready(status: Option>) -> bool { - status.is_some_and(|status| status.session_ready) -} - -pub fn session_running(status: Option>) -> bool { - status.is_some_and(|status| status.session_running) -} - impl Plugin for XrInitPlugin { fn build(&self, app: &mut App) { if let Err(e) = init_xr(&self, app) { error!("Failed to initialize openxr instance: {e}."); app.add_plugins(RenderPlugin::default()) - .insert_resource(XrStatus::UNINITIALIZED); + .insert_resource(XrStatus::Unavailable); } } } @@ -113,7 +98,12 @@ fn init_xr(config: &XrInitPlugin, app: &mut App) -> Result<()> { let exts = config.exts.clone() & available_exts; - let instance = entry.create_instance(config.app_info.clone(), exts, backend)?; + let instance = entry.create_instance( + config.app_info.clone(), + exts, + &["XR_APILAYER_LUNARG_api_dump"], + backend, + )?; let instance_props = instance.properties()?; info!( @@ -136,7 +126,6 @@ fn init_xr(config: &XrInitPlugin, app: &mut App) -> Result<()> { let (WgpuGraphics(device, queue, adapter_info, adapter, wgpu_instance), create_info) = instance.init_graphics(system_id)?; - app.world.send_event(XrInstanceCreated); app.add_plugins(( RenderPlugin { render_creation: RenderCreation::manual( @@ -154,10 +143,7 @@ fn init_xr(config: &XrInitPlugin, app: &mut App) -> Result<()> { )) .insert_resource(instance.clone()) .insert_resource(SystemId(system_id)) - .insert_resource(XrStatus { - instance_created: true, - ..Default::default() - }) + .insert_resource(XrStatus::Available) .insert_non_send_resource(XrSessionInitConfig { blend_modes: config.blend_modes.clone(), formats: config.formats.clone(), @@ -166,13 +152,16 @@ fn init_xr(config: &XrInitPlugin, app: &mut App) -> Result<()> { }) .add_systems( First, + poll_events.run_if(session_available).before(handle_session), + ) + .add_systems( + PreUpdate, ( - poll_events.run_if(instance_created), create_xr_session - .run_if(not(session_created)) - .run_if(on_event::()), + .run_if(on_event::()) + .run_if(status_equals(XrStatus::Available)), begin_xr_session - .run_if(session_ready) + .run_if(status_equals(XrStatus::Ready)) .run_if(on_event::()), adopt_open_xr_trackers, ) @@ -401,7 +390,6 @@ fn create_xr_session_inner( format, }; - world.resource_mut::().session_created = true; world.insert_resource(session.clone()); world.insert_resource(frame_waiter); world.insert_resource(images.clone()); @@ -420,16 +408,11 @@ fn create_xr_session_inner( Ok(()) } -pub fn begin_xr_session( - session: Res, - mut session_state: EventWriter, - mut status: ResMut, -) { +pub fn begin_xr_session(session: Res, mut status: ResMut) { session .begin(openxr::ViewConfigurationType::PRIMARY_STEREO) .expect("Failed to begin session"); - status.session_running = true; - session_state.send(XrSessionState::Running); + *status = XrStatus::Running; } /// This is used solely to transport resources from the main world to the render world. @@ -466,13 +449,7 @@ pub fn transfer_xr_resources(mut commands: Commands, mut world: ResMut, - session: Option>, - mut session_state: EventWriter, - mut instance_destroyed: EventWriter, - mut status: ResMut, -) { +pub fn poll_events(instance: Res, mut status: ResMut) { let mut buffer = Default::default(); while let Some(event) = instance .poll_event(&mut buffer) @@ -481,41 +458,28 @@ pub fn poll_events( use openxr::Event::*; match event { SessionStateChanged(e) => { - if let Some(ref session) = session { - info!("entered XR state {:?}", e.state()); - use openxr::SessionState; + info!("entered XR state {:?}", e.state()); + use openxr::SessionState; - match e.state() { - SessionState::IDLE => { - status.session_ready = false; - session_state.send(XrSessionState::Idle); - } - SessionState::READY => { - status.session_ready = true; - session_state.send(XrSessionState::Ready); - } - SessionState::STOPPING => { - status.session_running = false; - status.session_ready = false; - session.end().expect("Failed to end session"); - session_state.send(XrSessionState::Stopping); - } - // TODO: figure out how to destroy the session - SessionState::EXITING | SessionState::LOSS_PENDING => { - status.session_running = false; - status.session_created = false; - status.session_ready = false; - session.end().expect("Failed to end session"); - session_state.send(XrSessionState::Destroyed); - } - _ => {} + match e.state() { + SessionState::IDLE => { + *status = XrStatus::Idle; } + SessionState::READY => { + *status = XrStatus::Ready; + } + SessionState::SYNCHRONIZED | SessionState::VISIBLE | SessionState::FOCUSED => { + status.set_if_neq(XrStatus::Running); + } + SessionState::STOPPING => *status = XrStatus::Stopping, + // TODO: figure out how to destroy the session + SessionState::EXITING | SessionState::LOSS_PENDING => { + *status = XrStatus::Exiting; + } + _ => {} } } - InstanceLossPending(_) => { - *status = XrStatus::UNINITIALIZED; - instance_destroyed.send_default(); - } + InstanceLossPending(_) => {} _ => {} } } diff --git a/crates/bevy_openxr/src/render.rs b/crates/bevy_openxr/src/render.rs index a2acb67..0bd8698 100644 --- a/crates/bevy_openxr/src/render.rs +++ b/crates/bevy_openxr/src/render.rs @@ -8,14 +8,15 @@ use bevy::{ Render, RenderApp, RenderSet, }, }; -use bevy_xr::camera::{XrCamera, XrCameraBundle, XrProjection}; +use bevy_xr::{ + camera::{XrCamera, XrCameraBundle, XrProjection}, + session::session_running, +}; use openxr::{CompositionLayerFlags, ViewStateFlags}; use crate::{init::OpenXrTracker, resources::*}; use crate::{init::XrRoot, layer_builder::*}; -use crate::init::session_running; - pub struct XrRenderPlugin; impl Plugin for XrRenderPlugin { @@ -36,7 +37,7 @@ impl Plugin for XrRenderPlugin { Render, ( ( - locate_views, + //locate_views, update_views_render_world, insert_texture_views, ) diff --git a/crates/bevy_openxr/src/resources.rs b/crates/bevy_openxr/src/resources.rs index 535862c..14ca7f7 100644 --- a/crates/bevy_openxr/src/resources.rs +++ b/crates/bevy_openxr/src/resources.rs @@ -20,6 +20,7 @@ impl XrEntry { &self, app_info: AppInfo, exts: XrExtensions, + layers: &[&str], backend: GraphicsBackend, ) -> Result { let available_exts = self.enumerate_extensions()?; @@ -38,7 +39,7 @@ impl XrEntry { engine_version: Version::BEVY.to_u32(), }, &required_exts.into(), - &[], + layers, )?; Ok(XrInstance(instance, backend, app_info)) @@ -254,23 +255,6 @@ pub struct XrSwapchainInfo { #[derive(Debug, Copy, Clone, Deref, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Resource)] pub struct SystemId(pub openxr::SystemId); -#[derive(Clone, Copy, Eq, PartialEq, Default, Resource, ExtractResource)] -pub struct XrStatus { - pub instance_created: bool, - pub session_created: bool, - pub session_ready: bool, - pub session_running: bool, -} - -impl XrStatus { - pub const UNINITIALIZED: Self = Self { - instance_created: false, - session_created: false, - session_ready: false, - session_running: false, - }; -} - #[derive(Clone, Copy, Resource)] pub struct XrGraphicsInfo { pub blend_mode: EnvironmentBlendMode, diff --git a/crates/bevy_openxr/src/types.rs b/crates/bevy_openxr/src/types.rs index d10ef02..d1a1e96 100644 --- a/crates/bevy_openxr/src/types.rs +++ b/crates/bevy_openxr/src/types.rs @@ -4,7 +4,9 @@ use crate::error::XrError; pub use crate::extensions::XrExtensions; use crate::graphics::GraphicsExt; -pub use openxr::{EnvironmentBlendMode, SwapchainCreateFlags, SwapchainUsageFlags}; +pub use openxr::{ + ApiLayerProperties, EnvironmentBlendMode, SwapchainCreateFlags, SwapchainUsageFlags, +}; pub type Result = std::result::Result; diff --git a/crates/bevy_xr/src/session.rs b/crates/bevy_xr/src/session.rs index adfe357..1c885e6 100644 --- a/crates/bevy_xr/src/session.rs +++ b/crates/bevy_xr/src/session.rs @@ -1,89 +1,96 @@ -use bevy::app::{App, Plugin, PreUpdate}; -use bevy::ecs::event::{Event, EventReader, EventWriter}; -use bevy::ecs::system::Local; +use bevy::app::{App, First, Plugin}; +use bevy::ecs::event::{Event, EventWriter}; +use bevy::ecs::schedule::common_conditions::resource_exists_and_changed; +use bevy::ecs::schedule::IntoSystemConfigs; +use bevy::ecs::system::{Res, Resource}; +use bevy::render::extract_resource::ExtractResource; pub struct XrSessionPlugin; impl Plugin for XrSessionPlugin { fn build(&self, app: &mut App) { app.add_event::() + .add_event::() .add_event::() .add_event::() - .add_event::() - .add_event::() - .add_event::() - .add_systems(PreUpdate, handle_xr_events); + .add_systems( + First, + handle_session.run_if(resource_exists_and_changed::), + ); } } -pub fn handle_xr_events( - mut instance_created: EventReader, - mut session_state: EventReader, - mut instance_destroyed: EventReader, +#[derive(Resource, ExtractResource, Clone, Copy, Debug, PartialEq, Eq)] +pub enum XrStatus { + /// An XR session is not available here + Unavailable, + /// An XR session is available and ready to be created with a [`CreateXrSession`] event. + Available, + /// An XR session is created but not ready to begin. + Idle, + /// An XR session has been created and is ready to start rendering with a [`BeginXrSession`] event, or + Ready, + /// The XR session is running and can be stopped with an [`EndXrSession`] event. + Running, + /// The XR session is in the process of being stopped. + Stopping, + /// The XR session is in the process of being destroyed + Exiting, +} + +pub fn handle_session( + status: Res, mut create_session: EventWriter, mut begin_session: EventWriter, - mut has_instance: Local, - mut local_session_state: Local>, + mut end_session: EventWriter, ) { - // Don't do anything if no events recieved - if instance_created.is_empty() && instance_destroyed.is_empty() && session_state.is_empty() { - return; - } - if !instance_created.is_empty() { - *has_instance = true; - instance_created.clear(); - } - if !instance_destroyed.is_empty() { - *has_instance = false; - instance_destroyed.clear(); - } - for state in session_state.read() { - *local_session_state = Some(*state); - } - if *has_instance { - if local_session_state.is_none() { + match *status { + XrStatus::Unavailable => {} + XrStatus::Available => { create_session.send_default(); - } else if matches!(*local_session_state, Some(XrSessionState::Ready)) { + } + XrStatus::Idle => {} + XrStatus::Ready => { begin_session.send_default(); } + XrStatus::Running => {} + XrStatus::Stopping => {} + XrStatus::Exiting => {} } } -/// Event sent to backends to create an XR session. Should only be called after [`XrInstanceCreated`] is recieved. +/// A [`Condition`](bevy::ecs::schedule::Condition) system that says if the XR session is available. Returns true as long as [`XrStatus`] exists and isn't [`Unavailable`](XrStatus::Unavailable). +pub fn session_available(status: Option>) -> bool { + status.is_some_and(|s| *s != XrStatus::Unavailable) +} + +/// A [`Condition`](bevy::ecs::schedule::Condition) system that says if the XR session is ready or running +pub fn session_created(status: Option>) -> bool { + matches!(status.as_deref(), Some(XrStatus::Ready | XrStatus::Running)) +} + +/// A [`Condition`](bevy::ecs::schedule::Condition) system that says if the XR session is running +pub fn session_running(status: Option>) -> bool { + matches!(status.as_deref(), Some(XrStatus::Running)) +} + +/// A function that returns a [`Condition`](bevy::ecs::schedule::Condition) system that says if an the [`XrStatus`] is in a specific state +pub fn status_equals(status: XrStatus) -> impl FnMut(Option>) -> bool { + move |state: Option>| state.is_some_and(|s| *s == status) +} + +/// Event sent to backends to create an XR session. Should only be called in the [`XrStatus::Available`] state. #[derive(Event, Clone, Copy, Default)] pub struct CreateXrSession; -/// Event sent to backends to begin an XR session. Should only be called after [`XrSessionState::Ready`] is recieved. +/// Event sent to the backends to destroy an XR session. +#[derive(Event, Clone, Copy, Default)] +pub struct DestroyXrSession; + +/// Event sent to backends to begin an XR session. Should only be called in the [`XrStatus::Ready`] state. #[derive(Event, Clone, Copy, Default)] pub struct BeginXrSession; -/// Event sent to backends to end an XR session. Should only be called after [`XrSessionState::Running`] is recieved. +/// Event sent to backends to end an XR session. Should only be called in the [`XrStatus::Running`] state. #[derive(Event, Clone, Copy, Default)] pub struct EndXrSession; - -// /// Event sent to backends to destroy an XR session. -// #[derive(Event, Clone, Copy, Default)] -// pub struct DestroyXrSession; - -/// Event sent from backends to inform the frontend of the session state. -#[derive(Event, Clone, Copy)] -pub enum XrSessionState { - /// The session is in an idle state. Either just created or stopped - Idle, - /// The session is ready. You may send a [`BeginXrSession`] event. - Ready, - /// The session is running. - Running, - /// The session is being stopped - Stopping, - /// The session is destroyed - Destroyed, -} - -/// Event sent from backends to inform the frontend that the instance was created. -#[derive(Event, Clone, Copy, Default)] -pub struct XrInstanceCreated; - -/// Event sent from backends to inform the frontend that the instance was destroyed. -#[derive(Event, Clone, Copy, Default)] -pub struct XrInstanceDestroyed;