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 <marnistromer@gmail.com>
This commit is contained in:
Schmarni
2025-07-21 02:30:34 +02:00
committed by Schmarni
parent 9569f591ac
commit 62cf605f4a
13 changed files with 84 additions and 60 deletions

View File

@@ -13,10 +13,11 @@ fn main() -> AppExit {
DefaultPlugins.build().disable::<PipelinedRenderingPlugin>(), DefaultPlugins.build().disable::<PipelinedRenderingPlugin>(),
)) ))
.insert_resource(OxrSessionConfig { .insert_resource(OxrSessionConfig {
blend_modes: Some(vec![ blend_mode_preference: vec![
EnvironmentBlendMode::ALPHA_BLEND, EnvironmentBlendMode::ALPHA_BLEND,
EnvironmentBlendMode::ADDITIVE,
EnvironmentBlendMode::OPAQUE, EnvironmentBlendMode::OPAQUE,
]), ],
..default() ..default()
}) })
.add_plugins(bevy_mod_xr::hand_debug_gizmos::HandGizmosPlugin) .add_plugins(bevy_mod_xr::hand_debug_gizmos::HandGizmosPlugin)

View File

@@ -92,6 +92,6 @@ categories = [
# https://developer.android.com/studio/publish/app-signing # https://developer.android.com/studio/publish/app-signing
# #
# !! IMPORTANT !! # !! IMPORTANT !!
[package.metadata.android.signing.release] # [package.metadata.android.signing.release]
path = "./hotham_examples.keystore" # path = "./hotham_examples.keystore"
keystore_password = "chomsky-vigilant-spa" # keystore_password = "chomsky-vigilant-spa"

View File

@@ -16,6 +16,7 @@ fn main() {
..default() ..default()
})) }))
.add_plugins(bevy_mod_xr::hand_debug_gizmos::HandGizmosPlugin) .add_plugins(bevy_mod_xr::hand_debug_gizmos::HandGizmosPlugin)
.add_plugins(bevy_mod_openxr::features::fb_passthrough::OxrFbPassthroughPlugin)
.add_systems(Startup, setup) .add_systems(Startup, setup)
.add_systems(Update, modify_msaa) .add_systems(Update, modify_msaa)
.insert_resource(AmbientLight { .insert_resource(AmbientLight {

View File

@@ -19,12 +19,13 @@ fn main() {
..OxrInitPlugin::default() ..OxrInitPlugin::default()
})) }))
.insert_resource(OxrSessionConfig { .insert_resource(OxrSessionConfig {
blend_modes: Some({ blend_mode_preference: {
vec![ vec![
EnvironmentBlendMode::ALPHA_BLEND, EnvironmentBlendMode::ALPHA_BLEND,
EnvironmentBlendMode::ADDITIVE,
EnvironmentBlendMode::OPAQUE, EnvironmentBlendMode::OPAQUE,
] ]
}), },
..OxrSessionConfig::default() ..OxrSessionConfig::default()
}) })
.insert_resource(ClearColor(Color::NONE)) .insert_resource(ClearColor(Color::NONE))

View File

@@ -0,0 +1,39 @@
use bevy::{ecs::resource::Resource, render::extract_resource::ExtractResource};
use openxr::EnvironmentBlendMode;
#[derive(Resource, ExtractResource, Clone)]
pub struct OxrEnvironmentBlendModes {
available_blend_modes: Vec<EnvironmentBlendMode>,
current_blend_mode: EnvironmentBlendMode,
}
impl OxrEnvironmentBlendModes {
pub(crate) fn new(
available_modes: Vec<EnvironmentBlendMode>,
preferences: &[EnvironmentBlendMode],
) -> Option<OxrEnvironmentBlendModes> {
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
}
}

View File

