pull main, refactor passthrough
This commit is contained in:
226
src/passthrough.rs
Normal file
226
src/passthrough.rs
Normal file
@@ -0,0 +1,226 @@
|
||||
use bevy::render::extract_resource::ExtractResource;
|
||||
use bevy::{prelude::*, render::extract_resource::ExtractResourcePlugin};
|
||||
use std::{marker::PhantomData, mem, ptr};
|
||||
|
||||
use crate::resources::XrSession;
|
||||
use crate::{
|
||||
resources::XrInstance,
|
||||
xr_arc_resource_wrapper,
|
||||
xr_init::{XrCleanup, XrSetup},
|
||||
};
|
||||
use openxr as xr;
|
||||
use xr::{
|
||||
sys::{Space, SystemPassthroughProperties2FB},
|
||||
CompositionLayerBase, CompositionLayerFlags, FormFactor, Graphics,
|
||||
PassthroughCapabilityFlagsFB,
|
||||
};
|
||||
|
||||
#[derive(
|
||||
Clone, Copy, Default, Debug, Resource, PartialEq, PartialOrd, Ord, Eq, Reflect, ExtractResource,
|
||||
)]
|
||||
pub enum XrPassthroughState {
|
||||
#[default]
|
||||
Unsupported,
|
||||
Running,
|
||||
Paused,
|
||||
}
|
||||
|
||||
pub struct PassthroughPlugin;
|
||||
xr_arc_resource_wrapper!(XrPassthrough, xr::Passthrough);
|
||||
xr_arc_resource_wrapper!(XrPassthroughLayer, xr::PassthroughLayer);
|
||||
|
||||
impl Plugin for PassthroughPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_event::<ResumePassthrough>();
|
||||
app.add_event::<PausePassthrough>();
|
||||
app.add_plugins(ExtractResourcePlugin::<XrPassthroughLayer>::default());
|
||||
app.add_plugins(ExtractResourcePlugin::<XrPassthroughState>::default());
|
||||
app.register_type::<XrPassthroughState>();
|
||||
app.add_systems(Startup, check_passthrough_support);
|
||||
app.add_systems(
|
||||
XrSetup,
|
||||
setup_passthrough
|
||||
.run_if(|state: Res<XrPassthroughState>| *state != XrPassthroughState::Unsupported),
|
||||
);
|
||||
app.add_systems(XrCleanup, cleanup_passthrough);
|
||||
app.add_systems(
|
||||
Update,
|
||||
resume_passthrough.run_if(
|
||||
resource_exists_and_equals(XrPassthroughState::Paused)
|
||||
.and_then(on_event::<ResumePassthrough>()),
|
||||
),
|
||||
);
|
||||
app.add_systems(
|
||||
Update,
|
||||
pause_passthrough.run_if(
|
||||
resource_exists_and_equals(XrPassthroughState::Running)
|
||||
.and_then(on_event::<PausePassthrough>()),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_passthrough_support(mut cmds: Commands, instance: Option<Res<XrInstance>>) {
|
||||
match instance {
|
||||
None => cmds.insert_resource(XrPassthroughState::Unsupported),
|
||||
Some(instance) => {
|
||||
let supported = instance.exts().fb_passthrough.is_some()
|
||||
&& supports_passthrough(
|
||||
&instance,
|
||||
instance.system(FormFactor::HEAD_MOUNTED_DISPLAY).unwrap(),
|
||||
)
|
||||
.is_ok_and(|v| v);
|
||||
match supported {
|
||||
false => cmds.insert_resource(XrPassthroughState::Unsupported),
|
||||
true => cmds.insert_resource(XrPassthroughState::Paused),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resume_passthrough(layer: Res<XrPassthroughLayer>, mut state: ResMut<XrPassthroughState>) {
|
||||
if let Err(e) = layer.resume() {
|
||||
warn!("Unable to resume Passthrough: {}", e);
|
||||
return;
|
||||
}
|
||||
info!("<=> Resume Passthrough");
|
||||
*state = XrPassthroughState::Running;
|
||||
}
|
||||
fn pause_passthrough(layer: Res<XrPassthroughLayer>, mut state: ResMut<XrPassthroughState>) {
|
||||
if let Err(e) = layer.pause() {
|
||||
warn!("Unable to resume Passthrough: {}", e);
|
||||
return;
|
||||
}
|
||||
info!("<=> Pausing Passthrough");
|
||||
*state = XrPassthroughState::Paused;
|
||||
}
|
||||
|
||||
fn cleanup_passthrough(mut cmds: Commands) {
|
||||
cmds.remove_resource::<XrPassthrough>();
|
||||
cmds.remove_resource::<XrPassthroughLayer>();
|
||||
}
|
||||
|
||||
fn setup_passthrough(mut cmds: Commands, instance: Res<XrInstance>, session: Res<XrSession>) {
|
||||
match create_passthrough(&instance, &session) {
|
||||
Ok((passthrough, layer)) => {
|
||||
cmds.insert_resource(XrPassthrough::from(passthrough));
|
||||
cmds.insert_resource(XrPassthroughLayer::from(layer));
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Unable to create passthrough: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Reflect, Event)]
|
||||
pub struct ResumePassthrough;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Reflect, Event)]
|
||||
pub struct PausePassthrough;
|
||||
|
||||
fn cvt(x: xr::sys::Result) -> xr::Result<xr::sys::Result> {
|
||||
if x.into_raw() >= 0 {
|
||||
Ok(x)
|
||||
} else {
|
||||
Err(x)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct CompositionLayerPassthrough<'a, G: xr::Graphics> {
|
||||
inner: xr::sys::CompositionLayerPassthroughFB,
|
||||
_marker: PhantomData<&'a G>,
|
||||
}
|
||||
impl<'a, G: Graphics> std::ops::Deref for CompositionLayerPassthrough<'a, G> {
|
||||
type Target = CompositionLayerBase<'a, G>;
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { mem::transmute(&self.inner) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, G: xr::Graphics> CompositionLayerPassthrough<'a, G> {
|
||||
pub(crate) fn from_xr_passthrough_layer(layer: &XrPassthroughLayer) -> Self {
|
||||
Self {
|
||||
inner: xr::sys::CompositionLayerPassthroughFB {
|
||||
ty: xr::sys::CompositionLayerPassthroughFB::TYPE,
|
||||
next: ptr::null(),
|
||||
flags: CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA,
|
||||
space: Space::NULL,
|
||||
layer_handle: *layer.inner(),
|
||||
},
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn supports_passthrough(instance: &XrInstance, system: xr::SystemId) -> xr::Result<bool> {
|
||||
unsafe {
|
||||
let mut hand = xr::sys::SystemPassthroughProperties2FB {
|
||||
ty: SystemPassthroughProperties2FB::TYPE,
|
||||
next: ptr::null(),
|
||||
capabilities: PassthroughCapabilityFlagsFB::PASSTHROUGH_CAPABILITY,
|
||||
};
|
||||
let mut p = xr::sys::SystemProperties::out(&mut hand as *mut _ as _);
|
||||
cvt((instance.fp().get_system_properties)(
|
||||
instance.as_raw(),
|
||||
system,
|
||||
p.as_mut_ptr(),
|
||||
))?;
|
||||
bevy::log::info!(
|
||||
"From supports_passthrough: Passthrough capabilities: {:?}",
|
||||
hand.capabilities
|
||||
);
|
||||
Ok(
|
||||
(hand.capabilities & PassthroughCapabilityFlagsFB::PASSTHROUGH_CAPABILITY)
|
||||
== PassthroughCapabilityFlagsFB::PASSTHROUGH_CAPABILITY,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn create_passthrough(
|
||||
instance: &XrInstance,
|
||||
xr_session: &XrSession,
|
||||
) -> xr::Result<(xr::Passthrough, xr::PassthroughLayer)> {
|
||||
let passthrough = match xr_session {
|
||||
XrSession::Vulkan(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,
|
||||
),
|
||||
}?;
|
||||
Ok((passthrough, passthrough_layer))
|
||||
}
|
||||
|
||||
// #[inline]
|
||||
// pub fn passthrough_layer_resume(mut layer: ResMut<XrPassthroughLayer>, mut passthrough: ResMut<XrPassthrough>) -> xr::Result<()> {
|
||||
// layer.resume()
|
||||
// }
|
||||
|
||||
// #[inline]
|
||||
// pub fn passthrough_layer_pause(mut xr_data_resource: ResMut<XrRenderData>) -> xr::Result<()> {
|
||||
// unsafe {
|
||||
// let passthrough_layer = &xr_data_resource.xr_passthrough_layer;
|
||||
// {
|
||||
// let passthrough_layer_locked = passthrough_layer.lock().unwrap();
|
||||
// cvt((xr_data_resource
|
||||
// .xr_instance
|
||||
// .exts()
|
||||
// .fb_passthrough
|
||||
// .unwrap()
|
||||
// .passthrough_layer_pause)(
|
||||
// *passthrough_layer_locked
|
||||
// ))?;
|
||||
// }
|
||||
// xr_data_resource.xr_passthrough_active = false;
|
||||
// bevy::log::info!("Paused passthrough layer");
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
Reference in New Issue
Block a user