basic passthrough stuff

This commit is contained in:
awtterpip
2024-05-03 19:31:23 -05:00
parent 0e5bcd029f
commit 13890ddb53
7 changed files with 261 additions and 101 deletions

View File

@@ -41,6 +41,11 @@ launch_mode = "singleTask"
orientation = "landscape" orientation = "landscape"
resizeable_activity = false resizeable_activity = false
[[package.metadata.android.uses_feature]]
name = "com.oculus.feature.PASSTHROUGH"
required = true
version = 1
[[package.metadata.android.application.activity.intent_filter]] [[package.metadata.android.application.activity.intent_filter]]
actions = ["android.intent.action.MAIN"] actions = ["android.intent.action.MAIN"]
categories = [ categories = [

View File

@@ -8,6 +8,7 @@ fn main() {
App::new() App::new()
.add_plugins(add_xr_plugins(DefaultPlugins)) .add_plugins(add_xr_plugins(DefaultPlugins))
.add_systems(Startup, setup) .add_systems(Startup, setup)
.insert_resource(ClearColor(Color::NONE))
.run(); .run();
} }

View File

@@ -1,17 +1,73 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy::render::Render;
use bevy::render::RenderApp;
use bevy::render::RenderSet;
use openxr::sys::SystemPassthroughProperties2FB; use openxr::sys::SystemPassthroughProperties2FB;
use openxr::PassthroughCapabilityFlagsFB; use openxr::PassthroughCapabilityFlagsFB;
use crate::layer_builder::PassthroughLayer;
use crate::resources::*; use crate::resources::*;
use crate::types::*; use crate::types::*;
pub struct OxrPassthroughPlugin; pub struct OxrPassthroughPlugin;
impl Plugin for OxrPassthroughPlugin { impl Plugin for OxrPassthroughPlugin {
fn build(&self, _app: &mut App) { fn build(&self, app: &mut App) {
todo!() let resources = app
.world
.get_resource::<OxrInstance>()
.and_then(|instance| {
app.world
.get_resource::<OxrSystemId>()
.map(|system_id| (instance, system_id))
});
if resources.is_some_and(|(instance, system)| {
supports_passthrough(instance, *system).is_ok_and(|s| s)
}) {
app.sub_app_mut(RenderApp).add_systems(
Render,
insert_passthrough
.in_set(RenderSet::PrepareAssets)
.run_if(resource_added::<OxrSession>),
);
} else {
error!("Passthrough is not supported with this runtime")
} }
} }
}
pub fn insert_passthrough(world: &mut World) {
let session = world.resource::<OxrSession>();
let (passthrough, passthrough_layer) = create_passthrough(
session,
openxr::PassthroughFlagsFB::IS_RUNNING_AT_CREATION,
openxr::PassthroughLayerPurposeFB::RECONSTRUCTION,
)
.unwrap();
world
.resource_mut::<OxrRenderLayers>()
.insert(0, Box::new(PassthroughLayer));
world.insert_resource(passthrough);
world.insert_resource(passthrough_layer);
}
pub fn resume_passthrough(
passthrough: Res<OxrPassthrough>,
passthrough_layer: Res<OxrPassthroughLayer>,
) {
passthrough.start().unwrap();
passthrough_layer.resume().unwrap();
}
pub fn pause_passthrough(
passthrough: Res<OxrPassthrough>,
passthrough_layer: Res<OxrPassthroughLayer>,
) {
passthrough_layer.pause().unwrap();
passthrough.pause().unwrap();
}
pub fn create_passthrough( pub fn create_passthrough(
session: &OxrSession, session: &OxrSession,

View File

@@ -1,9 +1,70 @@
use std::mem; use std::mem;
use bevy::ecs::world::World;
use openxr::{sys, CompositionLayerFlags, Fovf, Posef, Rect2Di, Space}; use openxr::{sys, CompositionLayerFlags, Fovf, Posef, Rect2Di, Space};
use crate::graphics::graphics_match; use crate::graphics::graphics_match;
use crate::resources::{OxrPassthroughLayer, OxrSwapchain}; use crate::resources::*;
pub trait LayerProvider {
fn get<'a>(&'a self, world: &'a World) -> Box<dyn CompositionLayer + '_>;
}
pub struct ProjectionLayer;
pub struct PassthroughLayer;
impl LayerProvider for ProjectionLayer {
fn get<'a>(&self, world: &'a World) -> Box<dyn CompositionLayer<'a> + 'a> {
let stage = world.resource::<OxrStage>();
let openxr_views = world.resource::<OxrViews>();
let swapchain = world.resource::<OxrSwapchain>();
let graphics_info = world.resource::<OxrGraphicsInfo>();
let rect = openxr::Rect2Di {
offset: openxr::Offset2Di { x: 0, y: 0 },
extent: openxr::Extent2Di {
width: graphics_info.resolution.x as _,
height: graphics_info.resolution.y as _,
},
};
Box::new(
CompositionLayerProjection::new()
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
.space(&stage)
.views(&[
CompositionLayerProjectionView::new()
.pose(openxr_views.0[0].pose)
.fov(openxr_views.0[0].fov)
.sub_image(
SwapchainSubImage::new()
.swapchain(&swapchain)
.image_array_index(0)
.image_rect(rect),
),
CompositionLayerProjectionView::new()
.pose(openxr_views.0[1].pose)
.fov(openxr_views.0[1].fov)
.sub_image(
SwapchainSubImage::new()
.swapchain(&swapchain)
.image_array_index(1)
.image_rect(rect),
),
]),
)
}
}
impl LayerProvider for PassthroughLayer {
fn get<'a>(&'a self, world: &'a World) -> Box<dyn CompositionLayer + '_> {
Box::new(
CompositionLayerPassthrough::new()
.layer_handle(world.resource::<OxrPassthroughLayer>())
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA),
)
}
}
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct SwapchainSubImage<'a> { pub struct SwapchainSubImage<'a> {
@@ -144,12 +205,10 @@ impl<'a> CompositionLayerProjection<'a> {
self self
} }
#[inline] #[inline]
pub fn views(mut self, value: &'a [CompositionLayerProjectionView<'a>]) -> Self { pub fn views(mut self, value: &[CompositionLayerProjectionView<'a>]) -> Self {
for view in value { self.views = value.iter().map(|view| view.inner).collect();
self.views.push(view.inner.clone());
}
self.inner.views = self.views.as_slice().as_ptr() as *const _ as _; self.inner.views = self.views.as_slice().as_ptr() as *const _ as _;
self.inner.view_count = value.len() as u32; self.inner.view_count = self.views.len() as u32;
self self
} }
} }

