change xr status
This commit is contained in:
@@ -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<Res<XrStatus>>) -> bool {
|
||||
status.is_some_and(|status| status.instance_created)
|
||||
}
|
||||
|
||||
pub fn session_created(status: Option<Res<XrStatus>>) -> bool {
|
||||
status.is_some_and(|status| status.session_created)
|
||||
}
|
||||
|
||||
pub fn session_ready(status: Option<Res<XrStatus>>) -> bool {
|
||||
status.is_some_and(|status| status.session_ready)
|
||||
}
|
||||
|
||||
pub fn session_running(status: Option<Res<XrStatus>>) -> 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::<CreateXrSession>()),
|
||||
.run_if(on_event::<CreateXrSession>())
|
||||
.run_if(status_equals(XrStatus::Available)),
|
||||
begin_xr_session
|
||||
.run_if(session_ready)
|
||||
.run_if(status_equals(XrStatus::Ready))
|
||||
.run_if(on_event::<BeginXrSession>()),
|
||||
adopt_open_xr_trackers,
|
||||
)
|
||||
@@ -401,7 +390,6 @@ fn create_xr_session_inner(
|
||||
format,
|
||||
};
|
||||
|
||||
world.resource_mut::<XrStatus>().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<XrSession>,
|
||||
mut session_state: EventWriter<XrSessionState>,
|
||||
mut status: ResMut<XrStatus>,
|
||||
) {
|
||||
pub fn begin_xr_session(session: Res<XrSession>, mut status: ResMut<XrStatus>) {
|
||||
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<MainWorld
|
||||
}
|
||||
|
||||
/// Poll any OpenXR events and handle them accordingly
|
||||
pub fn poll_events(
|
||||
instance: Res<XrInstance>,
|
||||
session: Option<Res<XrSession>>,
|
||||
mut session_state: EventWriter<XrSessionState>,
|
||||
mut instance_destroyed: EventWriter<XrInstanceDestroyed>,
|
||||
mut status: ResMut<XrStatus>,
|
||||
) {
|
||||
pub fn poll_events(instance: Res<XrInstance>, mut status: ResMut<XrStatus>) {
|
||||
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(_) => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -20,6 +20,7 @@ impl XrEntry {
|
||||
&self,
|
||||
app_info: AppInfo,
|
||||
exts: XrExtensions,
|
||||
layers: &[&str],
|
||||
backend: GraphicsBackend,
|
||||
) -> Result<XrInstance> {
|
||||
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,
|
||||
|
||||
@@ -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<T> = std::result::Result<T, XrError>;
|
||||
|
||||
|
||||
@@ -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::<CreateXrSession>()
|
||||
.add_event::<DestroyXrSession>()
|
||||
.add_event::<BeginXrSession>()
|
||||
.add_event::<EndXrSession>()
|
||||
.add_event::<XrSessionState>()
|
||||
.add_event::<XrInstanceCreated>()
|
||||
.add_event::<XrInstanceDestroyed>()
|
||||
.add_systems(PreUpdate, handle_xr_events);
|
||||
.add_systems(
|
||||
First,
|
||||
handle_session.run_if(resource_exists_and_changed::<XrStatus>),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_xr_events(
|
||||
mut instance_created: EventReader<XrInstanceCreated>,
|
||||
mut session_state: EventReader<XrSessionState>,
|
||||
mut instance_destroyed: EventReader<XrInstanceDestroyed>,
|
||||
#[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<XrStatus>,
|
||||
mut create_session: EventWriter<CreateXrSession>,
|
||||
mut begin_session: EventWriter<BeginXrSession>,
|
||||
mut has_instance: Local<bool>,
|
||||
mut local_session_state: Local<Option<XrSessionState>>,
|
||||
mut end_session: EventWriter<EndXrSession>,
|
||||
) {
|
||||
// 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<Res<XrStatus>>) -> 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<Res<XrStatus>>) -> 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<Res<XrStatus>>) -> 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<Res<XrStatus>>) -> bool {
|
||||
move |state: Option<Res<XrStatus>>| 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;
|
||||
|
||||
Reference in New Issue
Block a user