diff --git a/examples/android/manifest.yaml b/examples/android/manifest.yaml index 9a6118c..7622de3 100644 --- a/examples/android/manifest.yaml +++ b/examples/android/manifest.yaml @@ -3,6 +3,15 @@ android: - "runtime_libs" manifest: package: "org.bevyengine.example_openxr_android" + uses_feature: + - name: "android.hardware.vr.headtracking" + required: true + - name: "oculus.software.handtracking" + required: true + - name: "com.oculus.feature.PASSTHROUGH" + required: true + - name: "com.oculus.experimental.enabled" + required: true application: label: "Bevy Openxr Android" theme: "@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen" @@ -14,7 +23,7 @@ android: - name: "com.oculus.supportedDevices" value: "quest|quest2|quest3|questpro" activities: - - config_changes: "density|keyboard|keyboardHidden|navigation|orientation|screenLayout|screenSize|uiMode" + - config_changes: "density|keyboard|keyboardHidden|navigation|orientation|screenLayout|screenSize|uiMode|screenLayout" launch_mode: "singleTask" orientation: "landscape" intent_filters: @@ -23,5 +32,6 @@ android: categories: - "com.oculus.intent.category.VR" - "android.intent.category.LAUNCHER" + - "org.khronos.openxr.intent.category.IMMERSIVE_HMD" sdk: target_sdk_version: 32 diff --git a/examples/android/src/lib.rs b/examples/android/src/lib.rs index 4fabb2b..efb835a 100644 --- a/examples/android/src/lib.rs +++ b/examples/android/src/lib.rs @@ -1,7 +1,11 @@ use bevy::diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}; use bevy::prelude::*; use bevy::transform::components::Transform; +use bevy_oxr::graphics::extensions::XrExtensions; use bevy_oxr::graphics::XrAppInfo; +use bevy_oxr::graphics::XrPreferdBlendMode::AlphaBlend; +use bevy_oxr::passthrough::{passthrough_layer_pause, passthrough_layer_resume}; +use bevy_oxr::xr_init::XrRenderData; use bevy_oxr::xr_input::debug_gizmos::OpenXrDebugRenderer; use bevy_oxr::xr_input::prototype_locomotion::{proto_locomotion, PrototypeLocomotionConfig}; use bevy_oxr::xr_input::trackers::{ @@ -11,18 +15,21 @@ use bevy_oxr::DefaultXrPlugins; #[bevy_main] fn main() { + let mut xr_extensions = XrExtensions::default(); + xr_extensions.enable_fb_passthrough(); App::new() .add_plugins(DefaultXrPlugins { + reqeusted_extensions: xr_extensions, app_info: XrAppInfo { name: "Bevy OXR Android Example".into(), }, - ..default() + prefered_blend_mode: bevy_oxr::graphics::XrPreferdBlendMode::Opaque, }) .add_plugins(OpenXrDebugRenderer) .add_plugins(LogDiagnosticsPlugin::default()) .add_plugins(FrameTimeDiagnosticsPlugin) .add_systems(Startup, setup) - .add_systems(Update, proto_locomotion) + .add_systems(Update, (proto_locomotion, toggle_passthrough)) .add_systems(Startup, spawn_controllers_example) .insert_resource(PrototypeLocomotionConfig::default()) .run(); @@ -64,11 +71,6 @@ fn setup( transform: Transform::from_xyz(4.0, 8.0, 4.0), ..default() }); - // camera - // commands.spawn((Camera3dBundle { - // transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), - // ..default() - // },)); } fn spawn_controllers_example(mut commands: Commands) { @@ -87,3 +89,16 @@ fn spawn_controllers_example(mut commands: Commands) { SpatialBundle::default(), )); } + +// Does this work? Not getting logs +fn toggle_passthrough(keys: Res>, mut xr_data: ResMut) { + if keys.just_pressed(KeyCode::Space) { + if xr_data.xr_passthrough_active { + passthrough_layer_pause(xr_data); + bevy::log::info!("Passthrough paused"); + } else { + passthrough_layer_resume(xr_data); + bevy::log::info!("Passthrough resumed"); + } + } +} diff --git a/examples/demo/manifest.yaml b/examples/demo/manifest.yaml index 6cba5b8..f514707 100644 --- a/examples/demo/manifest.yaml +++ b/examples/demo/manifest.yaml @@ -3,27 +3,27 @@ android: - "runtime_libs" manifest: package: "org.bevyengine.demo_openxr_android" - # Are features and permissions fliped? uses_feature: - name: "android.hardware.vr.headtracking" required: true - name: "oculus.software.handtracking" - required: false - # - name: "com.oculus.feature.PASSTHROUGH" - # required: true - uses_permission: - - name: "com.oculus.permission.HAND_TRACKING" - - name: "android.permission.INTERNET" + required: true + - name: "com.oculus.feature.PASSTHROUGH" + required: true + - name: "com.oculus.experimental.enabled" + required: true application: label: "Bevy Openxr Android" theme: "@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen" meta_data: + - name: "com.oculus.intent.category.VR" + value: "vr_only" - name: "com.samsung.android.vr.application.mode" value: "vr_only" - name: "com.oculus.supportedDevices" - value: "quest|quest2|quest3" + value: "quest|quest2|quest3|questpro" activities: - - config_changes: "density|keyboard|keyboardHidden|navigation|orientation|screenLayout|screenSize|uiMode" + - config_changes: "density|keyboard|keyboardHidden|navigation|orientation|screenLayout|screenSize|uiMode|screenLayout" launch_mode: "singleTask" orientation: "landscape" intent_filters: @@ -32,5 +32,6 @@ android: categories: - "com.oculus.intent.category.VR" - "android.intent.category.LAUNCHER" + - "org.khronos.openxr.intent.category.IMMERSIVE_HMD" sdk: target_sdk_version: 32 diff --git a/examples/demo/src/lib.rs b/examples/demo/src/lib.rs index e7feb94..d55db1a 100644 --- a/examples/demo/src/lib.rs +++ b/examples/demo/src/lib.rs @@ -65,6 +65,7 @@ pub fn main() { info!("Running bevy_openxr demo"); let mut app = App::new(); let mut xr_extensions = XrExtensions::default(); + xr_extensions.enable_fb_passthrough(); app //lets get the usual diagnostic stuff added diff --git a/src/graphics/extensions.rs b/src/graphics/extensions.rs index 2276b79..81d8ca7 100644 --- a/src/graphics/extensions.rs +++ b/src/graphics/extensions.rs @@ -10,14 +10,14 @@ impl XrExtensions { pub fn raw(&self) -> &ExtensionSet { &self.0 } - // pub fn enable_fb_passthrough(&mut self) -> &mut Self { - // self.0.fb_passthrough = true; - // self - // } - // pub fn disable_fb_passthrough(&mut self) -> &mut Self { - // self.0.fb_passthrough = false; - // self - // } + pub fn enable_fb_passthrough(&mut self) -> &mut Self { + self.0.fb_passthrough = true; + self + } + pub fn disable_fb_passthrough(&mut self) -> &mut Self { + self.0.fb_passthrough = false; + self + } pub fn enable_hand_tracking(&mut self) -> &mut Self { self.0.ext_hand_tracking = true; self diff --git a/src/graphics/mod.rs b/src/graphics/mod.rs index 33dc317..147fa1c 100644 --- a/src/graphics/mod.rs +++ b/src/graphics/mod.rs @@ -12,9 +12,10 @@ use wgpu::Instance; use crate::input::XrInput; use crate::resources::{ - OXrSessionSetupInfo, XrEnvironmentBlendMode, XrFormat, XrFrameState, XrFrameWaiter, XrInstance, - XrResolution, XrSession, XrSessionRunning, XrSwapchain, XrViews, + XrEnvironmentBlendMode, XrFormat, XrFrameState, XrFrameWaiter, XrInstance, XrResolution, + XrSession, XrSessionRunning, XrSwapchain, XrViews, }; +use crate::OXrSessionSetupInfo; use openxr as xr; diff --git a/src/graphics/vulkan.rs b/src/graphics/vulkan.rs index f4ca0f0..2a4bb44 100644 --- a/src/graphics/vulkan.rs +++ b/src/graphics/vulkan.rs @@ -16,6 +16,7 @@ use xr::EnvironmentBlendMode; use crate::graphics::extensions::XrExtensions; use crate::input::XrInput; + use crate::resources::{ OXrSessionSetupInfo, Swapchain, SwapchainInner, VulkanOXrSessionSetupInfo, XrEnvironmentBlendMode, XrFormat, XrFrameState, XrFrameWaiter, XrInstance, XrResolution, @@ -47,7 +48,7 @@ pub fn initialize_xr_instance( let available_extensions: XrExtensions = xr_entry.enumerate_extensions()?.into(); assert!(available_extensions.raw().khr_vulkan_enable2); - info!("available xr exts: {:#?}", available_extensions); + //info!("available xr exts: {:#?}", available_extensions); let mut enabled_extensions: xr::ExtensionSet = (available_extensions & reqeusted_extensions).into(); @@ -58,7 +59,7 @@ pub fn initialize_xr_instance( } let available_layers = xr_entry.enumerate_layers()?; - info!("available xr layers: {:#?}", available_layers); + //info!("available xr layers: {:#?}", available_layers); let xr_instance = xr_entry.create_instance( &xr::ApplicationInfo { @@ -88,17 +89,23 @@ pub fn initialize_xr_instance( let blend_modes = xr_instance.enumerate_environment_blend_modes(xr_system_id, VIEW_TYPE)?; let blend_mode: EnvironmentBlendMode = match prefered_blend_mode { XrPreferdBlendMode::Opaque if blend_modes.contains(&EnvironmentBlendMode::OPAQUE) => { + bevy::log::info!("Using Opaque"); EnvironmentBlendMode::OPAQUE } XrPreferdBlendMode::Additive if blend_modes.contains(&EnvironmentBlendMode::ADDITIVE) => { + bevy::log::info!("Using Additive"); EnvironmentBlendMode::ADDITIVE } XrPreferdBlendMode::AlphaBlend if blend_modes.contains(&EnvironmentBlendMode::ALPHA_BLEND) => { + bevy::log::info!("Using AlphaBlend"); EnvironmentBlendMode::ALPHA_BLEND } - _ => EnvironmentBlendMode::OPAQUE, + _ => { + bevy::log::info!("Using Opaque"); + EnvironmentBlendMode::OPAQUE + } }; #[cfg(not(target_os = "android"))] @@ -359,6 +366,8 @@ pub fn start_xr_session( .map(|surface| surface.get_capabilities(wgpu_adapter).formats[0]) .unwrap_or(wgpu::TextureFormat::Rgba8UnormSrgb); + // TODO: Log swapchain format + let resolution = uvec2( views[0].recommended_image_rect_width, views[0].recommended_image_rect_height, @@ -436,7 +445,7 @@ pub fn start_xr_session( .collect(); Ok(( - session.clone().into_any_graphics().into(), + XrSession::Vulkan(session.clone()), resolution.into(), swapchain_format.into(), AtomicBool::new(false).into(), diff --git a/src/lib.rs b/src/lib.rs index 647b666..ae0a5a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ pub mod graphics; pub mod input; -// pub mod passthrough; +pub mod passthrough; pub mod resource_macros; pub mod resources; pub mod xr_init; @@ -11,41 +11,31 @@ use std::io::{BufWriter, Write}; use std::sync::atomic::AtomicBool; use std::sync::{Arc, Mutex}; +use crate::passthrough::ResumePassthrough; use crate::xr_init::{StartXrSession, XrInitPlugin}; -use crate::xr_input::hands::hand_tracking::DisableHandTracking; use crate::xr_input::oculus_touch::ActionSets; use bevy::app::{AppExit, PluginGroupBuilder}; use bevy::core::TaskPoolThreadAssignmentPolicy; use bevy::ecs::system::SystemState; use bevy::prelude::*; -use bevy::render::camera::{ - camera_system, ManualTextureView, ManualTextureViewHandle, ManualTextureViews, -}; -use bevy::render::extract_resource::ExtractResourcePlugin; +use bevy::render::camera::{ManualTextureView, ManualTextureViewHandle, ManualTextureViews}; use bevy::render::pipelined_rendering::PipelinedRenderingPlugin; use bevy::render::renderer::{render_system, RenderInstance}; use bevy::render::settings::RenderCreation; -use bevy::render::view::ExtractedView; use bevy::render::{Render, RenderApp, RenderPlugin, RenderSet}; -use bevy::tasks::available_parallelism; -use bevy::transform::systems::{propagate_transforms, sync_simple_transforms}; use bevy::window::{PresentMode, PrimaryWindow, RawHandleWrapper}; use graphics::extensions::XrExtensions; use graphics::{XrAppInfo, XrPreferdBlendMode}; use input::XrInput; use openxr as xr; -// use passthrough::{start_passthrough, supports_passthrough, XrPassthroughLayer}; +use passthrough::{PassthroughPlugin, XrPassthroughLayer, XrPassthroughState}; use resources::*; -use xr::{FormFactor, FrameState}; use xr_init::{ xr_after_wait_only, xr_only, xr_render_only, CleanupXrData, XrEarlyInitPlugin, XrHasWaited, XrShouldRender, XrStatus, }; use xr_input::controllers::XrControllerType; -use xr_input::hands::emulated::HandEmulationPlugin; -use xr_input::hands::hand_tracking::{HandTrackingData, HandTrackingPlugin}; use xr_input::hands::XrHandPlugins; -use xr_input::xr_camera::{XRProjection, XrCameraType}; use xr_input::OpenXrInput; const VIEW_TYPE: xr::ViewConfigurationType = xr::ViewConfigurationType::PRIMARY_STEREO; @@ -61,6 +51,11 @@ pub struct OpenXrPlugin { app_info: XrAppInfo, } +fn mr_test(mut commands: Commands, mut resume: EventWriter) { + commands.insert_resource(ClearColor(Color::rgba(0.0, 0.0, 0.0, 0.0))); + resume.send_default(); +} + impl Plugin for OpenXrPlugin { fn build(&self, app: &mut App) { app.insert_resource(XrSessionRunning::new(AtomicBool::new(false))); @@ -88,6 +83,8 @@ impl Plugin for OpenXrPlugin { debug!("Configured wgpu adapter Limits: {:#?}", device.limits()); debug!("Configured wgpu adapter Features: {:#?}", device.features()); warn!("Starting with OpenXR Instance"); + app.insert_resource(xr_instance.clone()); + app.insert_resource(blend_mode.clone()); app.insert_resource(ActionSets(vec![])); app.insert_resource(xr_instance); app.insert_resource(blend_mode); @@ -117,6 +114,7 @@ impl Plugin for OpenXrPlugin { app.add_plugins(RenderPlugin::default()); app.insert_resource(XrStatus::Disabled); } + app.add_systems(Update, mr_test.run_if(xr_only())); app.add_systems( PreUpdate, xr_poll_events.run_if(|status: Res| *status != XrStatus::NoInstance), @@ -227,6 +225,7 @@ impl PluginGroup for DefaultXrPlugins { .add_after::(XrInitPlugin) .add_before::(XrEarlyInitPlugin) .add(XrHandPlugins) + .add(PassthroughPlugin) .add(XrResourcePlugin) .set(WindowPlugin { #[cfg(not(target_os = "android"))] @@ -237,7 +236,7 @@ impl PluginGroup for DefaultXrPlugins { ..default() }), #[cfg(target_os = "android")] - primary_window: None, + primary_window: None, // ? #[cfg(target_os = "android")] exit_condition: bevy::window::ExitCondition::DontExit, #[cfg(target_os = "android")] @@ -315,7 +314,6 @@ pub fn xr_wait_frame( ) { { let _span = info_span!("xr_wait_frame").entered(); - info!("Pre Frame Wait"); *frame_state = match frame_waiter.wait() { Ok(a) => a.into(), Err(e) => { @@ -323,15 +321,6 @@ pub fn xr_wait_frame( return; } }; - info!( - "Post Wait Time: {}", - frame_state.predicted_display_time.as_nanos() - ); - // frame_state.predicted_display_time = xr::Time::from_nanos( - // frame_state.predicted_display_time.as_nanos() - // + frame_state.predicted_display_period.as_nanos(), - // ); - info!("Post Frame Wait"); **should_render = frame_state.should_render; **waited = true; } @@ -369,6 +358,7 @@ pub fn xr_pre_frame( } } +#[allow(clippy::too_many_arguments)] pub fn xr_end_frame( xr_frame_state: Res, views: Res, @@ -376,6 +366,8 @@ pub fn xr_end_frame( swapchain: Res, resolution: Res, environment_blend_mode: Res, + passthrough_layer: Option>, + passthrough_state: Option>, ) { #[cfg(target_os = "android")] { @@ -383,23 +375,24 @@ pub fn xr_end_frame( let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }.unwrap(); let env = vm.attach_current_thread_as_daemon(); } + { let _span = info_span!("xr_release_image").entered(); swapchain.release_image().unwrap(); } { let _span = info_span!("xr_end_frame").entered(); - info!( - "End Frame Time: {}", - xr_frame_state.predicted_display_time.as_nanos() - ); + let pass_layer = match passthrough_state.as_deref() { + Some(XrPassthroughState::Running) => passthrough_layer.as_deref(), + _ => None, + }; let result = swapchain.end( xr_frame_state.predicted_display_time, &views, &input.stage, **resolution, **environment_blend_mode, - // passthrough_layer.map(|p| p.into_inner()), + pass_layer, ); match result { Ok(_) => {} diff --git a/src/passthrough.rs b/src/passthrough.rs new file mode 100644 index 0000000..51256e7 --- /dev/null +++ b/src/passthrough.rs @@ -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::(); + app.add_event::(); + app.add_plugins(ExtractResourcePlugin::::default()); + app.add_plugins(ExtractResourcePlugin::::default()); + app.register_type::(); + app.add_systems(Startup, check_passthrough_support); + app.add_systems( + XrSetup, + setup_passthrough + .run_if(|state: Res| *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::()), + ), + ); + app.add_systems( + Update, + pause_passthrough.run_if( + resource_exists_and_equals(XrPassthroughState::Running) + .and_then(on_event::()), + ), + ); + } +} + +fn check_passthrough_support(mut cmds: Commands, instance: Option>) { + 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, mut state: ResMut) { + if let Err(e) = layer.resume() { + warn!("Unable to resume Passthrough: {}", e); + return; + } + info!("<=> Resume Passthrough"); + *state = XrPassthroughState::Running; +} +fn pause_passthrough(layer: Res, mut state: ResMut) { + 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::(); + cmds.remove_resource::(); +} + +fn setup_passthrough(mut cmds: Commands, instance: Res, session: Res) { + 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 { + 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 { + 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, mut passthrough: ResMut) -> xr::Result<()> { +// layer.resume() +// } + +// #[inline] +// pub fn passthrough_layer_pause(mut xr_data_resource: ResMut) -> 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(()) +// } +// } diff --git a/src/resources.rs b/src/resources.rs index 41678bb..3b6ee26 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -3,14 +3,20 @@ use std::sync::atomic::AtomicBool; use std::sync::Mutex; use crate::input::XrInput; -// use crate::passthrough::XrPassthroughLayer; +use crate::passthrough::{CompositionLayerPassthrough, XrPassthroughLayer}; +use crate::resource_macros::*; +use crate::xr::sys::CompositionLayerPassthroughFB; +use crate::xr::{CompositionLayerBase, CompositionLayerFlags}; use crate::{resource_macros::*, xr_resource_wrapper_copy}; use bevy::prelude::*; -use bevy::render::extract_resource::ExtractResourcePlugin; +use bevy::prelude::*; +use bevy::render::extract_component::ExtractComponent; +use bevy::render::extract_resource::{ExtractResource, ExtractResourcePlugin}; +use core::ptr; use openxr as xr; xr_resource_wrapper!(XrInstance, xr::Instance); -xr_resource_wrapper!(XrSession, xr::Session); +// xr_resource_wrapper!(XrSession, xr::Session); xr_resource_wrapper_copy!(XrEnvironmentBlendMode, xr::EnvironmentBlendMode); xr_resource_wrapper_copy!(XrResolution, UVec2); xr_resource_wrapper_copy!(XrFormat, wgpu::TextureFormat); @@ -20,6 +26,23 @@ xr_arc_resource_wrapper!(XrSessionRunning, AtomicBool); xr_arc_resource_wrapper!(XrSwapchain, Swapchain); xr_no_clone_resource_wrapper!(XrFrameWaiter, xr::FrameWaiter); +#[derive(Clone, Resource, ExtractResource)] +pub enum XrSession { + Vulkan(xr::Session), +} + +impl std::ops::Deref for XrSession { + type Target = xr::Session; + + fn deref(&self) -> &Self::Target { + // SAFTEY: should be fine i think -Schmarni + unsafe { + match self { + XrSession::Vulkan(sess) => std::mem::transmute(sess), + } + } + } +} pub struct VulkanOXrSessionSetupInfo { pub(crate) device_ptr: *const c_void, @@ -91,7 +114,7 @@ impl Swapchain { stage: &xr::Space, resolution: UVec2, environment_blend_mode: xr::EnvironmentBlendMode, - // passthrough_layer: Option<&XrPassthroughLayer>, + passthrough_layer: Option<&XrPassthroughLayer>, ) -> xr::Result<()> { match self { Swapchain::Vulkan(swapchain) => swapchain.end( @@ -100,7 +123,7 @@ impl Swapchain { stage, resolution, environment_blend_mode, - // passthrough_layer, + passthrough_layer, ), } } @@ -160,7 +183,7 @@ impl SwapchainInner { stage: &xr::Space, resolution: UVec2, environment_blend_mode: xr::EnvironmentBlendMode, - // passthrough_layer: Option<&XrPassthroughLayer>, + passthrough_layer: Option<&XrPassthroughLayer>, ) -> xr::Result<()> { let rect = xr::Rect2Di { offset: xr::Offset2Di { x: 0, y: 0 }, @@ -174,76 +197,71 @@ impl SwapchainInner { warn!("views are len of 0"); return Ok(()); } - // match passthrough_layer { - // Some(pass) => { - // // info!("Rendering with pass through"); - // let passthrough_layer = xr::sys::CompositionLayerPassthroughFB { - // ty: CompositionLayerPassthroughFB::TYPE, - // next: ptr::null(), - // flags: CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA, - // space: xr::sys::Space::NULL, - // layer_handle: pass.0, - // }; - // self.stream.lock().unwrap().end( - // predicted_display_time, - // environment_blend_mode, - // &[ - // &xr::CompositionLayerProjection::new() - // .layer_flags(CompositionLayerFlags::UNPREMULTIPLIED_ALPHA) - // .space(stage) - // .views(&[ - // xr::CompositionLayerProjectionView::new() - // .pose(views[0].pose) - // .fov(views[0].fov) - // .sub_image( - // xr::SwapchainSubImage::new() - // .swapchain(&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(&swapchain) - // .image_array_index(1) - // .image_rect(rect), - // ), - // ]), - // unsafe { - // &*(&passthrough_layer as *const _ as *const CompositionLayerBase) - // }, - // ], - // ) - // } + match passthrough_layer { + Some(pass) => { + //bevy::log::info!("Rendering with pass through"); - // None => - let r = self.stream.lock().unwrap().end( - 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(&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(&swapchain) - .image_array_index(1) - .image_rect(rect), - ), - ])], - ); - r - // } + self.stream.lock().unwrap().end( + predicted_display_time, + environment_blend_mode, + &[ + &CompositionLayerPassthrough::from_xr_passthrough_layer( + pass, + ), + &xr::CompositionLayerProjection::new() + .layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA) + .space(stage) + .views(&[ + xr::CompositionLayerProjectionView::new() + .pose(views[0].pose) + .fov(views[0].fov) + .sub_image( + xr::SwapchainSubImage::new() + .swapchain(&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(&swapchain) + .image_array_index(1) + .image_rect(rect), + ), + ]), + ], + ) + } + + None => { + bevy::log::info!("Rendering without pass through"); + self.stream.lock().unwrap().end( + 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(&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(&swapchain) + .image_array_index(1) + .image_rect(rect), + ), + ])], + ) + } + } } }