From 62cf605f4a7c8acf788ee9eb85e46ef6d09878a5 Mon Sep 17 00:00:00 2001 From: Schmarni Date: Mon, 21 Jul 2025 02:30:34 +0200 Subject: [PATCH] refactor(openxr): make environment blend modes a completely runtime feature, like it should and remove the fb_passthrough plugin from the default oxr plugins as quests now support blendmode based passthrough Signed-off-by: Schmarni --- crates/bevy_openxr/examples/3d_scene.rs | 5 +- .../bevy_openxr/examples/android/Cargo.toml | 6 +-- .../examples/android/hotham_examples.keystore | Bin 2726 -> 0 bytes .../bevy_openxr/examples/android/src/lib.rs | 1 + crates/bevy_openxr/examples/overlay.rs | 5 +- .../src/openxr/environment_blend_mode.rs | 39 ++++++++++++++ .../{passthrough.rs => fb_passthrough.rs} | 12 ++--- crates/bevy_openxr/src/openxr/features/mod.rs | 2 +- crates/bevy_openxr/src/openxr/init.rs | 49 +++++++----------- crates/bevy_openxr/src/openxr/mod.rs | 14 +---- crates/bevy_openxr/src/openxr/render.rs | 4 +- crates/bevy_openxr/src/openxr/resources.rs | 5 +- crates/bevy_xr/src/session.rs | 2 +- 13 files changed, 84 insertions(+), 60 deletions(-) delete mode 100644 crates/bevy_openxr/examples/android/hotham_examples.keystore create mode 100644 crates/bevy_openxr/src/openxr/environment_blend_mode.rs rename crates/bevy_openxr/src/openxr/features/{passthrough.rs => fb_passthrough.rs} (90%) diff --git a/crates/bevy_openxr/examples/3d_scene.rs b/crates/bevy_openxr/examples/3d_scene.rs index 3b6237e..8d291da 100644 --- a/crates/bevy_openxr/examples/3d_scene.rs +++ b/crates/bevy_openxr/examples/3d_scene.rs @@ -13,10 +13,11 @@ fn main() -> AppExit { DefaultPlugins.build().disable::(), )) .insert_resource(OxrSessionConfig { - blend_modes: Some(vec![ + blend_mode_preference: vec![ EnvironmentBlendMode::ALPHA_BLEND, + EnvironmentBlendMode::ADDITIVE, EnvironmentBlendMode::OPAQUE, - ]), + ], ..default() }) .add_plugins(bevy_mod_xr::hand_debug_gizmos::HandGizmosPlugin) diff --git a/crates/bevy_openxr/examples/android/Cargo.toml b/crates/bevy_openxr/examples/android/Cargo.toml index 3663aec..aadfe5a 100644 --- a/crates/bevy_openxr/examples/android/Cargo.toml +++ b/crates/bevy_openxr/examples/android/Cargo.toml @@ -92,6 +92,6 @@ categories = [ # https://developer.android.com/studio/publish/app-signing # # !! IMPORTANT !! -[package.metadata.android.signing.release] -path = "./hotham_examples.keystore" -keystore_password = "chomsky-vigilant-spa" +# [package.metadata.android.signing.release] +# path = "./hotham_examples.keystore" +# keystore_password = "chomsky-vigilant-spa" diff --git a/crates/bevy_openxr/examples/android/hotham_examples.keystore b/crates/bevy_openxr/examples/android/hotham_examples.keystore deleted file mode 100644 index 62623c4d30c143b63d33e39fb5b742e403ab5dbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2726 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, + current_blend_mode: EnvironmentBlendMode, +} + +impl OxrEnvironmentBlendModes { + pub(crate) fn new( + available_modes: Vec, + preferences: &[EnvironmentBlendMode], + ) -> Option { + let blend_mode = preferences.iter().find(|m| available_modes.contains(m))?; + + Some(Self { + available_blend_modes: available_modes, + current_blend_mode: *blend_mode, + }) + } + + /// returns whether the blend_mode was changed + pub fn set_blend_mode(&mut self, blend_mode: EnvironmentBlendMode) -> bool { + if self.available_blend_modes.contains(&blend_mode) { + self.current_blend_mode = blend_mode; + return true; + } + false + } + + pub fn blend_mode(&self) -> EnvironmentBlendMode { + self.current_blend_mode + } + + pub fn available_blend_modes(&self) -> &[EnvironmentBlendMode] { + &self.available_blend_modes + } +} diff --git a/crates/bevy_openxr/src/openxr/features/passthrough.rs b/crates/bevy_openxr/src/openxr/features/fb_passthrough.rs similarity index 90% rename from crates/bevy_openxr/src/openxr/features/passthrough.rs rename to crates/bevy_openxr/src/openxr/features/fb_passthrough.rs index 1993fe5..5eddf00 100644 --- a/crates/bevy_openxr/src/openxr/features/passthrough.rs +++ b/crates/bevy_openxr/src/openxr/features/fb_passthrough.rs @@ -11,9 +11,9 @@ use crate::resources::*; use crate::session::OxrSession; use crate::types::Result as OxrResult; -pub struct OxrPassthroughPlugin; +pub struct OxrFbPassthroughPlugin; -impl Plugin for OxrPassthroughPlugin { +impl Plugin for OxrFbPassthroughPlugin { fn build(&self, app: &mut App) { if app .world() @@ -94,12 +94,12 @@ pub fn supports_passthrough(instance: &OxrInstance, system: OxrSystemId) -> OxrR return Ok(false); } unsafe { - let mut hand = openxr::sys::SystemPassthroughProperties2FB { + let mut properties = openxr::sys::SystemPassthroughProperties2FB { ty: SystemPassthroughProperties2FB::TYPE, next: std::ptr::null(), capabilities: PassthroughCapabilityFlagsFB::PASSTHROUGH_CAPABILITY, }; - let mut p = openxr::sys::SystemProperties::out(&mut hand as *mut _ as _); + let mut p = openxr::sys::SystemProperties::out(&mut properties as *mut _ as _); cvt((instance.fp().get_system_properties)( instance.as_raw(), system.0, @@ -107,10 +107,10 @@ pub fn supports_passthrough(instance: &OxrInstance, system: OxrSystemId) -> OxrR ))?; bevy::log::info!( "From supports_passthrough: Passthrough capabilities: {:?}", - hand.capabilities + properties.capabilities ); Ok( - (hand.capabilities & PassthroughCapabilityFlagsFB::PASSTHROUGH_CAPABILITY) + (properties.capabilities & PassthroughCapabilityFlagsFB::PASSTHROUGH_CAPABILITY) == PassthroughCapabilityFlagsFB::PASSTHROUGH_CAPABILITY, ) } diff --git a/crates/bevy_openxr/src/openxr/features/mod.rs b/crates/bevy_openxr/src/openxr/features/mod.rs index b5d55e3..d7a1da6 100644 --- a/crates/bevy_openxr/src/openxr/features/mod.rs +++ b/crates/bevy_openxr/src/openxr/features/mod.rs @@ -1,4 +1,4 @@ pub mod handtracking; #[cfg(feature = "passthrough")] -pub mod passthrough; +pub mod fb_passthrough; pub mod overlay; diff --git a/crates/bevy_openxr/src/openxr/init.rs b/crates/bevy_openxr/src/openxr/init.rs index b5a9ed5..8e2aca1 100644 --- a/crates/bevy_openxr/src/openxr/init.rs +++ b/crates/bevy_openxr/src/openxr/init.rs @@ -28,6 +28,7 @@ use crate::session::OxrSessionCreateNextChain; use crate::types::Result as OxrResult; use crate::types::*; +use super::environment_blend_mode::OxrEnvironmentBlendModes; use super::exts::OxrEnabledExtensions; use super::poll_events::OxrEventHandlerExt; use super::poll_events::OxrEventIn; @@ -66,7 +67,6 @@ impl Default for OxrInitPlugin { app_info: default(), exts: { let mut exts = OxrExtensions::default(); - exts.enable_fb_passthrough(); exts.enable_hand_tracking(); exts }, @@ -104,6 +104,7 @@ impl Plugin for OxrInitPlugin { debug_flags: self.render_debug_flags, }, ExtractResourcePlugin::::default(), + ExtractResourcePlugin::::default(), )) .add_oxr_event_handler(handle_events) .add_systems( @@ -249,7 +250,7 @@ impl OxrInitPlugin { // check available extensions and send a warning for any wanted extensions that aren't available. for ext in available_exts.unavailable_exts(&self.exts) { - error!( + warn!( "Extension \"{ext}\" not available in the current OpenXR runtime. Disabling extension." ); } @@ -273,13 +274,7 @@ impl OxrInitPlugin { let exts = self.exts.clone() & available_exts; - let instance = entry.create_instance( - self.app_info.clone(), - exts.clone(), - // &["XR_APILAYER_LUNARG_api_dump"], - &[], - backend, - )?; + let instance = entry.create_instance(self.app_info.clone(), exts.clone(), &[], backend)?; let instance_props = instance.properties()?; info!( @@ -362,7 +357,7 @@ fn init_xr_session( system_id: openxr::SystemId, chain: &mut OxrSessionCreateNextChain, OxrSessionConfig { - blend_modes, + blend_mode_preference, formats, resolutions, }: OxrSessionConfig, @@ -374,6 +369,7 @@ fn init_xr_session( OxrSwapchain, OxrSwapchainImages, OxrCurrentSessionConfig, + OxrEnvironmentBlendModes, )> { let (session, frame_waiter, frame_stream) = unsafe { instance.create_session(system_id, graphics_info, chain)? }; @@ -461,25 +457,10 @@ fn init_xr_session( instance.enumerate_environment_blend_modes(system_id, view_configuration_type)?; // blend mode selection - let blend_mode = if let Some(wanted_blend_modes) = &blend_modes { - let mut blend_mode = None; - for wanted_blend_mode in wanted_blend_modes { - if available_blend_modes.contains(wanted_blend_mode) { - blend_mode = Some(*wanted_blend_mode); - break; - } - } - blend_mode - } else { - available_blend_modes.first().copied() - } - .ok_or(OxrError::NoAvailableBlendMode)?; + let blend_modes = OxrEnvironmentBlendModes::new(available_blend_modes, &blend_mode_preference) + .ok_or(OxrError::NoAvailableBlendMode)?; - let graphics_info = OxrCurrentSessionConfig { - blend_mode, - resolution, - format, - }; + let graphics_info = OxrCurrentSessionConfig { resolution, format }; Ok(( session, @@ -488,6 +469,7 @@ fn init_xr_session( swapchain, images, graphics_info, + blend_modes, )) } @@ -508,7 +490,15 @@ pub fn create_xr_session(world: &mut World) { session_config.clone(), session_create_info.clone(), ) { - Ok((session, frame_waiter, frame_stream, swapchain, images, graphics_info)) => { + Ok(( + session, + frame_waiter, + frame_stream, + swapchain, + images, + graphics_info, + blend_modes, + )) => { world.insert_resource(session.clone()); world.insert_resource(frame_waiter); world.insert_resource(images); @@ -524,6 +514,7 @@ pub fn create_xr_session(world: &mut World) { .expect("added by xr session plugin") .clone(), }); + world.insert_resource(blend_modes); } Err(e) => error!("Failed to initialize XrSession: {e}"), } diff --git a/crates/bevy_openxr/src/openxr/mod.rs b/crates/bevy_openxr/src/openxr/mod.rs index 989f30d..dc23337 100644 --- a/crates/bevy_openxr/src/openxr/mod.rs +++ b/crates/bevy_openxr/src/openxr/mod.rs @@ -14,14 +14,12 @@ use render::OxrRenderPlugin; use resources::OxrInstance; use session::OxrSession; -use self::{ - features::{handtracking::HandTrackingPlugin, passthrough::OxrPassthroughPlugin}, - reference_space::OxrReferenceSpacePlugin, -}; +use self::{features::handtracking::HandTrackingPlugin, reference_space::OxrReferenceSpacePlugin}; pub mod action_binding; pub mod action_set_attaching; pub mod action_set_syncing; +pub mod environment_blend_mode; pub mod error; pub mod exts; pub mod features; @@ -59,13 +57,11 @@ pub fn add_xr_plugins(plugins: G) -> PluginGroupBuilder { plugins .build() .disable::() - // .disable::() .add_before::(XrSessionPlugin { auto_handle: true }) .add_before::(OxrInitPlugin::default()) .add(OxrEventsPlugin) .add(OxrReferenceSpacePlugin::default()) .add(OxrRenderPlugin::default()) - .add(OxrPassthroughPlugin) .add(HandTrackingPlugin::default()) .add(XrCameraPlugin) .add(action_set_attaching::OxrActionAttachingPlugin) @@ -74,20 +70,14 @@ pub fn add_xr_plugins(plugins: G) -> PluginGroupBuilder { .add(features::overlay::OxrOverlayPlugin) .add(spaces::OxrSpatialPlugin) .add(spaces::OxrSpacePatchingPlugin) - // .add(XrActionPlugin) // we should probably handle the exiting ourselfs so that we can correctly end the // session and instance .set(WindowPlugin { primary_window: Some(Window { transparent: true, present_mode: PresentMode::AutoNoVsync, - // title: self.app_info.name.clone(), ..default() }), - // #[cfg(target_os = "android")] - // exit_condition: bevy::window::ExitCondition::DontExit, - #[cfg(target_os = "android")] - close_when_requested: true, ..default() }) } diff --git a/crates/bevy_openxr/src/openxr/render.rs b/crates/bevy_openxr/src/openxr/render.rs index 1e5af76..1f6eed1 100644 --- a/crates/bevy_openxr/src/openxr/render.rs +++ b/crates/bevy_openxr/src/openxr/render.rs @@ -22,6 +22,8 @@ use openxr::ViewStateFlags; use crate::{init::should_run_frame_loop, resources::*}; use crate::{layer_builder::ProjectionLayer, session::OxrSession}; +use super::environment_blend_mode::OxrEnvironmentBlendModes; + #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, SystemSet)] pub struct OxrRenderBegin; @@ -393,7 +395,7 @@ pub fn end_frame(world: &mut World) { let _span = debug_span!("xr_end_frame").entered(); if let Err(e) = frame_stream.end( frame_state.predicted_display_time, - world.resource::().blend_mode, + world.resource::().blend_mode(), &layers, ) { error!("Failed to end frame stream: {e}"); diff --git a/crates/bevy_openxr/src/openxr/resources.rs b/crates/bevy_openxr/src/openxr/resources.rs index 58fce9c..c98e041 100644 --- a/crates/bevy_openxr/src/openxr/resources.rs +++ b/crates/bevy_openxr/src/openxr/resources.rs @@ -332,7 +332,6 @@ pub struct OxrRenderLayers(pub Vec>); /// Resource storing graphics info for the currently running session. #[derive(Clone, Copy, Resource, ExtractResource)] pub struct OxrCurrentSessionConfig { - pub blend_mode: EnvironmentBlendMode, pub resolution: UVec2, pub format: wgpu::TextureFormat, } @@ -341,7 +340,7 @@ pub struct OxrCurrentSessionConfig { /// This is used to store information from startup that is needed to create the session after the instance has been created. pub struct OxrSessionConfig { /// List of blend modes the openxr session can use. If [None], pick the first available blend mode. - pub blend_modes: Option>, + pub blend_mode_preference: Vec, /// List of formats the openxr session can use. If [None], pick the first available format pub formats: Option>, /// List of resolutions that the openxr swapchain can use. If [None] pick the first available resolution. @@ -350,7 +349,7 @@ pub struct OxrSessionConfig { impl Default for OxrSessionConfig { fn default() -> Self { Self { - blend_modes: Some(vec![openxr::EnvironmentBlendMode::OPAQUE]), + blend_mode_preference: vec![openxr::EnvironmentBlendMode::OPAQUE], formats: Some(vec![wgpu::TextureFormat::Rgba8UnormSrgb]), resolutions: default(), } diff --git a/crates/bevy_xr/src/session.rs b/crates/bevy_xr/src/session.rs index 5506a3b..8273619 100644 --- a/crates/bevy_xr/src/session.rs +++ b/crates/bevy_xr/src/session.rs @@ -191,7 +191,7 @@ impl Plugin for XrSessionPlugin { render_app .init_schedule(XrPreDestroySession) - .init_resource::() + // .init_resource::() .configure_sets( Render, (XrRenderSet::HandleEvents, XrRenderSet::PreRender).chain(),