Working passthrough for Meta Quest 3 (#66)

* Window is None

* Builds but check manifest

* debug prints

* Started, not passing last "cvt"

* Passthrough works, bevy not visible

* Passthrough working

* Passthrough working

* Working passthrough
This commit is contained in:
Rasmus Hogslätt
2024-02-01 05:05:24 +01:00
committed by GitHub
parent 53af86b073
commit 71a08798ef
9 changed files with 398 additions and 137 deletions

View File

@@ -3,6 +3,15 @@ android:
- "runtime_libs" - "runtime_libs"
manifest: manifest:
package: "org.bevyengine.example_openxr_android" 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: application:
label: "Bevy Openxr Android" label: "Bevy Openxr Android"
theme: "@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen" theme: "@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen"
@@ -14,7 +23,7 @@ android:
- name: "com.oculus.supportedDevices" - name: "com.oculus.supportedDevices"
value: "quest|quest2|quest3|questpro" value: "quest|quest2|quest3|questpro"
activities: 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" launch_mode: "singleTask"
orientation: "landscape" orientation: "landscape"
intent_filters: intent_filters:
@@ -23,5 +32,6 @@ android:
categories: categories:
- "com.oculus.intent.category.VR" - "com.oculus.intent.category.VR"
- "android.intent.category.LAUNCHER" - "android.intent.category.LAUNCHER"
- "org.khronos.openxr.intent.category.IMMERSIVE_HMD"
sdk: sdk:
target_sdk_version: 32 target_sdk_version: 32

View File

@@ -1,7 +1,11 @@
use bevy::diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}; use bevy::diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin};
use bevy::prelude::*; use bevy::prelude::*;
use bevy::transform::components::Transform; use bevy::transform::components::Transform;
use bevy_oxr::graphics::extensions::XrExtensions;
use bevy_oxr::graphics::XrAppInfo; 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::debug_gizmos::OpenXrDebugRenderer;
use bevy_oxr::xr_input::prototype_locomotion::{proto_locomotion, PrototypeLocomotionConfig}; use bevy_oxr::xr_input::prototype_locomotion::{proto_locomotion, PrototypeLocomotionConfig};
use bevy_oxr::xr_input::trackers::{ use bevy_oxr::xr_input::trackers::{
@@ -11,18 +15,21 @@ use bevy_oxr::DefaultXrPlugins;
#[bevy_main] #[bevy_main]
fn main() { fn main() {
let mut xr_extensions = XrExtensions::default();
xr_extensions.enable_fb_passthrough();
App::new() App::new()
.add_plugins(DefaultXrPlugins { .add_plugins(DefaultXrPlugins {
reqeusted_extensions: xr_extensions,
app_info: XrAppInfo { app_info: XrAppInfo {
name: "Bevy OXR Android Example".into(), name: "Bevy OXR Android Example".into(),
}, },
..default() prefered_blend_mode: bevy_oxr::graphics::XrPreferdBlendMode::Opaque,
}) })
.add_plugins(OpenXrDebugRenderer) .add_plugins(OpenXrDebugRenderer)
.add_plugins(LogDiagnosticsPlugin::default()) .add_plugins(LogDiagnosticsPlugin::default())
.add_plugins(FrameTimeDiagnosticsPlugin) .add_plugins(FrameTimeDiagnosticsPlugin)
.add_systems(Startup, setup) .add_systems(Startup, setup)
.add_systems(Update, proto_locomotion) .add_systems(Update, (proto_locomotion, toggle_passthrough))
.add_systems(Startup, spawn_controllers_example) .add_systems(Startup, spawn_controllers_example)
.insert_resource(PrototypeLocomotionConfig::default()) .insert_resource(PrototypeLocomotionConfig::default())
.run(); .run();
@@ -64,11 +71,6 @@ fn setup(
transform: Transform::from_xyz(4.0, 8.0, 4.0), transform: Transform::from_xyz(4.0, 8.0, 4.0),
..default() ..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) { fn spawn_controllers_example(mut commands: Commands) {
@@ -87,3 +89,16 @@ fn spawn_controllers_example(mut commands: Commands) {
SpatialBundle::default(), SpatialBundle::default(),
)); ));
} }
// Does this work? Not getting logs
fn toggle_passthrough(keys: Res<Input<KeyCode>>, mut xr_data: ResMut<XrRenderData>) {
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");
}
}
}

