Compare commits

...

11 Commits

Author SHA1 Message Date
aca40a0761 Stuff
Some checks failed
CI / Check Ubuntu (push) Has been cancelled
CI / Check Windows (push) Has been cancelled
CI / Check Wasm (push) Has been cancelled
2026-02-20 22:08:33 +01:00
7936b53a02 chore: cleanup
Some checks failed
CI / Check Ubuntu (push) Has been cancelled
CI / Check Windows (push) Has been cancelled
CI / Check Wasm (push) Has been cancelled
2026-02-19 22:01:09 +01:00
Schmarni
93fe370f12 chore: fix examples and bump version
Signed-off-by: Schmarni <marnistromer@gmail.com>
2026-02-12 07:05:33 +01:00
Schmarni
18879e9f06 chore: bump openxr crate version
Signed-off-by: Schmarni <marnistromer@gmail.com>
2026-02-12 06:51:20 +01:00
Schmarni
728581d8d7 fix(openxr): don't crash when failing to locate views
Signed-off-by: Schmarni <marnistromer@gmail.com>
2026-01-24 20:50:37 +01:00
Schmarni
337cfdb3f8 fix(openxr): fix frametime recovery on WiVRn (and probably other monado runtimes)
Signed-off-by: Schmarni <marnistromer@gmail.com>
2026-01-24 13:21:26 +01:00
WireWhiz
93523de2fd fix duplicate PhysicalDeviceMultiviewFeatures validation error (#201)
* fix duplicate PhysicalDeviceMultiviewFeatures validation error

* remove rogue debug log
2026-01-21 21:27:12 +01:00
Pierce Thompson
3974766f7a feat: Update to Bevy 0.18 (#202) 2026-01-21 21:14:48 +01:00
Schmarni
11bf9442e1 fix(bevy_xr_utils/example): fix tracking utils example
Signed-off-by: Schmarni <marnistromer@gmail.com>
2025-10-19 23:13:05 +02:00
Schmarni
c8e43c666f fix(ci): install wayland since bevy usesbit by default 2025-10-19 21:27:17 +02:00
Schmarni
d94c10c386 chore: bump version
Signed-off-by: Schmarni <marnistromer@gmail.com>
2025-10-19 17:25:06 +02:00
22 changed files with 1553 additions and 1420 deletions

View File

@@ -22,7 +22,7 @@ jobs:
- name: Install bevy dependencies - name: Install bevy dependencies
run: | run: |
sudo apt-get update && sudo apt-get install -y \ sudo apt-get update && sudo apt-get install -y \
g++ pkg-config libx11-dev libasound2-dev libudev-dev libopenxr-loader1 libopenxr-dev g++ pkg-config libx11-dev libwayland-dev libasound2-dev libudev-dev libopenxr-loader1 libopenxr-dev
- name: Cache cargo dependencies - name: Cache cargo dependencies
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2

2386
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,39 +1,41 @@
[workspace.package] [workspace.package]
version = "0.3.0" version = "0.5.0"
edition = "2024" edition = "2024"
description = "Community crate for XR in Bevy" description = "Community crate for XR in Bevy"
repository = "https://github.com/awtterpip/bevy_oxr" repository = "https://github.com/awtterpip/bevy_oxr"
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
[workspace] [workspace]
resolver = "2" resolver = "3"
members = ["crates/*", "crates/bevy_openxr/examples/android"] members = ["crates/*", "crates/bevy_openxr/examples/android"]
[workspace.dependencies] [workspace.dependencies]
bevy = "0.17" bevy = { version = "0.18", features = ["debug"] }
bevy_ecs = { version = "0.17.0", default-features = false } bevy_ecs = { version = "0.18", default-features = false }
bevy_math = { version = "0.17.0", default-features = false } bevy_math = { version = "0.18", default-features = false }
bevy_render = { version = "0.17.0", default-features = false } bevy_render = { version = "0.18", default-features = false }
bevy_core_pipeline = { version = "0.17.0", default-features = false } bevy_core_pipeline = { version = "0.18", default-features = false }
bevy_window = { version = "0.17.0", default-features = false } bevy_window = { version = "0.18", default-features = false }
bevy_winit = { version = "0.17.0", default-features = false } bevy_winit = { version = "0.18", default-features = false }
bevy_pbr = { version = "0.17.0", default-features = false } bevy_pbr = { version = "0.18", default-features = false }
bevy_app = { version = "0.17.0", default-features = false } bevy_app = { version = "0.18", default-features = false }
bevy_reflect = { version = "0.17.0", default-features = false } bevy_reflect = { version = "0.18", default-features = false }
bevy_log = { version = "0.17.0", default-features = false } bevy_log = { version = "0.18", default-features = false }
bevy_gizmos = { version = "0.17.0", default-features = false } bevy_gizmos = { version = "0.18", default-features = false }
bevy_camera = { version = "0.17.0", default-features = false } bevy_camera = { version = "0.18", default-features = false }
bevy_color = { version = "0.17.0", default-features = false } bevy_color = { version = "0.18", default-features = false }
bevy_transform = { version = "0.17.0", default-features = false } bevy_transform = { version = "0.18", default-features = false }
bevy_derive = { version = "0.17.0", default-features = false } bevy_derive = { version = "0.18", default-features = false }
bevy_platform = { version = "0.17.0", default-features = false } bevy_platform = { version = "0.18", default-features = false }
bevy_mod_xr = { path = "crates/bevy_xr", version = "0.3.0" }
bevy_mod_openxr = { path = "crates/bevy_openxr", version = "0.3.0" }
bevy_xr_utils = { path = "crates/bevy_xr_utils", version = "0.3.0" } bevy_mod_xr = { path = "crates/bevy_xr", version = "0.5.0" }
openxr = "0.19.0" bevy_mod_openxr = { path = "crates/bevy_openxr", version = "0.5.0" }
bevy_xr_utils = { path = "crates/bevy_xr_utils", version = "0.5.0" }
openxr = "0.21.1"
thiserror = "2.0.3" thiserror = "2.0.3"
wgpu = "26" wgpu = "27"
wgpu-hal = "26" wgpu-hal = "27"
# Enable a small amount of optimization in the dev profile. # Enable a small amount of optimization in the dev profile.
[profile.dev] [profile.dev]

View File

@@ -19,7 +19,7 @@ fn main() {
.add_plugins(bevy_mod_openxr::features::fb_passthrough::OxrFbPassthroughPlugin) .add_plugins(bevy_mod_openxr::features::fb_passthrough::OxrFbPassthroughPlugin)
.add_systems(Startup, setup) .add_systems(Startup, setup)
.add_systems(Update, modify_camera) .add_systems(Update, modify_camera)
.insert_resource(AmbientLight { .insert_resource(GlobalAmbientLight {
color: Default::default(), color: Default::default(),
brightness: 500.0, brightness: 500.0,
affects_lightmapped_meshes: false, affects_lightmapped_meshes: false,

View File

@@ -119,7 +119,7 @@ fn spawn_hands(
actions actions
.left .left
.create_space( .create_space(
session.deref().deref().clone(), session.deref().deref(),
openxr::Path::NULL, openxr::Path::NULL,
Posef::IDENTITY, Posef::IDENTITY,
) )

View File

@@ -10,7 +10,7 @@ fn main() -> AppExit {
.add_plugins(bevy_mod_xr::hand_debug_gizmos::HandGizmosPlugin) .add_plugins(bevy_mod_xr::hand_debug_gizmos::HandGizmosPlugin)
.add_systems(Startup, setup) .add_systems(Startup, setup)
.add_systems(Update, handle_input) .add_systems(Update, handle_input)
.insert_resource(AmbientLight::default()) .insert_resource(GlobalAmbientLight::default())
.run() .run()
} }

View File

@@ -2,3 +2,15 @@
mod openxr; mod openxr;
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]
pub use openxr::*; pub use openxr::*;
pub mod prelude {
pub use crate::{
action_binding::OxrSendActionBindings, action_binding::OxrSuggestActionBinding,
action_set_attaching::OxrAttachActionSet, action_set_syncing::OxrActionSetSyncSet,
action_set_syncing::OxrSyncActionSet, add_xr_plugins, exts::OxrExtensions,
features::handtracking::HandTrackingPlugin, init::OxrInitPlugin, openxr_session_running,
resources::OxrInstance, resources::OxrSessionConfig, session::OxrSession,
spaces::OxrSpaceExt,
};
pub use openxr::EnvironmentBlendMode;
}

View File

@@ -1,5 +1,11 @@
use std::{
ffi::CStr,
ops::{BitAnd, BitOr},
};
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
use bevy_ecs::resource::Resource; use bevy_ecs::resource::Resource;
use bevy_log::error;
use openxr::ExtensionSet; use openxr::ExtensionSet;
#[derive(Clone, Debug, Eq, PartialEq, Deref, DerefMut, Resource)] #[derive(Clone, Debug, Eq, PartialEq, Deref, DerefMut, Resource)]
@@ -36,7 +42,41 @@ impl OxrExtensions {
} }
/// returns true if all of the extensions enabled are also available in `available_exts` /// returns true if all of the extensions enabled are also available in `available_exts`
pub fn is_available(&self, available_exts: &OxrExtensions) -> bool { pub fn is_available(&self, available_exts: &OxrExtensions) -> bool {
self.clone() & available_exts.clone() == *self self.0.intersection(available_exts) == self.0
}
/// Returns any extensions needed by `required_exts` that aren't available in `self`
pub fn unavailable_exts(&self, required_exts: &Self) -> Vec<String> {
required_exts
.difference(self)
.names()
.into_iter()
.filter_map(|v| {
CStr::from_bytes_with_nul(v)
.inspect_err(|err| error!("failed to convert openxr ext name to CStr: {err}"))
.ok()
})
.filter_map(|v| {
v.to_str()
.inspect_err(|err| error!("openxr ext name is not valid utf8: {err}"))
.ok()
})
.map(|v| v.to_string())
.collect()
}
}
impl BitOr for OxrExtensions {
type Output = Self;
// this is horribly slow, but doesn't require a bunch of code duplication
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0.names().into_iter().chain(rhs.names()).collect())
}
}
impl BitAnd for OxrExtensions {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
Self(self.intersection(&rhs))
} }
} }
impl From<ExtensionSet> for OxrExtensions { impl From<ExtensionSet> for OxrExtensions {
@@ -56,293 +96,3 @@ impl Default for OxrExtensions {
Self(exts) Self(exts)
} }
} }
macro_rules! unavailable_exts {
(
$exts:ty;
$(
$(
#[$meta:meta]
)*
$ident:ident
),*
$(,)?
) => {
impl $exts {
/// Returns any extensions needed by `required_exts` that aren't available in `self`
pub fn unavailable_exts(&self, required_exts: &Self) -> Vec<std::borrow::Cow<'static, str>> {
let mut exts = vec![];
$(
$(
#[$meta]
)*
if required_exts.0.$ident && !self.0.$ident {
exts.push(std::borrow::Cow::Borrowed(stringify!($ident)))
}
)*
for ext in required_exts.0.other.iter() {
if !self.0.other.contains(ext) {
exts.push(std::borrow::Cow::Owned(ext.clone()))
}
}
exts
}
}
};
}
macro_rules! bitor {
(
$exts:ty;
$(
$(
#[$meta:meta]
)*
$ident:ident
),*
$(,)?
) => {
impl std::ops::BitOr for $exts {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
let mut out = ExtensionSet::default();
$(
$(
#[$meta]
)*
{
out.$ident = self.0.$ident || rhs.0.$ident;
}
)*
out.other = self.0.other;
for ext in rhs.0.other {
if !out.other.contains(&ext) {
out.other.push(ext);
}
}
Self(out)
}
}
};
}
macro_rules! bitand {
(
$exts:ty;
$(
$(
#[$meta:meta]
)*
$ident:ident
),*
$(,)?
) => {
impl std::ops::BitAnd for $exts {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
let mut out = ExtensionSet::default();
$(
$(
#[$meta]
)*
{
out.$ident = self.0.$ident && rhs.0.$ident;
}
)*
for ext in self.0.other {
if rhs.0.other.contains(&ext) {
out.other.push(ext);
}
}
Self(out)
}
}
};
}
macro_rules! impl_ext {
(
$(
$macro:ident
),*
) => {
$(
$macro! {
OxrExtensions;
almalence_digital_lens_control,
bd_controller_interaction,
epic_view_configuration_fov,
ext_performance_settings,
ext_thermal_query,
ext_debug_utils,
ext_eye_gaze_interaction,
ext_view_configuration_depth_range,
ext_conformance_automation,
ext_hand_tracking,
#[cfg(windows)]
ext_win32_appcontainer_compatible,
ext_dpad_binding,
ext_hand_joints_motion_range,
ext_samsung_odyssey_controller,
ext_hp_mixed_reality_controller,
ext_palm_pose,
ext_uuid,
ext_hand_interaction,
ext_active_action_set_priority,
ext_local_floor,
ext_hand_tracking_data_source,
ext_plane_detection,
ext_future,
ext_user_presence,
fb_composition_layer_image_layout,
fb_composition_layer_alpha_blend,
#[cfg(target_os = "android")]
fb_android_surface_swapchain_create,
fb_swapchain_update_state,
fb_composition_layer_secure_content,
fb_body_tracking,
fb_display_refresh_rate,
fb_color_space,
fb_hand_tracking_mesh,
fb_hand_tracking_aim,
fb_hand_tracking_capsules,
fb_spatial_entity,
fb_foveation,
fb_foveation_configuration,
fb_keyboard_tracking,
fb_triangle_mesh,
fb_passthrough,
fb_render_model,
fb_spatial_entity_query,
fb_spatial_entity_storage,
fb_foveation_vulkan,
#[cfg(target_os = "android")]
fb_swapchain_update_state_android_surface,
fb_swapchain_update_state_opengl_es,
fb_swapchain_update_state_vulkan,
fb_touch_controller_pro,
fb_spatial_entity_sharing,
fb_space_warp,
fb_haptic_amplitude_envelope,
fb_scene,
fb_scene_capture,
fb_spatial_entity_container,
fb_face_tracking,
fb_eye_tracking_social,
fb_passthrough_keyboard_hands,
fb_composition_layer_settings,
fb_touch_controller_proximity,
fb_haptic_pcm,
fb_composition_layer_depth_test,
fb_spatial_entity_storage_batch,
fb_spatial_entity_user,
fb_face_tracking2,
htc_vive_cosmos_controller_interaction,
htc_facial_tracking,
htc_vive_focus3_controller_interaction,
htc_hand_interaction,
htc_vive_wrist_tracker_interaction,
htc_passthrough,
htc_foveation,
htc_anchor,
huawei_controller_interaction,
#[cfg(target_os = "android")]
khr_android_thread_settings,
#[cfg(target_os = "android")]
khr_android_surface_swapchain,
khr_composition_layer_cube,
#[cfg(target_os = "android")]
khr_android_create_instance,
khr_composition_layer_depth,
khr_vulkan_swapchain_format_list,
khr_composition_layer_cylinder,
khr_composition_layer_equirect,
khr_opengl_enable,
khr_opengl_es_enable,
khr_vulkan_enable,
#[cfg(windows)]
khr_d3d11_enable,
#[cfg(windows)]
khr_d3d12_enable,
khr_visibility_mask,
khr_composition_layer_color_scale_bias,
#[cfg(windows)]
khr_win32_convert_performance_counter_time,
khr_convert_timespec_time,
khr_loader_init,
#[cfg(target_os = "android")]
khr_loader_init_android,
khr_vulkan_enable2,
khr_composition_layer_equirect2,
khr_binding_modification,
khr_swapchain_usage_input_attachment_bit,
khr_locate_spaces,
khr_maintenance1,
meta_foveation_eye_tracked,
meta_local_dimming,
meta_passthrough_preferences,
meta_virtual_keyboard,
meta_vulkan_swapchain_create_info,
meta_performance_metrics,
meta_headset_id,
meta_recommended_layer_resolution,
meta_passthrough_color_lut,
meta_spatial_entity_mesh,
meta_automatic_layer_filter,
meta_touch_controller_plus,
meta_environment_depth,
ml_ml2_controller_interaction,
ml_frame_end_info,
ml_global_dimmer,
ml_compat,
ml_marker_understanding,
ml_localization_map,
ml_user_calibration,
mnd_headless,
mnd_swapchain_usage_input_attachment_bit,
msft_unbounded_reference_space,
msft_spatial_anchor,
msft_spatial_graph_bridge,
msft_hand_interaction,
msft_hand_tracking_mesh,
msft_secondary_view_configuration,
msft_first_person_observer,
msft_controller_model,
#[cfg(windows)]
msft_perception_anchor_interop,
#[cfg(windows)]
msft_holographic_window_attachment,
msft_composition_layer_reprojection,
msft_spatial_anchor_persistence,
#[cfg(target_os = "android")]
oculus_android_session_state_enable,
oculus_audio_device_guid,
oculus_external_camera,
oppo_controller_interaction,
qcom_tracking_optimization_settings,
ultraleap_hand_tracking_forearm,
valve_analog_threshold,
varjo_quad_views,
varjo_foveated_rendering,
varjo_composition_layer_depth_test,
varjo_environment_depth_estimation,
varjo_marker_tracking,
varjo_view_offset,
varjo_xr4_controller_interaction,
yvr_controller_interaction,
extx_overlay,
mndx_egl_enable,
mndx_force_feedback_curl,
htcx_vive_tracker_interaction,
}
)*
};
}
impl_ext!(bitor, bitand, unavailable_exts);

