├── Cargo.toml ├── .gitignore ├── openxr ├── examples │ ├── fullscreen.vert.spv │ ├── debug_pattern.frag.spv │ ├── fullscreen.vert │ ├── debug_pattern.frag │ ├── README.md │ └── hello.rs ├── src │ ├── secondary_view.rs │ ├── localization_map_ml.rs │ ├── vive_tracker_paths.rs │ ├── graphics │ │ ├── mod.rs │ │ ├── headless.rs │ │ ├── opengles.rs │ │ ├── vulkan.rs │ │ ├── opengl.rs │ │ └── d3d.rs │ ├── action_set.rs │ ├── foveation_profile_fb.rs │ ├── hand_tracker.rs │ ├── display_refresh_rate.rs │ ├── swapchain.rs │ ├── passthrough.rs │ ├── frame_stream.rs │ ├── lib.rs │ ├── action.rs │ ├── space.rs │ ├── entry.rs │ ├── session.rs │ └── instance.rs └── Cargo.toml ├── .gitmodules ├── .github ├── dependabot.yml └── workflows │ └── rust.yml ├── generator └── Cargo.toml ├── sys ├── Cargo.toml ├── README.md ├── build.rs └── src │ ├── platform.rs │ ├── mint_impls.rs │ ├── support.rs │ ├── loader.rs │ └── lib.rs ├── LICENSE-MIT ├── README.md └── LICENSE-APACHE /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "3" 3 | members = ["generator", "sys", "openxr"] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /openxr/target 3 | /sys/target 4 | /generator/target 5 | **/*.rs.bk 6 | Cargo.lock 7 | *.so 8 | -------------------------------------------------------------------------------- /openxr/examples/fullscreen.vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ralith/openxrs/HEAD/openxr/examples/fullscreen.vert.spv -------------------------------------------------------------------------------- /openxr/examples/debug_pattern.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ralith/openxrs/HEAD/openxr/examples/debug_pattern.frag.spv -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "sys/OpenXR-SDK"] 2 | path = sys/OpenXR-SDK 3 | url = https://github.com/KhronosGroup/OpenXR-SDK.git 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | ignore: 9 | - dependency-name: libloading 10 | versions: 11 | - 0.7.0 12 | -------------------------------------------------------------------------------- /openxr/examples/fullscreen.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) out vec2 screen_coords; 4 | 5 | void main() { 6 | screen_coords = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); 7 | gl_Position = vec4(screen_coords * 2.0f + -1.0f, 0.0, 1.0f); 8 | } 9 | -------------------------------------------------------------------------------- /openxr/examples/debug_pattern.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_EXT_multiview : require 3 | 4 | layout(location = 0) in vec2 screen_coords; 5 | layout(location = 0) out vec4 color; 6 | 7 | void main() { 8 | color = vec4(screen_coords, gl_ViewIndex, 1); 9 | } 10 | -------------------------------------------------------------------------------- /openxr/src/secondary_view.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | pub struct SecondaryViewState { 4 | pub ty: ViewConfigurationType, 5 | pub active: bool, 6 | } 7 | 8 | pub struct SecondaryEndInfo<'a, 'b, 'c, G: Graphics> { 9 | pub ty: ViewConfigurationType, 10 | pub environment_blend_mode: EnvironmentBlendMode, 11 | pub layers: &'a [&'b CompositionLayerBase<'c, G>], 12 | } 13 | -------------------------------------------------------------------------------- /generator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "generator" 3 | version = "0.1.0" 4 | authors = ["Benjamin Saunders "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2024" 7 | publish = false 8 | 9 | [dependencies] 10 | xml = { package = "xml-rs", version = "0.8" } 11 | quote = "1.0" 12 | syn = { version = "2.0", default-features = false, features = ["derive", "printing"] } 13 | proc-macro2 = "1.0" 14 | heck = "0.5" 15 | indexmap = "2" 16 | regex = "1.2.1" 17 | -------------------------------------------------------------------------------- /openxr/src/localization_map_ml.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[derive(Clone)] 4 | pub struct LocalizationMapML { 5 | pub name: String, 6 | pub map_uuid: UuidEXT, 7 | pub map_type: LocalizationMapTypeML, 8 | } 9 | 10 | impl LocalizationMapML { 11 | #[inline] 12 | pub fn from_raw(inner: sys::LocalizationMapML) -> Self { 13 | Self { 14 | name: unsafe { fixed_str(&inner.name).into() }, 15 | map_uuid: inner.map_uuid, 16 | map_type: inner.map_type, 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /openxr/src/vive_tracker_paths.rs: -------------------------------------------------------------------------------- 1 | use crate::Path; 2 | 3 | pub struct ViveTrackerPathsHTCX { 4 | pub persistent: Path, 5 | pub role: Option, 6 | } 7 | 8 | impl From for ViveTrackerPathsHTCX { 9 | fn from(paths: sys::ViveTrackerPathsHTCX) -> Self { 10 | Self { 11 | persistent: paths.persistent_path, 12 | role: if paths.role_path.into_raw() == sys::NULL_PATH as u64 { 13 | None 14 | } else { 15 | Some(paths.role_path) 16 | }, 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "openxr-sys" 3 | description = "OpenXR FFI bindings" 4 | repository = "https://github.com/Ralith/openxrs" 5 | readme = "../README.md" 6 | version = "0.11.0" 7 | authors = ["Benjamin Saunders "] 8 | categories = ["external-ffi-bindings", "rendering"] 9 | keywords = ["openxr", "vr"] 10 | license = "MIT/Apache-2.0" 11 | edition = "2024" 12 | 13 | links = "openxr_loader" 14 | 15 | [badges] 16 | maintenance = { status = "experimental" } 17 | 18 | [features] 19 | linked = [] 20 | static = ["cmake", "linked"] 21 | 22 | [dependencies] 23 | libc = "0.2.50" 24 | mint = { version = "0.5.3", optional = true } 25 | 26 | [build-dependencies] 27 | cmake = { version = "0.1.35", optional = true } 28 | 29 | [package.metadata.docs.rs] 30 | features = ["linked", "mint"] 31 | -------------------------------------------------------------------------------- /sys/README.md: -------------------------------------------------------------------------------- 1 | # openxr-sys 2 | 3 | ## Cloning 4 | 5 | If you can, clone the repo using `git clone --recurse-submodules`. 6 | 7 | If you already cloned the repo without, you must run (from anywhere in the repo): `git submodule update --init`. 8 | 9 | ## Updating the OpenXR SDK 10 | 11 | 1. Update the OpenSR-SDK git submodule to a new commit hash or tag: 12 | a. `cd openxrs/sys/OpenXR-SDK`. 13 | b. `git up `. 14 | c. `cd ../..` to get back to the `openxrs` repo root and check that the 15 | submodule is updated (should be a single line change to what looks like a file 16 | at `openxrs/sys/OpenXR-SDK`). 17 | 2. `cd generator; cargo run --bin generator` to regenerate the `sys` crate. 18 | 3. `cd ..` to go back to the `openxrs` repo root`. 19 | 3. `cargo fmt && cargo build && cargo test` to find any issues that need fixing. 20 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 The openxrs Developers 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /sys/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | #[cfg(feature = "static")] 3 | { 4 | let dst = cmake::Config::new("OpenXR-SDK") 5 | .define("BUILD_API_LAYERS", "OFF") 6 | .define("BUILD_TESTS", "OFF") 7 | .define("OpenGL_GL_PREFERENCE", "GLVND") 8 | .define("DYNAMIC_LOADER", "OFF") 9 | .define("CMAKE_INSTALL_LIBDIR", "lib") 10 | .define("BUILD_WITH_SYSTEM_JSONCPP", "OFF") // See https://github.com/KhronosGroup/OpenXR-SDK-Source/issues/481 11 | .profile("Release") 12 | .build(); 13 | 14 | println!( 15 | "cargo:rustc-link-search=native={}", 16 | dst.join("lib").display() 17 | ); 18 | println!("cargo:rustc-link-lib=static=openxr_loader"); 19 | 20 | let target_os = std::env::var_os("CARGO_CFG_TARGET_OS") 21 | .expect("missing CARGO_CFG_TARGET_OS environment variable"); 22 | 23 | if target_os == "macos" || target_os == "freebsd" { 24 | println!("cargo:rustc-link-lib=c++"); 25 | } else if target_os != "windows" { 26 | println!("cargo:rustc-link-lib=stdc++"); 27 | } 28 | } 29 | #[cfg(all(not(feature = "static"), feature = "linked"))] 30 | { 31 | println!("cargo:rustc-link-lib=openxr_loader"); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /openxr/examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | ## `hello` 4 | 5 | Show information about the OpenXR runtime and XR headset. 6 | 7 | ## `vulkan` 8 | 9 | Display a head-locked gradient spanning both eyes. Controllers position is reported on the terminal. 10 | 11 | ## `vulkan-android` 12 | 13 | Same as `vulkan` but it can run on Android, specifically on the Oculus Quest and Quest 2. It shares the same source file. 14 | 15 | * Install [cargo-apk](https://crates.io/crates/cargo-apk) 16 | * Download Android SDK version 30, set `$ANDROID_HOME` to its path 17 | * Download Android NDK version 23, set `$ANDROID_NDK_ROOT` to its path 18 | * Copy `arm64-v8a` folder from "${ANDROID_NDK_ROOT}/sources/cxx-stl/llvm-libc++/libs/" into "openxr/examples/libs/" 19 | * Get `libopenxr_loader.so` from the Oculus OpenXR Mobile SDK and add it to "openxr/examples/libs/arm64-v8a" 20 | * Run: 21 | 22 | ```sh 23 | cd openxr 24 | cargo apk run --example vulkan-android 25 | ``` 26 | 27 | [Cargo.toml](../Cargo.toml) contains some metadata used by cargo-apk to generate the `AndroidManifest.xml`. In this example it is configured for the Oculus Quest and Quest 2, but changes are necessary to enable features like hand tracking or adding support for more headsets like the Oculus Go. You can read more details in the developer portal of your headset. 28 | -------------------------------------------------------------------------------- /openxr/src/graphics/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | /// Static dispatch for OpenXR graphics bindings 4 | /// 5 | /// The types and functions defined by this trait are an implementation detail, and should not be 6 | /// referenced externally. 7 | pub trait Graphics: Sized { 8 | /// Compatibility details within this graphics API 9 | type Requirements; 10 | /// Parameters required to construct a session for use with this graphics API 11 | type SessionCreateInfo; 12 | /// Swapchain image formats 13 | type Format: Copy; 14 | /// Identifiers for images to render to 15 | type SwapchainImage; 16 | 17 | #[doc(hidden)] 18 | fn raise_format(x: i64) -> Self::Format; 19 | #[doc(hidden)] 20 | fn lower_format(x: Self::Format) -> i64; 21 | 22 | #[doc(hidden)] 23 | fn requirements(instance: &Instance, system: SystemId) -> Result; 24 | 25 | #[doc(hidden)] 26 | unsafe fn create_session( 27 | instance: &Instance, 28 | system: SystemId, 29 | info: &Self::SessionCreateInfo, 30 | ) -> Result; 31 | 32 | #[doc(hidden)] 33 | fn enumerate_swapchain_images(swapchain: &Swapchain) 34 | -> Result>; 35 | } 36 | 37 | #[cfg(windows)] 38 | pub mod d3d; 39 | #[cfg(windows)] 40 | pub use d3d::D3D11; 41 | #[cfg(windows)] 42 | pub use d3d::D3D12; 43 | 44 | pub mod vulkan; 45 | pub use vulkan::Vulkan; 46 | 47 | pub mod opengl; 48 | pub use opengl::OpenGL; 49 | 50 | pub mod opengles; 51 | pub use opengles::OpenGlEs; 52 | 53 | pub mod headless; 54 | pub use headless::Headless; 55 | -------------------------------------------------------------------------------- /openxr/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "openxr" 3 | description = "High-level, mostly-safe OpenXR bindings" 4 | repository = "https://github.com/Ralith/openxrs" 5 | readme = "../README.md" 6 | version = "0.19.0" 7 | authors = ["Benjamin Saunders "] 8 | categories = ["api-bindings", "rendering"] 9 | keywords = ["vr"] 10 | license = "MIT/Apache-2.0" 11 | edition = "2024" 12 | 13 | [badges] 14 | maintenance = { status = "experimental" } 15 | 16 | [features] 17 | static = ["sys/static", "linked"] 18 | loaded = ["libloading"] 19 | linked = ["sys/linked"] 20 | mint = ["sys/mint"] 21 | default = ["loaded"] 22 | 23 | [dependencies] 24 | sys = { package = "openxr-sys", path = "../sys", version = "0.11.0" } 25 | libc = "0.2.50" 26 | libloading = { version = "0.8", optional = true } 27 | 28 | [dev-dependencies] 29 | ash = { version = "0.38", default-features = false, features = ["loaded"] } 30 | ctrlc = "3.1.5" 31 | 32 | [target.'cfg(target_os = "android")'.dev-dependencies] 33 | ndk-glue = "0.7" 34 | 35 | [target.'cfg(target_os = "android")'.dependencies] 36 | ndk-context = "0.1" 37 | 38 | [package.metadata.docs.rs] 39 | features = ["linked", "loaded", "mint"] 40 | 41 | [[example]] 42 | name = "vulkan" 43 | 44 | [[example]] 45 | name = "vulkan-android" 46 | path = "examples/vulkan.rs" 47 | crate-type = ["cdylib"] 48 | 49 | # The following manifest metadata is used by cargo-apk to configure the example Android app for the Oculus Quest 1 and Quest 2. 50 | # It does not affect the openxr crate. 51 | [package.metadata.android] 52 | build_targets = ["aarch64-linux-android"] 53 | runtime_libs = "examples/libs" 54 | 55 | [package.metadata.android.sdk] 56 | min_sdk_version = 21 57 | target_sdk_version = 30 58 | 59 | [package.metadata.android.application.activity] 60 | theme = "@android:style/Theme.Black.NoTitleBar.Fullscreen" 61 | config_changes = "density|keyboard|keyboardHidden|navigation|orientation|screenLayout|screenSize|uiMode" 62 | launch_mode = "singleTask" 63 | orientation = "landscape" 64 | resizeable_activity = false 65 | 66 | [[package.metadata.android.application.activity.intent_filter]] 67 | actions = ["android.intent.action.MAIN"] 68 | categories = [ 69 | "com.oculus.intent.category.VR", 70 | "android.intent.category.LAUNCHER", 71 | ] 72 | -------------------------------------------------------------------------------- /openxr/src/graphics/headless.rs: -------------------------------------------------------------------------------- 1 | use crate::sys::Handle as _; 2 | use crate::*; 3 | 4 | /// Used to create a session without graphics. 5 | /// 6 | /// See [`XR_MND_headless`] for details. 7 | /// 8 | /// [`XR_MND_headless`]: https://www.khronos.org/registry/OpenXR/specs/1.1/html/xrspec.html#XR_MND_headless 9 | pub enum Headless {} 10 | 11 | impl Graphics for Headless { 12 | type Requirements = Requirements; 13 | type SessionCreateInfo = SessionCreateInfo; 14 | type SwapchainImage = HeadlessSwapchainImage; 15 | type Format = HeadlessFormat; 16 | 17 | fn raise_format(_: i64) -> Self::Format { 18 | // used by enumerate_swapchain_formats, which returns empty 19 | unreachable!() 20 | } 21 | fn lower_format(f: Self::Format) -> i64 { 22 | // used by create_swapchain, which is not available in headless 23 | match f {} 24 | } 25 | 26 | fn requirements(_instance: &Instance, _system: SystemId) -> Result { 27 | Ok(Requirements {}) 28 | } 29 | 30 | unsafe fn create_session( 31 | instance: &Instance, 32 | system: SystemId, 33 | _info: &SessionCreateInfo, 34 | ) -> Result { 35 | let info = sys::SessionCreateInfo { 36 | ty: sys::SessionCreateInfo::TYPE, 37 | next: std::ptr::null(), 38 | create_flags: Default::default(), 39 | system_id: system, 40 | }; 41 | let mut out = sys::Session::NULL; 42 | unsafe { 43 | cvt((instance.fp().create_session)( 44 | instance.as_raw(), 45 | &info, 46 | &mut out, 47 | ))?; 48 | } 49 | Ok(out) 50 | } 51 | 52 | fn enumerate_swapchain_images( 53 | _swapchain: &Swapchain, 54 | ) -> Result> { 55 | // in a MND_headless session, xrEnumerateSwapchainFormats will always 56 | // enumerate 0 formats, and so it's not possible to create a swapchain 57 | unreachable!(); 58 | } 59 | } 60 | 61 | #[derive(Clone, Copy)] 62 | pub struct Requirements {} 63 | 64 | #[derive(Clone, Copy)] 65 | pub struct SessionCreateInfo {} 66 | 67 | #[derive(Clone, Copy)] 68 | pub enum HeadlessSwapchainImage {} 69 | 70 | #[derive(Clone, Copy)] 71 | pub enum HeadlessFormat {} 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenXRS 2 | 3 | [![Documentation](https://docs.rs/openxr/badge.svg)](https://docs.rs/openxr/) 4 | [![Crates.io](https://img.shields.io/crates/v/openxr.svg)](https://crates.io/crates/openxr) 5 | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE-MIT) 6 | [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE-APACHE) 7 | 8 | Rust bindings for the OpenXR virtual/augmented reality runtime 9 | API. Refer to [the 10 | specification](https://www.khronos.org/registry/OpenXR/specs/1.1/html/xrspec.html) 11 | for detailed documentation on individual API calls. 12 | 13 | ## `openxr` 14 | 15 | The high-level bindings provide abstractions focusing on ergonomics 16 | and safety. Escape hatches to the raw API are exposed to support 17 | unforeseen requirements, and patterns that cannot be efficiently 18 | exposed in safe terms are preserved as unsafe. 19 | 20 | The crate exposes a number of cargo features: 21 | - `static` builds in the Khronos OpenXR loader, which can then be 22 | accessed with `Entry::linked()`. This is the easiest way to get 23 | going, provided your environment has a working C++ compiler and 24 | CMake installation. 25 | - `loaded` allows access to a manually identified OpenXR 26 | implementation at run time. This allows for cases where a built-in 27 | Khronos loader, normally responsible for that task, cannot be used. 28 | - `linked` attempts to link to an OpenXR loader in the build 29 | environment. This is appropriate for target environments like 30 | desktop Linux which guarantee the presence of an OpenXR 31 | implementation or loader at a specific location, making a built-in 32 | loader redundant. 33 | - `mint` exposes `From` impls for converting to and from 34 | [mint](https://github.com/kvark/mint) types where appropriate. 35 | 36 | See `openxr/examples/vulkan.rs` for an example high-performance Vulkan 37 | rendering workflow. 38 | 39 | ## `openxr-sys` 40 | 41 | The low-level bindings provide faithful unsafe access to the raw API, 42 | with ergonomics and type safety improved as much as feasible compared 43 | to a `bindgen`-style binding without reducing expressiveness. For 44 | example, symbols are named according to Rust conventions, enums and 45 | bitmasks are strongly typed, and many types have helpful `Debug` 46 | impls. This crate is almost entirely generated from the Khronos XML 47 | registry. 48 | -------------------------------------------------------------------------------- /openxr/examples/hello.rs: -------------------------------------------------------------------------------- 1 | use openxr as xr; 2 | 3 | #[cfg_attr(target_os = "android", ndk_glue::main)] 4 | fn main() { 5 | #[cfg(feature = "linked")] 6 | let entry = xr::Entry::linked(); 7 | #[cfg(not(feature = "linked"))] 8 | let entry = unsafe { 9 | xr::Entry::load() 10 | .expect("couldn't find the OpenXR loader; try enabling the \"static\" feature") 11 | }; 12 | 13 | #[cfg(target_os = "android")] 14 | entry.initialize_android_loader(); 15 | 16 | let extensions = entry.enumerate_extensions().unwrap(); 17 | println!("supported extensions: {:#?}", extensions); 18 | let layers = entry.enumerate_layers().unwrap(); 19 | println!("supported layers: {:?}", layers); 20 | println!("layer extensions:"); 21 | for layer in layers { 22 | let extensions = entry.enumerate_layer_extensions(&layer.layer_name).unwrap(); 23 | let extension_names = extensions.names(); 24 | if !extension_names.is_empty() { 25 | println!(" - {}:", layer.layer_name); 26 | for ext in extension_names { 27 | println!(" - {}", String::from_utf8_lossy(ext)); 28 | } 29 | } 30 | } 31 | let instance = entry 32 | .create_instance( 33 | &xr::ApplicationInfo { 34 | application_name: "hello openxrs", 35 | ..Default::default() 36 | }, 37 | &xr::ExtensionSet::default(), 38 | &[], 39 | ) 40 | .unwrap(); 41 | let instance_props = instance.properties().unwrap(); 42 | println!( 43 | "loaded instance: {} v{}", 44 | instance_props.runtime_name, instance_props.runtime_version 45 | ); 46 | 47 | let system = instance 48 | .system(xr::FormFactor::HEAD_MOUNTED_DISPLAY) 49 | .unwrap(); 50 | let system_props = instance.system_properties(system).unwrap(); 51 | println!( 52 | "selected system {}: {}", 53 | system_props.system_id.into_raw(), 54 | if system_props.system_name.is_empty() { 55 | "" 56 | } else { 57 | &system_props.system_name 58 | } 59 | ); 60 | 61 | let view_config_views = instance 62 | .enumerate_view_configuration_views(system, xr::ViewConfigurationType::PRIMARY_STEREO) 63 | .unwrap(); 64 | println!("view configuration views: {:#?}", view_config_views); 65 | } 66 | -------------------------------------------------------------------------------- /sys/src/platform.rs: -------------------------------------------------------------------------------- 1 | //! Stubbed-out platform types for use in graphics bindings 2 | //! 3 | //! Cast to these from your bindings of choice. 4 | #![allow(non_camel_case_types)] 5 | 6 | use std::os::raw::{c_char, c_uint, c_ulong, c_void}; 7 | 8 | // Vulkan 9 | pub type VkInstance = *const c_void; 10 | pub type VkPhysicalDevice = *const c_void; 11 | pub type VkDevice = *const c_void; 12 | pub type VkImage = u64; 13 | pub type VkImageCreateFlags = u64; 14 | pub type VkImageUsageFlags = u64; 15 | pub type VkFormat = u32; 16 | pub type VkInstanceCreateInfo = c_void; 17 | pub type VkDeviceCreateInfo = c_void; 18 | pub type VkAllocationCallbacks = c_void; 19 | pub type VkResult = i32; 20 | pub type VkSamplerMipmapMode = i32; 21 | pub type VkSamplerAddressMode = i32; 22 | pub type VkComponentSwizzle = i32; 23 | pub type VkFilter = i32; 24 | pub type VkGetInstanceProcAddr = 25 | unsafe extern "system" fn(VkInstance, *const c_char) -> Option; 26 | 27 | // Xlib 28 | pub type GLXFBConfig = *mut c_void; 29 | pub type GLXDrawable = c_ulong; 30 | pub type GLXContext = *mut c_void; 31 | pub type Display = std::os::raw::c_void; 32 | 33 | // Xcb 34 | pub type xcb_connection_t = c_void; 35 | pub type xcb_glx_fbconfig_t = u32; 36 | pub type xcb_visualid_t = u32; 37 | pub type xcb_glx_drawable_t = u32; 38 | pub type xcb_glx_context_t = u32; 39 | 40 | // Wayland 41 | pub type wl_display = c_void; 42 | 43 | pub type jobject = *mut c_void; 44 | 45 | // Win32 46 | #[cfg(windows)] 47 | pub use windows::*; 48 | #[cfg(windows)] 49 | #[allow(non_snake_case)] 50 | mod windows { 51 | //! Transcribed from windows-sys 52 | 53 | use std::os::raw::c_void; 54 | 55 | pub type IUnknown = c_void; 56 | pub type LARGE_INTEGER = i64; 57 | pub type HDC = isize; 58 | pub type HGLRC = isize; 59 | pub type ID3D11Device = c_void; 60 | pub type ID3D11Texture2D = c_void; 61 | pub type ID3D12CommandQueue = c_void; 62 | pub type ID3D12Device = c_void; 63 | pub type ID3D12Resource = c_void; 64 | pub type D3D_FEATURE_LEVEL = i32; 65 | 66 | #[derive(Copy, Clone)] 67 | #[repr(C)] 68 | pub struct LUID { 69 | pub LowPart: u32, 70 | pub HighPart: i32, 71 | } 72 | } 73 | 74 | // EGL 75 | pub type EGLConfig = *mut c_void; 76 | pub type EGLContext = *mut c_void; 77 | pub type EGLDisplay = *mut c_void; 78 | pub type EGLenum = c_uint; 79 | pub type EglGetProcAddressMNDX = 80 | unsafe extern "system" fn(*const c_char) -> Option; 81 | -------------------------------------------------------------------------------- /openxr/src/action_set.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::sys::Handle as _; 4 | use crate::*; 5 | 6 | #[derive(Clone)] 7 | pub struct ActionSet { 8 | inner: Arc, 9 | } 10 | 11 | impl ActionSet { 12 | /// Take ownership of an existing action set handle 13 | /// 14 | /// # Safety 15 | /// 16 | /// `handle` must be a valid action set handle associated with `session`. 17 | #[inline] 18 | pub unsafe fn from_raw(instance: Instance, handle: sys::ActionSet) -> Self { 19 | Self { 20 | inner: Arc::new(ActionSetInner { instance, handle }), 21 | } 22 | } 23 | 24 | /// Access the raw swapchain handle 25 | #[inline] 26 | pub fn as_raw(&self) -> sys::ActionSet { 27 | self.inner.handle 28 | } 29 | 30 | /// Access the `Instance` self is descended from 31 | #[inline] 32 | pub fn instance(&self) -> &Instance { 33 | &self.inner.instance 34 | } 35 | 36 | /// Set the debug name of this `ActionSet`, if `XR_EXT_debug_utils` is loaded 37 | #[inline] 38 | pub fn set_name(&mut self, name: &str) -> Result<()> { 39 | self.instance().set_name_raw(self.as_raw().into_raw(), name) 40 | } 41 | 42 | /// Create a new logical input action 43 | #[inline] 44 | pub fn create_action( 45 | &self, 46 | name: &str, 47 | localized_name: &str, 48 | subaction_paths: &[Path], 49 | ) -> Result> { 50 | let info = builder::ActionCreateInfo::new() 51 | .action_name(name) 52 | .localized_action_name(localized_name) 53 | .subaction_paths(subaction_paths) 54 | .action_type(T::TYPE); 55 | unsafe { 56 | let mut out = sys::Action::NULL; 57 | cvt((self.fp().create_action)( 58 | self.as_raw(), 59 | info.as_raw(), 60 | &mut out, 61 | ))?; 62 | Ok(Action::from_raw(self.clone(), out)) 63 | } 64 | } 65 | 66 | // Private helper 67 | #[inline] 68 | fn fp(&self) -> &raw::Instance { 69 | self.inner.instance.fp() 70 | } 71 | 72 | // Private because safety requires that only one copy of the `ActionSet` exist externally. 73 | pub(crate) fn clone(&self) -> Self { 74 | Self { 75 | inner: self.inner.clone(), 76 | } 77 | } 78 | } 79 | 80 | impl AsHandle for ActionSet { 81 | type Handle = sys::ActionSet; 82 | fn as_handle(&self) -> Self::Handle { 83 | self.inner.handle 84 | } 85 | } 86 | 87 | struct ActionSetInner { 88 | instance: Instance, 89 | handle: sys::ActionSet, 90 | } 91 | 92 | impl Drop for ActionSetInner { 93 | fn drop(&mut self) { 94 | unsafe { 95 | (self.instance.fp().destroy_action_set)(self.handle); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: ["master"] 6 | pull_request: 7 | 8 | jobs: 9 | test: 10 | strategy: 11 | matrix: 12 | include: 13 | - os: ubuntu-latest 14 | rust: stable 15 | target: x86_64-unknown-linux-gnu 16 | - os: ubuntu-latest 17 | rust: beta 18 | target: x86_64-unknown-linux-gnu 19 | - os: ubuntu-latest 20 | rust: stable 21 | target: aarch64-linux-android 22 | - os: windows-latest 23 | rust: stable 24 | target: x86_64-pc-windows-msvc 25 | - os: macOS-latest 26 | rust: stable 27 | target: x86_64-apple-darwin 28 | 29 | runs-on: ${{ matrix.os }} 30 | 31 | steps: 32 | - uses: actions/checkout@v1 33 | - uses: actions-rs/toolchain@v1 34 | with: 35 | profile: minimal 36 | toolchain: ${{ matrix.rust }} 37 | target: ${{ matrix.target }} 38 | override: true 39 | - uses: actions-rs/cargo@v1 40 | if: matrix.target != 'aarch64-linux-android' 41 | with: 42 | command: build 43 | args: --workspace --all-targets --target ${{ matrix.target }} 44 | - uses: actions-rs/cargo@v1 45 | if: matrix.target == 'aarch64-linux-android' 46 | env: 47 | TARGET_aarch64_linux_android_CC: ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-clang 48 | TARGET_aarch64_linux_android_CXX: ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-clang++ 49 | TARGET_aarch64_linux_android_AR: ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-ar 50 | RUSTFLAGS: -C linker=aarch64-linux-android-ld.gold 51 | with: 52 | command: build 53 | args: --target ${{ matrix.target }} --manifest-path openxr/Cargo.toml 54 | - uses: actions-rs/cargo@v1 55 | if: matrix.target != 'aarch64-linux-android' 56 | with: 57 | command: test 58 | args: --workspace 59 | 60 | lint: 61 | runs-on: ubuntu-latest 62 | steps: 63 | - name: Install foreign dependencies 64 | run: sudo apt update && sudo apt-get -y install libxxf86vm-dev libxrandr-dev libgl1-mesa-dev 65 | - uses: actions/checkout@v1 66 | - uses: actions-rs/toolchain@v1 67 | with: 68 | profile: minimal 69 | toolchain: stable 70 | override: true 71 | components: rustfmt, clippy 72 | - uses: actions-rs/cargo@v1 73 | with: 74 | command: fmt 75 | args: --all -- --check 76 | - uses: actions-rs/cargo@v1 77 | if: always() 78 | with: 79 | command: clippy 80 | args: --workspace --all-targets -- -D warnings 81 | -------------------------------------------------------------------------------- /openxr/src/foveation_profile_fb.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::sys::Handle as _; 4 | use crate::*; 5 | 6 | #[derive(Clone)] 7 | pub struct FoveationProfileFB { 8 | inner: Arc, 9 | } 10 | 11 | pub struct FoveationLevelProfile { 12 | pub level: FoveationLevelFB, 13 | pub vertical_offset: f32, 14 | pub dynamic: FoveationDynamicFB, 15 | } 16 | 17 | impl FoveationProfileFB { 18 | /// Take ownership of an existing foveation profile handle 19 | /// 20 | /// # Safety 21 | /// 22 | /// `handle` must be a valid foveation profile handle created with a [Session] associated with `instance`. 23 | #[inline] 24 | pub unsafe fn from_raw(instance: Instance, handle: sys::FoveationProfileFB) -> Self { 25 | Self { 26 | inner: Arc::new(FoveationProfileFBInner { instance, handle }), 27 | } 28 | } 29 | 30 | #[inline] 31 | pub fn as_raw(&self) -> sys::FoveationProfileFB { 32 | self.inner.handle 33 | } 34 | } 35 | 36 | impl AsHandle for FoveationProfileFB { 37 | type Handle = sys::FoveationProfileFB; 38 | fn as_handle(&self) -> Self::Handle { 39 | self.inner.handle 40 | } 41 | } 42 | 43 | impl Session { 44 | pub fn create_foveation_profile( 45 | &self, 46 | level_profile: Option, 47 | ) -> Result { 48 | let fp = self 49 | .instance() 50 | .exts() 51 | .fb_foveation 52 | .as_ref() 53 | .ok_or(sys::Result::ERROR_EXTENSION_NOT_PRESENT)?; 54 | 55 | let mut level_profile = level_profile.map(|lp| sys::FoveationLevelProfileCreateInfoFB { 56 | ty: sys::FoveationLevelProfileCreateInfoFB::TYPE, 57 | next: std::ptr::null_mut(), 58 | vertical_offset: lp.vertical_offset, 59 | level: lp.level, 60 | dynamic: lp.dynamic, 61 | }); 62 | let next = if let Some(level_profile) = level_profile.as_mut() { 63 | level_profile as *mut _ as *mut _ 64 | } else { 65 | std::ptr::null_mut() 66 | }; 67 | 68 | let mut create_info = sys::FoveationProfileCreateInfoFB { 69 | ty: sys::FoveationProfileCreateInfoFB::TYPE, 70 | next, 71 | }; 72 | let mut profile = sys::FoveationProfileFB::NULL; 73 | let res = 74 | unsafe { (fp.create_foveation_profile)(self.as_raw(), &mut create_info, &mut profile) }; 75 | cvt(res)?; 76 | 77 | Ok(unsafe { FoveationProfileFB::from_raw(self.instance().clone(), profile) }) 78 | } 79 | } 80 | 81 | struct FoveationProfileFBInner { 82 | instance: Instance, 83 | handle: sys::FoveationProfileFB, 84 | } 85 | 86 | impl Drop for FoveationProfileFBInner { 87 | fn drop(&mut self) { 88 | if let Some(fp) = self.instance.exts().fb_foveation.as_ref() { 89 | unsafe { (fp.destroy_foveation_profile)(self.handle) }; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /openxr/src/hand_tracker.rs: -------------------------------------------------------------------------------- 1 | use std::{ptr, sync::Arc}; 2 | 3 | use crate::sys::Handle as _; 4 | use crate::*; 5 | 6 | pub use sys::HandEXT as Hand; 7 | pub use sys::HandJointEXT as HandJoint; 8 | pub use sys::HandJointLocationEXT as HandJointLocation; 9 | pub use sys::HandJointVelocityEXT as HandJointVelocity; 10 | 11 | pub const HAND_JOINT_COUNT: usize = sys::HAND_JOINT_COUNT_EXT as usize; 12 | 13 | pub struct HandTracker { 14 | pub(crate) session: Arc, 15 | handle: sys::HandTrackerEXT, 16 | } 17 | 18 | impl HandTracker { 19 | #[inline] 20 | pub fn as_raw(&self) -> sys::HandTrackerEXT { 21 | self.handle 22 | } 23 | 24 | /// Take ownership of an existing hand tracker 25 | /// 26 | /// # Safety 27 | /// 28 | /// `handle` must be a valid hand tracker handle associated with `session`. 29 | #[inline] 30 | pub unsafe fn from_raw(session: &Session, handle: sys::HandTrackerEXT) -> Self { 31 | Self { 32 | handle, 33 | session: session.inner.clone(), 34 | } 35 | } 36 | 37 | pub(crate) fn create(session: &Session, hand: Hand) -> Result { 38 | let fp = session.inner.instance.exts().ext_hand_tracking.as_ref(); 39 | let fp = if let Some(fp) = fp { 40 | fp 41 | } else { 42 | return Err(sys::Result::ERROR_EXTENSION_NOT_PRESENT); 43 | }; 44 | 45 | let mut out = sys::HandTrackerEXT::NULL; 46 | let info = sys::HandTrackerCreateInfoEXT { 47 | ty: sys::HandTrackerCreateInfoEXT::TYPE, 48 | next: ptr::null(), 49 | hand, 50 | // If this ever changes, update the joint_counts set in `Space::locate_hand_joints` 51 | hand_joint_set: sys::HandJointSetEXT::DEFAULT, 52 | }; 53 | let handle = unsafe { 54 | cvt((fp.create_hand_tracker)(session.as_raw(), &info, &mut out))?; 55 | out 56 | }; 57 | Ok(HandTracker { 58 | session: session.inner.clone(), 59 | handle, 60 | }) 61 | } 62 | 63 | #[inline] 64 | pub(crate) fn fp(&self) -> &raw::HandTrackingEXT { 65 | self.session 66 | .instance 67 | .exts() 68 | .ext_hand_tracking 69 | .as_ref() 70 | .expect("Somehow created HandTracker without XR_EXT_hand_tracking being enabled") 71 | } 72 | } 73 | 74 | impl AsHandle for HandTracker { 75 | type Handle = sys::HandTrackerEXT; 76 | fn as_handle(&self) -> Self::Handle { 77 | self.handle 78 | } 79 | } 80 | 81 | impl Drop for HandTracker { 82 | fn drop(&mut self) { 83 | unsafe { 84 | (self.fp().destroy_hand_tracker)(self.handle); 85 | } 86 | } 87 | } 88 | 89 | /// An array of `HandJointLocation`s, one for each `HandJoint`. 90 | /// 91 | /// `HandJoint`s can be used directly as an index for convenience. 92 | pub type HandJointLocations = [HandJointLocation; HAND_JOINT_COUNT]; 93 | 94 | /// An array of `HandJointVelocity`s, one for each `HandJoint`. 95 | /// 96 | /// `HandJoint`s can be used directly as an index for convenience. 97 | pub type HandJointVelocities = [HandJointVelocity; HAND_JOINT_COUNT]; 98 | -------------------------------------------------------------------------------- /openxr/src/display_refresh_rate.rs: -------------------------------------------------------------------------------- 1 | //! Implements the functions from [`XR_FB_display_refresh_rate`]. 2 | //! The extension also includes a new event ([`Event::DisplayRefreshRateChangedFB`]) that gets 3 | //! sent when the display refresh rate changes. 4 | //! 5 | //! [`XR_FB_display_refresh_rate`]: https://www.khronos.org/registry/OpenXR/specs/1.1/html/xrspec.html#XR_FB_display_refresh_rate 6 | //! [`Event::DisplayRefreshRateChangedFB`]: crate::Event::DisplayRefreshRateChangedFB 7 | 8 | use crate::{Result, get_arr}; 9 | use crate::{Session, cvt}; 10 | use std::mem::MaybeUninit; 11 | 12 | impl Session { 13 | /// [Enumerates] the supported display refresh rates. 14 | /// Requires [`XR_FB_display_refresh_rate`] 15 | /// 16 | /// [Enumerates]: https://www.khronos.org/registry/OpenXR/specs/1.1/html/xrspec.html#xrEnumerateDisplayRefreshRatesFB 17 | /// [`XR_FB_display_refresh_rate`]: https://www.khronos.org/registry/OpenXR/specs/1.1/html/xrspec.html#XR_FB_display_refresh_rate 18 | pub fn enumerate_display_refresh_rates(&self) -> Result> { 19 | let ext = self 20 | .inner 21 | .instance 22 | .exts() 23 | .fb_display_refresh_rate 24 | .as_ref() 25 | .expect("XR_FB_display_refresh_rate not loaded"); 26 | get_arr(|cap, count, buf| unsafe { 27 | (ext.enumerate_display_refresh_rates)(self.as_raw(), cap, count, buf) 28 | }) 29 | } 30 | 31 | /// Retrieves the [current display refresh rate]. 32 | /// Requires [`XR_FB_display_refresh_rate`] 33 | /// 34 | /// [current display refresh rate]: https://www.khronos.org/registry/OpenXR/specs/1.1/html/xrspec.html#xrGetDisplayRefreshRateFB 35 | /// [`XR_FB_display_refresh_rate`]: https://www.khronos.org/registry/OpenXR/specs/1.1/html/xrspec.html#XR_FB_display_refresh_rate 36 | pub fn get_display_refresh_rate(&self) -> Result { 37 | let ext = self 38 | .inner 39 | .instance 40 | .exts() 41 | .fb_display_refresh_rate 42 | .as_ref() 43 | .expect("XR_FB_display_refresh_rate not loaded"); 44 | unsafe { 45 | let mut out = MaybeUninit::uninit(); 46 | cvt((ext.get_display_refresh_rate)( 47 | self.as_raw(), 48 | out.as_mut_ptr(), 49 | ))?; 50 | Ok(out.assume_init()) 51 | } 52 | } 53 | 54 | /// [Requests] a change to the `display_refresh_rate`. 55 | /// Requires [`XR_FB_display_refresh_rate`] 56 | /// 57 | /// [Requests]: https://www.khronos.org/registry/OpenXR/specs/1.1/html/xrspec.html#xrRequestDisplayRefreshRateFB 58 | /// [`XR_FB_display_refresh_rate`]: https://www.khronos.org/registry/OpenXR/specs/1.1/html/xrspec.html#XR_FB_display_refresh_rate 59 | pub fn request_display_refresh_rate(&self, display_refresh_rate: f32) -> Result<()> { 60 | let ext = self 61 | .inner 62 | .instance 63 | .exts() 64 | .fb_display_refresh_rate 65 | .as_ref() 66 | .expect("XR_FB_display_refresh_rate not loaded"); 67 | cvt(unsafe { (ext.request_display_refresh_rate)(self.as_raw(), display_refresh_rate) })?; 68 | Ok(()) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /sys/src/mint_impls.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | Extent2Df, Extent2Di, Offset2Df, Offset2Di, Quaternionf, Vector2f, Vector3f, Vector4f, 3 | }; 4 | use mint::{Quaternion, Vector2, Vector3, Vector4}; 5 | 6 | impl From for Vector2 { 7 | fn from(v: Vector2f) -> Self { 8 | Vector2 { x: v.x, y: v.y } 9 | } 10 | } 11 | 12 | impl From> for Vector2f { 13 | fn from(v: Vector2) -> Self { 14 | Self { x: v.x, y: v.y } 15 | } 16 | } 17 | 18 | impl From for Vector2 { 19 | fn from(v: Offset2Df) -> Self { 20 | Vector2 { x: v.x, y: v.y } 21 | } 22 | } 23 | 24 | impl From> for Offset2Df { 25 | fn from(v: Vector2) -> Self { 26 | Self { x: v.x, y: v.y } 27 | } 28 | } 29 | 30 | impl From for Vector2 { 31 | fn from(v: Offset2Di) -> Self { 32 | Vector2 { x: v.x, y: v.y } 33 | } 34 | } 35 | 36 | impl From> for Offset2Di { 37 | fn from(v: Vector2) -> Self { 38 | Self { x: v.x, y: v.y } 39 | } 40 | } 41 | 42 | impl From for Vector2 { 43 | fn from(v: Extent2Df) -> Self { 44 | Vector2 { 45 | x: v.width, 46 | y: v.height, 47 | } 48 | } 49 | } 50 | 51 | impl From> for Extent2Df { 52 | fn from(v: Vector2) -> Self { 53 | Self { 54 | width: v.x, 55 | height: v.y, 56 | } 57 | } 58 | } 59 | 60 | impl From for Vector2 { 61 | fn from(v: Extent2Di) -> Self { 62 | Vector2 { 63 | x: v.width, 64 | y: v.height, 65 | } 66 | } 67 | } 68 | 69 | impl From> for Extent2Di { 70 | fn from(v: Vector2) -> Self { 71 | Self { 72 | width: v.x, 73 | height: v.y, 74 | } 75 | } 76 | } 77 | 78 | impl From for Vector3 { 79 | fn from(v: Vector3f) -> Self { 80 | Vector3 { 81 | x: v.x, 82 | y: v.y, 83 | z: v.z, 84 | } 85 | } 86 | } 87 | 88 | impl From> for Vector3f { 89 | fn from(v: Vector3) -> Self { 90 | Self { 91 | x: v.x, 92 | y: v.y, 93 | z: v.z, 94 | } 95 | } 96 | } 97 | 98 | impl From for Vector4 { 99 | fn from(v: Vector4f) -> Self { 100 | Vector4 { 101 | x: v.x, 102 | y: v.y, 103 | z: v.z, 104 | w: v.w, 105 | } 106 | } 107 | } 108 | 109 | impl From> for Vector4f { 110 | fn from(v: Vector4) -> Self { 111 | Self { 112 | x: v.x, 113 | y: v.y, 114 | z: v.z, 115 | w: v.w, 116 | } 117 | } 118 | } 119 | 120 | impl From> for Quaternionf { 121 | fn from(q: Quaternion) -> Self { 122 | Self { 123 | x: q.v.x, 124 | y: q.v.y, 125 | z: q.v.z, 126 | w: q.s, 127 | } 128 | } 129 | } 130 | 131 | impl From for Quaternion { 132 | fn from(q: Quaternionf) -> Quaternion { 133 | Quaternion { 134 | s: q.w, 135 | v: Vector3 { 136 | x: q.x, 137 | y: q.y, 138 | z: q.z, 139 | }, 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /sys/src/support.rs: -------------------------------------------------------------------------------- 1 | //! Static helpers used by generated code 2 | use std::fmt; 3 | 4 | macro_rules! wrapper { 5 | {$(#[$meta: meta])* $ident:ident($ty:ty)} => { 6 | $(#[$meta])* #[repr(transparent)] 7 | pub struct $ident($ty); 8 | impl $ident { 9 | pub fn from_raw(x: $ty) -> Self { Self(x) } 10 | pub fn into_raw(self) -> $ty { self.0 } 11 | } 12 | } 13 | } 14 | 15 | macro_rules! bitmask { 16 | ($name:ident) => { 17 | impl $name { 18 | pub const EMPTY: Self = Self(0); 19 | 20 | #[inline] 21 | pub fn from_raw(x: u64) -> Self { 22 | Self(x) 23 | } 24 | 25 | #[inline] 26 | pub fn into_raw(self) -> u64 { 27 | self.0 28 | } 29 | 30 | #[inline] 31 | pub fn is_empty(self) -> bool { 32 | self == $name::EMPTY 33 | } 34 | 35 | #[inline] 36 | pub fn intersects(self, other: $name) -> bool { 37 | self & other != $name::EMPTY 38 | } 39 | 40 | /// Returns whether `other` is a subset of `self` 41 | #[inline] 42 | pub fn contains(self, other: $name) -> bool { 43 | self & other == other 44 | } 45 | } 46 | 47 | impl Default for $name { 48 | fn default() -> Self { 49 | Self::EMPTY 50 | } 51 | } 52 | 53 | impl std::ops::BitOr for $name { 54 | type Output = $name; 55 | 56 | #[inline] 57 | fn bitor(self, rhs: $name) -> $name { 58 | $name(self.0 | rhs.0) 59 | } 60 | } 61 | 62 | impl std::ops::BitOrAssign for $name { 63 | #[inline] 64 | fn bitor_assign(&mut self, rhs: $name) { 65 | *self = *self | rhs 66 | } 67 | } 68 | 69 | impl std::ops::BitAnd for $name { 70 | type Output = $name; 71 | 72 | #[inline] 73 | fn bitand(self, rhs: $name) -> $name { 74 | $name(self.0 & rhs.0) 75 | } 76 | } 77 | 78 | impl std::ops::BitAndAssign for $name { 79 | #[inline] 80 | fn bitand_assign(&mut self, rhs: $name) { 81 | *self = *self & rhs 82 | } 83 | } 84 | 85 | impl std::ops::BitXor for $name { 86 | type Output = $name; 87 | 88 | #[inline] 89 | fn bitxor(self, rhs: $name) -> $name { 90 | $name(self.0 ^ rhs.0) 91 | } 92 | } 93 | 94 | impl std::ops::BitXorAssign for $name { 95 | #[inline] 96 | fn bitxor_assign(&mut self, rhs: $name) { 97 | *self = *self ^ rhs 98 | } 99 | } 100 | 101 | impl std::ops::Not for $name { 102 | type Output = $name; 103 | 104 | #[inline] 105 | fn not(self) -> $name { 106 | Self(!self.0) 107 | } 108 | } 109 | }; 110 | } 111 | 112 | macro_rules! handle { 113 | ($name:ident) => { 114 | impl $crate::Handle for $name { 115 | const NULL: Self = Self(0); 116 | #[inline] 117 | fn from_raw(x: u64) -> Self { 118 | Self(x) 119 | } 120 | #[inline] 121 | fn into_raw(self) -> u64 { 122 | self.0 123 | } 124 | } 125 | impl Default for $name { 126 | fn default() -> Self { 127 | Self::NULL 128 | } 129 | } 130 | }; 131 | } 132 | 133 | pub fn fmt_enum(f: &mut fmt::Formatter, value: i32, name: Option<&'static str>) -> fmt::Result { 134 | match name { 135 | Some(x) => f.pad(x), 136 | None => ::fmt(&value, f), 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /openxr/src/graphics/opengles.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | #[cfg(target_os = "android")] 4 | use sys::{Handle as _, platform::*}; 5 | 6 | use crate::*; 7 | 8 | /// The OpenGL ES graphics API 9 | /// 10 | /// See [`XR_KHR_opengl_es_enable`] for safety details. 11 | /// 12 | /// [`XR_KHR_opengl_es_enable`]: https://www.khronos.org/registry/OpenXR/specs/1.1/html/xrspec.html#XR_KHR_opengl_es_enable 13 | pub enum OpenGlEs {} 14 | 15 | impl Graphics for OpenGlEs { 16 | type Requirements = Requirements; 17 | type SessionCreateInfo = SessionCreateInfo; 18 | type Format = u32; 19 | type SwapchainImage = u32; 20 | 21 | fn raise_format(x: i64) -> u32 { 22 | x as _ 23 | } 24 | fn lower_format(x: u32) -> i64 { 25 | x.into() 26 | } 27 | 28 | fn requirements(inst: &Instance, system: SystemId) -> Result { 29 | let out = unsafe { 30 | let mut x = sys::GraphicsRequirementsOpenGLESKHR::out(ptr::null_mut()); 31 | cvt((inst.opengles().get_open_gles_graphics_requirements)( 32 | inst.as_raw(), 33 | system, 34 | x.as_mut_ptr(), 35 | ))?; 36 | x.assume_init() 37 | }; 38 | Ok(Requirements { 39 | min_api_version_supported: out.min_api_version_supported, 40 | max_api_version_supported: out.max_api_version_supported, 41 | }) 42 | } 43 | #[allow(unused_variables)] 44 | unsafe fn create_session( 45 | instance: &Instance, 46 | system: SystemId, 47 | info: &Self::SessionCreateInfo, 48 | ) -> Result { 49 | match *info { 50 | #[cfg(target_os = "android")] 51 | SessionCreateInfo::Android { 52 | display, 53 | config, 54 | context, 55 | } => { 56 | let binding = sys::GraphicsBindingOpenGLESAndroidKHR { 57 | ty: sys::GraphicsBindingOpenGLESAndroidKHR::TYPE, 58 | next: ptr::null(), 59 | display, 60 | config, 61 | context, 62 | }; 63 | let info = sys::SessionCreateInfo { 64 | ty: sys::SessionCreateInfo::TYPE, 65 | next: &binding as *const _ as *const _, 66 | create_flags: Default::default(), 67 | system_id: system, 68 | }; 69 | let mut out = sys::Session::NULL; 70 | cvt((instance.fp().create_session)( 71 | instance.as_raw(), 72 | &info, 73 | &mut out, 74 | ))?; 75 | 76 | Ok(out) 77 | } 78 | } 79 | } 80 | 81 | fn enumerate_swapchain_images( 82 | swapchain: &Swapchain, 83 | ) -> Result> { 84 | let images = get_arr_init( 85 | sys::SwapchainImageOpenGLESKHR { 86 | ty: sys::SwapchainImageOpenGLESKHR::TYPE, 87 | next: ptr::null_mut(), 88 | image: 0, 89 | }, 90 | |capacity, count, buf| unsafe { 91 | (swapchain.instance().fp().enumerate_swapchain_images)( 92 | swapchain.as_raw(), 93 | capacity, 94 | count, 95 | buf as *mut _, 96 | ) 97 | }, 98 | )?; 99 | Ok(images.into_iter().map(|x| x.image).collect()) 100 | } 101 | } 102 | 103 | #[derive(Copy, Clone)] 104 | pub struct Requirements { 105 | pub min_api_version_supported: Version, 106 | pub max_api_version_supported: Version, 107 | } 108 | 109 | pub enum SessionCreateInfo { 110 | #[cfg(target_os = "android")] 111 | Android { 112 | display: EGLDisplay, 113 | config: EGLConfig, 114 | context: EGLContext, 115 | }, 116 | } 117 | -------------------------------------------------------------------------------- /openxr/src/graphics/vulkan.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use sys::platform::*; 4 | 5 | use crate::sys::Handle as _; 6 | use crate::*; 7 | 8 | /// The Vulkan graphics API 9 | /// 10 | /// See [`XR_KHR_vulkan_enable2`] for safety details. 11 | /// 12 | /// [`XR_KHR_vulkan_enable2`]: https://www.khronos.org/registry/OpenXR/specs/1.1/html/xrspec.html#XR_KHR_vulkan_enable2 13 | pub enum Vulkan {} 14 | 15 | impl Graphics for Vulkan { 16 | type Requirements = Requirements; 17 | type Format = VkFormat; 18 | type SessionCreateInfo = SessionCreateInfo; 19 | type SwapchainImage = VkImage; 20 | 21 | fn raise_format(x: i64) -> Self::Format { 22 | x as _ 23 | } 24 | fn lower_format(x: Self::Format) -> i64 { 25 | x as _ 26 | } 27 | 28 | fn requirements(instance: &Instance, system: SystemId) -> Result { 29 | let out = unsafe { 30 | let mut x = sys::GraphicsRequirementsVulkanKHR::out(ptr::null_mut()); 31 | let fp = if instance.exts().khr_vulkan_enable2.is_some() { 32 | instance.vulkan().get_vulkan_graphics_requirements2 33 | } else { 34 | instance.vulkan_legacy().get_vulkan_graphics_requirements 35 | }; 36 | cvt(fp(instance.as_raw(), system, x.as_mut_ptr()))?; 37 | x.assume_init() 38 | }; 39 | Ok(Requirements { 40 | min_api_version_supported: out.min_api_version_supported, 41 | max_api_version_supported: out.max_api_version_supported, 42 | }) 43 | } 44 | 45 | unsafe fn create_session( 46 | instance: &Instance, 47 | system: SystemId, 48 | info: &Self::SessionCreateInfo, 49 | ) -> Result { 50 | unsafe { 51 | let binding = sys::GraphicsBindingVulkanKHR { 52 | ty: sys::GraphicsBindingVulkanKHR::TYPE, 53 | next: ptr::null(), 54 | instance: info.instance, 55 | physical_device: info.physical_device, 56 | device: info.device, 57 | queue_family_index: info.queue_family_index, 58 | queue_index: info.queue_index, 59 | }; 60 | let info = sys::SessionCreateInfo { 61 | ty: sys::SessionCreateInfo::TYPE, 62 | next: &binding as *const _ as *const _, 63 | create_flags: Default::default(), 64 | system_id: system, 65 | }; 66 | let mut out = sys::Session::NULL; 67 | cvt((instance.fp().create_session)( 68 | instance.as_raw(), 69 | &info, 70 | &mut out, 71 | ))?; 72 | Ok(out) 73 | } 74 | } 75 | 76 | fn enumerate_swapchain_images( 77 | swapchain: &Swapchain, 78 | ) -> Result> { 79 | let images = get_arr_init( 80 | sys::SwapchainImageVulkanKHR { 81 | ty: sys::SwapchainImageVulkanKHR::TYPE, 82 | next: ptr::null_mut(), 83 | image: 0, 84 | }, 85 | |capacity, count, buf| unsafe { 86 | (swapchain.instance().fp().enumerate_swapchain_images)( 87 | swapchain.as_raw(), 88 | capacity, 89 | count, 90 | buf as *mut _, 91 | ) 92 | }, 93 | )?; 94 | Ok(images.into_iter().map(|x| x.image as _).collect()) 95 | } 96 | } 97 | 98 | #[derive(Debug, Copy, Clone)] 99 | pub struct Requirements { 100 | pub min_api_version_supported: Version, 101 | pub max_api_version_supported: Version, 102 | } 103 | 104 | #[derive(Copy, Clone)] 105 | pub struct SessionCreateInfo { 106 | pub instance: VkInstance, 107 | pub physical_device: VkPhysicalDevice, 108 | pub device: VkDevice, 109 | pub queue_family_index: u32, 110 | pub queue_index: u32, 111 | } 112 | -------------------------------------------------------------------------------- /openxr/src/swapchain.rs: -------------------------------------------------------------------------------- 1 | use std::{ffi::CString, marker::PhantomData, ptr}; 2 | 3 | use crate::sys::Handle as _; 4 | use crate::*; 5 | 6 | /// A set of images to be rendered to using a particular graphics API `G` 7 | pub struct Swapchain { 8 | session: Session, 9 | handle: sys::Swapchain, 10 | _marker: PhantomData, 11 | /// Whether `wait_image` was called more recently than `release_image` 12 | waited: bool, 13 | } 14 | 15 | impl Swapchain { 16 | /// Take ownership of an existing swapchain handle 17 | /// 18 | /// # Safety 19 | /// 20 | /// `handle` must be a valid swapchain handle associated with `session` and created with `flags`. 21 | #[inline] 22 | pub unsafe fn from_raw(session: Session, handle: sys::Swapchain) -> Self { 23 | Self { 24 | session, 25 | handle, 26 | _marker: PhantomData, 27 | waited: false, 28 | } 29 | } 30 | 31 | /// Access the raw swapchain handle 32 | #[inline] 33 | pub fn as_raw(&self) -> sys::Swapchain { 34 | self.handle 35 | } 36 | 37 | /// Access the `Instance` self is descended from 38 | #[inline] 39 | pub fn instance(&self) -> &Instance { 40 | self.session.instance() 41 | } 42 | 43 | /// Set the debug name of this `Swapchain`, if `XR_EXT_debug_utils` is loaded 44 | #[inline] 45 | pub fn set_name(&mut self, name: &str) -> Result<()> { 46 | // We don't forward to the locking version on Instance because this object can't be cloned 47 | if let Some(fp) = self.instance().exts().ext_debug_utils.as_ref() { 48 | let name = CString::new(name).unwrap(); 49 | let info = sys::DebugUtilsObjectNameInfoEXT { 50 | ty: sys::DebugUtilsObjectNameInfoEXT::TYPE, 51 | next: ptr::null(), 52 | object_type: ObjectType::SWAPCHAIN, 53 | object_handle: self.as_raw().into_raw(), 54 | object_name: name.as_ptr(), 55 | }; 56 | unsafe { 57 | cvt((fp.set_debug_utils_object_name)( 58 | self.instance().as_raw(), 59 | &info, 60 | ))?; 61 | } 62 | } 63 | Ok(()) 64 | } 65 | 66 | #[inline] 67 | pub fn enumerate_images(&self) -> Result> { 68 | G::enumerate_swapchain_images(self) 69 | } 70 | 71 | /// Determine the index of the next image to render to in the swapchain image array 72 | #[inline] 73 | pub fn acquire_image(&mut self) -> Result { 74 | let mut out = 0; 75 | unsafe { 76 | cvt((self.fp().acquire_swapchain_image)( 77 | self.as_raw(), 78 | ptr::null(), 79 | &mut out, 80 | ))?; 81 | } 82 | Ok(out) 83 | } 84 | 85 | /// Wait for the compositor to finish reading from the oldest unwaited acquired image 86 | #[inline] 87 | pub fn wait_image(&mut self, timeout: Duration) -> Result<()> { 88 | assert!( 89 | !self.waited, 90 | "release_image must be called before wait_image can be called again" 91 | ); 92 | let info = sys::SwapchainImageWaitInfo { 93 | ty: sys::SwapchainImageWaitInfo::TYPE, 94 | next: ptr::null_mut(), 95 | timeout, 96 | }; 97 | unsafe { 98 | cvt((self.fp().wait_swapchain_image)(self.as_raw(), &info))?; 99 | } 100 | self.waited = true; 101 | Ok(()) 102 | } 103 | 104 | /// Release the oldest acquired image 105 | #[inline] 106 | pub fn release_image(&mut self) -> Result<()> { 107 | assert!( 108 | self.waited, 109 | "wait_image must be called before release_image" 110 | ); 111 | unsafe { 112 | cvt((self.fp().release_swapchain_image)( 113 | self.as_raw(), 114 | ptr::null(), 115 | ))?; 116 | } 117 | self.waited = false; 118 | Ok(()) 119 | } 120 | 121 | // Private helper 122 | #[inline] 123 | fn fp(&self) -> &raw::Instance { 124 | self.session.instance().fp() 125 | } 126 | } 127 | 128 | impl AsHandle for Swapchain { 129 | type Handle = sys::Swapchain; 130 | fn as_handle(&self) -> Self::Handle { 131 | self.handle 132 | } 133 | } 134 | 135 | impl Drop for Swapchain { 136 | fn drop(&mut self) { 137 | unsafe { 138 | (self.fp().destroy_swapchain)(self.as_raw()); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /sys/src/loader.rs: -------------------------------------------------------------------------------- 1 | use crate::Version; 2 | use crate::generated::*; 3 | use crate::support::fmt_enum; 4 | 5 | /// Function pointer prototype for the xrCreateApiLayerInstance function used in place of xrCreateInstance. 6 | /// This function allows us to pass special API layer information to each layer during the process of creating an Instance. 7 | pub type FnCreateApiLayerInstance = unsafe extern "system" fn( 8 | info: *const InstanceCreateInfo, 9 | api_layer_info: *const ApiLayerCreateInfo, 10 | instance: *mut Instance, 11 | ) -> Result; 12 | 13 | /// Loader/API Layer Interface versions 14 | /// 15 | /// 1 - First version, introduces negotiation structure and functions 16 | pub const CURRENT_LOADER_API_LAYER_VERSION: u32 = 1; 17 | 18 | /// Loader/Runtime Interface versions 19 | /// 20 | /// 1 - First version, introduces negotiation structure and functions 21 | pub const CURRENT_LOADER_RUNTIME_VERSION: u32 = 1; 22 | 23 | #[repr(transparent)] 24 | #[derive(Copy, Clone, Eq, PartialEq)] 25 | pub struct LoaderInterfaceStructureType(i32); 26 | impl LoaderInterfaceStructureType { 27 | pub const UNINTIALIZED: Self = Self(0); 28 | pub const LOADER_INFO: Self = Self(1); 29 | pub const API_LAYER_REQUEST: Self = Self(2); 30 | pub const RUNTIME_REQUEST: Self = Self(3); 31 | pub const API_LAYER_CREATE_INFO: Self = Self(4); 32 | pub const API_LAYER_NEXT_INFO: Self = Self(5); 33 | } 34 | 35 | impl core::fmt::Debug for LoaderInterfaceStructureType { 36 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 37 | let name = match *self { 38 | Self::UNINTIALIZED => Some("UNINTIALIZED"), 39 | Self::LOADER_INFO => Some("LOADER_INFO"), 40 | Self::API_LAYER_REQUEST => Some("API_LAYER_REQUEST"), 41 | Self::RUNTIME_REQUEST => Some("RUNTIME_REQUEST"), 42 | Self::API_LAYER_CREATE_INFO => Some("API_LAYER_CREATE_INFO"), 43 | Self::API_LAYER_NEXT_INFO => Some("API_LAYER_NEXT_INFO"), 44 | _ => None, 45 | }; 46 | fmt_enum(fmt, self.0, name) 47 | } 48 | } 49 | 50 | #[repr(C)] 51 | #[derive(Copy, Clone, Debug)] 52 | pub struct XrNegotiateLoaderInfo { 53 | pub ty: LoaderInterfaceStructureType, 54 | pub struct_version: u32, 55 | pub struct_size: usize, 56 | pub min_interface_version: u32, 57 | pub max_interface_version: u32, 58 | pub min_api_version: Version, 59 | pub max_api_version: Version, 60 | } 61 | impl XrNegotiateLoaderInfo { 62 | pub const TYPE: LoaderInterfaceStructureType = LoaderInterfaceStructureType::LOADER_INFO; 63 | pub const VERSION: u32 = 1; 64 | } 65 | 66 | #[repr(C)] 67 | #[derive(Copy, Clone)] 68 | pub struct XrNegotiateApiLayerRequest { 69 | pub ty: LoaderInterfaceStructureType, 70 | pub struct_version: u32, 71 | pub struct_size: usize, 72 | pub layer_interface_version: u32, 73 | pub layer_api_version: Version, 74 | pub get_instance_proc_addr: Option, 75 | pub create_api_layer_instance: Option, 76 | } 77 | impl XrNegotiateApiLayerRequest { 78 | pub const TYPE: LoaderInterfaceStructureType = LoaderInterfaceStructureType::API_LAYER_REQUEST; 79 | pub const VERSION: u32 = 1; 80 | } 81 | 82 | #[repr(C)] 83 | #[derive(Copy, Clone)] 84 | pub struct XrNegotiateRuntimeRequest { 85 | pub ty: LoaderInterfaceStructureType, 86 | pub struct_version: u32, 87 | pub struct_size: usize, 88 | pub runtime_interface_version: u32, 89 | pub runtime_api_version: Version, 90 | pub get_instance_proc_addr: Option, 91 | } 92 | impl XrNegotiateRuntimeRequest { 93 | pub const TYPE: LoaderInterfaceStructureType = LoaderInterfaceStructureType::RUNTIME_REQUEST; 94 | pub const VERSION: u32 = 1; 95 | } 96 | 97 | /// Function used to negotiate an interface between the loader and an API layer. Each library exposing one or 98 | /// more API layers needs to expose at least this function. 99 | pub type FnNegotiateLoaderApiLayerInterface = unsafe extern "system" fn( 100 | loader_info: *const XrNegotiateLoaderInfo, 101 | api_layer_name: *const i8, 102 | api_layer_request: *mut XrNegotiateApiLayerRequest, 103 | ) -> Result; 104 | 105 | /// Function used to negotiate an interface between the loader and a runtime. Each runtime should expose 106 | /// at least this function. 107 | pub type FnNegotiateLoaderRuntimeInterface = unsafe extern "system" fn( 108 | loader_info: *const XrNegotiateLoaderInfo, 109 | runtime_request: *mut XrNegotiateRuntimeRequest, 110 | ) -> Result; 111 | 112 | #[repr(C)] 113 | #[derive(Copy, Clone)] 114 | pub struct XrApiLayerNextInfo { 115 | pub ty: LoaderInterfaceStructureType, 116 | pub struct_version: u32, 117 | pub struct_size: usize, 118 | /// Name of API layer which should receive this info 119 | pub layer_name: [i8; MAX_API_LAYER_NAME_SIZE], 120 | /// Pointer to next API layer's xrGetInstanceProcAddr 121 | pub next_get_instance_proc_addr: pfn::GetInstanceProcAddr, 122 | /// Pointer to next API layer's xrCreateApiLayerInstance 123 | pub next_create_api_layer_instance: FnCreateApiLayerInstance, 124 | /// Pointer to the next API layer info in the sequence 125 | pub next: *mut XrApiLayerNextInfo, 126 | } 127 | impl XrApiLayerNextInfo { 128 | pub const TYPE: LoaderInterfaceStructureType = 129 | LoaderInterfaceStructureType::API_LAYER_NEXT_INFO; 130 | pub const VERSION: u32 = 1; 131 | } 132 | 133 | pub const API_LAYER_MAX_SETTINGS_PATH_SIZE: usize = 512; 134 | 135 | #[repr(C)] 136 | #[derive(Copy, Clone, Debug)] 137 | pub struct ApiLayerCreateInfo { 138 | pub ty: LoaderInterfaceStructureType, 139 | pub struct_version: u32, 140 | pub struct_size: usize, 141 | /// Pointer to the LoaderInstance class 142 | pub loader_instance: *const (), 143 | /// Location to the found settings file (or empty '\0') 144 | pub settings_file_location: [i8; API_LAYER_MAX_SETTINGS_PATH_SIZE], 145 | /// Pointer to the next API layer's Info 146 | pub next_info: *mut XrApiLayerNextInfo, 147 | } 148 | impl ApiLayerCreateInfo { 149 | pub const TYPE: LoaderInterfaceStructureType = 150 | LoaderInterfaceStructureType::API_LAYER_CREATE_INFO; 151 | pub const VERSION: u32 = 1; 152 | } 153 | -------------------------------------------------------------------------------- /openxr/src/graphics/opengl.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use sys::platform::*; 4 | 5 | use crate::sys::Handle as _; 6 | use crate::*; 7 | 8 | /// The OpenGL graphics API 9 | /// 10 | /// See [`XR_KHR_opengl_enable`] for safety details. 11 | /// 12 | /// [`XR_KHR_opengl_enable`]: https://www.khronos.org/registry/OpenXR/specs/1.1/html/xrspec.html#XR_KHR_opengl_enable 13 | pub enum OpenGL {} 14 | 15 | impl Graphics for OpenGL { 16 | type Requirements = Requirements; 17 | type SessionCreateInfo = SessionCreateInfo; 18 | type Format = u32; 19 | type SwapchainImage = u32; 20 | 21 | fn raise_format(x: i64) -> u32 { 22 | x as _ 23 | } 24 | fn lower_format(x: u32) -> i64 { 25 | x.into() 26 | } 27 | 28 | fn requirements(inst: &Instance, system: SystemId) -> Result { 29 | let out = unsafe { 30 | let mut x = sys::GraphicsRequirementsOpenGLKHR::out(ptr::null_mut()); 31 | cvt((inst.opengl().get_open_gl_graphics_requirements)( 32 | inst.as_raw(), 33 | system, 34 | x.as_mut_ptr(), 35 | ))?; 36 | x.assume_init() 37 | }; 38 | Ok(Requirements { 39 | min_api_version_supported: out.min_api_version_supported, 40 | max_api_version_supported: out.max_api_version_supported, 41 | }) 42 | } 43 | 44 | unsafe fn create_session( 45 | instance: &Instance, 46 | system: SystemId, 47 | info: &Self::SessionCreateInfo, 48 | ) -> Result { 49 | unsafe { 50 | match *info { 51 | #[cfg(windows)] 52 | SessionCreateInfo::Windows { h_dc, h_glrc } => { 53 | let binding = sys::GraphicsBindingOpenGLWin32KHR { 54 | ty: sys::GraphicsBindingOpenGLWin32KHR::TYPE, 55 | next: ptr::null(), 56 | h_dc, 57 | h_glrc, 58 | }; 59 | let info = sys::SessionCreateInfo { 60 | ty: sys::SessionCreateInfo::TYPE, 61 | next: &binding as *const _ as *const _, 62 | create_flags: Default::default(), 63 | system_id: system, 64 | }; 65 | let mut out = sys::Session::NULL; 66 | cvt((instance.fp().create_session)( 67 | instance.as_raw(), 68 | &info, 69 | &mut out, 70 | ))?; 71 | Ok(out) 72 | } 73 | SessionCreateInfo::Xlib { 74 | x_display, 75 | visualid, 76 | glx_fb_config, 77 | glx_drawable, 78 | glx_context, 79 | } => { 80 | let binding = sys::GraphicsBindingOpenGLXlibKHR { 81 | ty: sys::GraphicsBindingOpenGLXlibKHR::TYPE, 82 | next: ptr::null(), 83 | x_display, 84 | visualid, 85 | glx_fb_config, 86 | glx_drawable, 87 | glx_context, 88 | }; 89 | let info = sys::SessionCreateInfo { 90 | ty: sys::SessionCreateInfo::TYPE, 91 | next: &binding as *const _ as *const _, 92 | create_flags: Default::default(), 93 | system_id: system, 94 | }; 95 | let mut out = sys::Session::NULL; 96 | cvt((instance.fp().create_session)( 97 | instance.as_raw(), 98 | &info, 99 | &mut out, 100 | ))?; 101 | Ok(out) 102 | } 103 | SessionCreateInfo::Wayland { display } => { 104 | let binding = sys::GraphicsBindingOpenGLWaylandKHR { 105 | ty: sys::GraphicsBindingOpenGLWaylandKHR::TYPE, 106 | next: ptr::null(), 107 | display, 108 | }; 109 | let info = sys::SessionCreateInfo { 110 | ty: sys::SessionCreateInfo::TYPE, 111 | next: &binding as *const _ as *const _, 112 | create_flags: Default::default(), 113 | system_id: system, 114 | }; 115 | let mut out = sys::Session::NULL; 116 | cvt((instance.fp().create_session)( 117 | instance.as_raw(), 118 | &info, 119 | &mut out, 120 | ))?; 121 | Ok(out) 122 | } 123 | } 124 | } 125 | } 126 | 127 | fn enumerate_swapchain_images( 128 | swapchain: &Swapchain, 129 | ) -> Result> { 130 | let images = get_arr_init( 131 | sys::SwapchainImageOpenGLKHR { 132 | ty: sys::SwapchainImageOpenGLKHR::TYPE, 133 | next: ptr::null_mut(), 134 | image: 0, 135 | }, 136 | |capacity, count, buf| unsafe { 137 | (swapchain.instance().fp().enumerate_swapchain_images)( 138 | swapchain.as_raw(), 139 | capacity, 140 | count, 141 | buf as *mut _, 142 | ) 143 | }, 144 | )?; 145 | Ok(images.into_iter().map(|x| x.image).collect()) 146 | } 147 | } 148 | 149 | #[derive(Copy, Clone)] 150 | pub struct Requirements { 151 | pub min_api_version_supported: Version, 152 | pub max_api_version_supported: Version, 153 | } 154 | 155 | pub enum SessionCreateInfo { 156 | Xlib { 157 | x_display: *mut Display, 158 | visualid: u32, 159 | glx_fb_config: GLXFBConfig, 160 | glx_drawable: GLXDrawable, 161 | glx_context: GLXContext, 162 | }, 163 | Wayland { 164 | display: *mut wl_display, 165 | }, 166 | #[cfg(windows)] 167 | Windows { 168 | h_dc: HDC, 169 | h_glrc: HGLRC, 170 | }, 171 | } 172 | -------------------------------------------------------------------------------- /openxr/src/graphics/d3d.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use sys::{Handle as _, platform::*}; 4 | 5 | use crate::*; 6 | 7 | /// The D3D11 graphics API 8 | /// 9 | /// See [`XR_KHR_d3d11_enable`] for safety details. 10 | /// 11 | /// [`XR_KHR_d3d_enable`]: https://www.khronos.org/registry/OpenXR/specs/1.1/html/xrspec.html#XR_KHR_D3D11_enable 12 | pub enum D3D11 {} 13 | 14 | impl Graphics for D3D11 { 15 | type Requirements = Requirements; 16 | type SessionCreateInfo = SessionCreateInfoD3D11; 17 | type Format = u32; 18 | type SwapchainImage = *mut ID3D11Texture2D; 19 | 20 | fn raise_format(x: i64) -> u32 { 21 | x as _ 22 | } 23 | fn lower_format(x: u32) -> i64 { 24 | x.into() 25 | } 26 | 27 | fn requirements(inst: &Instance, system: SystemId) -> Result { 28 | let out = unsafe { 29 | let mut x = sys::GraphicsRequirementsD3D11KHR::out(ptr::null_mut()); 30 | cvt((inst.d3d11().get_d3d11_graphics_requirements)( 31 | inst.as_raw(), 32 | system, 33 | x.as_mut_ptr(), 34 | ))?; 35 | x.assume_init() 36 | }; 37 | Ok(Requirements { 38 | adapter_luid: out.adapter_luid, 39 | min_feature_level: out.min_feature_level, 40 | }) 41 | } 42 | 43 | unsafe fn create_session( 44 | instance: &Instance, 45 | system: SystemId, 46 | info: &Self::SessionCreateInfo, 47 | ) -> Result { 48 | let binding = sys::GraphicsBindingD3D11KHR { 49 | ty: sys::GraphicsBindingD3D11KHR::TYPE, 50 | next: ptr::null(), 51 | device: info.device, 52 | }; 53 | let info = sys::SessionCreateInfo { 54 | ty: sys::SessionCreateInfo::TYPE, 55 | next: &binding as *const _ as *const _, 56 | create_flags: Default::default(), 57 | system_id: system, 58 | }; 59 | let mut out = sys::Session::NULL; 60 | unsafe { 61 | cvt((instance.fp().create_session)( 62 | instance.as_raw(), 63 | &info, 64 | &mut out, 65 | ))?; 66 | } 67 | Ok(out) 68 | } 69 | 70 | fn enumerate_swapchain_images( 71 | swapchain: &Swapchain, 72 | ) -> Result> { 73 | let images = get_arr_init( 74 | sys::SwapchainImageD3D11KHR { 75 | ty: sys::SwapchainImageD3D11KHR::TYPE, 76 | next: ptr::null_mut(), 77 | texture: ptr::null_mut(), 78 | }, 79 | |capacity, count, buf| unsafe { 80 | (swapchain.instance().fp().enumerate_swapchain_images)( 81 | swapchain.as_raw(), 82 | capacity, 83 | count, 84 | buf as *mut _, 85 | ) 86 | }, 87 | )?; 88 | Ok(images.into_iter().map(|x| x.texture).collect()) 89 | } 90 | } 91 | 92 | /// The D3D12 graphics API 93 | /// 94 | /// See [`XR_KHR_d3d12_enable`] for safety details. 95 | /// 96 | /// [`XR_KHR_d3d_enable`]: https://www.khronos.org/registry/OpenXR/specs/1.1/html/xrspec.html#XR_KHR_D3D12_enable 97 | pub enum D3D12 {} 98 | 99 | impl Graphics for D3D12 { 100 | type Requirements = Requirements; 101 | type SessionCreateInfo = SessionCreateInfoD3D12; 102 | type Format = u32; 103 | type SwapchainImage = *mut ID3D12Resource; 104 | 105 | fn raise_format(x: i64) -> u32 { 106 | x as _ 107 | } 108 | fn lower_format(x: u32) -> i64 { 109 | x.into() 110 | } 111 | 112 | fn requirements(inst: &Instance, system: SystemId) -> Result { 113 | let out = unsafe { 114 | let mut x = sys::GraphicsRequirementsD3D12KHR::out(ptr::null_mut()); 115 | cvt((inst.d3d12().get_d3d12_graphics_requirements)( 116 | inst.as_raw(), 117 | system, 118 | x.as_mut_ptr(), 119 | ))?; 120 | x.assume_init() 121 | }; 122 | Ok(Requirements { 123 | adapter_luid: out.adapter_luid, 124 | min_feature_level: out.min_feature_level, 125 | }) 126 | } 127 | 128 | unsafe fn create_session( 129 | instance: &Instance, 130 | system: SystemId, 131 | info: &Self::SessionCreateInfo, 132 | ) -> Result { 133 | let binding = sys::GraphicsBindingD3D12KHR { 134 | ty: sys::GraphicsBindingD3D12KHR::TYPE, 135 | next: ptr::null(), 136 | device: info.device, 137 | queue: info.queue, 138 | }; 139 | let info = sys::SessionCreateInfo { 140 | ty: sys::SessionCreateInfo::TYPE, 141 | next: &binding as *const _ as *const _, 142 | create_flags: Default::default(), 143 | system_id: system, 144 | }; 145 | let mut out = sys::Session::NULL; 146 | unsafe { 147 | cvt((instance.fp().create_session)( 148 | instance.as_raw(), 149 | &info, 150 | &mut out, 151 | ))?; 152 | } 153 | Ok(out) 154 | } 155 | 156 | fn enumerate_swapchain_images( 157 | swapchain: &Swapchain, 158 | ) -> Result> { 159 | let images = get_arr_init( 160 | sys::SwapchainImageD3D12KHR { 161 | ty: sys::SwapchainImageD3D12KHR::TYPE, 162 | next: ptr::null_mut(), 163 | texture: ptr::null_mut(), 164 | }, 165 | |capacity, count, buf| unsafe { 166 | (swapchain.instance().fp().enumerate_swapchain_images)( 167 | swapchain.as_raw(), 168 | capacity, 169 | count, 170 | buf as *mut _, 171 | ) 172 | }, 173 | )?; 174 | Ok(images.into_iter().map(|x| x.texture).collect()) 175 | } 176 | } 177 | 178 | #[derive(Copy, Clone)] 179 | pub struct Requirements { 180 | pub adapter_luid: LUID, 181 | pub min_feature_level: D3D_FEATURE_LEVEL, 182 | } 183 | 184 | #[derive(Copy, Clone)] 185 | pub struct SessionCreateInfoD3D11 { 186 | pub device: *mut ID3D11Device, 187 | } 188 | 189 | #[derive(Copy, Clone)] 190 | pub struct SessionCreateInfoD3D12 { 191 | pub device: *mut ID3D12Device, 192 | pub queue: *mut ID3D12CommandQueue, 193 | } 194 | -------------------------------------------------------------------------------- /openxr/src/passthrough.rs: -------------------------------------------------------------------------------- 1 | //! The [Passthrough feature](https://support.oculus.com/articles/in-vr-experiences/oculus-features/what-is-passthrough/) 2 | //! allows you to step outside your view in VR to see a real-time view of your surroundings. 3 | //! 4 | //! This feature is exclusive to the Oculus Quest 2 as of March 2022. 5 | //! 6 | //! More details about passthrough can be found in the [Oculus Native SDK documentation](https://developer.oculus.com/documentation/native/android/mobile-passthrough/) 7 | //! as well as in the [OpenXR specification](https://www.khronos.org/registry/OpenXR/specs/1.1/html/xrspec.html#XrPassthroughFB) 8 | //! 9 | //! Not all features are currently implemented. Execution control features are implemented, style-oriented features are not. 10 | use crate::{ 11 | AsHandle, PassthroughFlagsFB, PassthroughLayerPurposeFB, Result, Session, SessionInner, cvt, 12 | raw, session, sys, sys::Handle as _, 13 | }; 14 | use std::ptr; 15 | use std::sync::Arc; 16 | 17 | /// A [passthrough feature]. 18 | /// 19 | /// Requires [`XR_FB_passthrough`] to be enabled. 20 | /// 21 | /// See the [`PassthroughFB struct`]. 22 | /// 23 | /// [`XR_FB_passthrough`]: https://www.khronos.org/registry/OpenXR/specs/1.1/html/xrspec.html#XR_FB_passthrough 24 | /// [`PassthroughFB struct`]: https://docs.rs/openxr-sys/latest/openxr_sys/struct.PassthroughFB.html 25 | /// [passthrough feature]: https://developer.oculus.com/documentation/native/android/mobile-passthrough/#create-and-start-a-passthrough-feature 26 | pub struct Passthrough { 27 | pub(crate) session: Arc, 28 | handle: sys::PassthroughFB, 29 | } 30 | 31 | impl Passthrough { 32 | /// [Create](https://www.khronos.org/registry/OpenXR/specs/1.0/man/html/openxr.html#xrCreatePassthroughFB) 33 | /// a passthrough feature. 34 | pub(crate) fn create(session: &Session, flags: PassthroughFlagsFB) -> Result { 35 | let info = sys::PassthroughCreateInfoFB { 36 | ty: sys::PassthroughCreateInfoFB::TYPE, 37 | next: ptr::null(), 38 | flags, 39 | }; 40 | let fp = fp(&session.inner); 41 | let mut handle = sys::PassthroughFB::NULL; 42 | unsafe { 43 | cvt((fp.create_passthrough)( 44 | session.as_raw(), 45 | &info, 46 | &mut handle, 47 | ))?; 48 | } 49 | Ok(Passthrough { 50 | session: session.inner.clone(), 51 | handle, 52 | }) 53 | } 54 | 55 | /// [Start](https://www.khronos.org/registry/OpenXR/specs/1.0/man/html/openxr.html#_xrpassthroughstartfb3) 56 | /// a passthrough feature. 57 | pub fn start(&self) -> Result<()> { 58 | let fp = fp(&self.session); 59 | unsafe { 60 | cvt((fp.passthrough_pause)(self.handle))?; 61 | } 62 | Ok(()) 63 | } 64 | 65 | /// [Pause](https://www.khronos.org/registry/OpenXR/specs/1.0/man/html/openxr.html#_xrpassthroughstartfb3) 66 | /// a passthrough feature. 67 | pub fn pause(&self) -> Result<()> { 68 | let fp = fp(&self.session); 69 | unsafe { 70 | cvt((fp.passthrough_pause)(self.handle))?; 71 | } 72 | Ok(()) 73 | } 74 | } 75 | 76 | impl Drop for Passthrough { 77 | fn drop(&mut self) { 78 | unsafe { 79 | (fp(&self.session).destroy_passthrough)(self.handle); 80 | } 81 | } 82 | } 83 | 84 | /// A [passthrough layer]. 85 | /// 86 | /// Requires [`XR_FB_passthrough`]. 87 | /// 88 | /// [`XR_FB_passthrough`]: https://www.khronos.org/registry/OpenXR/specs/1.1/html/xrspec.html#XR_FB_passthrough 89 | /// [passthrough layer]: https://developer.oculus.com/documentation/native/android/mobile-passthrough/#create-and-start-a-passthrough-layer 90 | pub struct PassthroughLayerFB { 91 | pub(crate) session: Arc, 92 | handle: sys::PassthroughLayerFB, 93 | } 94 | 95 | impl PassthroughLayerFB { 96 | /// [Create](https://www.khronos.org/registry/OpenXR/specs/1.0/man/html/openxr.html#xrCreatePassthroughLayerFB) 97 | /// a passthrough layer. 98 | pub(crate) fn create( 99 | session: &Session, 100 | passthrough: &Passthrough, 101 | flags: PassthroughFlagsFB, 102 | purpose: PassthroughLayerPurposeFB, 103 | ) -> Result { 104 | let info = sys::PassthroughLayerCreateInfoFB { 105 | ty: sys::PassthroughLayerCreateInfoFB::TYPE, 106 | next: ptr::null(), 107 | passthrough: passthrough.handle, 108 | flags, 109 | purpose, 110 | }; 111 | let fp = fp(&session.inner); 112 | let mut handle = sys::PassthroughLayerFB::NULL; 113 | unsafe { 114 | cvt((fp.create_passthrough_layer)( 115 | session.as_raw(), 116 | &info, 117 | &mut handle, 118 | ))?; 119 | } 120 | Ok(PassthroughLayerFB { 121 | session: session.inner.clone(), 122 | handle, 123 | }) 124 | } 125 | 126 | /// [Resume](https://www.khronos.org/registry/OpenXR/specs/1.0/man/html/openxr.html#_xrpassthroughlayerresumefb3) 127 | /// a passthrough layer. 128 | pub fn resume(&self) -> Result<()> { 129 | let fp = fp(&self.session); 130 | unsafe { 131 | cvt((fp.passthrough_layer_resume)(self.handle))?; 132 | } 133 | Ok(()) 134 | } 135 | 136 | /// [Pause](https://www.khronos.org/registry/OpenXR/specs/1.0/man/html/openxr.html#_xrpassthroughlayerpausefb3) 137 | /// a passthrough layer. 138 | pub fn pause(&self) -> Result<()> { 139 | let fp = fp(&self.session); 140 | unsafe { 141 | cvt((fp.passthrough_layer_pause)(self.handle))?; 142 | } 143 | Ok(()) 144 | } 145 | 146 | pub fn inner(&self) -> &sys::PassthroughLayerFB { 147 | &self.handle 148 | } 149 | 150 | pub fn as_raw(&self) -> sys::PassthroughLayerFB { 151 | self.handle 152 | } 153 | } 154 | 155 | impl AsHandle for PassthroughLayerFB { 156 | type Handle = sys::PassthroughLayerFB; 157 | fn as_handle(&self) -> Self::Handle { 158 | self.handle 159 | } 160 | } 161 | 162 | impl Drop for PassthroughLayerFB { 163 | fn drop(&mut self) { 164 | unsafe { 165 | (fp(&self.session).destroy_passthrough_layer)(self.handle); 166 | } 167 | } 168 | } 169 | 170 | #[inline] 171 | pub(crate) fn fp(session: &SessionInner) -> &raw::PassthroughFB { 172 | session 173 | .instance 174 | .exts() 175 | .fb_passthrough 176 | .as_ref() 177 | .expect("`XR_FB_passthrough` not loaded") 178 | } 179 | -------------------------------------------------------------------------------- /sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{convert::TryFrom, fmt}; 2 | 3 | #[macro_use] 4 | mod support; 5 | mod generated; 6 | pub mod loader; 7 | pub mod platform; 8 | 9 | #[cfg(feature = "mint")] 10 | mod mint_impls; 11 | 12 | pub trait Handle: Copy { 13 | const NULL: Self; 14 | fn into_raw(self) -> u64; 15 | fn from_raw(handle: u64) -> Self; 16 | } 17 | 18 | // Hand-written bindings for cases which are too few or weird to bother automating 19 | 20 | wrapper! { 21 | #[derive(Copy, Clone, Eq, PartialEq, Default)] 22 | Bool32(u32) 23 | } 24 | pub const TRUE: Bool32 = Bool32(1); 25 | pub const FALSE: Bool32 = Bool32(0); 26 | impl fmt::Display for Bool32 { 27 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 28 | (*self != FALSE).fmt(fmt) 29 | } 30 | } 31 | 32 | impl fmt::Debug for Bool32 { 33 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 34 | (*self != FALSE).fmt(fmt) 35 | } 36 | } 37 | 38 | impl From for bool { 39 | fn from(x: Bool32) -> Self { 40 | x != FALSE 41 | } 42 | } 43 | 44 | impl From for Bool32 { 45 | fn from(x: bool) -> Self { 46 | Self(x as _) 47 | } 48 | } 49 | 50 | #[derive(Copy, Clone, Eq, PartialEq)] 51 | #[repr(transparent)] 52 | pub struct Time(i64); 53 | impl Time { 54 | pub fn from_nanos(x: i64) -> Self { 55 | Self(x) 56 | } 57 | 58 | pub fn as_nanos(self) -> i64 { 59 | self.0 60 | } 61 | } 62 | 63 | impl fmt::Debug for Time { 64 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 65 | self.0.fmt(fmt) 66 | } 67 | } 68 | 69 | impl std::ops::Sub