xr resources code
This commit is contained in:
@@ -12,9 +12,11 @@ linked = ["openxr/linked"]
|
|||||||
vulkan = ["dep:ash"]
|
vulkan = ["dep:ash"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0.79"
|
||||||
async-std = "1.12.0"
|
async-std = "1.12.0"
|
||||||
bevy = "0.12.1"
|
bevy = "0.12.1"
|
||||||
paste = "1.0.14"
|
paste = "1.0.14"
|
||||||
|
thiserror = "1.0.57"
|
||||||
wgpu = "0.17.1"
|
wgpu = "0.17.1"
|
||||||
wgpu-hal = "0.17.1"
|
wgpu-hal = "0.17.1"
|
||||||
winit = "0.28.7"
|
winit = "0.28.7"
|
||||||
@@ -31,6 +33,7 @@ ash = { version = "0.37.3", optional = true }
|
|||||||
[target.'cfg(target_family = "wasm")'.dependencies]
|
[target.'cfg(target_family = "wasm")'.dependencies]
|
||||||
js-sys = "0.3"
|
js-sys = "0.3"
|
||||||
wasm-bindgen = "0.2.91"
|
wasm-bindgen = "0.2.91"
|
||||||
|
glow = "0.12.1"
|
||||||
web-sys = { version = "0.3.68", features = [
|
web-sys = { version = "0.3.68", features = [
|
||||||
# STANDARD
|
# STANDARD
|
||||||
'console',
|
'console',
|
||||||
|
|||||||
@@ -1,48 +1,56 @@
|
|||||||
//! A simple 3D scene with light shining over a cube sitting on a plane.
|
// //! A simple 3D scene with light shining over a cube sitting on a plane.
|
||||||
|
|
||||||
use bevy::prelude::*;
|
// use bevy::prelude::*;
|
||||||
use bevy_oxr::webxr::XrInitPlugin;
|
// use bevy::render::camera::RenderTarget;
|
||||||
|
// use bevy_oxr::webxr::render::{XrRenderingPlugin, XR_TEXTURE_VIEW_HANDLE};
|
||||||
|
// use bevy_oxr::webxr::XrInitPlugin;
|
||||||
|
|
||||||
fn main() {
|
// fn main() {
|
||||||
App::new()
|
// App::new()
|
||||||
.add_plugins((DefaultPlugins, XrInitPlugin))
|
// .add_plugins((DefaultPlugins, XrInitPlugin, XrRenderingPlugin))
|
||||||
.add_systems(Startup, setup)
|
// .add_systems(Startup, setup)
|
||||||
.run();
|
// .run();
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// set up a simple 3D scene
|
// /// set up a simple 3D scene
|
||||||
fn setup(
|
// fn setup(
|
||||||
mut commands: Commands,
|
// mut commands: Commands,
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
// mut meshes: ResMut<Assets<Mesh>>,
|
||||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
// mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
) {
|
// ) {
|
||||||
// circular base
|
// // circular base
|
||||||
commands.spawn(PbrBundle {
|
// commands.spawn(PbrBundle {
|
||||||
mesh: meshes.add(shape::Circle::new(4.0).into()),
|
// mesh: meshes.add(shape::Circle::new(4.0).into()),
|
||||||
material: materials.add(Color::WHITE.into()),
|
// material: materials.add(Color::WHITE.into()),
|
||||||
transform: Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
|
// transform: Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
|
||||||
..default()
|
// ..default()
|
||||||
});
|
// });
|
||||||
// cube
|
// // cube
|
||||||
commands.spawn(PbrBundle {
|
// commands.spawn(PbrBundle {
|
||||||
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
|
// mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
|
||||||
material: materials.add(Color::rgb_u8(124, 144, 255).into()),
|
// material: materials.add(Color::rgb_u8(124, 144, 255).into()),
|
||||||
transform: Transform::from_xyz(0.0, 0.5, 0.0),
|
// transform: Transform::from_xyz(0.0, 0.5, 0.0),
|
||||||
..default()
|
// ..default()
|
||||||
});
|
// });
|
||||||
// light
|
// // light
|
||||||
commands.spawn(PointLightBundle {
|
// commands.spawn(PointLightBundle {
|
||||||
point_light: PointLight {
|
// point_light: PointLight {
|
||||||
intensity: 1500.0,
|
// intensity: 1500.0,
|
||||||
shadows_enabled: true,
|
// shadows_enabled: true,
|
||||||
..default()
|
// ..default()
|
||||||
},
|
// },
|
||||||
transform: Transform::from_xyz(4.0, 8.0, 4.0),
|
// transform: Transform::from_xyz(4.0, 8.0, 4.0),
|
||||||
..default()
|
// ..default()
|
||||||
});
|
// });
|
||||||
// camera
|
// // camera
|
||||||
commands.spawn(Camera3dBundle {
|
// commands.spawn(Camera3dBundle {
|
||||||
transform: Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
|
// transform: Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
..default()
|
// camera: Camera {
|
||||||
});
|
// target: RenderTarget::TextureView(XR_TEXTURE_VIEW_HANDLE),
|
||||||
}
|
// ..default()
|
||||||
|
// },
|
||||||
|
// ..default()
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
pub mod actions;
|
pub mod actions;
|
||||||
|
#[cfg(not(target_family = "wasm"))]
|
||||||
|
pub mod openxr;
|
||||||
|
pub mod render;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
#[cfg(target_family = "wasm")]
|
#[cfg(target_family = "wasm")]
|
||||||
pub mod webxr;
|
pub mod webxr;
|
||||||
|
|||||||
16
src/openxr.rs
Normal file
16
src/openxr.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
pub mod extensions;
|
||||||
|
pub mod init;
|
||||||
|
mod resources;
|
||||||
|
pub mod types;
|
||||||
|
|
||||||
|
pub use resources::*;
|
||||||
|
|
||||||
|
use bevy::app::{App, Plugin};
|
||||||
|
|
||||||
|
pub struct XrInitPlugin;
|
||||||
|
|
||||||
|
impl Plugin for XrInitPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
291
src/openxr/extensions.rs
Normal file
291
src/openxr/extensions.rs
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
use bevy::prelude::{Deref, DerefMut};
|
||||||
|
use openxr::ExtensionSet;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Deref, DerefMut)]
|
||||||
|
pub struct XrExtensions(ExtensionSet);
|
||||||
|
impl XrExtensions {
|
||||||
|
pub fn raw_mut(&mut self) -> &mut ExtensionSet {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
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_hand_tracking(&mut self) -> &mut Self {
|
||||||
|
self.0.ext_hand_tracking = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn disable_hand_tracking(&mut self) -> &mut Self {
|
||||||
|
self.0.ext_hand_tracking = false;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// returns true if all of the extensions enabled are also available in `available_exts`
|
||||||
|
pub fn is_available(&self, available_exts: &XrExtensions) -> bool {
|
||||||
|
self.clone() & available_exts.clone() == *self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<ExtensionSet> for XrExtensions {
|
||||||
|
fn from(value: ExtensionSet) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<XrExtensions> for ExtensionSet {
|
||||||
|
fn from(val: XrExtensions) -> Self {
|
||||||
|
val.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Default for XrExtensions {
|
||||||
|
fn default() -> Self {
|
||||||
|
let mut exts = ExtensionSet::default();
|
||||||
|
exts.ext_hand_tracking = true;
|
||||||
|
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(crate) 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! {
|
||||||
|
XrExtensions;
|
||||||
|
almalence_digital_lens_control,
|
||||||
|
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,
|
||||||
|
extx_overlay,
|
||||||
|
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_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_space_warp,
|
||||||
|
fb_scene,
|
||||||
|
fb_spatial_entity_container,
|
||||||
|
fb_passthrough_keyboard_hands,
|
||||||
|
fb_composition_layer_settings,
|
||||||
|
htc_vive_cosmos_controller_interaction,
|
||||||
|
htc_facial_tracking,
|
||||||
|
htc_vive_focus3_controller_interaction,
|
||||||
|
htc_hand_interaction,
|
||||||
|
htc_vive_wrist_tracker_interaction,
|
||||||
|
htcx_vive_tracker_interaction,
|
||||||
|
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,
|
||||||
|
meta_vulkan_swapchain_create_info,
|
||||||
|
meta_performance_metrics,
|
||||||
|
ml_ml2_controller_interaction,
|
||||||
|
mnd_headless,
|
||||||
|
mnd_swapchain_usage_input_attachment_bit,
|
||||||
|
mndx_egl_enable,
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_ext!(bitor, bitand, unavailable_exts);
|
||||||
224
src/openxr/init.rs
Normal file
224
src/openxr/init.rs
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
pub mod vulkan;
|
||||||
|
|
||||||
|
use bevy::log::{info, warn};
|
||||||
|
use bevy::math::{uvec2, UVec2};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::openxr::resources::*;
|
||||||
|
use crate::types::BlendMode;
|
||||||
|
|
||||||
|
use super::extensions::XrExtensions;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
|
pub struct Version(pub u8, pub u8, pub u16);
|
||||||
|
|
||||||
|
impl Version {
|
||||||
|
pub const BEVY: Self = Self(0, 12, 1);
|
||||||
|
|
||||||
|
pub const fn to_u32(self) -> u32 {
|
||||||
|
let major = (self.0 as u32) << 24;
|
||||||
|
let minor = (self.1 as u32) << 16;
|
||||||
|
self.2 as u32 | major | minor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
|
pub struct AppInfo<'a> {
|
||||||
|
pub name: &'a str,
|
||||||
|
pub version: Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum GraphicsBackend {
|
||||||
|
Vulkan,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GraphicsBackend {
|
||||||
|
const ALL: &'static [Self] = &[Self::Vulkan];
|
||||||
|
|
||||||
|
pub fn available_backends(exts: &XrExtensions) -> Vec<Self> {
|
||||||
|
Self::ALL
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.filter(|backend| backend.is_available(exts))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_available(&self, exts: &XrExtensions) -> bool {
|
||||||
|
self.required_exts().is_available(exts)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn required_exts(&self) -> XrExtensions {
|
||||||
|
match self {
|
||||||
|
GraphicsBackend::Vulkan => vulkan::required_exts(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum XrInitError {
|
||||||
|
#[error("OpenXR error: {0}")]
|
||||||
|
OpenXrError(#[from] openxr::sys::Result),
|
||||||
|
#[error("OpenXR loading error: {0}")]
|
||||||
|
OpenXrLoadingError(#[from] openxr::LoadError),
|
||||||
|
#[error("WGPU instance error: {0}")]
|
||||||
|
WgpuInstanceError(#[from] wgpu_hal::InstanceError),
|
||||||
|
#[error("WGPU device error: {0}")]
|
||||||
|
WgpuDeviceError(#[from] wgpu_hal::DeviceError),
|
||||||
|
#[error("WGPU request device error: {0}")]
|
||||||
|
WgpuRequestDeviceError(#[from] wgpu::RequestDeviceError),
|
||||||
|
#[error("Unsupported texture format: {0:?}")]
|
||||||
|
UnsupportedTextureFormat(wgpu::TextureFormat),
|
||||||
|
#[error("Vulkan error: {0}")]
|
||||||
|
VulkanError(#[from] ash::vk::Result),
|
||||||
|
#[error("Vulkan loading error: {0}")]
|
||||||
|
VulkanLoadingError(#[from] ash::LoadingError),
|
||||||
|
#[error("Graphics backend '{0:?}' is not available")]
|
||||||
|
UnavailableBackend(GraphicsBackend),
|
||||||
|
#[error("Could not meet graphics requirements for platform. See console for details")]
|
||||||
|
FailedGraphicsRequirements,
|
||||||
|
#[error("Failed to create CString: {0}")]
|
||||||
|
NulError(#[from] std::ffi::NulError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait GraphicsExt: openxr::Graphics {
|
||||||
|
fn from_wgpu_format(format: wgpu::TextureFormat) -> Option<Self::Format>;
|
||||||
|
fn to_wgpu_format(format: Self::Format) -> Option<wgpu::TextureFormat>;
|
||||||
|
fn create_session(
|
||||||
|
app_info: AppInfo,
|
||||||
|
instance: &openxr::Instance,
|
||||||
|
system_id: openxr::SystemId,
|
||||||
|
format: wgpu::TextureFormat,
|
||||||
|
resolution: UVec2,
|
||||||
|
) -> Result<
|
||||||
|
(
|
||||||
|
wgpu::Device,
|
||||||
|
wgpu::Queue,
|
||||||
|
wgpu::Adapter,
|
||||||
|
wgpu::Instance,
|
||||||
|
openxr::Session<openxr::AnyGraphics>,
|
||||||
|
openxr::FrameWaiter,
|
||||||
|
FrameStreamInner,
|
||||||
|
),
|
||||||
|
XrInitError,
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn xr_entry() -> Result<openxr::Entry, XrInitError> {
|
||||||
|
#[cfg(windows)]
|
||||||
|
let entry = Some(openxr::Entry::linked());
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let entry = unsafe { openxr::Entry::load()? };
|
||||||
|
Ok(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn init_xr(
|
||||||
|
// app_info: AppInfo,
|
||||||
|
// requested_exts: XrExtensions,
|
||||||
|
// format: wgpu::TextureFormat,
|
||||||
|
// preferred_blend_mode: BlendMode,
|
||||||
|
// ) -> Result<
|
||||||
|
// (
|
||||||
|
// wgpu::Device,
|
||||||
|
// wgpu::Queue,
|
||||||
|
// wgpu::Adapter,
|
||||||
|
// wgpu::Instance,
|
||||||
|
// openxr::Instance,
|
||||||
|
// openxr::Session<openxr::AnyGraphics>,
|
||||||
|
// openxr::FrameWaiter,
|
||||||
|
// FrameStreamInner,
|
||||||
|
// XrSwapchain,
|
||||||
|
// ),
|
||||||
|
// XrInitError,
|
||||||
|
// > {
|
||||||
|
// let entry = xr_entry().unwrap();
|
||||||
|
|
||||||
|
// let required_exts = vulkan::required_exts() | requested_exts;
|
||||||
|
// let available_exts: XrExtensions = entry.enumerate_extensions()?.into();
|
||||||
|
// for ext in available_exts.unavailable_exts(&required_exts) {
|
||||||
|
// warn!("OpenXR extension '{ext}' is not supported by the current OpenXR runtime")
|
||||||
|
// }
|
||||||
|
// let enabled_exts = required_exts & available_exts;
|
||||||
|
|
||||||
|
// let instance = entry.create_instance(
|
||||||
|
// &openxr::ApplicationInfo {
|
||||||
|
// application_name: app_info.name,
|
||||||
|
// application_version: app_info.version.to_u32(),
|
||||||
|
// engine_name: "Bevy",
|
||||||
|
// engine_version: Version::BEVY.to_u32(),
|
||||||
|
// },
|
||||||
|
// &enabled_exts.into(),
|
||||||
|
// &[],
|
||||||
|
// )?;
|
||||||
|
// info!("Created OpenXR Instance: {:#?}", instance.properties()?);
|
||||||
|
|
||||||
|
// let system_id = instance.system(openxr::FormFactor::HEAD_MOUNTED_DISPLAY)?;
|
||||||
|
// info!(
|
||||||
|
// "Using system: {:#?}",
|
||||||
|
// instance.system_properties(system_id)?
|
||||||
|
// );
|
||||||
|
|
||||||
|
// let view = instance
|
||||||
|
// .enumerate_view_configurations(system_id)?
|
||||||
|
// .first()
|
||||||
|
// .copied()
|
||||||
|
// .unwrap_or(openxr::ViewConfigurationType::PRIMARY_STEREO);
|
||||||
|
|
||||||
|
// let resolution = instance
|
||||||
|
// .enumerate_view_configuration_views(system_id, view)?
|
||||||
|
// .first()
|
||||||
|
// .map(|c| {
|
||||||
|
// uvec2(
|
||||||
|
// c.recommended_image_rect_width,
|
||||||
|
// c.recommended_image_rect_height,
|
||||||
|
// )
|
||||||
|
// })
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
|
// let (wgpu_device, wgpu_queue, wgpu_adapter, wgpu_instance, session, frame_waiter, frame_stream) =
|
||||||
|
// vulkan::create_session(app_info, &instance, system_id, format, resolution)?;
|
||||||
|
|
||||||
|
// let blend_modes = instance.enumerate_environment_blend_modes(system_id, view)?;
|
||||||
|
|
||||||
|
// let blend_mode = if blend_modes.contains(&preferred_blend_mode.into()) {
|
||||||
|
// preferred_blend_mode.into()
|
||||||
|
// } else {
|
||||||
|
// warn!(
|
||||||
|
// "Runtime does not support blend mode '{:?}'",
|
||||||
|
// preferred_blend_mode
|
||||||
|
// );
|
||||||
|
// blend_modes
|
||||||
|
// .first()
|
||||||
|
// .copied()
|
||||||
|
// .unwrap_or(openxr::EnvironmentBlendMode::OPAQUE)
|
||||||
|
// };
|
||||||
|
// info!("Using blend mode '{:?}'", blend_mode);
|
||||||
|
|
||||||
|
// todo!()
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl From<openxr::EnvironmentBlendMode> for BlendMode {
|
||||||
|
fn from(value: openxr::EnvironmentBlendMode) -> Self {
|
||||||
|
use openxr::EnvironmentBlendMode;
|
||||||
|
if value == EnvironmentBlendMode::OPAQUE {
|
||||||
|
BlendMode::Opaque
|
||||||
|
} else if value == EnvironmentBlendMode::ADDITIVE {
|
||||||
|
BlendMode::Additive
|
||||||
|
} else if value == EnvironmentBlendMode::ALPHA_BLEND {
|
||||||
|
BlendMode::AlphaBlend
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BlendMode> for openxr::EnvironmentBlendMode {
|
||||||
|
fn from(value: BlendMode) -> Self {
|
||||||
|
use openxr::EnvironmentBlendMode;
|
||||||
|
match value {
|
||||||
|
BlendMode::Opaque => EnvironmentBlendMode::OPAQUE,
|
||||||
|
BlendMode::Additive => EnvironmentBlendMode::ADDITIVE,
|
||||||
|
BlendMode::AlphaBlend => EnvironmentBlendMode::ALPHA_BLEND,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
670
src/openxr/init/vulkan.rs
Normal file
670
src/openxr/init/vulkan.rs
Normal file
@@ -0,0 +1,670 @@
|
|||||||
|
use std::ffi::{c_void, CString};
|
||||||
|
|
||||||
|
use ash::vk::Handle;
|
||||||
|
use bevy::log::error;
|
||||||
|
use bevy::math::UVec2;
|
||||||
|
use openxr::Version;
|
||||||
|
use wgpu_hal::api::Vulkan;
|
||||||
|
use wgpu_hal::Api;
|
||||||
|
|
||||||
|
use crate::openxr::extensions::XrExtensions;
|
||||||
|
use crate::openxr::resources::*;
|
||||||
|
|
||||||
|
use super::{AppInfo, GraphicsExt, XrInitError};
|
||||||
|
|
||||||
|
pub fn required_exts() -> XrExtensions {
|
||||||
|
let mut extensions = openxr::ExtensionSet::default();
|
||||||
|
extensions.khr_vulkan_enable2 = true;
|
||||||
|
extensions.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "android"))]
|
||||||
|
const VK_TARGET_VERSION: Version = Version::new(1, 2, 0);
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
const VK_TARGET_VERSION: Version = Version::new(1, 1, 0);
|
||||||
|
|
||||||
|
const VK_TARGET_VERSION_ASH: u32 = ash::vk::make_api_version(
|
||||||
|
0,
|
||||||
|
VK_TARGET_VERSION.major() as u32,
|
||||||
|
VK_TARGET_VERSION.minor() as u32,
|
||||||
|
VK_TARGET_VERSION.patch() as u32,
|
||||||
|
);
|
||||||
|
|
||||||
|
pub fn enumerate_swapchain_formats(
|
||||||
|
session: &openxr::Session<openxr::Vulkan>,
|
||||||
|
) -> openxr::Result<Vec<wgpu::TextureFormat>> {
|
||||||
|
let formats = session
|
||||||
|
.enumerate_swapchain_formats()?
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|f| vulkan_to_wgpu(ash::vk::Format::from_raw(f as _)))
|
||||||
|
.collect();
|
||||||
|
Ok(formats)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_session(
|
||||||
|
app_info: AppInfo,
|
||||||
|
instance: &openxr::Instance,
|
||||||
|
system_id: openxr::SystemId,
|
||||||
|
format: wgpu::TextureFormat,
|
||||||
|
resolution: UVec2,
|
||||||
|
) -> Result<
|
||||||
|
(
|
||||||
|
wgpu::Device,
|
||||||
|
wgpu::Queue,
|
||||||
|
wgpu::Adapter,
|
||||||
|
wgpu::Instance,
|
||||||
|
openxr::Session<openxr::AnyGraphics>,
|
||||||
|
openxr::FrameWaiter,
|
||||||
|
FrameStreamInner,
|
||||||
|
),
|
||||||
|
XrInitError,
|
||||||
|
> {
|
||||||
|
let reqs = instance.graphics_requirements::<openxr::Vulkan>(system_id)?;
|
||||||
|
if VK_TARGET_VERSION < reqs.min_api_version_supported
|
||||||
|
|| VK_TARGET_VERSION.major() > reqs.max_api_version_supported.major()
|
||||||
|
{
|
||||||
|
error!(
|
||||||
|
"OpenXR runtime requires Vulkan version > {}, < {}.0.0",
|
||||||
|
reqs.min_api_version_supported,
|
||||||
|
reqs.max_api_version_supported.major() + 1
|
||||||
|
);
|
||||||
|
return Err(XrInitError::FailedGraphicsRequirements);
|
||||||
|
};
|
||||||
|
let vk_entry = unsafe { ash::Entry::load() }?;
|
||||||
|
let flags = wgpu_hal::InstanceFlags::empty();
|
||||||
|
let extensions =
|
||||||
|
<Vulkan as Api>::Instance::required_extensions(&vk_entry, VK_TARGET_VERSION_ASH, flags)?;
|
||||||
|
let device_extensions = vec![
|
||||||
|
ash::extensions::khr::Swapchain::name(),
|
||||||
|
ash::extensions::khr::DrawIndirectCount::name(),
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
ash::extensions::khr::TimelineSemaphore::name(),
|
||||||
|
];
|
||||||
|
|
||||||
|
let vk_instance = unsafe {
|
||||||
|
let extensions_cchar: Vec<_> = extensions.iter().map(|s| s.as_ptr()).collect();
|
||||||
|
|
||||||
|
let app_name = CString::new(app_info.name)?;
|
||||||
|
let vk_app_info = ash::vk::ApplicationInfo::builder()
|
||||||
|
.application_name(&app_name)
|
||||||
|
.application_version(1)
|
||||||
|
.engine_name(&app_name)
|
||||||
|
.engine_version(1)
|
||||||
|
.api_version(VK_TARGET_VERSION_ASH);
|
||||||
|
|
||||||
|
let vk_instance = instance
|
||||||
|
.create_vulkan_instance(
|
||||||
|
system_id,
|
||||||
|
std::mem::transmute(vk_entry.static_fn().get_instance_proc_addr),
|
||||||
|
&ash::vk::InstanceCreateInfo::builder()
|
||||||
|
.application_info(&vk_app_info)
|
||||||
|
.enabled_extension_names(&extensions_cchar) as *const _
|
||||||
|
as *const _,
|
||||||
|
)?
|
||||||
|
.map_err(ash::vk::Result::from_raw)?;
|
||||||
|
|
||||||
|
ash::Instance::load(
|
||||||
|
vk_entry.static_fn(),
|
||||||
|
ash::vk::Instance::from_raw(vk_instance as _),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let vk_instance_ptr = vk_instance.handle().as_raw() as *const c_void;
|
||||||
|
|
||||||
|
let vk_physical_device = ash::vk::PhysicalDevice::from_raw(unsafe {
|
||||||
|
instance.vulkan_graphics_device(system_id, vk_instance.handle().as_raw() as _)? as _
|
||||||
|
});
|
||||||
|
let vk_physical_device_ptr = vk_physical_device.as_raw() as *const c_void;
|
||||||
|
|
||||||
|
let vk_device_properties =
|
||||||
|
unsafe { vk_instance.get_physical_device_properties(vk_physical_device) };
|
||||||
|
|
||||||
|
if vk_device_properties.api_version < VK_TARGET_VERSION_ASH {
|
||||||
|
unsafe { vk_instance.destroy_instance(None) }
|
||||||
|
error!(
|
||||||
|
"Vulkan physical device doesn't support version {}.{}.{}",
|
||||||
|
VK_TARGET_VERSION.major(),
|
||||||
|
VK_TARGET_VERSION.minor(),
|
||||||
|
VK_TARGET_VERSION.patch()
|
||||||
|
);
|
||||||
|
return Err(XrInitError::FailedGraphicsRequirements);
|
||||||
|
}
|
||||||
|
|
||||||
|
let wgpu_vk_instance = unsafe {
|
||||||
|
<Vulkan as Api>::Instance::from_raw(
|
||||||
|
vk_entry.clone(),
|
||||||
|
vk_instance.clone(),
|
||||||
|
VK_TARGET_VERSION_ASH,
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
extensions,
|
||||||
|
flags,
|
||||||
|
false,
|
||||||
|
Some(Box::new(())),
|
||||||
|
)?
|
||||||
|
};
|
||||||
|
|
||||||
|
let wgpu_features = wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
|
||||||
|
| wgpu::Features::MULTIVIEW
|
||||||
|
| wgpu::Features::MULTI_DRAW_INDIRECT_COUNT
|
||||||
|
| wgpu::Features::MULTI_DRAW_INDIRECT;
|
||||||
|
|
||||||
|
let Some(wgpu_exposed_adapter) = wgpu_vk_instance.expose_adapter(vk_physical_device) else {
|
||||||
|
error!("WGPU failed to provide an adapter");
|
||||||
|
return Err(XrInitError::FailedGraphicsRequirements);
|
||||||
|
};
|
||||||
|
|
||||||
|
let enabled_extensions = wgpu_exposed_adapter
|
||||||
|
.adapter
|
||||||
|
.required_device_extensions(wgpu_features);
|
||||||
|
|
||||||
|
let (wgpu_open_device, vk_device_ptr, queue_family_index) = {
|
||||||
|
let extensions_cchar: Vec<_> = device_extensions.iter().map(|s| s.as_ptr()).collect();
|
||||||
|
let mut enabled_phd_features = wgpu_exposed_adapter
|
||||||
|
.adapter
|
||||||
|
.physical_device_features(&enabled_extensions, wgpu_features);
|
||||||
|
let family_index = 0;
|
||||||
|
let family_info = ash::vk::DeviceQueueCreateInfo::builder()
|
||||||
|
.queue_family_index(family_index)
|
||||||
|
.queue_priorities(&[1.0])
|
||||||
|
.build();
|
||||||
|
let family_infos = [family_info];
|
||||||
|
let info = enabled_phd_features
|
||||||
|
.add_to_device_create_builder(
|
||||||
|
ash::vk::DeviceCreateInfo::builder()
|
||||||
|
.queue_create_infos(&family_infos)
|
||||||
|
.push_next(&mut ash::vk::PhysicalDeviceMultiviewFeatures {
|
||||||
|
multiview: ash::vk::TRUE,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.enabled_extension_names(&extensions_cchar)
|
||||||
|
.build();
|
||||||
|
let vk_device = unsafe {
|
||||||
|
let vk_device = instance
|
||||||
|
.create_vulkan_device(
|
||||||
|
system_id,
|
||||||
|
std::mem::transmute(vk_entry.static_fn().get_instance_proc_addr),
|
||||||
|
vk_physical_device.as_raw() as _,
|
||||||
|
&info as *const _ as *const _,
|
||||||
|
)?
|
||||||
|
.map_err(ash::vk::Result::from_raw)?;
|
||||||
|
|
||||||
|
ash::Device::load(
|
||||||
|
vk_instance.fp_v1_0(),
|
||||||
|
ash::vk::Device::from_raw(vk_device as _),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let vk_device_ptr = vk_device.handle().as_raw() as *const c_void;
|
||||||
|
|
||||||
|
let wgpu_open_device = unsafe {
|
||||||
|
wgpu_exposed_adapter.adapter.device_from_raw(
|
||||||
|
vk_device,
|
||||||
|
true,
|
||||||
|
&enabled_extensions,
|
||||||
|
wgpu_features,
|
||||||
|
family_info.queue_family_index,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
}?;
|
||||||
|
|
||||||
|
(
|
||||||
|
wgpu_open_device,
|
||||||
|
vk_device_ptr,
|
||||||
|
family_info.queue_family_index,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let wgpu_instance =
|
||||||
|
unsafe { wgpu::Instance::from_hal::<wgpu_hal::api::Vulkan>(wgpu_vk_instance) };
|
||||||
|
let wgpu_adapter = unsafe { wgpu_instance.create_adapter_from_hal(wgpu_exposed_adapter) };
|
||||||
|
let (wgpu_device, wgpu_queue) = unsafe {
|
||||||
|
wgpu_adapter.create_device_from_hal(
|
||||||
|
wgpu_open_device,
|
||||||
|
&wgpu::DeviceDescriptor {
|
||||||
|
label: None,
|
||||||
|
features: wgpu_features,
|
||||||
|
limits: wgpu::Limits {
|
||||||
|
max_bind_groups: 8,
|
||||||
|
max_storage_buffer_binding_size: wgpu_adapter
|
||||||
|
.limits()
|
||||||
|
.max_storage_buffer_binding_size,
|
||||||
|
max_push_constant_size: 4,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let (session, frame_wait, frame_stream) = unsafe {
|
||||||
|
instance.create_session::<openxr::Vulkan>(
|
||||||
|
system_id,
|
||||||
|
&openxr::vulkan::SessionCreateInfo {
|
||||||
|
instance: vk_instance_ptr,
|
||||||
|
physical_device: vk_physical_device_ptr,
|
||||||
|
device: vk_device_ptr,
|
||||||
|
queue_family_index,
|
||||||
|
queue_index: 0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
wgpu_device,
|
||||||
|
wgpu_queue,
|
||||||
|
wgpu_adapter,
|
||||||
|
wgpu_instance,
|
||||||
|
session.into_any_graphics(),
|
||||||
|
frame_wait,
|
||||||
|
FrameStreamInner::Vulkan(frame_stream),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GraphicsExt for openxr::Vulkan {
|
||||||
|
fn from_wgpu_format(format: wgpu::TextureFormat) -> Option<Self::Format> {
|
||||||
|
wgpu_to_vulkan(format).map(|f| f.as_raw() as _)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_wgpu_format(format: Self::Format) -> Option<wgpu::TextureFormat> {
|
||||||
|
vulkan_to_wgpu(ash::vk::Format::from_raw(format as _))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_session(
|
||||||
|
app_info: AppInfo,
|
||||||
|
instance: &openxr::Instance,
|
||||||
|
system_id: openxr::SystemId,
|
||||||
|
format: wgpu::TextureFormat,
|
||||||
|
resolution: UVec2,
|
||||||
|
) -> Result<
|
||||||
|
(
|
||||||
|
wgpu::Device,
|
||||||
|
wgpu::Queue,
|
||||||
|
wgpu::Adapter,
|
||||||
|
wgpu::Instance,
|
||||||
|
openxr::Session<openxr::AnyGraphics>,
|
||||||
|
openxr::FrameWaiter,
|
||||||
|
FrameStreamInner,
|
||||||
|
),
|
||||||
|
XrInitError,
|
||||||
|
> {
|
||||||
|
create_session(app_info, instance, system_id, format, resolution)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vulkan_to_wgpu(format: ash::vk::Format) -> Option<wgpu::TextureFormat> {
|
||||||
|
use ash::vk::Format as F;
|
||||||
|
use wgpu::TextureFormat as Tf;
|
||||||
|
use wgpu::{AstcBlock, AstcChannel};
|
||||||
|
Some(match format {
|
||||||
|
F::R8_UNORM => Tf::R8Unorm,
|
||||||
|
F::R8_SNORM => Tf::R8Snorm,
|
||||||
|
F::R8_UINT => Tf::R8Uint,
|
||||||
|
F::R8_SINT => Tf::R8Sint,
|
||||||
|
F::R16_UINT => Tf::R16Uint,
|
||||||
|
F::R16_SINT => Tf::R16Sint,
|
||||||
|
F::R16_UNORM => Tf::R16Unorm,
|
||||||
|
F::R16_SNORM => Tf::R16Snorm,
|
||||||
|
F::R16_SFLOAT => Tf::R16Float,
|
||||||
|
F::R8G8_UNORM => Tf::Rg8Unorm,
|
||||||
|
F::R8G8_SNORM => Tf::Rg8Snorm,
|
||||||
|
F::R8G8_UINT => Tf::Rg8Uint,
|
||||||
|
F::R8G8_SINT => Tf::Rg8Sint,
|
||||||
|
F::R16G16_UNORM => Tf::Rg16Unorm,
|
||||||
|
F::R16G16_SNORM => Tf::Rg16Snorm,
|
||||||
|
F::R32_UINT => Tf::R32Uint,
|
||||||
|
F::R32_SINT => Tf::R32Sint,
|
||||||
|
F::R32_SFLOAT => Tf::R32Float,
|
||||||
|
F::R16G16_UINT => Tf::Rg16Uint,
|
||||||
|
F::R16G16_SINT => Tf::Rg16Sint,
|
||||||
|
F::R16G16_SFLOAT => Tf::Rg16Float,
|
||||||
|
F::R8G8B8A8_UNORM => Tf::Rgba8Unorm,
|
||||||
|
F::R8G8B8A8_SRGB => Tf::Rgba8UnormSrgb,
|
||||||
|
F::B8G8R8A8_SRGB => Tf::Bgra8UnormSrgb,
|
||||||
|
F::R8G8B8A8_SNORM => Tf::Rgba8Snorm,
|
||||||
|
F::B8G8R8A8_UNORM => Tf::Bgra8Unorm,
|
||||||
|
F::R8G8B8A8_UINT => Tf::Rgba8Uint,
|
||||||
|
F::R8G8B8A8_SINT => Tf::Rgba8Sint,
|
||||||
|
F::A2B10G10R10_UNORM_PACK32 => Tf::Rgb10a2Unorm,
|
||||||
|
F::B10G11R11_UFLOAT_PACK32 => Tf::Rg11b10Float,
|
||||||
|
F::R32G32_UINT => Tf::Rg32Uint,
|
||||||
|
F::R32G32_SINT => Tf::Rg32Sint,
|
||||||
|
F::R32G32_SFLOAT => Tf::Rg32Float,
|
||||||
|
F::R16G16B16A16_UINT => Tf::Rgba16Uint,
|
||||||
|
F::R16G16B16A16_SINT => Tf::Rgba16Sint,
|
||||||
|
F::R16G16B16A16_UNORM => Tf::Rgba16Unorm,
|
||||||
|
F::R16G16B16A16_SNORM => Tf::Rgba16Snorm,
|
||||||
|
F::R16G16B16A16_SFLOAT => Tf::Rgba16Float,
|
||||||
|
F::R32G32B32A32_UINT => Tf::Rgba32Uint,
|
||||||
|
F::R32G32B32A32_SINT => Tf::Rgba32Sint,
|
||||||
|
F::R32G32B32A32_SFLOAT => Tf::Rgba32Float,
|
||||||
|
F::D32_SFLOAT => Tf::Depth32Float,
|
||||||
|
F::D32_SFLOAT_S8_UINT => Tf::Depth32FloatStencil8,
|
||||||
|
F::D16_UNORM => Tf::Depth16Unorm,
|
||||||
|
F::E5B9G9R9_UFLOAT_PACK32 => Tf::Rgb9e5Ufloat,
|
||||||
|
F::BC1_RGBA_UNORM_BLOCK => Tf::Bc1RgbaUnorm,
|
||||||
|
F::BC1_RGBA_SRGB_BLOCK => Tf::Bc1RgbaUnormSrgb,
|
||||||
|
F::BC2_UNORM_BLOCK => Tf::Bc2RgbaUnorm,
|
||||||
|
F::BC2_SRGB_BLOCK => Tf::Bc2RgbaUnormSrgb,
|
||||||
|
F::BC3_UNORM_BLOCK => Tf::Bc3RgbaUnorm,
|
||||||
|
F::BC3_SRGB_BLOCK => Tf::Bc3RgbaUnormSrgb,
|
||||||
|
F::BC4_UNORM_BLOCK => Tf::Bc4RUnorm,
|
||||||
|
F::BC4_SNORM_BLOCK => Tf::Bc4RSnorm,
|
||||||
|
F::BC5_UNORM_BLOCK => Tf::Bc5RgUnorm,
|
||||||
|
F::BC5_SNORM_BLOCK => Tf::Bc5RgSnorm,
|
||||||
|
F::BC6H_UFLOAT_BLOCK => Tf::Bc6hRgbUfloat,
|
||||||
|
F::BC6H_SFLOAT_BLOCK => Tf::Bc6hRgbFloat,
|
||||||
|
F::BC7_UNORM_BLOCK => Tf::Bc7RgbaUnorm,
|
||||||
|
F::BC7_SRGB_BLOCK => Tf::Bc7RgbaUnormSrgb,
|
||||||
|
F::ETC2_R8G8B8_UNORM_BLOCK => Tf::Etc2Rgb8Unorm,
|
||||||
|
F::ETC2_R8G8B8_SRGB_BLOCK => Tf::Etc2Rgb8UnormSrgb,
|
||||||
|
F::ETC2_R8G8B8A1_UNORM_BLOCK => Tf::Etc2Rgb8A1Unorm,
|
||||||
|
F::ETC2_R8G8B8A1_SRGB_BLOCK => Tf::Etc2Rgb8A1UnormSrgb,
|
||||||
|
F::ETC2_R8G8B8A8_UNORM_BLOCK => Tf::Etc2Rgba8Unorm,
|
||||||
|
F::ETC2_R8G8B8A8_SRGB_BLOCK => Tf::Etc2Rgba8UnormSrgb,
|
||||||
|
F::EAC_R11_UNORM_BLOCK => Tf::EacR11Unorm,
|
||||||
|
F::EAC_R11_SNORM_BLOCK => Tf::EacR11Snorm,
|
||||||
|
F::EAC_R11G11_UNORM_BLOCK => Tf::EacRg11Unorm,
|
||||||
|
F::EAC_R11G11_SNORM_BLOCK => Tf::EacRg11Snorm,
|
||||||
|
F::ASTC_4X4_UNORM_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B4x4,
|
||||||
|
channel: AstcChannel::Unorm,
|
||||||
|
},
|
||||||
|
F::ASTC_5X4_UNORM_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B5x4,
|
||||||
|
channel: AstcChannel::Unorm,
|
||||||
|
},
|
||||||
|
F::ASTC_5X5_UNORM_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B5x5,
|
||||||
|
channel: AstcChannel::Unorm,
|
||||||
|
},
|
||||||
|
F::ASTC_6X5_UNORM_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B6x5,
|
||||||
|
channel: AstcChannel::Unorm,
|
||||||
|
},
|
||||||
|
F::ASTC_6X6_UNORM_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B6x6,
|
||||||
|
channel: AstcChannel::Unorm,
|
||||||
|
},
|
||||||
|
F::ASTC_8X5_UNORM_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B8x5,
|
||||||
|
channel: AstcChannel::Unorm,
|
||||||
|
},
|
||||||
|
F::ASTC_8X6_UNORM_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B8x6,
|
||||||
|
channel: AstcChannel::Unorm,
|
||||||
|
},
|
||||||
|
F::ASTC_8X8_UNORM_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B8x8,
|
||||||
|
channel: AstcChannel::Unorm,
|
||||||
|
},
|
||||||
|
F::ASTC_10X5_UNORM_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B10x5,
|
||||||
|
channel: AstcChannel::Unorm,
|
||||||
|
},
|
||||||
|
F::ASTC_10X6_UNORM_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B10x6,
|
||||||
|
channel: AstcChannel::Unorm,
|
||||||
|
},
|
||||||
|
F::ASTC_10X8_UNORM_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B10x8,
|
||||||
|
channel: AstcChannel::Unorm,
|
||||||
|
},
|
||||||
|
F::ASTC_10X10_UNORM_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B10x10,
|
||||||
|
channel: AstcChannel::Unorm,
|
||||||
|
},
|
||||||
|
F::ASTC_12X10_UNORM_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B12x10,
|
||||||
|
channel: AstcChannel::Unorm,
|
||||||
|
},
|
||||||
|
F::ASTC_12X12_UNORM_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B12x12,
|
||||||
|
channel: AstcChannel::Unorm,
|
||||||
|
},
|
||||||
|
F::ASTC_4X4_SRGB_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B4x4,
|
||||||
|
channel: AstcChannel::UnormSrgb,
|
||||||
|
},
|
||||||
|
F::ASTC_5X4_SRGB_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B5x4,
|
||||||
|
channel: AstcChannel::UnormSrgb,
|
||||||
|
},
|
||||||
|
F::ASTC_5X5_SRGB_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B5x5,
|
||||||
|
channel: AstcChannel::UnormSrgb,
|
||||||
|
},
|
||||||
|
F::ASTC_6X5_SRGB_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B6x5,
|
||||||
|
channel: AstcChannel::UnormSrgb,
|
||||||
|
},
|
||||||
|
F::ASTC_6X6_SRGB_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B6x6,
|
||||||
|
channel: AstcChannel::UnormSrgb,
|
||||||
|
},
|
||||||
|
F::ASTC_8X5_SRGB_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B8x5,
|
||||||
|
channel: AstcChannel::UnormSrgb,
|
||||||
|
},
|
||||||
|
F::ASTC_8X6_SRGB_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B8x6,
|
||||||
|
channel: AstcChannel::UnormSrgb,
|
||||||
|
},
|
||||||
|
F::ASTC_8X8_SRGB_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B8x8,
|
||||||
|
channel: AstcChannel::UnormSrgb,
|
||||||
|
},
|
||||||
|
F::ASTC_10X5_SRGB_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B10x5,
|
||||||
|
channel: AstcChannel::UnormSrgb,
|
||||||
|
},
|
||||||
|
F::ASTC_10X6_SRGB_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B10x6,
|
||||||
|
channel: AstcChannel::UnormSrgb,
|
||||||
|
},
|
||||||
|
F::ASTC_10X8_SRGB_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B10x8,
|
||||||
|
channel: AstcChannel::UnormSrgb,
|
||||||
|
},
|
||||||
|
F::ASTC_10X10_SRGB_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B10x10,
|
||||||
|
channel: AstcChannel::UnormSrgb,
|
||||||
|
},
|
||||||
|
F::ASTC_12X10_SRGB_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B12x10,
|
||||||
|
channel: AstcChannel::UnormSrgb,
|
||||||
|
},
|
||||||
|
F::ASTC_12X12_SRGB_BLOCK => Tf::Astc {
|
||||||
|
block: AstcBlock::B12x12,
|
||||||
|
channel: AstcChannel::UnormSrgb,
|
||||||
|
},
|
||||||
|
F::ASTC_4X4_SFLOAT_BLOCK_EXT => Tf::Astc {
|
||||||
|
block: AstcBlock::B4x4,
|
||||||
|
channel: AstcChannel::Hdr,
|
||||||
|
},
|
||||||
|
F::ASTC_5X4_SFLOAT_BLOCK_EXT => Tf::Astc {
|
||||||
|
block: AstcBlock::B5x4,
|
||||||
|
channel: AstcChannel::Hdr,
|
||||||
|
},
|
||||||
|
F::ASTC_5X5_SFLOAT_BLOCK_EXT => Tf::Astc {
|
||||||
|
block: AstcBlock::B5x5,
|
||||||
|
channel: AstcChannel::Hdr,
|
||||||
|
},
|
||||||
|
F::ASTC_6X5_SFLOAT_BLOCK_EXT => Tf::Astc {
|
||||||
|
block: AstcBlock::B6x5,
|
||||||
|
channel: AstcChannel::Hdr,
|
||||||
|
},
|
||||||
|
F::ASTC_6X6_SFLOAT_BLOCK_EXT => Tf::Astc {
|
||||||
|
block: AstcBlock::B6x6,
|
||||||
|
channel: AstcChannel::Hdr,
|
||||||
|
},
|
||||||
|
F::ASTC_8X5_SFLOAT_BLOCK_EXT => Tf::Astc {
|
||||||
|
block: AstcBlock::B8x5,
|
||||||
|
channel: AstcChannel::Hdr,
|
||||||
|
},
|
||||||
|
F::ASTC_8X6_SFLOAT_BLOCK_EXT => Tf::Astc {
|
||||||
|
block: AstcBlock::B8x6,
|
||||||
|
channel: AstcChannel::Hdr,
|
||||||
|
},
|
||||||
|
F::ASTC_8X8_SFLOAT_BLOCK_EXT => Tf::Astc {
|
||||||
|
block: AstcBlock::B8x8,
|
||||||
|
channel: AstcChannel::Hdr,
|
||||||
|
},
|
||||||
|
F::ASTC_10X5_SFLOAT_BLOCK_EXT => Tf::Astc {
|
||||||
|
block: AstcBlock::B10x5,
|
||||||
|
channel: AstcChannel::Hdr,
|
||||||
|
},
|
||||||
|
F::ASTC_10X6_SFLOAT_BLOCK_EXT => Tf::Astc {
|
||||||
|
block: AstcBlock::B10x6,
|
||||||
|
channel: AstcChannel::Hdr,
|
||||||
|
},
|
||||||
|
F::ASTC_10X8_SFLOAT_BLOCK_EXT => Tf::Astc {
|
||||||
|
block: AstcBlock::B10x8,
|
||||||
|
channel: AstcChannel::Hdr,
|
||||||
|
},
|
||||||
|
F::ASTC_10X10_SFLOAT_BLOCK_EXT => Tf::Astc {
|
||||||
|
block: AstcBlock::B10x10,
|
||||||
|
channel: AstcChannel::Hdr,
|
||||||
|
},
|
||||||
|
F::ASTC_12X10_SFLOAT_BLOCK_EXT => Tf::Astc {
|
||||||
|
block: AstcBlock::B12x10,
|
||||||
|
channel: AstcChannel::Hdr,
|
||||||
|
},
|
||||||
|
F::ASTC_12X12_SFLOAT_BLOCK_EXT => Tf::Astc {
|
||||||
|
block: AstcBlock::B12x12,
|
||||||
|
channel: AstcChannel::Hdr,
|
||||||
|
},
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wgpu_to_vulkan(format: wgpu::TextureFormat) -> Option<ash::vk::Format> {
|
||||||
|
// Copied with minor modification from:
|
||||||
|
// https://github.com/gfx-rs/wgpu/blob/a7defb723f856d946d6d220e9897d20dbb7b8f61/wgpu-hal/src/vulkan/conv.rs#L5-L151
|
||||||
|
// license: MIT OR Apache-2.0
|
||||||
|
use ash::vk::Format as F;
|
||||||
|
use wgpu::TextureFormat as Tf;
|
||||||
|
use wgpu::{AstcBlock, AstcChannel};
|
||||||
|
Some(match format {
|
||||||
|
Tf::R8Unorm => F::R8_UNORM,
|
||||||
|
Tf::R8Snorm => F::R8_SNORM,
|
||||||
|
Tf::R8Uint => F::R8_UINT,
|
||||||
|
Tf::R8Sint => F::R8_SINT,
|
||||||
|
Tf::R16Uint => F::R16_UINT,
|
||||||
|
Tf::R16Sint => F::R16_SINT,
|
||||||
|
Tf::R16Unorm => F::R16_UNORM,
|
||||||
|
Tf::R16Snorm => F::R16_SNORM,
|
||||||
|
Tf::R16Float => F::R16_SFLOAT,
|
||||||
|
Tf::Rg8Unorm => F::R8G8_UNORM,
|
||||||
|
Tf::Rg8Snorm => F::R8G8_SNORM,
|
||||||
|
Tf::Rg8Uint => F::R8G8_UINT,
|
||||||
|
Tf::Rg8Sint => F::R8G8_SINT,
|
||||||
|
Tf::Rg16Unorm => F::R16G16_UNORM,
|
||||||
|
Tf::Rg16Snorm => F::R16G16_SNORM,
|
||||||
|
Tf::R32Uint => F::R32_UINT,
|
||||||
|
Tf::R32Sint => F::R32_SINT,
|
||||||
|
Tf::R32Float => F::R32_SFLOAT,
|
||||||
|
Tf::Rg16Uint => F::R16G16_UINT,
|
||||||
|
Tf::Rg16Sint => F::R16G16_SINT,
|
||||||
|
Tf::Rg16Float => F::R16G16_SFLOAT,
|
||||||
|
Tf::Rgba8Unorm => F::R8G8B8A8_UNORM,
|
||||||
|
Tf::Rgba8UnormSrgb => F::R8G8B8A8_SRGB,
|
||||||
|
Tf::Bgra8UnormSrgb => F::B8G8R8A8_SRGB,
|
||||||
|
Tf::Rgba8Snorm => F::R8G8B8A8_SNORM,
|
||||||
|
Tf::Bgra8Unorm => F::B8G8R8A8_UNORM,
|
||||||
|
Tf::Rgba8Uint => F::R8G8B8A8_UINT,
|
||||||
|
Tf::Rgba8Sint => F::R8G8B8A8_SINT,
|
||||||
|
Tf::Rgb10a2Unorm => F::A2B10G10R10_UNORM_PACK32,
|
||||||
|
Tf::Rg11b10Float => F::B10G11R11_UFLOAT_PACK32,
|
||||||
|
Tf::Rg32Uint => F::R32G32_UINT,
|
||||||
|
Tf::Rg32Sint => F::R32G32_SINT,
|
||||||
|
Tf::Rg32Float => F::R32G32_SFLOAT,
|
||||||
|
Tf::Rgba16Uint => F::R16G16B16A16_UINT,
|
||||||
|
Tf::Rgba16Sint => F::R16G16B16A16_SINT,
|
||||||
|
Tf::Rgba16Unorm => F::R16G16B16A16_UNORM,
|
||||||
|
Tf::Rgba16Snorm => F::R16G16B16A16_SNORM,
|
||||||
|
Tf::Rgba16Float => F::R16G16B16A16_SFLOAT,
|
||||||
|
Tf::Rgba32Uint => F::R32G32B32A32_UINT,
|
||||||
|
Tf::Rgba32Sint => F::R32G32B32A32_SINT,
|
||||||
|
Tf::Rgba32Float => F::R32G32B32A32_SFLOAT,
|
||||||
|
Tf::Depth32Float => F::D32_SFLOAT,
|
||||||
|
Tf::Depth32FloatStencil8 => F::D32_SFLOAT_S8_UINT,
|
||||||
|
Tf::Depth24Plus | Tf::Depth24PlusStencil8 | Tf::Stencil8 => return None, // Dependent on device properties
|
||||||
|
Tf::Depth16Unorm => F::D16_UNORM,
|
||||||
|
Tf::Rgb9e5Ufloat => F::E5B9G9R9_UFLOAT_PACK32,
|
||||||
|
Tf::Bc1RgbaUnorm => F::BC1_RGBA_UNORM_BLOCK,
|
||||||
|
Tf::Bc1RgbaUnormSrgb => F::BC1_RGBA_SRGB_BLOCK,
|
||||||
|
Tf::Bc2RgbaUnorm => F::BC2_UNORM_BLOCK,
|
||||||
|
Tf::Bc2RgbaUnormSrgb => F::BC2_SRGB_BLOCK,
|
||||||
|
Tf::Bc3RgbaUnorm => F::BC3_UNORM_BLOCK,
|
||||||
|
Tf::Bc3RgbaUnormSrgb => F::BC3_SRGB_BLOCK,
|
||||||
|
Tf::Bc4RUnorm => F::BC4_UNORM_BLOCK,
|
||||||
|
Tf::Bc4RSnorm => F::BC4_SNORM_BLOCK,
|
||||||
|
Tf::Bc5RgUnorm => F::BC5_UNORM_BLOCK,
|
||||||
|
Tf::Bc5RgSnorm => F::BC5_SNORM_BLOCK,
|
||||||
|
Tf::Bc6hRgbUfloat => F::BC6H_UFLOAT_BLOCK,
|
||||||
|
Tf::Bc6hRgbFloat => F::BC6H_SFLOAT_BLOCK,
|
||||||
|
Tf::Bc7RgbaUnorm => F::BC7_UNORM_BLOCK,
|
||||||
|
Tf::Bc7RgbaUnormSrgb => F::BC7_SRGB_BLOCK,
|
||||||
|
Tf::Etc2Rgb8Unorm => F::ETC2_R8G8B8_UNORM_BLOCK,
|
||||||
|
Tf::Etc2Rgb8UnormSrgb => F::ETC2_R8G8B8_SRGB_BLOCK,
|
||||||
|
Tf::Etc2Rgb8A1Unorm => F::ETC2_R8G8B8A1_UNORM_BLOCK,
|
||||||
|
Tf::Etc2Rgb8A1UnormSrgb => F::ETC2_R8G8B8A1_SRGB_BLOCK,
|
||||||
|
Tf::Etc2Rgba8Unorm => F::ETC2_R8G8B8A8_UNORM_BLOCK,
|
||||||
|
Tf::Etc2Rgba8UnormSrgb => F::ETC2_R8G8B8A8_SRGB_BLOCK,
|
||||||
|
Tf::EacR11Unorm => F::EAC_R11_UNORM_BLOCK,
|
||||||
|
Tf::EacR11Snorm => F::EAC_R11_SNORM_BLOCK,
|
||||||
|
Tf::EacRg11Unorm => F::EAC_R11G11_UNORM_BLOCK,
|
||||||
|
Tf::EacRg11Snorm => F::EAC_R11G11_SNORM_BLOCK,
|
||||||
|
Tf::Astc { block, channel } => match channel {
|
||||||
|
AstcChannel::Unorm => match block {
|
||||||
|
AstcBlock::B4x4 => F::ASTC_4X4_UNORM_BLOCK,
|
||||||
|
AstcBlock::B5x4 => F::ASTC_5X4_UNORM_BLOCK,
|
||||||
|
AstcBlock::B5x5 => F::ASTC_5X5_UNORM_BLOCK,
|
||||||
|
AstcBlock::B6x5 => F::ASTC_6X5_UNORM_BLOCK,
|
||||||
|
AstcBlock::B6x6 => F::ASTC_6X6_UNORM_BLOCK,
|
||||||
|
AstcBlock::B8x5 => F::ASTC_8X5_UNORM_BLOCK,
|
||||||
|
AstcBlock::B8x6 => F::ASTC_8X6_UNORM_BLOCK,
|
||||||
|
AstcBlock::B8x8 => F::ASTC_8X8_UNORM_BLOCK,
|
||||||
|
AstcBlock::B10x5 => F::ASTC_10X5_UNORM_BLOCK,
|
||||||
|
AstcBlock::B10x6 => F::ASTC_10X6_UNORM_BLOCK,
|
||||||
|
AstcBlock::B10x8 => F::ASTC_10X8_UNORM_BLOCK,
|
||||||
|
AstcBlock::B10x10 => F::ASTC_10X10_UNORM_BLOCK,
|
||||||
|
AstcBlock::B12x10 => F::ASTC_12X10_UNORM_BLOCK,
|
||||||
|
AstcBlock::B12x12 => F::ASTC_12X12_UNORM_BLOCK,
|
||||||
|
},
|
||||||
|
AstcChannel::UnormSrgb => match block {
|
||||||
|
AstcBlock::B4x4 => F::ASTC_4X4_SRGB_BLOCK,
|
||||||
|
AstcBlock::B5x4 => F::ASTC_5X4_SRGB_BLOCK,
|
||||||
|
AstcBlock::B5x5 => F::ASTC_5X5_SRGB_BLOCK,
|
||||||
|
AstcBlock::B6x5 => F::ASTC_6X5_SRGB_BLOCK,
|
||||||
|
AstcBlock::B6x6 => F::ASTC_6X6_SRGB_BLOCK,
|
||||||
|
AstcBlock::B8x5 => F::ASTC_8X5_SRGB_BLOCK,
|
||||||
|
AstcBlock::B8x6 => F::ASTC_8X6_SRGB_BLOCK,
|
||||||
|
AstcBlock::B8x8 => F::ASTC_8X8_SRGB_BLOCK,
|
||||||
|
AstcBlock::B10x5 => F::ASTC_10X5_SRGB_BLOCK,
|
||||||
|
AstcBlock::B10x6 => F::ASTC_10X6_SRGB_BLOCK,
|
||||||
|
AstcBlock::B10x8 => F::ASTC_10X8_SRGB_BLOCK,
|
||||||
|
AstcBlock::B10x10 => F::ASTC_10X10_SRGB_BLOCK,
|
||||||
|
AstcBlock::B12x10 => F::ASTC_12X10_SRGB_BLOCK,
|
||||||
|
AstcBlock::B12x12 => F::ASTC_12X12_SRGB_BLOCK,
|
||||||
|
},
|
||||||
|
AstcChannel::Hdr => match block {
|
||||||
|
AstcBlock::B4x4 => F::ASTC_4X4_SFLOAT_BLOCK_EXT,
|
||||||
|
AstcBlock::B5x4 => F::ASTC_5X4_SFLOAT_BLOCK_EXT,
|
||||||
|
AstcBlock::B5x5 => F::ASTC_5X5_SFLOAT_BLOCK_EXT,
|
||||||
|
AstcBlock::B6x5 => F::ASTC_6X5_SFLOAT_BLOCK_EXT,
|
||||||
|
AstcBlock::B6x6 => F::ASTC_6X6_SFLOAT_BLOCK_EXT,
|
||||||
|
AstcBlock::B8x5 => F::ASTC_8X5_SFLOAT_BLOCK_EXT,
|
||||||
|
AstcBlock::B8x6 => F::ASTC_8X6_SFLOAT_BLOCK_EXT,
|
||||||
|
AstcBlock::B8x8 => F::ASTC_8X8_SFLOAT_BLOCK_EXT,
|
||||||
|
AstcBlock::B10x5 => F::ASTC_10X5_SFLOAT_BLOCK_EXT,
|
||||||
|
AstcBlock::B10x6 => F::ASTC_10X6_SFLOAT_BLOCK_EXT,
|
||||||
|
AstcBlock::B10x8 => F::ASTC_10X8_SFLOAT_BLOCK_EXT,
|
||||||
|
AstcBlock::B10x10 => F::ASTC_10X10_SFLOAT_BLOCK_EXT,
|
||||||
|
AstcBlock::B12x10 => F::ASTC_12X10_SFLOAT_BLOCK_EXT,
|
||||||
|
AstcBlock::B12x12 => F::ASTC_12X12_SFLOAT_BLOCK_EXT,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
251
src/openxr/resources.rs
Normal file
251
src/openxr/resources.rs
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use ash::vk::Handle;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
use openxr::{AnyGraphics, Vulkan};
|
||||||
|
|
||||||
|
use crate::openxr::init::Version;
|
||||||
|
|
||||||
|
use super::extensions::XrExtensions;
|
||||||
|
use super::init::{self, AppInfo, GraphicsBackend, GraphicsExt, XrInitError};
|
||||||
|
type Result<T> = std::result::Result<T, XrInitError>;
|
||||||
|
use super::types::*;
|
||||||
|
|
||||||
|
#[derive(Deref, Clone)]
|
||||||
|
pub struct XrEntry(openxr::Entry);
|
||||||
|
|
||||||
|
impl XrEntry {
|
||||||
|
pub fn enumerate_extensions(&self) -> Result<XrExtensions> {
|
||||||
|
Ok(self.0.enumerate_extensions().map(Into::into)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_instance(
|
||||||
|
entry: XrEntry,
|
||||||
|
app_info: AppInfo,
|
||||||
|
exts: XrExtensions,
|
||||||
|
backend: GraphicsBackend,
|
||||||
|
) -> Result<XrInstance> {
|
||||||
|
let available_exts = entry.enumerate_extensions()?;
|
||||||
|
|
||||||
|
if !backend.is_available(&available_exts) {
|
||||||
|
return Err(XrInitError::UnavailableBackend(backend));
|
||||||
|
}
|
||||||
|
|
||||||
|
let required_exts = exts | backend.required_exts();
|
||||||
|
|
||||||
|
let instance = entry.create_instance(
|
||||||
|
&openxr::ApplicationInfo {
|
||||||
|
application_name: app_info.name,
|
||||||
|
application_version: app_info.version.to_u32(),
|
||||||
|
engine_name: "Bevy",
|
||||||
|
engine_version: Version::BEVY.to_u32(),
|
||||||
|
},
|
||||||
|
&required_exts.into(),
|
||||||
|
&[],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(XrInstance(instance, backend))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn available_backends(&self) -> Result<Vec<GraphicsBackend>> {
|
||||||
|
Ok(GraphicsBackend::available_backends(
|
||||||
|
&self.enumerate_extensions()?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<openxr::Entry> for XrEntry {
|
||||||
|
fn from(value: openxr::Entry) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource, Deref, Clone)]
|
||||||
|
pub struct XrInstance(
|
||||||
|
#[deref] pub(crate) openxr::Instance,
|
||||||
|
pub(crate) GraphicsBackend,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl XrInstance {
|
||||||
|
pub fn create_session(
|
||||||
|
&self,
|
||||||
|
app_info: AppInfo,
|
||||||
|
system_id: openxr::SystemId,
|
||||||
|
format: wgpu::TextureFormat,
|
||||||
|
resolution: UVec2,
|
||||||
|
) -> Result<(
|
||||||
|
wgpu::Device,
|
||||||
|
wgpu::Queue,
|
||||||
|
wgpu::Adapter,
|
||||||
|
wgpu::Instance,
|
||||||
|
openxr::Session<openxr::AnyGraphics>,
|
||||||
|
openxr::FrameWaiter,
|
||||||
|
FrameStreamInner,
|
||||||
|
)> {
|
||||||
|
match self.1 {
|
||||||
|
GraphicsBackend::Vulkan => {
|
||||||
|
openxr::Vulkan::create_session(app_info, &self.0, system_id, format, resolution)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource, Deref, Clone)]
|
||||||
|
pub struct XrSession(
|
||||||
|
#[deref] pub(crate) openxr::Session<AnyGraphics>,
|
||||||
|
pub(crate) TypedSession,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl XrSession {
|
||||||
|
pub fn enumerate_swapchain_formats(&self) -> Result<Vec<wgpu::TextureFormat>> {
|
||||||
|
self.1.enumerate_swapchain_formats()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_swapchain(&self, info: SwapchainCreateInfo) -> Result<XrSwapchain> {
|
||||||
|
self.1.create_swapchain(info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum TypedSession {
|
||||||
|
Vulkan(openxr::Session<Vulkan>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypedSession {
|
||||||
|
pub fn into_any_graphics(&self) -> openxr::Session<AnyGraphics> {
|
||||||
|
match self {
|
||||||
|
TypedSession::Vulkan(session) => session.clone().into_any_graphics(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enumerate_swapchain_formats(&self) -> Result<Vec<wgpu::TextureFormat>> {
|
||||||
|
Ok(match self {
|
||||||
|
TypedSession::Vulkan(session) => init::vulkan::enumerate_swapchain_formats(session),
|
||||||
|
}?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_swapchain(&self, info: SwapchainCreateInfo) -> Result<XrSwapchain> {
|
||||||
|
Ok(match self {
|
||||||
|
TypedSession::Vulkan(session) => {
|
||||||
|
XrSwapchain::Vulkan(session.create_swapchain(&info.try_into()?)?)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource, Default, Deref)]
|
||||||
|
pub struct Framebuffers(pub Vec<wgpu::Texture>);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Swapchain {
|
||||||
|
pub inner: Arc<Mutex<XrSwapchain>>,
|
||||||
|
pub format: wgpu::TextureFormat,
|
||||||
|
pub resolution: UVec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum FrameStreamInner {
|
||||||
|
Vulkan(openxr::FrameStream<openxr::Vulkan>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum XrSwapchain {
|
||||||
|
Vulkan(openxr::Swapchain<openxr::Vulkan>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XrSwapchain {
|
||||||
|
pub fn acquire_image(&mut self) -> Result<u32> {
|
||||||
|
Ok(match self {
|
||||||
|
XrSwapchain::Vulkan(swap) => swap.acquire_image()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wait_image(&mut self, timeout: openxr::Duration) -> Result<()> {
|
||||||
|
Ok(match self {
|
||||||
|
XrSwapchain::Vulkan(swap) => swap.wait_image(timeout)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release_image(&mut self) -> Result<()> {
|
||||||
|
Ok(match self {
|
||||||
|
XrSwapchain::Vulkan(swap) => swap.release_image()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enumerate_images(
|
||||||
|
&mut self,
|
||||||
|
device: wgpu::Device,
|
||||||
|
format: wgpu::TextureFormat,
|
||||||
|
resolution: UVec2,
|
||||||
|
) -> Result<Vec<wgpu::Texture>> {
|
||||||
|
match self {
|
||||||
|
XrSwapchain::Vulkan(swap) => swap.enumerate_imgs(device, format, resolution),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait EnumerateImages {
|
||||||
|
fn enumerate_imgs(
|
||||||
|
&mut self,
|
||||||
|
device: wgpu::Device,
|
||||||
|
format: wgpu::TextureFormat,
|
||||||
|
resolution: UVec2,
|
||||||
|
) -> Result<Vec<wgpu::Texture>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EnumerateImages for openxr::Swapchain<openxr::Vulkan> {
|
||||||
|
fn enumerate_imgs(
|
||||||
|
&mut self,
|
||||||
|
device: wgpu::Device,
|
||||||
|
format: wgpu::TextureFormat,
|
||||||
|
resolution: UVec2,
|
||||||
|
) -> Result<Vec<wgpu::Texture>> {
|
||||||
|
let images = self.enumerate_images()?;
|
||||||
|
let images = images.into_iter().map(|color_image| {
|
||||||
|
let color_image = ash::vk::Image::from_raw(color_image);
|
||||||
|
let wgpu_hal_texture = unsafe {
|
||||||
|
<wgpu_hal::vulkan::Api as wgpu_hal::Api>::Device::texture_from_raw(
|
||||||
|
color_image,
|
||||||
|
&wgpu_hal::TextureDescriptor {
|
||||||
|
label: Some("VR Swapchain"),
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: resolution.x,
|
||||||
|
height: resolution.y,
|
||||||
|
depth_or_array_layers: 2,
|
||||||
|
},
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: format,
|
||||||
|
usage: wgpu_hal::TextureUses::COLOR_TARGET
|
||||||
|
| wgpu_hal::TextureUses::COPY_DST,
|
||||||
|
memory_flags: wgpu_hal::MemoryFlags::empty(),
|
||||||
|
view_formats: vec![],
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let texture = unsafe {
|
||||||
|
device.create_texture_from_hal::<wgpu_hal::vulkan::Api>(
|
||||||
|
wgpu_hal_texture,
|
||||||
|
&wgpu::TextureDescriptor {
|
||||||
|
label: Some("VR Swapchain"),
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: resolution.x,
|
||||||
|
height: resolution.y,
|
||||||
|
depth_or_array_layers: 2,
|
||||||
|
},
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: format,
|
||||||
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
|
||||||
|
| wgpu::TextureUsages::COPY_DST,
|
||||||
|
view_formats: &[],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
};
|
||||||
|
texture
|
||||||
|
});
|
||||||
|
Ok(images.collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/openxr/types.rs
Normal file
35
src/openxr/types.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
pub use openxr::{SwapchainCreateFlags, SwapchainUsageFlags, SystemId};
|
||||||
|
|
||||||
|
use super::init::{GraphicsExt, XrInitError};
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct SwapchainCreateInfo {
|
||||||
|
pub create_flags: SwapchainCreateFlags,
|
||||||
|
pub usage_flags: SwapchainUsageFlags,
|
||||||
|
pub format: wgpu::TextureFormat,
|
||||||
|
pub sample_count: u32,
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
pub face_count: u32,
|
||||||
|
pub array_size: u32,
|
||||||
|
pub mip_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: GraphicsExt> TryFrom<SwapchainCreateInfo> for openxr::SwapchainCreateInfo<G> {
|
||||||
|
type Error = XrInitError;
|
||||||
|
|
||||||
|
fn try_from(value: SwapchainCreateInfo) -> Result<Self, Self::Error> {
|
||||||
|
Ok(openxr::SwapchainCreateInfo {
|
||||||
|
create_flags: value.create_flags,
|
||||||
|
usage_flags: value.usage_flags,
|
||||||
|
format: G::from_wgpu_format(value.format)
|
||||||
|
.ok_or(XrInitError::UnsupportedTextureFormat(value.format))?,
|
||||||
|
sample_count: value.sample_count,
|
||||||
|
width: value.width,
|
||||||
|
height: value.height,
|
||||||
|
face_count: value.face_count,
|
||||||
|
array_size: value.array_size,
|
||||||
|
mip_count: value.mip_count,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/render.rs
Normal file
19
src/render.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
use bevy::ecs::system::Resource;
|
||||||
|
use bevy::math::Mat4;
|
||||||
|
use bevy::prelude::{Deref, DerefMut};
|
||||||
|
use bevy::render::camera::{RenderTarget, Viewport};
|
||||||
|
|
||||||
|
use crate::types::Pose;
|
||||||
|
|
||||||
|
pub(crate) const XR_TEXTURE_VIEW_INDEX: u32 = 1208214591;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct XrView {
|
||||||
|
pub projection_matrix: Mat4,
|
||||||
|
pub pose: Pose,
|
||||||
|
pub render_target: RenderTarget,
|
||||||
|
pub view_port: Option<Viewport>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deref, DerefMut, Default, Debug, Clone, Resource)]
|
||||||
|
pub struct XrViews(#[deref] pub Vec<XrView>);
|
||||||
@@ -1,8 +1,16 @@
|
|||||||
use bevy::math::{Quat, Vec3};
|
use bevy::math::{Quat, Vec3};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Pose {
|
pub struct Pose {
|
||||||
pub translation: Vec3,
|
pub translation: Vec3,
|
||||||
pub rotation: Quat,
|
pub rotation: Quat,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Haptic;
|
pub struct Haptic;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum BlendMode {
|
||||||
|
Opaque,
|
||||||
|
Additive,
|
||||||
|
AlphaBlend,
|
||||||
|
}
|
||||||
|
|||||||
35
src/webxr.rs
35
src/webxr.rs
@@ -1,3 +1,8 @@
|
|||||||
|
pub mod render;
|
||||||
|
mod resources;
|
||||||
|
|
||||||
|
pub use resources::*;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
@@ -16,8 +21,8 @@ use wasm_bindgen::closure::Closure;
|
|||||||
use wasm_bindgen::{JsCast, JsValue};
|
use wasm_bindgen::{JsCast, JsValue};
|
||||||
use wasm_bindgen_futures::JsFuture;
|
use wasm_bindgen_futures::JsFuture;
|
||||||
use web_sys::{
|
use web_sys::{
|
||||||
HtmlCanvasElement, WebGl2RenderingContext, XrFrame, XrReferenceSpace, XrReferenceSpaceType,
|
HtmlCanvasElement, WebGl2RenderingContext, XrReferenceSpaceType, XrRenderStateInit,
|
||||||
XrRenderStateInit, XrSession, XrSessionMode, XrWebGlLayer,
|
XrSessionMode,
|
||||||
};
|
};
|
||||||
use winit::platform::web::WindowExtWebSys;
|
use winit::platform::web::WindowExtWebSys;
|
||||||
|
|
||||||
@@ -33,12 +38,8 @@ impl Plugin for XrInitPlugin {
|
|||||||
app.set_runner(webxr_runner);
|
app.set_runner(webxr_runner);
|
||||||
app.insert_non_send_resource(future_session.clone());
|
app.insert_non_send_resource(future_session.clone());
|
||||||
bevy::tasks::IoTaskPool::get().spawn_local(async move {
|
bevy::tasks::IoTaskPool::get().spawn_local(async move {
|
||||||
let result = init_webxr(
|
let result =
|
||||||
canvas,
|
init_webxr(canvas, XrSessionMode::Inline, XrReferenceSpaceType::Viewer).await;
|
||||||
XrSessionMode::ImmersiveVr,
|
|
||||||
XrReferenceSpaceType::Local,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
*future_session.0.lock().unwrap() = Some(result);
|
*future_session.0.lock().unwrap() = Some(result);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -53,11 +54,12 @@ impl Plugin for XrInitPlugin {
|
|||||||
fn finish(&self, app: &mut App) {
|
fn finish(&self, app: &mut App) {
|
||||||
info!("finishing");
|
info!("finishing");
|
||||||
|
|
||||||
if let Some(Ok((session, reference_space))) = app
|
if let Some(result) = app
|
||||||
.world
|
.world
|
||||||
.remove_non_send_resource::<FutureXrSession>()
|
.remove_non_send_resource::<FutureXrSession>()
|
||||||
.and_then(|fxr| fxr.0.lock().unwrap().take())
|
.and_then(|fxr| fxr.0.lock().unwrap().take())
|
||||||
{
|
{
|
||||||
|
let (session, reference_space) = result.unwrap();
|
||||||
app.insert_non_send_resource(session.clone())
|
app.insert_non_send_resource(session.clone())
|
||||||
.insert_non_send_resource(reference_space.clone());
|
.insert_non_send_resource(reference_space.clone());
|
||||||
app.sub_app_mut(RenderApp)
|
app.sub_app_mut(RenderApp)
|
||||||
@@ -97,13 +99,14 @@ fn webxr_runner(mut app: App) {
|
|||||||
|
|
||||||
fn run_xr_app(mut app: App) {
|
fn run_xr_app(mut app: App) {
|
||||||
let session = app.world.non_send_resource::<XrSession>().clone();
|
let session = app.world.non_send_resource::<XrSession>().clone();
|
||||||
let inner_closure: Rc<RefCell<Option<Closure<dyn FnMut(f64, XrFrame)>>>> =
|
let inner_closure: Rc<RefCell<Option<Closure<dyn FnMut(f64, web_sys::XrFrame)>>>> =
|
||||||
Rc::new(RefCell::new(None));
|
Rc::new(RefCell::new(None));
|
||||||
let closure = inner_closure.clone();
|
let closure = inner_closure.clone();
|
||||||
*closure.borrow_mut() = Some(Closure::new(move |_time, frame: XrFrame| {
|
*closure.borrow_mut() = Some(Closure::new(move |_time, frame: web_sys::XrFrame| {
|
||||||
let session = frame.session();
|
let session = frame.session();
|
||||||
app.insert_non_send_resource(frame);
|
app.insert_non_send_resource(XrFrame(frame.clone()));
|
||||||
info!("update");
|
app.sub_app_mut(RenderApp)
|
||||||
|
.insert_non_send_resource(XrFrame(frame));
|
||||||
app.update();
|
app.update();
|
||||||
session.request_animation_frame(
|
session.request_animation_frame(
|
||||||
inner_closure
|
inner_closure
|
||||||
@@ -143,7 +146,7 @@ async fn init_webxr(
|
|||||||
}
|
}
|
||||||
|
|
||||||
info!("creating session");
|
info!("creating session");
|
||||||
let session: XrSession = JsFuture::from(xr.request_session(mode)).await?.into();
|
let session: web_sys::XrSession = JsFuture::from(xr.request_session(mode)).await?.into();
|
||||||
|
|
||||||
info!("creating gl");
|
info!("creating gl");
|
||||||
let gl: WebGl2RenderingContext = {
|
let gl: WebGl2RenderingContext = {
|
||||||
@@ -161,7 +164,7 @@ async fn init_webxr(
|
|||||||
.dyn_into()?
|
.dyn_into()?
|
||||||
};
|
};
|
||||||
|
|
||||||
let xr_gl_layer = XrWebGlLayer::new_with_web_gl2_rendering_context(&session, &gl)?;
|
let xr_gl_layer = web_sys::XrWebGlLayer::new_with_web_gl2_rendering_context(&session, &gl)?;
|
||||||
let mut render_state_init = XrRenderStateInit::new();
|
let mut render_state_init = XrRenderStateInit::new();
|
||||||
render_state_init.base_layer(Some(&xr_gl_layer));
|
render_state_init.base_layer(Some(&xr_gl_layer));
|
||||||
session.update_render_state_with_state(&render_state_init);
|
session.update_render_state_with_state(&render_state_init);
|
||||||
@@ -171,5 +174,5 @@ async fn init_webxr(
|
|||||||
.into();
|
.into();
|
||||||
|
|
||||||
info!("finished");
|
info!("finished");
|
||||||
Ok((session, reference_space))
|
Ok((XrSession(session), XrReferenceSpace(reference_space)))
|
||||||
}
|
}
|
||||||
|
|||||||
154
src/webxr/render.rs
Normal file
154
src/webxr/render.rs
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
use crate::render::{XrView, XrViews};
|
||||||
|
use crate::types::Pose;
|
||||||
|
|
||||||
|
use super::resources::*;
|
||||||
|
|
||||||
|
use bevy::app::{App, Plugin, PreUpdate};
|
||||||
|
use bevy::ecs::schedule::IntoSystemConfigs;
|
||||||
|
use bevy::ecs::system::{NonSend, Res, ResMut};
|
||||||
|
use bevy::ecs::world::World;
|
||||||
|
use bevy::math::{quat, uvec2, vec3, Mat4};
|
||||||
|
use bevy::render::camera::{
|
||||||
|
ManualTextureView, ManualTextureViewHandle, ManualTextureViews, RenderTarget, Viewport,
|
||||||
|
};
|
||||||
|
use bevy::render::renderer::RenderDevice;
|
||||||
|
use bevy::utils::default;
|
||||||
|
|
||||||
|
pub const XR_TEXTURE_VIEW_HANDLE: ManualTextureViewHandle =
|
||||||
|
ManualTextureViewHandle(crate::render::XR_TEXTURE_VIEW_INDEX);
|
||||||
|
|
||||||
|
pub struct XrRenderingPlugin;
|
||||||
|
|
||||||
|
impl Plugin for XrRenderingPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.init_resource::<XrViews>();
|
||||||
|
app.add_systems(
|
||||||
|
PreUpdate,
|
||||||
|
(insert_gl_layer, update_manual_texture_views, insert_views).chain(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_gl_layer(world: &mut World) {
|
||||||
|
let gl_layer = world
|
||||||
|
.non_send_resource::<XrFrame>()
|
||||||
|
.session()
|
||||||
|
.render_state()
|
||||||
|
.base_layer()
|
||||||
|
.unwrap();
|
||||||
|
world.insert_non_send_resource(XrWebGlLayer(gl_layer));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_manual_texture_views(
|
||||||
|
gl_layer: NonSend<XrWebGlLayer>,
|
||||||
|
render_device: Res<RenderDevice>,
|
||||||
|
mut manual_tex_view: ResMut<ManualTextureViews>,
|
||||||
|
) {
|
||||||
|
let dest_texture = create_framebuffer_texture(render_device.wgpu_device(), &gl_layer);
|
||||||
|
let view = dest_texture.create_view(&default());
|
||||||
|
|
||||||
|
manual_tex_view.insert(
|
||||||
|
XR_TEXTURE_VIEW_HANDLE,
|
||||||
|
ManualTextureView::with_default_format(
|
||||||
|
view.into(),
|
||||||
|
uvec2(gl_layer.framebuffer_width(), gl_layer.framebuffer_height()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_views(
|
||||||
|
gl_layer: NonSend<XrWebGlLayer>,
|
||||||
|
reference_space: NonSend<XrReferenceSpace>,
|
||||||
|
frame: NonSend<XrFrame>,
|
||||||
|
mut xr_views: ResMut<XrViews>,
|
||||||
|
) {
|
||||||
|
let Some(viewer_pose) = frame.get_viewer_pose(&reference_space) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let views = viewer_pose
|
||||||
|
.views()
|
||||||
|
.into_iter()
|
||||||
|
.map(Into::<web_sys::XrView>::into)
|
||||||
|
.map(|view| {
|
||||||
|
let transform = view.transform();
|
||||||
|
let position = transform.position();
|
||||||
|
let orientation = transform.orientation();
|
||||||
|
let viewport = gl_layer
|
||||||
|
.get_viewport(&view)
|
||||||
|
.map(|viewport| Viewport {
|
||||||
|
physical_position: uvec2(viewport.x() as u32, viewport.y() as u32),
|
||||||
|
physical_size: uvec2(viewport.width() as u32, viewport.height() as u32),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
XrView {
|
||||||
|
projection_matrix: Mat4::from_cols_array(
|
||||||
|
&view.projection_matrix().try_into().unwrap(),
|
||||||
|
),
|
||||||
|
pose: Pose {
|
||||||
|
translation: vec3(
|
||||||
|
position.x() as f32,
|
||||||
|
position.y() as f32,
|
||||||
|
position.z() as f32,
|
||||||
|
),
|
||||||
|
rotation: quat(
|
||||||
|
orientation.x() as f32,
|
||||||
|
orientation.y() as f32,
|
||||||
|
orientation.z() as f32,
|
||||||
|
orientation.w() as f32,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
render_target: RenderTarget::TextureView(XR_TEXTURE_VIEW_HANDLE),
|
||||||
|
view_port: Some(viewport),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
xr_views.0 = views;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_framebuffer_texture(device: &wgpu::Device, gl_layer: &XrWebGlLayer) -> wgpu::Texture {
|
||||||
|
unsafe {
|
||||||
|
device.create_texture_from_hal::<wgpu_hal::gles::Api>(
|
||||||
|
wgpu_hal::gles::Texture {
|
||||||
|
inner: wgpu_hal::gles::TextureInner::ExternalFramebuffer {
|
||||||
|
// inner: framebuffer,
|
||||||
|
inner: gl_layer.framebuffer_unwrapped(),
|
||||||
|
// inner: framebuffer.as_ref().unwrap().clone(),
|
||||||
|
},
|
||||||
|
mip_level_count: 1,
|
||||||
|
array_layer_count: 1,
|
||||||
|
format: wgpu::TextureFormat::Rgba8Unorm, //TODO check this is ok, different from bevy default
|
||||||
|
format_desc: wgpu_hal::gles::TextureFormatDesc {
|
||||||
|
internal: glow::RGBA,
|
||||||
|
external: glow::RGBA,
|
||||||
|
data_type: glow::UNSIGNED_BYTE,
|
||||||
|
},
|
||||||
|
copy_size: wgpu_hal::CopyExtent {
|
||||||
|
width: gl_layer.framebuffer_width(),
|
||||||
|
height: gl_layer.framebuffer_height(),
|
||||||
|
depth: 1,
|
||||||
|
},
|
||||||
|
drop_guard: None,
|
||||||
|
is_cubemap: false,
|
||||||
|
},
|
||||||
|
&wgpu::TextureDescriptor {
|
||||||
|
label: Some("framebuffer (color)"),
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: gl_layer.framebuffer_width(),
|
||||||
|
height: gl_layer.framebuffer_height(),
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||||
|
view_formats: &[wgpu::TextureFormat::Rgba8UnormSrgb],
|
||||||
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
|
||||||
|
| wgpu::TextureUsages::TEXTURE_BINDING,
|
||||||
|
// | wgpu::TextureUsages::COPY_SRC,
|
||||||
|
// | wgpu::TextureUsages::COPY_DST,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/webxr/resources.rs
Normal file
25
src/webxr/resources.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
use bevy::prelude::{Deref, DerefMut};
|
||||||
|
use web_sys::WebGlFramebuffer;
|
||||||
|
|
||||||
|
#[derive(Deref, DerefMut, Clone)]
|
||||||
|
pub struct XrSession(#[deref] pub(crate) web_sys::XrSession);
|
||||||
|
|
||||||
|
#[derive(Deref, DerefMut, Clone)]
|
||||||
|
pub struct XrReferenceSpace(#[deref] pub(crate) web_sys::XrReferenceSpace);
|
||||||
|
|
||||||
|
#[derive(Deref, DerefMut, Clone)]
|
||||||
|
pub struct XrFrame(#[deref] pub(crate) web_sys::XrFrame);
|
||||||
|
|
||||||
|
#[derive(Deref, DerefMut, Clone)]
|
||||||
|
pub struct XrWebGlLayer(#[deref] pub(crate) web_sys::XrWebGlLayer);
|
||||||
|
|
||||||
|
impl XrWebGlLayer {
|
||||||
|
pub(crate) fn framebuffer_unwrapped(&self) -> WebGlFramebuffer {
|
||||||
|
js_sys::Reflect::get(&self, &"framebuffer".into())
|
||||||
|
.unwrap()
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct XrView(pub(crate) web_sys::XrView);
|
||||||
Reference in New Issue
Block a user