From 5ba2238571ce374d9e0197eab42e9f090c8387e1 Mon Sep 17 00:00:00 2001 From: Maximilian Hausen Date: Tue, 2 Jun 2026 00:52:13 +0200 Subject: [PATCH 1/2] Allow passing a proper activity pointer to the android loader Extends 613af27f29b75579d7dc1b00f5769ef39567f5f8 to also use the provided activity pointer for loader initialization. This was necessary to get android-activity 0.6.1 running on my quest 3. --- openxr/examples/hello.rs | 16 +++---- openxr/examples/vulkan.rs | 16 +++---- openxr/src/entry.rs | 91 +++++++++++++++++++++++---------------- 3 files changed, 69 insertions(+), 54 deletions(-) diff --git a/openxr/examples/hello.rs b/openxr/examples/hello.rs index c42c209..578adc9 100644 --- a/openxr/examples/hello.rs +++ b/openxr/examples/hello.rs @@ -2,11 +2,16 @@ use openxr as xr; #[cfg_attr(target_os = "android", ndk_glue::main)] fn main() { + #[cfg(not(target_os = "android"))] + let platform_info = (); + #[cfg(target_os = "android")] + let platform_info = + unsafe { openxr::AndroidPlatformInfo::new(ndk_glue::native_activity().activity().cast()) }; #[cfg(feature = "linked")] - let entry = xr::Entry::linked().unwrap(); + let entry = xr::Entry::linked(&platform_info).unwrap(); #[cfg(not(feature = "linked"))] let entry = unsafe { - xr::Entry::load() + xr::Entry::load(&platform_info) .expect("couldn't find the OpenXR loader; try enabling the \"static\" feature") }; @@ -33,12 +38,7 @@ fn main() { }, &xr::ExtensionSet::default(), &[], - #[cfg(not(target_os = "android"))] - (), - #[cfg(target_os = "android")] - unsafe { - openxr::AndroidPlatformInfo::new(ndk_glue::native_activity().activity().cast()) - }, + &platform_info, ) .unwrap(); let instance_props = instance.properties().unwrap(); diff --git a/openxr/examples/vulkan.rs b/openxr/examples/vulkan.rs index 63ae7f7..cab6366 100644 --- a/openxr/examples/vulkan.rs +++ b/openxr/examples/vulkan.rs @@ -31,11 +31,16 @@ pub fn main() { }) .expect("setting Ctrl-C handler"); + #[cfg(not(target_os = "android"))] + let platform_info = (); + #[cfg(target_os = "android")] + let platform_info = + unsafe { openxr::AndroidPlatformInfo::new(ndk_glue::native_activity().activity().cast()) }; #[cfg(feature = "static")] - let entry = xr::Entry::linked().unwrap(); + let entry = xr::Entry::linked(&platform_info).unwrap(); #[cfg(not(feature = "static"))] let entry = unsafe { - xr::Entry::load() + xr::Entry::load(&platform_info) .expect("couldn't find the OpenXR loader; try enabling the \"static\" feature") }; @@ -69,12 +74,7 @@ pub fn main() { }, &enabled_extensions, &[], - #[cfg(not(target_os = "android"))] - (), - #[cfg(target_os = "android")] - unsafe { - openxr::AndroidPlatformInfo::new(ndk_glue::native_activity().activity().cast()) - }, + &platform_info, ) .unwrap(); let instance_props = xr_instance.properties().unwrap(); diff --git a/openxr/src/entry.rs b/openxr/src/entry.rs index f1272ca..56e933d 100644 --- a/openxr/src/entry.rs +++ b/openxr/src/entry.rs @@ -29,8 +29,11 @@ impl Entry { /// Available if the `linked` feature is enabled. You must ensure that the entry points are /// actually linked into the binary, e.g. by enabling the `static` feature or manually linking /// to an external loader or implementation. + /// + /// Needs an [`AndroidPlatformInfo`] to correctly initialize the loader on Android. + /// Other platforms can pass `()`. #[cfg(feature = "linked")] - pub fn linked() -> Result { + pub fn linked(platform_info: &impl PlatformInfo) -> Result { let entry = Self { inner: Arc::new(Inner { raw: RawEntry { @@ -44,8 +47,7 @@ impl Entry { _lib_guard: None, }), }; - #[cfg(target_os = "android")] - entry.initialize_android_loader()?; + platform_info.init_loader(&entry)?; Ok(entry) } @@ -54,12 +56,15 @@ impl Entry { /// /// Available if the `loaded` feature is enabled. /// + /// Needs an [`AndroidPlatformInfo`] to correctly initialize the loader on Android. + /// Other platforms can pass `()`. + /// /// # Safety /// /// The OpenXR loader shared library in the dynamic loader's search path must conform to the /// OpenXR specification. #[cfg(feature = "loaded")] - pub unsafe fn load() -> std::result::Result { + pub unsafe fn load(platform_info: &impl PlatformInfo) -> std::result::Result { let entry = unsafe { #[cfg(target_os = "windows")] const PATH: &str = "openxr_loader.dll"; @@ -67,10 +72,9 @@ impl Entry { const PATH: &str = "libopenxr_loader.dylib"; #[cfg(not(any(target_os = "windows", target_os = "macos")))] const PATH: &str = "libopenxr_loader.so"; - Self::load_from(Path::new(PATH)) + Self::load_from(Path::new(PATH), platform_info) }?; - #[cfg(target_os = "android")] - entry.initialize_android_loader().map_err(EntryError::Xr)?; + platform_info.init_loader(&entry).map_err(EntryError::Xr)?; Ok(entry) } @@ -78,12 +82,18 @@ impl Entry { /// /// Available if the `loaded` feature is enabled. /// + /// Needs an [`AndroidPlatformInfo`] to correctly initialize the loader on Android. + /// Other platforms can pass `()`. + /// /// # Safety /// /// `path` must be a shared library that provides OpenXR-compliant definitions for every core /// OpenXR entry point. #[cfg(feature = "loaded")] - pub unsafe fn load_from(path: &Path) -> std::result::Result { + pub unsafe fn load_from( + path: &Path, + platform_info: &impl PlatformInfo, + ) -> std::result::Result { let entry = unsafe { let lib = Library::new(path).map_err(|err| EntryError::Load(LoadError(err)))?; Self { @@ -107,13 +117,15 @@ impl Entry { }), } }; - #[cfg(target_os = "android")] - entry.initialize_android_loader().map_err(EntryError::Xr)?; + platform_info.init_loader(&entry).map_err(EntryError::Xr)?; Ok(entry) } /// Load entry points using an arbitrary `xrGetInstanceProcAddr` implementation /// + /// Needs an [`AndroidPlatformInfo`] to correctly initialize the loader on Android. + /// Other platforms can pass `()`. + /// /// # Safety /// /// For all core OpenXR instance functions, `get_instance_proc_addr` must yield function @@ -121,6 +133,7 @@ impl Entry { #[allow(clippy::missing_transmute_annotations)] pub unsafe fn from_get_instance_proc_addr( get_instance_proc_addr: sys::pfn::GetInstanceProcAddr, + platform_info: &impl PlatformInfo, ) -> Result { let entry = unsafe { Self { @@ -152,8 +165,7 @@ impl Entry { }), } }; - #[cfg(target_os = "android")] - entry.initialize_android_loader()?; + platform_info.init_loader(&entry)?; Ok(entry) } @@ -172,31 +184,6 @@ impl Entry { unsafe { get_instance_proc_addr_helper(self.fp().get_instance_proc_addr, instance, name) } } - /// Initialize Android loader. This must be called before any other OpenXR call. - #[cfg(target_os = "android")] - fn initialize_android_loader(&self) -> Result<()> { - let loader_init = unsafe { raw::LoaderInitKHR::load(self, sys::Instance::NULL)? }; - - let context = ndk_context::android_context(); - - let loader_info = sys::LoaderInitInfoAndroidKHR { - ty: sys::LoaderInitInfoAndroidKHR::TYPE, - next: ptr::null(), - application_vm: context.vm(), - application_context: context.context(), - }; - - // The loader init extension doesn't forbid calling initialization multiple times, - // and the Khronos Loader implementation handles it correctly. - unsafe { - cvt((loader_init.initialize_loader)( - &loader_info as *const _ as _, - ))?; - } - - Ok(()) - } - /// Create an OpenXR instance with certain extensions enabled. /// /// On Android, pass [`AndroidPlatformInfo`] as `platform_info` @@ -210,7 +197,7 @@ impl Entry { app_info: &ApplicationInfo, required_extensions: &ExtensionSet, layers: &[&str], - platform_info: impl PlatformInfo, + platform_info: &impl PlatformInfo, ) -> Result { assert!( app_info.application_name.len() < sys::MAX_APPLICATION_NAME_SIZE, @@ -455,6 +442,11 @@ pub unsafe trait PlatformInfo { entry: &Entry, info: sys::InstanceCreateInfo, ) -> Result; + + /// Callback for platform-specific loader initialization. + fn init_loader(&self, _entry: &Entry) -> Result<()> { + Ok(()) + } } unsafe impl PlatformInfo for () { @@ -515,4 +507,27 @@ unsafe impl PlatformInfo for AndroidPlatformInfo { cvt(unsafe { (entry.fp().create_instance)(&info, &mut handle) })?; Ok(handle) } + + fn init_loader(&self, entry: &Entry) -> Result<()> { + let loader_init = unsafe { raw::LoaderInitKHR::load(entry, sys::Instance::NULL)? }; + + let context = ndk_context::android_context(); + + let loader_info = sys::LoaderInitInfoAndroidKHR { + ty: sys::LoaderInitInfoAndroidKHR::TYPE, + next: ptr::null(), + application_vm: context.vm(), + application_context: self.activity, + }; + + // The loader init extension doesn't forbid calling initialization multiple times, + // and the Khronos Loader implementation handles it correctly. + unsafe { + cvt((loader_init.initialize_loader)( + &loader_info as *const _ as _, + ))?; + } + + Ok(()) + } } From 340bfff28827554920c9a629dff5d73fb1b82a44 Mon Sep 17 00:00:00 2001 From: Maximilian Hausen Date: Wed, 3 Jun 2026 17:13:56 +0200 Subject: [PATCH 2/2] Remove the duplicated init_loader call from Entry::load() --- openxr/src/entry.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/openxr/src/entry.rs b/openxr/src/entry.rs index 56e933d..9e542b9 100644 --- a/openxr/src/entry.rs +++ b/openxr/src/entry.rs @@ -65,17 +65,14 @@ impl Entry { /// OpenXR specification. #[cfg(feature = "loaded")] pub unsafe fn load(platform_info: &impl PlatformInfo) -> std::result::Result { - let entry = unsafe { - #[cfg(target_os = "windows")] - const PATH: &str = "openxr_loader.dll"; - #[cfg(target_os = "macos")] - const PATH: &str = "libopenxr_loader.dylib"; - #[cfg(not(any(target_os = "windows", target_os = "macos")))] - const PATH: &str = "libopenxr_loader.so"; - Self::load_from(Path::new(PATH), platform_info) - }?; - platform_info.init_loader(&entry).map_err(EntryError::Xr)?; - Ok(entry) + #[cfg(target_os = "windows")] + const PATH: &str = "openxr_loader.dll"; + #[cfg(target_os = "macos")] + const PATH: &str = "libopenxr_loader.dylib"; + #[cfg(not(any(target_os = "windows", target_os = "macos")))] + const PATH: &str = "libopenxr_loader.so"; + + unsafe { Self::load_from(Path::new(PATH), platform_info) } } /// Load entry points at run time from the dynamic library identified by `path`