View File

@@ -69,7 +69,7 @@ pub fn insert_passthrough(world: &mut World) {
pub fn resume_passthrough( pub fn resume_passthrough(
passthrough: Res<OxrPassthrough>, passthrough: Res<OxrPassthrough>,
passthrough_layer: Res<OxrPassthroughLayer>, passthrough_layer: Res<OxrPassthroughLayerFB>,
) { ) {
passthrough.start().unwrap(); passthrough.start().unwrap();
passthrough_layer.resume().unwrap(); passthrough_layer.resume().unwrap();
@@ -77,7 +77,7 @@ pub fn resume_passthrough(
pub fn pause_passthrough( pub fn pause_passthrough(
passthrough: Res<OxrPassthrough>, passthrough: Res<OxrPassthrough>,
passthrough_layer: Res<OxrPassthroughLayer>, passthrough_layer: Res<OxrPassthroughLayerFB>,
) { ) {
passthrough_layer.pause().unwrap(); passthrough_layer.pause().unwrap();
passthrough.pause().unwrap(); passthrough.pause().unwrap();
@@ -87,7 +87,7 @@ pub fn create_passthrough(
session: &OxrSession, session: &OxrSession,
flags: openxr::PassthroughFlagsFB, flags: openxr::PassthroughFlagsFB,
purpose: openxr::PassthroughLayerPurposeFB, purpose: openxr::PassthroughLayerPurposeFB,
) -> OxrResult<(OxrPassthrough, OxrPassthroughLayer)> { ) -> OxrResult<(OxrPassthrough, OxrPassthroughLayerFB)> {
let passthrough = session.create_passthrough(flags)?; let passthrough = session.create_passthrough(flags)?;
let passthrough_layer = session.create_passthrough_layer(&passthrough, purpose)?; let passthrough_layer = session.create_passthrough_layer(&passthrough, purpose)?;

View File

@@ -14,6 +14,7 @@ use crate::{
types::{AppInfo, OxrExtensions, Result, WgpuGraphics}, types::{AppInfo, OxrExtensions, Result, WgpuGraphics},
}; };
#[allow(clippy::missing_safety_doc)]
/// This is an extension trait to the [`Graphics`](openxr::Graphics) trait and is how the graphics API should be interacted with. /// This is an extension trait to the [`Graphics`](openxr::Graphics) trait and is how the graphics API should be interacted with.
pub unsafe trait GraphicsExt: openxr::Graphics { pub unsafe trait GraphicsExt: openxr::Graphics {
/// Wrap the graphics specific type into the [GraphicsWrap] enum /// Wrap the graphics specific type into the [GraphicsWrap] enum

View File

@@ -1,14 +1,15 @@
use std::ffi::{c_void, CStr, CString}; use std::ffi::{CStr, CString, c_void};
use std::str::FromStr; use std::str::FromStr;
use ash::vk::{self, Handle}; use ash::vk::{self, Handle};
use bevy_log::{debug, error}; use bevy_log::{debug, error};
use bevy_math::UVec2; use bevy_math::UVec2;
use openxr::{sys, Version}; use openxr::sys::Handle as _;
use wgpu::{InstanceFlags, MemoryBudgetThresholds}; use openxr::{Version, sys};
use wgpu::TextureUses; use wgpu::TextureUses;
use wgpu_hal::api::Vulkan; use wgpu::{ExperimentalFeatures, InstanceFlags, MemoryBudgetThresholds};
use wgpu_hal::Api; use wgpu_hal::Api;
use wgpu_hal::api::Vulkan;
use super::{GraphicsExt, GraphicsType, GraphicsWrap, OxrManualGraphicsConfig}; use super::{GraphicsExt, GraphicsType, GraphicsWrap, OxrManualGraphicsConfig};
use crate::error::OxrError; use crate::error::OxrError;
@@ -54,14 +55,13 @@ unsafe impl GraphicsExt for openxr::Vulkan {
) -> Result<wgpu::Texture> { ) -> Result<wgpu::Texture> {
let color_image = vk::Image::from_raw(color_image); let color_image = vk::Image::from_raw(color_image);
let wgpu_hal_texture = unsafe { let wgpu_hal_texture = unsafe {
let hal_dev = let hal_dev = device.as_hal::<wgpu_hal::vulkan::Api>().ok_or(
device OxrError::GraphicsBackendMismatch {
.as_hal::<wgpu_hal::vulkan::Api>() item: "Wgpu Device",
.ok_or(OxrError::GraphicsBackendMismatch { backend: "unknown",
item: "Wgpu Device", expected_backend: "vulkan",
backend: "unknown", },
expected_backend: "vulkan", )?;
})?;
hal_dev.texture_from_raw( hal_dev.texture_from_raw(
color_image, color_image,
&wgpu_hal::TextureDescriptor { &wgpu_hal::TextureDescriptor {
@@ -207,16 +207,8 @@ unsafe impl GraphicsExt for openxr::Vulkan {
system_id, system_id,
}; };
let mut out = sys::Session::NULL; let mut out = sys::Session::NULL;
cvt(unsafe { (instance.fp().create_session)( cvt(unsafe { (instance.fp().create_session)(instance.as_raw(), &info, &mut out) })?;
instance.as_raw(), Ok(unsafe { openxr::Session::from_raw(instance.clone(), out, Box::new(())) })
&info,
&mut out,
) })?;
Ok(unsafe { openxr::Session::from_raw(
instance.clone(),
out,
Box::new(()),
) })
} }
fn init_fallback_graphics( fn init_fallback_graphics(
@@ -300,6 +292,7 @@ fn get_extensions(
instance_exts.extend(&cfg.vk_instance_exts); instance_exts.extend(&cfg.vk_instance_exts);
device_exts.extend(&cfg.vk_device_exts); device_exts.extend(&cfg.vk_device_exts);
} }
instance_exts.dedup(); instance_exts.dedup();
device_exts.dedup(); device_exts.dedup();
@@ -392,12 +385,13 @@ fn init_from_instance_and_dev(
let wgpu_features = wgpu_exposed_adapter.features; let wgpu_features = wgpu_exposed_adapter.features;
debug!("wgpu features: {wgpu_features:#?}"); debug!("wgpu features: {wgpu_features:#?}");
let enabled_extensions = wgpu_exposed_adapter let mut enabled_extensions = wgpu_exposed_adapter
.adapter .adapter
.required_device_extensions(wgpu_features); .required_device_extensions(wgpu_features);
enabled_extensions.extend(device_exts);
enabled_extensions.dedup();
let (wgpu_open_device, vk_device_ptr, queue_family_index) = { let (wgpu_open_device, vk_device_ptr, queue_family_index) = {
let extensions_cchar: Vec<_> = device_exts.iter().map(|s| s.as_ptr()).collect();
let mut enabled_phd_features = wgpu_exposed_adapter let mut enabled_phd_features = wgpu_exposed_adapter
.adapter .adapter
.physical_device_features(&enabled_extensions, wgpu_features); .physical_device_features(&enabled_extensions, wgpu_features);
@@ -406,17 +400,13 @@ fn init_from_instance_and_dev(
.queue_family_index(family_index) .queue_family_index(family_index)
.queue_priorities(&[1.0]); .queue_priorities(&[1.0]);
let family_infos = [family_info]; let family_infos = [family_info];
let mut physical_device_multiview_features = vk::PhysicalDeviceMultiviewFeatures { let ext_names = enabled_extensions
multiview: vk::TRUE, .iter()
..Default::default() .map(|e| e.as_ptr())
}; .collect::<Vec<_>>();
let info = enabled_phd_features let info = enabled_phd_features
.add_to_device_create( .add_to_device_create(vk::DeviceCreateInfo::default().queue_create_infos(&family_infos))
vk::DeviceCreateInfo::default() .enabled_extension_names(&ext_names);
.queue_create_infos(&family_infos)
.push_next(&mut physical_device_multiview_features),
)
.enabled_extension_names(&extensions_cchar);
let vk_device = create_dev(&info)?; let vk_device = create_dev(&info)?;
let vk_device_ptr = vk_device.handle().as_raw() as *const c_void; let vk_device_ptr = vk_device.handle().as_raw() as *const c_void;
@@ -453,6 +443,7 @@ fn init_from_instance_and_dev(
required_limits: limits, required_limits: limits,
memory_hints: wgpu::MemoryHints::Performance, memory_hints: wgpu::MemoryHints::Performance,
trace: wgpu::Trace::Off, trace: wgpu::Trace::Off,
experimental_features: ExperimentalFeatures::enabled(),
}, },
) )
}?; }?;
@@ -475,11 +466,7 @@ fn init_from_instance_and_dev(
} }
fn cvt(x: sys::Result) -> openxr::Result<sys::Result> { fn cvt(x: sys::Result) -> openxr::Result<sys::Result> {
if x.into_raw() >= 0 { if x.into_raw() >= 0 { Ok(x) } else { Err(x) }
Ok(x)
} else {
Err(x)
}
} }
fn vulkan_to_wgpu(format: vk::Format) -> Option<wgpu::TextureFormat> { fn vulkan_to_wgpu(format: vk::Format) -> Option<wgpu::TextureFormat> {
@@ -811,6 +798,7 @@ fn wgpu_to_vulkan(format: wgpu::TextureFormat) -> Option<vk::Format> {
Tf::EacR11Snorm => F::EAC_R11_SNORM_BLOCK, Tf::EacR11Snorm => F::EAC_R11_SNORM_BLOCK,
Tf::EacRg11Unorm => F::EAC_R11G11_UNORM_BLOCK, Tf::EacRg11Unorm => F::EAC_R11G11_UNORM_BLOCK,
Tf::EacRg11Snorm => F::EAC_R11G11_SNORM_BLOCK, Tf::EacRg11Snorm => F::EAC_R11G11_SNORM_BLOCK,
Tf::P010 => F::G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16,
Tf::Astc { block, channel } => match channel { Tf::Astc { block, channel } => match channel {
AstcChannel::Unorm => match block { AstcChannel::Unorm => match block {
AstcBlock::B4x4 => F::ASTC_4X4_UNORM_BLOCK, AstcBlock::B4x4 => F::ASTC_4X4_UNORM_BLOCK,

View File

@@ -2,7 +2,7 @@ use std::mem;
use bevy_ecs::world::World; use bevy_ecs::world::World;
use bevy_mod_xr::spaces::{XrPrimaryReferenceSpace, XrSpace}; use bevy_mod_xr::spaces::{XrPrimaryReferenceSpace, XrSpace};
use openxr::{sys, CompositionLayerFlags, Fovf, Posef, Rect2Di}; use openxr::{CompositionLayerFlags, Fovf, Posef, Rect2Di, sys};
use crate::graphics::graphics_match; use crate::graphics::graphics_match;
use crate::resources::*; use crate::resources::*;
@@ -65,8 +65,8 @@ impl LayerProvider for ProjectionLayer {
impl LayerProvider for PassthroughLayer { impl LayerProvider for PassthroughLayer {
fn get(&self, world: &World) -> Option<Box<dyn CompositionLayer<'_>>> { fn get(&self, world: &World) -> Option<Box<dyn CompositionLayer<'_>>> {
Some(Box::new( Some(Box::new(
CompositionLayerPassthrough::new() CompositionLayerPassthroughFB::new()
.layer_handle(world.get_resource::<OxrPassthroughLayer>()?) .layer_handle(world.get_resource::<OxrPassthroughLayerFB>()?)
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA), .layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA),
)) ))
} }
@@ -236,16 +236,16 @@ impl Default for CompositionLayerProjection<'_> {
Self::new() Self::new()
} }
} }
pub struct CompositionLayerPassthrough { pub struct CompositionLayerPassthroughFB {
inner: sys::CompositionLayerPassthroughFB, inner: sys::CompositionLayerPassthroughFB,
} }
impl Default for CompositionLayerPassthrough { impl Default for CompositionLayerPassthroughFB {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
} }
} }
impl CompositionLayerPassthrough { impl CompositionLayerPassthroughFB {
#[inline] #[inline]
pub const fn new() -> Self { pub const fn new() -> Self {
Self { Self {
@@ -256,7 +256,7 @@ impl CompositionLayerPassthrough {
} }
} }
#[inline] #[inline]
pub fn layer_handle(mut self, layer_handle: &OxrPassthroughLayer) -> Self { pub fn layer_handle(mut self, layer_handle: &OxrPassthroughLayerFB) -> Self {
self.inner.layer_handle = *layer_handle.inner(); self.inner.layer_handle = *layer_handle.inner();
self self
} }
@@ -266,7 +266,7 @@ impl CompositionLayerPassthrough {
self self
} }
} }
unsafe impl<'a> CompositionLayer<'a> for CompositionLayerPassthrough { unsafe impl<'a> CompositionLayer<'a> for CompositionLayerPassthroughFB {
fn swapchain(&self) -> Option<&'a OxrSwapchain> { fn swapchain(&self) -> Option<&'a OxrSwapchain> {
None None
} }

View File

@@ -21,7 +21,7 @@ pub struct OxrReferenceSpacePlugin {
impl Default for OxrReferenceSpacePlugin { impl Default for OxrReferenceSpacePlugin {
fn default() -> Self { fn default() -> Self {
Self { Self {
default_primary_ref_space: openxr::ReferenceSpaceType::STAGE, default_primary_ref_space: openxr::ReferenceSpaceType::LOCAL,
} }
} }
} }

View File

@@ -7,24 +7,24 @@ use bevy_ecs::{
system::{Commands, Query, Res, ResMut}, system::{Commands, Query, Res, ResMut},
world::World, world::World,
}; };
use bevy_log::{debug_span, error, info}; use bevy_log::{debug_span, error, info, warn};
use bevy_render::{ use bevy_render::{
Render, RenderApp,
extract_resource::ExtractResourcePlugin, extract_resource::ExtractResourcePlugin,
pipelined_rendering::PipelinedRenderingPlugin, pipelined_rendering::PipelinedRenderingPlugin,
texture::{ManualTextureView, ManualTextureViews}, texture::{ManualTextureView, ManualTextureViews},
view::ExtractedView, view::ExtractedView,
Render, RenderApp,
}; };
use bevy_mod_xr::{ use bevy_mod_xr::{
camera::{calculate_projection, Fov, XrCamera, XrProjection, XrViewInit}, camera::{Fov, XrCamera, XrProjection, XrViewInit, calculate_projection},
session::{ session::{
XrFirst, XrHandleEvents, XrPreDestroySession, XrRenderSystems, XrRootTransform, XrFirst, XrHandleEvents, XrPreDestroySession, XrRenderSystems, XrRootTransform,
XrSessionCreated, XrSessionCreated,
}, },
spaces::XrPrimaryReferenceSpace, spaces::XrPrimaryReferenceSpace,
}; };
use bevy_transform::{components::Transform, TransformSystems}; use bevy_transform::{TransformSystems, components::Transform};
use openxr::ViewStateFlags; use openxr::ViewStateFlags;
use crate::{helper_traits::ToTransform as _, init::should_run_frame_loop, resources::*}; use crate::{helper_traits::ToTransform as _, init::should_run_frame_loop, resources::*};
@@ -158,10 +158,7 @@ pub fn init_views<const SPAWN_CAMERAS: bool>(
add_texture_view(&mut manual_texture_views, temp_tex, &graphics_info, index); add_texture_view(&mut manual_texture_views, temp_tex, &graphics_info, index);
if SPAWN_CAMERAS { if SPAWN_CAMERAS {
commands.spawn(( commands.spawn((
Camera { RenderTarget::TextureView(view_handle),
target: RenderTarget::TextureView(view_handle),
..Default::default()
},
XrCamera(index), XrCamera(index),
Projection::custom(XrProjection::default()), Projection::custom(XrProjection::default()),
// NoFrustumCulling, // NoFrustumCulling,
@@ -177,14 +174,14 @@ pub fn wait_frame(mut frame_waiter: ResMut<OxrFrameWaiter>, mut commands: Comman
pub fn update_cameras( pub fn update_cameras(
frame_state: Res<OxrFrameState>, frame_state: Res<OxrFrameState>,
mut cameras: Query<(&mut Camera, &XrCamera)>, mut cameras: Query<(&mut Camera, &mut RenderTarget, &XrCamera)>,
) { ) {
for (mut camera, xr_camera) in &mut cameras { for (_, mut target, xr_camera) in &mut cameras {
camera.target = *target =
RenderTarget::TextureView(ManualTextureViewHandle(XR_TEXTURE_INDEX + xr_camera.0)); RenderTarget::TextureView(ManualTextureViewHandle(XR_TEXTURE_INDEX + xr_camera.0));
} }
if frame_state.is_changed() { if frame_state.is_changed() {
for (mut camera, _) in &mut cameras { for (mut camera, _, _) in &mut cameras {
camera.is_active = frame_state.should_render camera.is_active = frame_state.should_render
} }
} }
@@ -205,13 +202,16 @@ pub fn locate_views(
} else { } else {
frame_state.predicted_display_time frame_state.predicted_display_time
}; };
let (flags, xr_views) = session let Ok((flags, xr_views)) = session
.locate_views( .locate_views(
openxr::ViewConfigurationType::PRIMARY_STEREO, openxr::ViewConfigurationType::PRIMARY_STEREO,
time, time,
&ref_space, &ref_space,
) )
.expect("Failed to locate views"); .inspect_err(|err| warn!("failed to locate views: {err}"))
else {
return;
};
match ( match (
flags & ViewStateFlags::ORIENTATION_VALID == ViewStateFlags::ORIENTATION_VALID, flags & ViewStateFlags::ORIENTATION_VALID == ViewStateFlags::ORIENTATION_VALID,
@@ -286,7 +286,11 @@ pub fn insert_texture_views(
mut swapchain: ResMut<OxrSwapchain>, mut swapchain: ResMut<OxrSwapchain>,
mut manual_texture_views: ResMut<ManualTextureViews>, mut manual_texture_views: ResMut<ManualTextureViews>,
graphics_info: Res<OxrCurrentSessionConfig>, graphics_info: Res<OxrCurrentSessionConfig>,
frame_state: Res<OxrFrameState>,
) { ) {
if !frame_state.should_render {
return;
}
let index = swapchain.acquire_image().expect("Failed to acquire image"); let index = swapchain.acquire_image().expect("Failed to acquire image");
let image = &swapchain_images[index as usize]; let image = &swapchain_images[index as usize];
@@ -296,10 +300,12 @@ pub fn insert_texture_views(
} }
} }
pub fn wait_image(mut swapchain: ResMut<OxrSwapchain>) { pub fn wait_image(mut swapchain: ResMut<OxrSwapchain>, state: Res<OxrFrameState>) {
swapchain if state.should_render {
.wait_image(openxr::Duration::INFINITE) swapchain
.expect("Failed to wait image"); .wait_image(openxr::Duration::INFINITE)
.expect("Failed to wait image");
}
} }
pub fn add_texture_view( pub fn add_texture_view(
@@ -317,7 +323,7 @@ pub fn add_texture_view(
let view = ManualTextureView { let view = ManualTextureView {
texture_view: view.into(), texture_view: view.into(),
size: info.resolution, size: info.resolution,
format: info.format, view_format: info.format,
}; };
let handle = ManualTextureViewHandle(XR_TEXTURE_INDEX + index); let handle = ManualTextureViewHandle(XR_TEXTURE_INDEX + index);
manual_texture_views.insert(handle, view); manual_texture_views.insert(handle, view);
@@ -328,7 +334,10 @@ 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 release_image(mut swapchain: ResMut<OxrSwapchain>) { pub fn release_image(mut swapchain: ResMut<OxrSwapchain>, state: Res<OxrFrameState>) {
if !state.should_render {
return;
}
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
{ {
let ctx = ndk_context::android_context(); let ctx = ndk_context::android_context();

View File

@@ -47,7 +47,7 @@ impl OxrEntry {
application_version: app_info.version.to_u32(), application_version: app_info.version.to_u32(),
engine_name: "Bevy", engine_name: "Bevy",
engine_version: Version::BEVY.to_u32(), engine_version: Version::BEVY.to_u32(),
api_version: openxr::Version::new(1, 0, 34), api_version: openxr::Version::new(1, 1, 54),
}, },
&required_exts.into(), &required_exts.into(),
layers, layers,
@@ -322,11 +322,11 @@ impl OxrPassthrough {
/// Wrapper around [`openxr::Passthrough`]. /// 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. /// Used to create a [`CompositionLayerPassthrough`](crate::layer_builder::CompositionLayerPassthrough), and to [`pause`](openxr::PassthroughLayerFB::pause) or [`resume`](openxr::PassthroughLayerFB::resume) rendering of the passthrough layer.
/// ///
/// See [`openxr::PassthroughLayer`] for available methods. /// See [`openxr::PassthroughLayerFB`] for available methods.
#[derive(Resource, Deref, DerefMut)] #[derive(Resource, Deref, DerefMut)]
pub struct OxrPassthroughLayer(pub openxr::PassthroughLayer); pub struct OxrPassthroughLayerFB(pub openxr::PassthroughLayerFB);
#[derive(Resource, Deref, DerefMut, Default)] #[derive(Resource, Deref, DerefMut, Default)]
pub struct OxrRenderLayers(pub Vec<Box<dyn LayerProvider + Send + Sync>>); pub struct OxrRenderLayers(pub Vec<Box<dyn LayerProvider + Send + Sync>>);

View File

@@ -1,7 +1,7 @@
use std::ffi::c_void; use std::ffi::c_void;
use crate::next_chain::{OxrNextChain, OxrNextChainStructBase, OxrNextChainStructProvider}; use crate::next_chain::{OxrNextChain, OxrNextChainStructBase, OxrNextChainStructProvider};
use crate::resources::{OxrPassthrough, OxrPassthroughLayer, OxrSwapchain}; use crate::resources::{OxrPassthrough, OxrPassthroughLayerFB, OxrSwapchain};
use crate::types::{Result, SwapchainCreateInfo}; use crate::types::{Result, SwapchainCreateInfo};
use bevy_derive::Deref; use bevy_derive::Deref;
use bevy_ecs::resource::Resource; use bevy_ecs::resource::Resource;
@@ -91,8 +91,8 @@ impl OxrSession {
&self, &self,
passthrough: &OxrPassthrough, passthrough: &OxrPassthrough,
purpose: openxr::PassthroughLayerPurposeFB, purpose: openxr::PassthroughLayerPurposeFB,
) -> Result<OxrPassthroughLayer> { ) -> Result<OxrPassthroughLayerFB> {
Ok(OxrPassthroughLayer(graphics_match! { Ok(OxrPassthroughLayerFB(graphics_match! {
&self.1; &self.1;
session => session.create_passthrough_layer(&passthrough.0, passthrough.1, purpose)? session => session.create_passthrough_layer(&passthrough.0, passthrough.1, purpose)?
})) }))

View File

@@ -19,7 +19,7 @@ use bevy_platform::collections::hash_set::HashSet;
use bevy_transform::components::Transform; use bevy_transform::components::Transform;
use openxr::{ use openxr::{
HAND_JOINT_COUNT, HandJointLocation, HandJointLocations, HandJointVelocities, HAND_JOINT_COUNT, HandJointLocation, HandJointLocations, HandJointVelocities,
HandJointVelocity, ReferenceSpaceType, SpaceLocationFlags, SpaceVelocityFlags, sys, HandJointVelocity, ReferenceSpaceType, SpaceLocationFlags, SpaceVelocityFlags, sys::{self, Handle as _},
}; };
use std::{mem::MaybeUninit, ptr, sync::Mutex}; use std::{mem::MaybeUninit, ptr, sync::Mutex};

View File

@@ -120,7 +120,7 @@ fn on_tracker_add(mut world: DeferredWorld, HookContext { entity, .. }: HookCont
if world if world
.entity(entity) .entity(entity)
.get_components::<Has<Children>>() .get_components::<Has<Children>>()
.is_some_and(identity) .is_ok_and(identity)
{ {
return; return;
} }

View File

@@ -26,7 +26,7 @@ bevy.workspace = true
[target.'cfg(not(target_family = "wasm"))'.dependencies] [target.'cfg(not(target_family = "wasm"))'.dependencies]
openxr.workspace = true openxr.workspace = true
openxr_mndx_xdev_space = "0.1.0" openxr_mndx_xdev_space = "0.2.0"
[lints.clippy] [lints.clippy]
too_many_arguments = "allow" too_many_arguments = "allow"

View File

@@ -19,9 +19,9 @@ fn main() {
.add_systems(Update, read_action_with_marker_component) .add_systems(Update, read_action_with_marker_component)
.add_systems(Update, handle_flight_input) .add_systems(Update, handle_flight_input)
// Realtime lighting is expensive, use ambient light instead // Realtime lighting is expensive, use ambient light instead
.insert_resource(AmbientLight { .insert_resource(GlobalAmbientLight {
brightness: 500.0, brightness: 500.0,
..AmbientLight::default() ..GlobalAmbientLight::default()
}) })
.run(); .run();
} }

View File

@@ -11,7 +11,7 @@ fn main() -> AppExit {
exts: { exts: {
let mut exts = OxrExtensions::default(); let mut exts = OxrExtensions::default();
exts.enable_hand_tracking(); exts.enable_hand_tracking();
exts.other.push("XR_MNDX_xdev_space".to_string()); exts.other.push(c"XR_MNDX_xdev_space".to_bytes_with_nul().to_vec());
exts exts
}, },
..Default::default() ..Default::default()

View File

@@ -3,9 +3,8 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_mod_openxr::add_xr_plugins; use bevy_mod_openxr::add_xr_plugins;
use bevy_xr_utils::transform_utils::{self, SnapToPosition, SnapToRotation}; use bevy_xr_utils::transform_utils::{self, SnapToPosition, SnapToRotation};
use bevy_xr_utils::xr_utils_actions::{ use bevy_xr_utils::actions::{
ActiveSet, XRUtilsAction, XRUtilsActionSet, XRUtilsActionState, XRUtilsActionSystems, ActionType, ActiveSet, XRUtilsAction, XRUtilsActionSet, XRUtilsActionState, XRUtilsActionSystems, XRUtilsActionsPlugin, XRUtilsBinding
XRUtilsActionsPlugin, XRUtilsBinding,
}; };
fn main() -> AppExit { fn main() -> AppExit {
@@ -27,9 +26,9 @@ fn main() -> AppExit {
Update, Update,
send_recenter.after(XRUtilsActionSystems::SyncActionStates), send_recenter.after(XRUtilsActionSystems::SyncActionStates),
) )
.insert_resource(AmbientLight { .insert_resource(GlobalAmbientLight {
brightness: 500.0, brightness: 500.0,
..AmbientLight::default() ..GlobalAmbientLight::default()
}) })
.run() .run()
} }
@@ -104,7 +103,7 @@ fn create_action_entities(mut commands: Commands) {
XRUtilsAction { XRUtilsAction {
action_name: "face_red".into(), action_name: "face_red".into(),
localized_name: "face_red_localized".into(), localized_name: "face_red_localized".into(),
action_type: bevy_mod_xr::actions::ActionType::Bool, action_type: ActionType::Bool,
}, },
FaceRedAction, //lets try a marker component FaceRedAction, //lets try a marker component
)) ))
@@ -128,7 +127,7 @@ fn create_action_entities(mut commands: Commands) {
XRUtilsAction { XRUtilsAction {
action_name: "center".into(), action_name: "center".into(),
localized_name: "center_localized".into(), localized_name: "center_localized".into(),
action_type: bevy_mod_xr::actions::ActionType::Bool, action_type: ActionType::Bool,
}, },
Center, //lets try a marker component Center, //lets try a marker component
)) ))
@@ -150,7 +149,7 @@ fn create_action_entities(mut commands: Commands) {
fn send_look_at_red_cube_event( fn send_look_at_red_cube_event(
mut action_query: Query<&XRUtilsActionState, With<FaceRedAction>>, mut action_query: Query<&XRUtilsActionState, With<FaceRedAction>>,
mut event_writer: EventWriter<SnapToRotation>, mut event_writer: MessageWriter<SnapToRotation>,
) { ) {
//now for the actual checking //now for the actual checking
for state in action_query.iter_mut() { for state in action_query.iter_mut() {
@@ -175,7 +174,7 @@ pub struct Center;
fn send_recenter( fn send_recenter(
mut action_query: Query<&XRUtilsActionState, With<Center>>, mut action_query: Query<&XRUtilsActionState, With<Center>>,
mut event_writer: EventWriter<SnapToPosition>, mut event_writer: MessageWriter<SnapToPosition>,
) { ) {
//now for the actual checking //now for the actual checking
for state in action_query.iter_mut() { for state in action_query.iter_mut() {