View File

@@ -10,6 +10,8 @@ use bevy_xr::session::XrSessionPlugin;
use init::OxrInitPlugin; use init::OxrInitPlugin;
use render::OxrRenderPlugin; use render::OxrRenderPlugin;
use self::{exts::OxrExtensions, features::passthrough::OxrPassthroughPlugin};
pub mod error; pub mod error;
mod exts; mod exts;
pub mod features; pub mod features;
@@ -28,7 +30,11 @@ pub fn add_xr_plugins<G: PluginGroup>(plugins: G) -> PluginGroupBuilder {
.add_before::<RenderPlugin, _>(XrSessionPlugin) .add_before::<RenderPlugin, _>(XrSessionPlugin)
.add_before::<RenderPlugin, _>(OxrInitPlugin { .add_before::<RenderPlugin, _>(OxrInitPlugin {
app_info: default(), app_info: default(),
exts: default(), exts: {
let mut exts = OxrExtensions::default();
exts.enable_fb_passthrough();
exts
},
blend_modes: default(), blend_modes: default(),
backends: default(), backends: default(),
formats: Some(vec![wgpu::TextureFormat::Rgba8UnormSrgb]), formats: Some(vec![wgpu::TextureFormat::Rgba8UnormSrgb]),
@@ -36,6 +42,7 @@ pub fn add_xr_plugins<G: PluginGroup>(plugins: G) -> PluginGroupBuilder {
synchronous_pipeline_compilation: default(), synchronous_pipeline_compilation: default(),
}) })
.add(OxrRenderPlugin) .add(OxrRenderPlugin)
.add(OxrPassthroughPlugin)
.add(XrCameraPlugin) .add(XrCameraPlugin)
// .add(XrActionPlugin) // .add(XrActionPlugin)
.set(WindowPlugin { .set(WindowPlugin {

View File

@@ -10,11 +10,13 @@ use bevy::{
transform::TransformSystem, transform::TransformSystem,
}; };
use bevy_xr::camera::{XrCamera, XrCameraBundle, XrProjection}; use bevy_xr::camera::{XrCamera, XrCameraBundle, XrProjection};
use openxr::{CompositionLayerFlags, ViewStateFlags}; use openxr::ViewStateFlags;
use crate::init::{session_started, OxrPreUpdateSet};
use crate::layer_builder::*;
use crate::resources::*; use crate::resources::*;
use crate::{
init::{session_started, OxrPreUpdateSet},
layer_builder::ProjectionLayer,
};
pub struct OxrRenderPlugin; pub struct OxrRenderPlugin;
@@ -39,7 +41,8 @@ impl Plugin for OxrRenderPlugin {
.run_if(session_started) .run_if(session_started)
.before(TransformSystem::TransformPropagate), .before(TransformSystem::TransformPropagate),
); );
app.sub_app_mut(RenderApp).add_systems( app.sub_app_mut(RenderApp)
.add_systems(
Render, Render,
( (
( (
@@ -53,10 +56,13 @@ impl Plugin for OxrRenderPlugin {
.before(RenderSet::Queue) .before(RenderSet::Queue)
.before(insert_texture_views), .before(insert_texture_views),
wait_image.in_set(RenderSet::Render).before(render_system), wait_image.in_set(RenderSet::Render).before(render_system),
(end_frame).chain().in_set(RenderSet::Cleanup), (release_image, end_frame)
.chain()
.in_set(RenderSet::Cleanup),
) )
.run_if(session_started), .run_if(session_started),
); )
.insert_resource(OxrRenderLayers(vec![Box::new(ProjectionLayer)]));
} }
} }
@@ -326,50 +332,73 @@ pub fn begin_frame(mut frame_stream: ResMut<OxrFrameStream>) {
frame_stream.begin().expect("Failed to begin frame") frame_stream.begin().expect("Failed to begin frame")
} }
pub fn end_frame( pub fn release_image(mut swapchain: ResMut<OxrSwapchain>) {
mut frame_stream: ResMut<OxrFrameStream>, let _span = info_span!("xr_release_image");
mut swapchain: ResMut<OxrSwapchain>,
stage: Res<OxrStage>,
display_time: Res<OxrTime>,
graphics_info: Res<OxrGraphicsInfo>,
openxr_views: Res<OxrViews>,
) {
let _span = info_span!("xr_end_frame");
swapchain.release_image().unwrap(); swapchain.release_image().unwrap();
let rect = openxr::Rect2Di { }
offset: openxr::Offset2Di { x: 0, y: 0 },
extent: openxr::Extent2Di { pub fn end_frame(world: &mut World) {
width: graphics_info.resolution.x as _, let _span = info_span!("xr_end_frame");
height: graphics_info.resolution.y as _, world.resource_scope::<OxrFrameStream, ()>(|world, mut frame_stream| {
}, let mut layers = vec![];
}; for layer in world.resource::<OxrRenderLayers>().iter() {
layers.push(layer.get(world));
}
let layers: Vec<_> = layers.iter().map(Box::as_ref).collect();
frame_stream frame_stream
.end( .end(
**display_time, **world.resource::<OxrTime>(),
graphics_info.blend_mode, world.resource::<OxrGraphicsInfo>().blend_mode,
&[&CompositionLayerProjection::new() &layers,
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
.space(&stage)
.views(&[
CompositionLayerProjectionView::new()
.pose(openxr_views.0[0].pose)
.fov(openxr_views.0[0].fov)
.sub_image(
SwapchainSubImage::new()
.swapchain(&swapchain)
.image_array_index(0)
.image_rect(rect),
),
CompositionLayerProjectionView::new()
.pose(openxr_views.0[1].pose)
.fov(openxr_views.0[1].fov)
.sub_image(
SwapchainSubImage::new()
.swapchain(&swapchain)
.image_array_index(1)
.image_rect(rect),
),
])],
) )
.expect("Failed to end frame"); .expect("Failed to end frame");
});
} }
// pub fn end_frame(
// mut frame_stream: ResMut<OxrFrameStream>,
// mut swapchain: ResMut<OxrSwapchain>,
// stage: Res<OxrStage>,
// display_time: Res<OxrTime>,
// graphics_info: Res<OxrGraphicsInfo>,
// openxr_views: Res<OxrViews>,
// ) {
// let _span = info_span!("xr_end_frame");
// swapchain.release_image().unwrap();
// let rect = openxr::Rect2Di {
// offset: openxr::Offset2Di { x: 0, y: 0 },
// extent: openxr::Extent2Di {
// width: graphics_info.resolution.x as _,
// height: graphics_info.resolution.y as _,
// },
// };
// frame_stream
// .end(
// **display_time,
// graphics_info.blend_mode,
// &[&CompositionLayerProjection::new()
// .layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
// .space(&stage)
// .views(&[
// CompositionLayerProjectionView::new()
// .pose(openxr_views.0[0].pose)
// .fov(openxr_views.0[0].fov)
// .sub_image(
// SwapchainSubImage::new()
// .swapchain(&swapchain)
// .image_array_index(0)
// .image_rect(rect),
// ),
// CompositionLayerProjectionView::new()
// .pose(openxr_views.0[1].pose)
// .fov(openxr_views.0[1].fov)
// .sub_image(
// SwapchainSubImage::new()
// .swapchain(&swapchain)
// .image_array_index(1)
// .image_rect(rect),
// ),
// ])],
// )
// .expect("Failed to end frame");
// }

View File

@@ -7,7 +7,7 @@ use openxr::AnyGraphics;
use crate::error::OxrError; use crate::error::OxrError;
use crate::graphics::*; use crate::graphics::*;
use crate::layer_builder::CompositionLayer; use crate::layer_builder::{CompositionLayer, LayerProvider};
use crate::types::*; use crate::types::*;
/// Wrapper around an [`Entry`](openxr::Entry) with some methods overridden to use bevy types. /// Wrapper around an [`Entry`](openxr::Entry) with some methods overridden to use bevy types.
@@ -237,34 +237,6 @@ impl OxrSession {
} }
} }
/// Wrapper around [`openxr::Passthrough`].
///
/// Used to [`start`](openxr::Passthrough::start) or [`pause`](openxr::Passthrough::pause) passthrough on the physical device.
///
/// See [`openxr::Passthrough`] for available methods.
#[derive(Resource, Deref, DerefMut)]
pub struct OxrPassthrough(
#[deref] pub openxr::Passthrough,
/// The flags are stored here so that they don't need to be passed in again when creating an [`OxrPassthroughLayer`].
openxr::PassthroughFlagsFB,
);
impl OxrPassthrough {
/// This function can create an [`OxrPassthrough`] from raw openxr types if needed.
/// In the majority of cases, you should use [`create_passthrough`](OxrSession::create_passthrough) instead.
pub fn from_inner(passthrough: openxr::Passthrough, flags: openxr::PassthroughFlagsFB) -> Self {
Self(passthrough, flags)
}
}
/// Wrapper around [`openxr::Passthrough`].
///
/// Used to create a [`CompositionLayerPassthrough`](crate::layer_builder::CompositionLayerPassthrough), and to [`pause`](openxr::PassthroughLayer::pause) or [`resume`](openxr::PassthroughLayer::resume) rendering of the passthrough layer.
///
/// See [`openxr::PassthroughLayer`] for available methods.
#[derive(Resource, Deref, DerefMut)]
pub struct OxrPassthroughLayer(pub openxr::PassthroughLayer);
/// Graphics agnostic wrapper around [openxr::FrameStream] /// Graphics agnostic wrapper around [openxr::FrameStream]
#[derive(Resource)] #[derive(Resource)]
pub struct OxrFrameStream(pub GraphicsWrap<Self>); pub struct OxrFrameStream(pub GraphicsWrap<Self>);
@@ -416,6 +388,37 @@ pub struct OxrViews(pub Vec<openxr::View>);
#[derive(Debug, Copy, Clone, Deref, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Resource)] #[derive(Debug, Copy, Clone, Deref, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Resource)]
pub struct OxrSystemId(pub openxr::SystemId); pub struct OxrSystemId(pub openxr::SystemId);
/// Wrapper around [`openxr::Passthrough`].
///
/// Used to [`start`](openxr::Passthrough::start) or [`pause`](openxr::Passthrough::pause) passthrough on the physical device.
///
/// See [`openxr::Passthrough`] for available methods.
#[derive(Resource, Deref, DerefMut)]
pub struct OxrPassthrough(
#[deref] pub openxr::Passthrough,
/// The flags are stored here so that they don't need to be passed in again when creating an [`OxrPassthroughLayer`].
openxr::PassthroughFlagsFB,
);
impl OxrPassthrough {
/// This function can create an [`OxrPassthrough`] from raw openxr types if needed.
/// In the majority of cases, you should use [`create_passthrough`](OxrSession::create_passthrough) instead.
pub fn from_inner(passthrough: openxr::Passthrough, flags: openxr::PassthroughFlagsFB) -> Self {
Self(passthrough, flags)
}
}
/// Wrapper around [`openxr::Passthrough`].
///
/// Used to create a [`CompositionLayerPassthrough`](crate::layer_builder::CompositionLayerPassthrough), and to [`pause`](openxr::PassthroughLayer::pause) or [`resume`](openxr::PassthroughLayer::resume) rendering of the passthrough layer.
///
/// See [`openxr::PassthroughLayer`] for available methods.
#[derive(Resource, Deref, DerefMut)]
pub struct OxrPassthroughLayer(pub openxr::PassthroughLayer);
#[derive(Resource, Deref, DerefMut, Default)]
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)] #[derive(Clone, Copy, Resource)]
pub struct OxrGraphicsInfo { pub struct OxrGraphicsInfo {