@@ -11,9 +11,9 @@ use crate::resources::*;
use crate::session::OxrSession; use crate::session::OxrSession;
use crate::types::Result as OxrResult; 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) { fn build(&self, app: &mut App) {
if app if app
.world() .world()
@@ -94,12 +94,12 @@ pub fn supports_passthrough(instance: &OxrInstance, system: OxrSystemId) -> OxrR
return Ok(false); return Ok(false);
} }
unsafe { unsafe {
let mut hand = openxr::sys::SystemPassthroughProperties2FB { let mut properties = openxr::sys::SystemPassthroughProperties2FB {
ty: SystemPassthroughProperties2FB::TYPE, ty: SystemPassthroughProperties2FB::TYPE,
next: std::ptr::null(), next: std::ptr::null(),
capabilities: PassthroughCapabilityFlagsFB::PASSTHROUGH_CAPABILITY, 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)( cvt((instance.fp().get_system_properties)(
instance.as_raw(), instance.as_raw(),
system.0, system.0,
@@ -107,10 +107,10 @@ pub fn supports_passthrough(instance: &OxrInstance, system: OxrSystemId) -> OxrR
))?; ))?;
bevy::log::info!( bevy::log::info!(
"From supports_passthrough: Passthrough capabilities: {:?}", "From supports_passthrough: Passthrough capabilities: {:?}",
hand.capabilities properties.capabilities
); );
Ok( Ok(
(hand.capabilities & PassthroughCapabilityFlagsFB::PASSTHROUGH_CAPABILITY) (properties.capabilities & PassthroughCapabilityFlagsFB::PASSTHROUGH_CAPABILITY)
== PassthroughCapabilityFlagsFB::PASSTHROUGH_CAPABILITY, == PassthroughCapabilityFlagsFB::PASSTHROUGH_CAPABILITY,
) )
} }

View File

@@ -1,4 +1,4 @@
pub mod handtracking; pub mod handtracking;
#[cfg(feature = "passthrough")] #[cfg(feature = "passthrough")]
pub mod passthrough; pub mod fb_passthrough;
pub mod overlay; pub mod overlay;

View File

