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

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
pub mod graphics;
pub mod input;
// pub mod passthrough;
pub mod passthrough;
pub mod resource_macros;
pub mod resources;
pub mod xr_init;
@@ -24,7 +24,7 @@ use graphics::extensions::XrExtensions;
use graphics::{XrAppInfo, XrPreferdBlendMode};
use input::XrInput;
use openxr as xr;
// use passthrough::{start_passthrough, supports_passthrough, XrPassthroughLayer};
use passthrough::{start_passthrough, supports_passthrough, Passthrough, PassthroughLayer};
use resources::*;
use xr::FormFactor;
use xr_init::{xr_only, XrEnableStatus, XrRenderData};
@@ -62,13 +62,16 @@ pub struct FutureXrResources(
XrInput,
XrViews,
XrFrameState,
bool,
XrPassthrough,
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)));
// }
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)));
}
impl Plugin for OpenXrPlugin {
fn build(&self, app: &mut App) {
@@ -115,20 +118,60 @@ impl Plugin for OpenXrPlugin {
app.insert_resource(input.clone());
app.insert_resource(views.clone());
app.insert_resource(frame_state.clone());
let xr_data = XrRenderData {
xr_instance,
xr_session: session,
xr_blend_mode: blend_mode,
xr_resolution: resolution,
xr_format: format,
xr_session_running: session_running,
xr_frame_waiter: frame_waiter,
xr_swapchain: swapchain,
xr_input: input,
xr_views: views,
xr_frame_state: frame_state,
};
app.insert_resource(xr_data);
// 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 {
xr_instance,
xr_session: session,
xr_blend_mode: blend_mode,
xr_resolution: resolution,
xr_format: format,
xr_session_running: session_running,
xr_frame_waiter: frame_waiter,
xr_swapchain: swapchain,
xr_input: input,
xr_views: views,
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(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.add_plugins(RenderPlugin {
render_creation: RenderCreation::Manual(
@@ -179,23 +222,6 @@ impl Plugin for OpenXrPlugin {
} else {
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 = ManualTextureView {
@@ -226,6 +252,8 @@ impl Plugin for OpenXrPlugin {
render_app.insert_resource(data.xr_input.clone());
render_app.insert_resource(data.xr_views.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.add_systems(
Render,
@@ -272,7 +300,7 @@ impl PluginGroup for DefaultXrPlugins {
..default()
}),
#[cfg(target_os = "android")]
primary_window: None,
primary_window: None, // ?
#[cfg(target_os = "android")]
exit_condition: bevy::window::ExitCondition::DontExit,
#[cfg(target_os = "android")]
@@ -393,7 +421,7 @@ pub fn end_frame(
swapchain: Res<XrSwapchain>,
resolution: Res<XrResolution>,
environment_blend_mode: Res<XrEnvironmentBlendMode>,
// passthrough_layer: Option<Res<XrPassthroughLayer>>,
passthrough_layer: Option<Res<XrPassthroughLayer>>,
) {
#[cfg(target_os = "android")]
{
@@ -408,13 +436,18 @@ pub fn end_frame(
}
{
let _span = info_span!("xr_end_frame").entered();
// bevy::log::info!(
// "passthrough_layer.is_some(): {:?}",
// passthrough_layer.is_some()
// );
let result = swapchain.end(
xr_frame_state.lock().unwrap().predicted_display_time,
&views.lock().unwrap(),
&input.stage,
**resolution,
**environment_blend_mode,
// passthrough_layer.map(|p| p.into_inner()),
passthrough_layer.map(|p| PassthroughLayer(*p.lock().unwrap())),
);
match result {
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::Mutex;
// use crate::passthrough::XrPassthroughLayer;
use crate::passthrough::{Passthrough, PassthroughLayer};
use crate::resource_macros::*;
use crate::xr::sys::CompositionLayerPassthroughFB;
use crate::xr::{CompositionLayerBase, CompositionLayerFlags};
use bevy::prelude::*;
use core::ptr;
use openxr as xr;
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!(XrFrameState, Mutex<xr::FrameState>);
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 {
Vulkan(SwapchainInner<xr::Vulkan>),
@@ -59,7 +64,7 @@ impl Swapchain {
stage: &xr::Space,
resolution: UVec2,
environment_blend_mode: xr::EnvironmentBlendMode,
// passthrough_layer: Option<&XrPassthroughLayer>,
passthrough_layer: Option<PassthroughLayer>,
) -> xr::Result<()> {
match self {
Swapchain::Vulkan(swapchain) => swapchain.end(
@@ -68,7 +73,7 @@ impl Swapchain {
stage,
resolution,
environment_blend_mode,
// passthrough_layer,
passthrough_layer,
),
}
}
@@ -128,7 +133,7 @@ impl<G: xr::Graphics> SwapchainInner<G> {
stage: &xr::Space,
resolution: UVec2,
environment_blend_mode: xr::EnvironmentBlendMode,
// passthrough_layer: Option<&XrPassthroughLayer>,
passthrough_layer: Option<PassthroughLayer>,
) -> xr::Result<()> {
let rect = xr::Rect2Di {
offset: xr::Offset2Di { x: 0, y: 0 },
@@ -142,75 +147,79 @@ impl<G: xr::Graphics> SwapchainInner<G> {
warn!("views are len of 0");
return Ok(());
}
// match passthrough_layer {
// Some(pass) => {
// // info!("Rendering with pass through");
// let passthrough_layer = xr::sys::CompositionLayerPassthroughFB {
// ty: CompositionLayerPassthroughFB::TYPE,
// next: ptr::null(),
// flags: CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA,
// space: xr::sys::Space::NULL,
// layer_handle: pass.0,
// };
// self.stream.lock().unwrap().end(
// predicted_display_time,
// environment_blend_mode,
// &[
// &xr::CompositionLayerProjection::new()
// .layer_flags(CompositionLayerFlags::UNPREMULTIPLIED_ALPHA)
// .space(stage)
// .views(&[
// xr::CompositionLayerProjectionView::new()
// .pose(views[0].pose)
// .fov(views[0].fov)
// .sub_image(
// xr::SwapchainSubImage::new()
// .swapchain(&swapchain)
// .image_array_index(0)
// .image_rect(rect),
// ),
// xr::CompositionLayerProjectionView::new()
// .pose(views[1].pose)
// .fov(views[1].fov)
// .sub_image(
// xr::SwapchainSubImage::new()
// .swapchain(&swapchain)
// .image_array_index(1)
// .image_rect(rect),
// ),
// ]),
// unsafe {
// &*(&passthrough_layer as *const _ as *const CompositionLayerBase<G>)
// },
// ],
// )
// }
match passthrough_layer {
Some(pass) => {
//bevy::log::info!("Rendering with pass through");
// None =>
self.stream.lock().unwrap().end(
predicted_display_time,
environment_blend_mode,
&[&xr::CompositionLayerProjection::new().space(stage).views(&[
xr::CompositionLayerProjectionView::new()
.pose(views[0].pose)
.fov(views[0].fov)
.sub_image(
xr::SwapchainSubImage::new()
.swapchain(&swapchain)
.image_array_index(0)
.image_rect(rect),
),
xr::CompositionLayerProjectionView::new()
.pose(views[1].pose)
.fov(views[1].fov)
.sub_image(
xr::SwapchainSubImage::new()
.swapchain(&swapchain)
.image_array_index(1)
.image_rect(rect),
),
])],
)
// }
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(
predicted_display_time,
environment_blend_mode,
&[&xr::CompositionLayerProjection::new().space(stage).views(&[
xr::CompositionLayerProjectionView::new()
.pose(views[0].pose)
.fov(views[0].fov)
.sub_image(
xr::SwapchainSubImage::new()
.swapchain(&swapchain)
.image_array_index(0)
.image_rect(rect),
),
xr::CompositionLayerProjectionView::new()
.pose(views[1].pose)
.fov(views[1].fov)
.sub_image(
xr::SwapchainSubImage::new()
.swapchain(&swapchain)
.image_array_index(1)
.image_rect(rect),
),
])],
)
}
}
}
}

View File

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