View File

@@ -10,14 +10,14 @@ impl XrExtensions {
pub fn raw(&self) -> &ExtensionSet { pub fn raw(&self) -> &ExtensionSet {
&self.0 &self.0
} }
// pub fn enable_fb_passthrough(&mut self) -> &mut Self { pub fn enable_fb_passthrough(&mut self) -> &mut Self {
// self.0.fb_passthrough = true; self.0.fb_passthrough = true;
// self self
// } }
// pub fn disable_fb_passthrough(&mut self) -> &mut Self { pub fn disable_fb_passthrough(&mut self) -> &mut Self {
// self.0.fb_passthrough = false; self.0.fb_passthrough = false;
// self self
// } }
pub fn enable_hand_tracking(&mut self) -> &mut Self { pub fn enable_hand_tracking(&mut self) -> &mut Self {
self.0.ext_hand_tracking = true; self.0.ext_hand_tracking = true;
self self

View File

@@ -6,9 +6,10 @@ use bevy::window::RawHandleWrapper;
use wgpu::Instance; use wgpu::Instance;
use crate::input::XrInput; use crate::input::XrInput;
use crate::passthrough::{Passthrough, PassthroughLayer};
use crate::resources::{ use crate::resources::{
XrEnvironmentBlendMode, XrFormat, XrFrameState, XrFrameWaiter, XrInstance, XrResolution, XrEnvironmentBlendMode, XrFormat, XrFrameState, XrFrameWaiter, XrInstance, XrPassthrough,
XrSession, XrSessionRunning, XrSwapchain, XrViews, XrPassthroughLayer, XrResolution, XrSession, XrSessionRunning, XrSwapchain, XrViews,
}; };
use openxr as xr; use openxr as xr;

View File

@@ -14,9 +14,12 @@ use xr::EnvironmentBlendMode;
use crate::graphics::extensions::XrExtensions; use crate::graphics::extensions::XrExtensions;
use crate::input::XrInput; use crate::input::XrInput;
use crate::passthrough::{Passthrough, PassthroughLayer};
use crate::resources::{ use crate::resources::{
Swapchain, SwapchainInner, XrEnvironmentBlendMode, XrFormat, XrFrameState, XrFrameWaiter, Swapchain, SwapchainInner, XrEnvironmentBlendMode, XrFormat, XrFrameState, XrFrameWaiter,
XrInstance, XrResolution, XrSession, XrSessionRunning, XrSwapchain, XrViews, XrInstance, XrPassthrough, XrPassthroughLayer, XrResolution, XrSession, XrSessionRunning,
XrSwapchain, XrViews,
}; };
use crate::VIEW_TYPE; use crate::VIEW_TYPE;
@@ -54,7 +57,7 @@ pub fn initialize_xr_graphics(
let available_extensions: XrExtensions = xr_entry.enumerate_extensions()?.into(); let available_extensions: XrExtensions = xr_entry.enumerate_extensions()?.into();
assert!(available_extensions.raw().khr_vulkan_enable2); 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 = let mut enabled_extensions: xr::ExtensionSet =
(available_extensions & reqeusted_extensions).into(); (available_extensions & reqeusted_extensions).into();
@@ -65,7 +68,7 @@ pub fn initialize_xr_graphics(
} }
let available_layers = xr_entry.enumerate_layers()?; 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( let xr_instance = xr_entry.create_instance(
&xr::ApplicationInfo { &xr::ApplicationInfo {
@@ -95,20 +98,25 @@ pub fn initialize_xr_graphics(
let blend_modes = xr_instance.enumerate_environment_blend_modes(xr_system_id, VIEW_TYPE)?; let blend_modes = xr_instance.enumerate_environment_blend_modes(xr_system_id, VIEW_TYPE)?;
let blend_mode: EnvironmentBlendMode = match prefered_blend_mode { let blend_mode: EnvironmentBlendMode = match prefered_blend_mode {
XrPreferdBlendMode::Opaque if blend_modes.contains(&EnvironmentBlendMode::OPAQUE) => { XrPreferdBlendMode::Opaque if blend_modes.contains(&EnvironmentBlendMode::OPAQUE) => {
bevy::log::info!("Using Opaque");
EnvironmentBlendMode::OPAQUE EnvironmentBlendMode::OPAQUE
} }
XrPreferdBlendMode::Additive if blend_modes.contains(&EnvironmentBlendMode::ADDITIVE) => { XrPreferdBlendMode::Additive if blend_modes.contains(&EnvironmentBlendMode::ADDITIVE) => {
bevy::log::info!("Using Additive");
EnvironmentBlendMode::ADDITIVE EnvironmentBlendMode::ADDITIVE
} }
XrPreferdBlendMode::AlphaBlend XrPreferdBlendMode::AlphaBlend
if blend_modes.contains(&EnvironmentBlendMode::ALPHA_BLEND) => if blend_modes.contains(&EnvironmentBlendMode::ALPHA_BLEND) =>
{ {
bevy::log::info!("Using AlphaBlend");
EnvironmentBlendMode::ALPHA_BLEND EnvironmentBlendMode::ALPHA_BLEND
} }
_ => EnvironmentBlendMode::OPAQUE, _ => {
bevy::log::info!("Using Opaque");
EnvironmentBlendMode::OPAQUE
}
}; };
#[cfg(not(target_os = "android"))] #[cfg(not(target_os = "android"))]
let vk_target_version = vk::make_api_version(0, 1, 2, 0); let vk_target_version = vk::make_api_version(0, 1, 2, 0);
#[cfg(not(target_os = "android"))] #[cfg(not(target_os = "android"))]
@@ -324,6 +332,8 @@ pub fn initialize_xr_graphics(
.map(|surface| surface.get_capabilities(&wgpu_adapter).formats[0]) .map(|surface| surface.get_capabilities(&wgpu_adapter).formats[0])
.unwrap_or(wgpu::TextureFormat::Rgba8UnormSrgb); .unwrap_or(wgpu::TextureFormat::Rgba8UnormSrgb);
// TODO: Log swapchain format
let resolution = uvec2( let resolution = uvec2(
views[0].recommended_image_rect_width, views[0].recommended_image_rect_width,
views[0].recommended_image_rect_height, views[0].recommended_image_rect_height,

View File

@@ -1,6 +1,6 @@
pub mod graphics; pub mod graphics;
pub mod input; pub mod input;
// pub mod passthrough; pub mod passthrough;
pub mod resource_macros; pub mod resource_macros;
pub mod resources; pub mod resources;
pub mod xr_init; pub mod xr_init;
@@ -24,7 +24,7 @@ use graphics::extensions::XrExtensions;
use graphics::{XrAppInfo, XrPreferdBlendMode}; use graphics::{XrAppInfo, XrPreferdBlendMode};
use input::XrInput; use input::XrInput;
use openxr as xr; use openxr as xr;
// use passthrough::{start_passthrough, supports_passthrough, XrPassthroughLayer}; use passthrough::{start_passthrough, supports_passthrough, Passthrough, PassthroughLayer};
use resources::*; use resources::*;
use xr::FormFactor; use xr::FormFactor;
use xr_init::{xr_only, XrEnableStatus, XrRenderData}; use xr_init::{xr_only, XrEnableStatus, XrRenderData};
@@ -62,13 +62,16 @@ pub struct FutureXrResources(
XrInput, XrInput,
XrViews, XrViews,
XrFrameState, XrFrameState,
bool,
XrPassthrough,
XrPassthroughLayer,
)>, )>,
>, >,
>, >,
); );
// fn mr_test(mut commands: Commands, passthrough_layer: Option<Res<XrPassthroughLayer>>) { fn mr_test(mut commands: Commands, passthrough_layer: Option<Res<XrPassthroughLayer>>) {
// commands.insert_resource(ClearColor(Color::rgba(0.0, 0.0, 0.0, 0.0))); commands.insert_resource(ClearColor(Color::rgba(0.0, 0.0, 0.0, 0.0)));
// } }
impl Plugin for OpenXrPlugin { impl Plugin for OpenXrPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
@@ -115,6 +118,35 @@ impl Plugin for OpenXrPlugin {
app.insert_resource(input.clone()); app.insert_resource(input.clone());
app.insert_resource(views.clone()); app.insert_resource(views.clone());
app.insert_resource(frame_state.clone()); app.insert_resource(frame_state.clone());
// Check if the fb_passthrough extension is available
let fb_passthrough_available = xr_instance.exts().fb_passthrough.is_some();
bevy::log::info!(
"From OpenXrPlugin: fb_passthrough_available: {}",
fb_passthrough_available
);
// Get the system for the head-mounted display
let hmd_system = xr_instance
.system(FormFactor::HEAD_MOUNTED_DISPLAY)
.unwrap();
bevy::log::info!("From OpenXrPlugin: hmd_system: {:?}", hmd_system);
// Check if the system supports passthrough
let passthrough_supported =
supports_passthrough(&xr_instance, hmd_system).is_ok_and(|v| v);
bevy::log::info!(
"From OpenXrPlugin: passthrough_supported: {}",
passthrough_supported
);
// The passthrough variable will be true only if both fb_passthrough is available and the system supports passthrough
let passthrough = fb_passthrough_available && passthrough_supported;
bevy::log::info!("From OpenXrPlugin: passthrough: {}", passthrough);
let mut p: Option<XrPassthrough> = None;
let mut pl: Option<XrPassthroughLayer> = None;
if passthrough {
if let Ok((p, pl)) = start_passthrough(&xr_instance, &session) {
let xr_data = XrRenderData { let xr_data = XrRenderData {
xr_instance, xr_instance,
xr_session: session, xr_session: session,
@@ -127,8 +159,19 @@ impl Plugin for OpenXrPlugin {
xr_input: input, xr_input: input,
xr_views: views, xr_views: views,
xr_frame_state: frame_state, xr_frame_state: frame_state,
xr_passthrough_active: true,
xr_passthrough: XrPassthrough::new(Mutex::new(p)),
xr_passthrough_layer: XrPassthroughLayer::new(Mutex::new(pl)),
}; };
bevy::log::info!("Passthrough is supported!");
app.insert_resource(xr_data); app.insert_resource(xr_data);
app.insert_resource(ClearColor(Color::rgba(0.0, 0.0, 0.0, 0.0)));
}
if !app.world.contains_resource::<ClearColor>() {
info!("ClearColor!");
}
}
app.insert_resource(ActionSets(vec![])); app.insert_resource(ActionSets(vec![]));
app.add_plugins(RenderPlugin { app.add_plugins(RenderPlugin {
render_creation: RenderCreation::Manual( render_creation: RenderCreation::Manual(
@@ -179,23 +222,6 @@ impl Plugin for OpenXrPlugin {
} else { } else {
app.insert_resource(DisableHandTracking::Both); app.insert_resource(DisableHandTracking::Both);
} }
// let passthrough = data.xr_instance.exts().fb_passthrough.is_some()
// && supports_passthrough(
// &data.xr_instance,
// data.xr_instance
// .system(FormFactor::HEAD_MOUNTED_DISPLAY)
// .unwrap(),
// )
// .is_ok_and(|v| v);
// if passthrough {
// info!("Passthrough!");
// let (pl, p) = start_passthrough(&data);
// app.insert_resource(pl);
// app.insert_resource(p);
// // if !app.world.contains_resource::<ClearColor>() {
// // info!("ClearColor!");
// // }
// }
let (left, right) = data.xr_swapchain.get_render_views(); let (left, right) = data.xr_swapchain.get_render_views();
let left = ManualTextureView { let left = ManualTextureView {
@@ -226,6 +252,8 @@ impl Plugin for OpenXrPlugin {
render_app.insert_resource(data.xr_input.clone()); render_app.insert_resource(data.xr_input.clone());
render_app.insert_resource(data.xr_views.clone()); render_app.insert_resource(data.xr_views.clone());
render_app.insert_resource(data.xr_frame_state.clone()); render_app.insert_resource(data.xr_frame_state.clone());
render_app.insert_resource(data.xr_passthrough.clone());
render_app.insert_resource(data.xr_passthrough_layer.clone());
render_app.insert_resource(XrEnableStatus::Enabled); render_app.insert_resource(XrEnableStatus::Enabled);
render_app.add_systems( render_app.add_systems(
Render, Render,
@@ -272,7 +300,7 @@ impl PluginGroup for DefaultXrPlugins {
..default() ..default()
}), }),
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
primary_window: None, primary_window: None, // ?
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
exit_condition: bevy::window::ExitCondition::DontExit, exit_condition: bevy::window::ExitCondition::DontExit,
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
@@ -393,7 +421,7 @@ pub fn end_frame(
swapchain: Res<XrSwapchain>, swapchain: Res<XrSwapchain>,
resolution: Res<XrResolution>, resolution: Res<XrResolution>,
environment_blend_mode: Res<XrEnvironmentBlendMode>, environment_blend_mode: Res<XrEnvironmentBlendMode>,
// passthrough_layer: Option<Res<XrPassthroughLayer>>, passthrough_layer: Option<Res<XrPassthroughLayer>>,
) { ) {
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
{ {
@@ -408,13 +436,18 @@ pub fn end_frame(
} }
{ {
let _span = info_span!("xr_end_frame").entered(); let _span = info_span!("xr_end_frame").entered();
// bevy::log::info!(
// "passthrough_layer.is_some(): {:?}",
// passthrough_layer.is_some()
// );
let result = swapchain.end( let result = swapchain.end(
xr_frame_state.lock().unwrap().predicted_display_time, xr_frame_state.lock().unwrap().predicted_display_time,
&views.lock().unwrap(), &views.lock().unwrap(),
&input.stage, &input.stage,
**resolution, **resolution,
**environment_blend_mode, **environment_blend_mode,
// passthrough_layer.map(|p| p.into_inner()), passthrough_layer.map(|p| PassthroughLayer(*p.lock().unwrap())),
); );
match result { match result {
Ok(_) => {} Ok(_) => {}

179
src/passthrough.rs Normal file
View File

@@ -0,0 +1,179 @@
use bevy::prelude::*;
use std::{marker::PhantomData, mem, ptr};
use crate::xr_init::XrRenderData;
use openxr as xr;
use xr::{
sys::{
PassthroughCreateInfoFB, PassthroughFB, PassthroughLayerFB, Space,
SystemPassthroughProperties2FB,
},
CompositionLayerBase, CompositionLayerFlags, Graphics, PassthroughCapabilityFlagsFB,
};
use crate::resources::XrInstance;
use crate::resources::XrSession;
pub struct PassthroughLayer(pub xr::sys::PassthroughLayerFB);
pub struct Passthrough(pub xr::sys::PassthroughFB);
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: &PassthroughLayer) -> 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.0,
},
_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 start_passthrough(
instance: &XrInstance,
xr_session: &XrSession,
) -> xr::Result<(xr::sys::PassthroughFB, xr::sys::PassthroughLayerFB)> {
unsafe {
// Create feature
let mut passthrough_feature = xr::sys::PassthroughFB::NULL;
let mut passthrough_create_info = xr::sys::PassthroughCreateInfoFB {
ty: xr::sys::StructureType::PASSTHROUGH_CREATE_INFO_FB, // XR_TYPE_PASSTHROUGH_CREATE_INFO_FB
next: ptr::null(),
flags: xr::sys::PassthroughFlagsFB::IS_RUNNING_AT_CREATION,
};
// bevy::log::info!("xr_session.as_raw(): {:?}", xr_session.as_raw());
// bevy::log::info!("&passthrough_create_info: {:?}", &passthrough_create_info);
// bevy::log::info!("&mut passthrough_feature: {:?}", &mut passthrough_feature);
// bevy::log::info!(
// "instance.exts().fb_passthrough.unwrap(): {:?}",
// instance.exts().fb_passthrough.is_some()
// );
cvt(
(instance.exts().fb_passthrough.unwrap().create_passthrough)(
xr_session.as_raw(),
&passthrough_create_info as *const _,
&mut passthrough_feature as *mut _,
),
)?;
// bevy::log::info!("Created passthrough feature");
// Create layer
let mut passthrough_layer = xr::sys::PassthroughLayerFB::NULL;
let mut layer_create_info: xr::sys::PassthroughLayerCreateInfoFB =
xr::sys::PassthroughLayerCreateInfoFB {
ty: xr::sys::StructureType::PASSTHROUGH_LAYER_CREATE_INFO_FB, // XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB
next: ptr::null(),
passthrough: passthrough_feature, // XR_PASSTHROUGH_HANDLE
flags: xr::sys::PassthroughFlagsFB::IS_RUNNING_AT_CREATION, // XR_PASSTHROUGH_IS_RUNNING_AT_CREATION_BIT_FB
purpose: xr::sys::PassthroughLayerPurposeFB::RECONSTRUCTION, // XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB
};
cvt((instance
.exts()
.fb_passthrough
.unwrap()
.create_passthrough_layer)(
xr_session.as_raw(),
&layer_create_info as *const _,
&mut passthrough_layer as *mut _,
))?;
// bevy::log::info!("Created passthrough layer");
// // Start layer
// bevy::log::info!("passthrough_feature: {:?}", passthrough_feature);
// // cvt((instance.exts().fb_passthrough.unwrap().passthrough_start)(
// // passthrough_feature,
// // ))?;
// bevy::log::info!("Started passthrough layer");
// bevy::log::info!("Passed everything in start");
Ok((passthrough_feature, passthrough_layer))
}
}
#[inline]
pub fn passthrough_layer_resume(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_resume)(
*passthrough_layer_locked
))?;
}
xr_data_resource.xr_passthrough_active = true;
bevy::log::info!("Resumed passthrough layer");
Ok(())
}
}
#[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(())
}
}

View File

@@ -1,9 +1,12 @@
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use std::sync::Mutex; use std::sync::Mutex;
// use crate::passthrough::XrPassthroughLayer; use crate::passthrough::{Passthrough, PassthroughLayer};
use crate::resource_macros::*; use crate::resource_macros::*;
use crate::xr::sys::CompositionLayerPassthroughFB;
use crate::xr::{CompositionLayerBase, CompositionLayerFlags};
use bevy::prelude::*; use bevy::prelude::*;
use core::ptr;
use openxr as xr; use openxr as xr;
xr_resource_wrapper!(XrInstance, xr::Instance); xr_resource_wrapper!(XrInstance, xr::Instance);
@@ -16,6 +19,8 @@ xr_arc_resource_wrapper!(XrFrameWaiter, Mutex<xr::FrameWaiter>);
xr_arc_resource_wrapper!(XrSwapchain, Swapchain); xr_arc_resource_wrapper!(XrSwapchain, Swapchain);
xr_arc_resource_wrapper!(XrFrameState, Mutex<xr::FrameState>); xr_arc_resource_wrapper!(XrFrameState, Mutex<xr::FrameState>);
xr_arc_resource_wrapper!(XrViews, Mutex<Vec<xr::View>>); xr_arc_resource_wrapper!(XrViews, Mutex<Vec<xr::View>>);
xr_arc_resource_wrapper!(XrPassthrough, Mutex<xr::sys::PassthroughFB>);
xr_arc_resource_wrapper!(XrPassthroughLayer, Mutex<xr::sys::PassthroughLayerFB>);
pub enum Swapchain { pub enum Swapchain {
Vulkan(SwapchainInner<xr::Vulkan>), Vulkan(SwapchainInner<xr::Vulkan>),
@@ -59,7 +64,7 @@ impl Swapchain {
stage: &xr::Space, stage: &xr::Space,
resolution: UVec2, resolution: UVec2,
environment_blend_mode: xr::EnvironmentBlendMode, environment_blend_mode: xr::EnvironmentBlendMode,
// passthrough_layer: Option<&XrPassthroughLayer>, passthrough_layer: Option<PassthroughLayer>,
) -> xr::Result<()> { ) -> xr::Result<()> {
match self { match self {
Swapchain::Vulkan(swapchain) => swapchain.end( Swapchain::Vulkan(swapchain) => swapchain.end(
@@ -68,7 +73,7 @@ impl Swapchain {
stage, stage,
resolution, resolution,
environment_blend_mode, environment_blend_mode,
// passthrough_layer, passthrough_layer,
), ),
} }
} }
@@ -128,7 +133,7 @@ impl<G: xr::Graphics> SwapchainInner<G> {
stage: &xr::Space, stage: &xr::Space,
resolution: UVec2, resolution: UVec2,
environment_blend_mode: xr::EnvironmentBlendMode, environment_blend_mode: xr::EnvironmentBlendMode,
// passthrough_layer: Option<&XrPassthroughLayer>, passthrough_layer: Option<PassthroughLayer>,
) -> xr::Result<()> { ) -> xr::Result<()> {
let rect = xr::Rect2Di { let rect = xr::Rect2Di {
offset: xr::Offset2Di { x: 0, y: 0 }, offset: xr::Offset2Di { x: 0, y: 0 },
@@ -142,51 +147,54 @@ impl<G: xr::Graphics> SwapchainInner<G> {
warn!("views are len of 0"); warn!("views are len of 0");
return Ok(()); return Ok(());
} }
// match passthrough_layer { match passthrough_layer {
// Some(pass) => { Some(pass) => {
// // info!("Rendering with pass through"); //bevy::log::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<G>)
// },
// ],
// )
// }
// None => let passthrough_layer = xr::sys::CompositionLayerPassthroughFB {
ty: CompositionLayerPassthroughFB::TYPE,
next: ptr::null(),
flags: CompositionLayerFlags::UNPREMULTIPLIED_ALPHA,
space: xr::sys::Space::NULL,
layer_handle: pass.0,
};
self.stream.lock().unwrap().end(
predicted_display_time,
environment_blend_mode,
&[
unsafe {
&*(&passthrough_layer as *const _ as *const CompositionLayerBase<G>)
},
&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( self.stream.lock().unwrap().end(
predicted_display_time, predicted_display_time,
environment_blend_mode, environment_blend_mode,
@@ -211,6 +219,7 @@ impl<G: xr::Graphics> SwapchainInner<G> {
), ),
])], ])],
) )
// } }
}
} }
} }

View File

@@ -17,9 +17,10 @@ use wgpu::Instance;
use crate::{ use crate::{
input::XrInput, input::XrInput,
passthrough::{Passthrough, PassthroughLayer},
resources::{ resources::{
XrEnvironmentBlendMode, XrFormat, XrFrameState, XrFrameWaiter, XrInstance, XrResolution, XrEnvironmentBlendMode, XrFormat, XrFrameState, XrFrameWaiter, XrInstance, XrPassthrough,
XrSession, XrSessionRunning, XrSwapchain, XrViews, XrPassthroughLayer, XrResolution, XrSession, XrSessionRunning, XrSwapchain, XrViews,
}, },
}; };
@@ -45,6 +46,9 @@ pub struct XrRenderData {
pub xr_input: XrInput, pub xr_input: XrInput,
pub xr_views: XrViews, pub xr_views: XrViews,
pub xr_frame_state: XrFrameState, pub xr_frame_state: XrFrameState,
pub xr_passthrough_active: bool,
pub xr_passthrough: XrPassthrough,
pub xr_passthrough_layer: XrPassthroughLayer,
} }
#[derive(Event, Clone, Copy, Debug)] #[derive(Event, Clone, Copy, Debug)]