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:
@@ -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
|
||||||
|
|||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
83
src/lib.rs
83
src/lib.rs
@@ -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
179
src/passthrough.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
107
src/resources.rs
107
src/resources.rs
@@ -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> {
|
|||||||
),
|
),
|
||||||
])],
|
])],
|
||||||
)
|
)
|
||||||
// }
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
Reference in New Issue
Block a user