@@ -28,6 +28,7 @@ use crate::session::OxrSessionCreateNextChain;
use crate::types::Result as OxrResult; use crate::types::Result as OxrResult;
use crate::types::*; use crate::types::*;
use super::environment_blend_mode::OxrEnvironmentBlendModes;
use super::exts::OxrEnabledExtensions; use super::exts::OxrEnabledExtensions;
use super::poll_events::OxrEventHandlerExt; use super::poll_events::OxrEventHandlerExt;
use super::poll_events::OxrEventIn; use super::poll_events::OxrEventIn;
@@ -66,7 +67,6 @@ impl Default for OxrInitPlugin {
app_info: default(), app_info: default(),
exts: { exts: {
let mut exts = OxrExtensions::default(); let mut exts = OxrExtensions::default();
exts.enable_fb_passthrough();
exts.enable_hand_tracking(); exts.enable_hand_tracking();
exts exts
}, },
@@ -104,6 +104,7 @@ impl Plugin for OxrInitPlugin {
debug_flags: self.render_debug_flags, debug_flags: self.render_debug_flags,
}, },
ExtractResourcePlugin::<OxrSessionStarted>::default(), ExtractResourcePlugin::<OxrSessionStarted>::default(),
ExtractResourcePlugin::<OxrEnvironmentBlendModes>::default(),
)) ))
.add_oxr_event_handler(handle_events) .add_oxr_event_handler(handle_events)
.add_systems( .add_systems(
@@ -249,7 +250,7 @@ impl OxrInitPlugin {
// check available extensions and send a warning for any wanted extensions that aren't available. // check available extensions and send a warning for any wanted extensions that aren't available.
for ext in available_exts.unavailable_exts(&self.exts) { for ext in available_exts.unavailable_exts(&self.exts) {
error!( warn!(
"Extension \"{ext}\" not available in the current OpenXR runtime. Disabling extension." "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 exts = self.exts.clone() & available_exts;
let instance = entry.create_instance( let instance = entry.create_instance(self.app_info.clone(), exts.clone(), &[], backend)?;
self.app_info.clone(),
exts.clone(),
// &["XR_APILAYER_LUNARG_api_dump"],
&[],
backend,
)?;
let instance_props = instance.properties()?; let instance_props = instance.properties()?;
info!( info!(
@@ -362,7 +357,7 @@ fn init_xr_session(
system_id: openxr::SystemId, system_id: openxr::SystemId,
chain: &mut OxrSessionCreateNextChain, chain: &mut OxrSessionCreateNextChain,
OxrSessionConfig { OxrSessionConfig {
blend_modes, blend_mode_preference,
formats, formats,
resolutions, resolutions,
}: OxrSessionConfig, }: OxrSessionConfig,
@@ -374,6 +369,7 @@ fn init_xr_session(
OxrSwapchain, OxrSwapchain,
OxrSwapchainImages, OxrSwapchainImages,
OxrCurrentSessionConfig, OxrCurrentSessionConfig,
OxrEnvironmentBlendModes,
)> { )> {
let (session, frame_waiter, frame_stream) = let (session, frame_waiter, frame_stream) =
unsafe { instance.create_session(system_id, graphics_info, chain)? }; 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)?; instance.enumerate_environment_blend_modes(system_id, view_configuration_type)?;
// blend mode selection // blend mode selection
let blend_mode = if let Some(wanted_blend_modes) = &blend_modes { let blend_modes = OxrEnvironmentBlendModes::new(available_blend_modes, &blend_mode_preference)
let mut blend_mode = None; .ok_or(OxrError::NoAvailableBlendMode)?;
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 graphics_info = OxrCurrentSessionConfig { let graphics_info = OxrCurrentSessionConfig { resolution, format };
blend_mode,
resolution,
format,
};
Ok(( Ok((
session, session,
@@ -488,6 +469,7 @@ fn init_xr_session(
swapchain, swapchain,
images, images,
graphics_info, graphics_info,
blend_modes,
)) ))
} }
@@ -508,7 +490,15 @@ pub fn create_xr_session(world: &mut World) {
session_config.clone(), session_config.clone(),
session_create_info.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(session.clone());
world.insert_resource(frame_waiter); world.insert_resource(frame_waiter);
world.insert_resource(images); world.insert_resource(images);
@@ -524,6 +514,7 @@ pub fn create_xr_session(world: &mut World) {
.expect("added by xr session plugin") .expect("added by xr session plugin")
.clone(), .clone(),
}); });
world.insert_resource(blend_modes);
} }
Err(e) => error!("Failed to initialize XrSession: {e}"), Err(e) => error!("Failed to initialize XrSession: {e}"),
} }

View File

@@ -14,14 +14,12 @@ use render::OxrRenderPlugin;
use resources::OxrInstance; use resources::OxrInstance;
use session::OxrSession; use session::OxrSession;
use self::{ use self::{features::handtracking::HandTrackingPlugin, reference_space::OxrReferenceSpacePlugin};
features::{handtracking::HandTrackingPlugin, passthrough::OxrPassthroughPlugin},
reference_space::OxrReferenceSpacePlugin,
};
pub mod action_binding; pub mod action_binding;
pub mod action_set_attaching; pub mod action_set_attaching;
pub mod action_set_syncing; pub mod action_set_syncing;
pub mod environment_blend_mode;
pub mod error; pub mod error;
pub mod exts; pub mod exts;
pub mod features; pub mod features;
@@ -59,13 +57,11 @@ pub fn add_xr_plugins<G: PluginGroup>(plugins: G) -> PluginGroupBuilder {
plugins plugins
.build() .build()
.disable::<RenderPlugin>() .disable::<RenderPlugin>()
// .disable::<PipelinedRenderingPlugin>()
.add_before::<RenderPlugin>(XrSessionPlugin { auto_handle: true }) .add_before::<RenderPlugin>(XrSessionPlugin { auto_handle: true })
.add_before::<RenderPlugin>(OxrInitPlugin::default()) .add_before::<RenderPlugin>(OxrInitPlugin::default())
.add(OxrEventsPlugin) .add(OxrEventsPlugin)
.add(OxrReferenceSpacePlugin::default()) .add(OxrReferenceSpacePlugin::default())
.add(OxrRenderPlugin::default()) .add(OxrRenderPlugin::default())
.add(OxrPassthroughPlugin)
.add(HandTrackingPlugin::default()) .add(HandTrackingPlugin::default())
.add(XrCameraPlugin) .add(XrCameraPlugin)
.add(action_set_attaching::OxrActionAttachingPlugin) .add(action_set_attaching::OxrActionAttachingPlugin)
@@ -74,20 +70,14 @@ pub fn add_xr_plugins<G: PluginGroup>(plugins: G) -> PluginGroupBuilder {
.add(features::overlay::OxrOverlayPlugin) .add(features::overlay::OxrOverlayPlugin)
.add(spaces::OxrSpatialPlugin) .add(spaces::OxrSpatialPlugin)
.add(spaces::OxrSpacePatchingPlugin) .add(spaces::OxrSpacePatchingPlugin)
// .add(XrActionPlugin)
// we should probably handle the exiting ourselfs so that we can correctly end the // we should probably handle the exiting ourselfs so that we can correctly end the
// session and instance // session and instance
.set(WindowPlugin { .set(WindowPlugin {
primary_window: Some(Window { primary_window: Some(Window {
transparent: true, transparent: true,
present_mode: PresentMode::AutoNoVsync, present_mode: PresentMode::AutoNoVsync,
// title: self.app_info.name.clone(),
..default() ..default()
}), }),
// #[cfg(target_os = "android")]
// exit_condition: bevy::window::ExitCondition::DontExit,
#[cfg(target_os = "android")]
close_when_requested: true,
..default() ..default()
}) })
} }

View File

@@ -22,6 +22,8 @@ use openxr::ViewStateFlags;
use crate::{init::should_run_frame_loop, resources::*}; use crate::{init::should_run_frame_loop, resources::*};
use crate::{layer_builder::ProjectionLayer, session::OxrSession}; use crate::{layer_builder::ProjectionLayer, session::OxrSession};
use super::environment_blend_mode::OxrEnvironmentBlendModes;
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, SystemSet)] #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, SystemSet)]
pub struct OxrRenderBegin; pub struct OxrRenderBegin;
@@ -393,7 +395,7 @@ pub fn end_frame(world: &mut World) {
let _span = debug_span!("xr_end_frame").entered(); let _span = debug_span!("xr_end_frame").entered();
if let Err(e) = frame_stream.end( if let Err(e) = frame_stream.end(
frame_state.predicted_display_time, frame_state.predicted_display_time,
world.resource::<OxrCurrentSessionConfig>().blend_mode, world.resource::<OxrEnvironmentBlendModes>().blend_mode(),
&layers, &layers,
) { ) {
error!("Failed to end frame stream: {e}"); error!("Failed to end frame stream: {e}");

View File

@@ -332,7 +332,6 @@ pub struct OxrRenderLayers(pub Vec<Box<dyn LayerProvider + Send + Sync>>);
/// Resource storing graphics info for the currently running session. /// Resource storing graphics info for the currently running session.
#[derive(Clone, Copy, Resource, ExtractResource)] #[derive(Clone, Copy, Resource, ExtractResource)]
pub struct OxrCurrentSessionConfig { pub struct OxrCurrentSessionConfig {
pub blend_mode: EnvironmentBlendMode,
pub resolution: UVec2, pub resolution: UVec2,
pub format: wgpu::TextureFormat, 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. /// This is used to store information from startup that is needed to create the session after the instance has been created.
pub struct OxrSessionConfig { pub struct OxrSessionConfig {
/// List of blend modes the openxr session can use. If [None], pick the first available blend mode. /// List of blend modes the openxr session can use. If [None], pick the first available blend mode.
pub blend_modes: Option<Vec<EnvironmentBlendMode>>, pub blend_mode_preference: Vec<EnvironmentBlendMode>,
/// List of formats the openxr session can use. If [None], pick the first available format /// List of formats the openxr session can use. If [None], pick the first available format
pub formats: Option<Vec<wgpu::TextureFormat>>, pub formats: Option<Vec<wgpu::TextureFormat>>,
/// List of resolutions that the openxr swapchain can use. If [None] pick the first available resolution. /// 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 { impl Default for OxrSessionConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
blend_modes: Some(vec![openxr::EnvironmentBlendMode::OPAQUE]), blend_mode_preference: vec![openxr::EnvironmentBlendMode::OPAQUE],
formats: Some(vec![wgpu::TextureFormat::Rgba8UnormSrgb]), formats: Some(vec![wgpu::TextureFormat::Rgba8UnormSrgb]),
resolutions: default(), resolutions: default(),
} }

View File

@@ -191,7 +191,7 @@ impl Plugin for XrSessionPlugin {
render_app render_app
.init_schedule(XrPreDestroySession) .init_schedule(XrPreDestroySession)
.init_resource::<XrRootTransform>() // .init_resource::<XrRootTransform>()
.configure_sets( .configure_sets(
Render, Render,
(XrRenderSet::HandleEvents, XrRenderSet::PreRender).chain(), (XrRenderSet::HandleEvents, XrRenderSet::PreRender).chain(),