Browse Source

Merge pull request #65798 from kisg/dynamic_openxr_loader

Dynamic loading of OpenXR Loader on Android
Rémi Verschelde 2 years ago
parent
commit
3472bdd6b6

+ 33 - 28
modules/openxr/SCsub

@@ -18,38 +18,36 @@ env_openxr.Prepend(
         thirdparty_dir + "/src",
         thirdparty_dir + "/src",
         thirdparty_dir + "/src/common",
         thirdparty_dir + "/src/common",
         thirdparty_dir + "/src/external/jsoncpp/include",
         thirdparty_dir + "/src/external/jsoncpp/include",
-        thirdparty_dir + "/src/loader",
     ]
     ]
 )
 )
 
 
-# may need to check and set:
-# - XR_USE_TIMESPEC
-
-env_thirdparty = env_openxr.Clone()
-env_thirdparty.disable_warnings()
-env_thirdparty.AppendUnique(CPPDEFINES=["DISABLE_STD_FILESYSTEM"])
-
 if env["platform"] == "android":
 if env["platform"] == "android":
     # may need to set OPENXR_ANDROID_VERSION_SUFFIX
     # may need to set OPENXR_ANDROID_VERSION_SUFFIX
-    env_thirdparty.AppendUnique(CPPDEFINES=["XR_OS_ANDROID", "XR_USE_PLATFORM_ANDROID"])
+    env_openxr.AppendUnique(CPPDEFINES=["XR_OS_ANDROID", "XR_USE_PLATFORM_ANDROID"])
+    env_openxr.AppendUnique(CPPDEFINES=["JSON_USE_EXCEPTION=0"])
 
 
     # may need to include java parts of the openxr loader
     # may need to include java parts of the openxr loader
 elif env["platform"] == "linuxbsd":
 elif env["platform"] == "linuxbsd":
-    env_thirdparty.AppendUnique(CPPDEFINES=["XR_OS_LINUX"])
+    env_openxr.AppendUnique(CPPDEFINES=["XR_OS_LINUX"])
 
 
     if env["x11"]:
     if env["x11"]:
-        env_thirdparty.AppendUnique(CPPDEFINES=["XR_USE_PLATFORM_XLIB"])
+        env_openxr.AppendUnique(CPPDEFINES=["XR_USE_PLATFORM_XLIB"])
 
 
     # FIXME: Review what needs to be set for Android and macOS.
     # FIXME: Review what needs to be set for Android and macOS.
-    env_thirdparty.AppendUnique(CPPDEFINES=["HAVE_SECURE_GETENV"])
+    env_openxr.AppendUnique(CPPDEFINES=["HAVE_SECURE_GETENV"])
 elif env["platform"] == "windows":
 elif env["platform"] == "windows":
-    env_thirdparty.AppendUnique(CPPDEFINES=["XR_OS_WINDOWS", "NOMINMAX", "XR_USE_PLATFORM_WIN32"])
+    env_openxr.AppendUnique(CPPDEFINES=["XR_OS_WINDOWS", "NOMINMAX", "XR_USE_PLATFORM_WIN32"])
 
 
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/xr_generated_dispatch_table.c")
+# may need to check and set:
+# - XR_USE_TIMESPEC
 
 
-# add in common files (hope these don't clash with us)
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/common/filesystem_utils.cpp")
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/common/object_info.cpp")
+env_thirdparty = env_openxr.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.AppendUnique(CPPDEFINES=["DISABLE_STD_FILESYSTEM"])
+
+if "-fno-exceptions" in env_thirdparty["CXXFLAGS"]:
+    env_thirdparty["CXXFLAGS"].remove("-fno-exceptions")
+env_thirdparty.Append(CPPPATH=[thirdparty_dir + "/src/loader"])
 
 
 # add in external jsoncpp dependency
 # add in external jsoncpp dependency
 env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/external/jsoncpp/src/lib_json/json_reader.cpp")
 env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/external/jsoncpp/src/lib_json/json_reader.cpp")
@@ -57,17 +55,24 @@ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/external/
 env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/external/jsoncpp/src/lib_json/json_writer.cpp")
 env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/external/jsoncpp/src/lib_json/json_writer.cpp")
 
 
 # add in load
 # add in load
-if env["platform"] == "android":
-    env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/android_utilities.cpp")
-
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/api_layer_interface.cpp")
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/loader_core.cpp")
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/loader_instance.cpp")
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/loader_logger_recorders.cpp")
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/loader_logger.cpp")
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/manifest_file.cpp")
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/runtime_interface.cpp")
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/xr_generated_loader.cpp")
+if env["platform"] != "android":
+    # On Android the openxr_loader is provided by separate plugins for each device
+    # Build the engine using object files
+    khrloader_obj = []
+    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/xr_generated_dispatch_table.c")
+
+    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/filesystem_utils.cpp")
+    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/object_info.cpp")
+
+    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/api_layer_interface.cpp")
+    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_core.cpp")
+    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_instance.cpp")
+    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_logger_recorders.cpp")
+    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_logger.cpp")
+    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/manifest_file.cpp")
+    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/runtime_interface.cpp")
+    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/xr_generated_loader.cpp")
+    env.modules_sources += khrloader_obj
 
 
 env.modules_sources += thirdparty_obj
 env.modules_sources += thirdparty_obj
 
 

+ 1 - 3
modules/openxr/config.py

@@ -1,7 +1,5 @@
 def can_build(env, platform):
 def can_build(env, platform):
-    if (
-        platform == "linuxbsd" or platform == "windows"
-    ):  # or platform == "android" -- temporarily disabled android support
+    if platform in ("linuxbsd", "windows", "android"):
         return env["openxr"]
         return env["openxr"]
     else:
     else:
         # not supported on these platforms
         # not supported on these platforms

+ 11 - 9
modules/openxr/extensions/openxr_android_extension.cpp

@@ -29,7 +29,12 @@
 /*************************************************************************/
 /*************************************************************************/
 
 
 #include "openxr_android_extension.h"
 #include "openxr_android_extension.h"
+#include "java_godot_wrapper.h"
+#include "os_android.h"
+#include "thread_jandroid.h"
 
 
+#include <jni.h>
+#include <modules/openxr/openxr_api.h>
 #include <openxr/openxr.h>
 #include <openxr/openxr.h>
 #include <openxr/openxr_platform.h>
 #include <openxr/openxr_platform.h>
 
 
@@ -42,19 +47,16 @@ OpenXRAndroidExtension *OpenXRAndroidExtension::get_singleton() {
 OpenXRAndroidExtension::OpenXRAndroidExtension(OpenXRAPI *p_openxr_api) :
 OpenXRAndroidExtension::OpenXRAndroidExtension(OpenXRAPI *p_openxr_api) :
 		OpenXRExtensionWrapper(p_openxr_api) {
 		OpenXRExtensionWrapper(p_openxr_api) {
 	singleton = this;
 	singleton = this;
-
 	request_extensions[XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME] = nullptr; // must be available
 	request_extensions[XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME] = nullptr; // must be available
+}
 
 
-	// Initialize the loader
-	PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR;
-	result = xrGetInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", (PFN_xrVoidFunction *)(&xrInitializeLoaderKHR));
-	ERR_FAIL_COND_MSG(XR_FAILED(result), "Failed to retrieve pointer to xrInitializeLoaderKHR");
+void OpenXRAndroidExtension::on_before_instance_created() {
+	EXT_INIT_XR_FUNC(xrInitializeLoaderKHR);
 
 
-	// TODO fix this code, this is still code from GDNative!
-	JNIEnv *env = android_api->godot_android_get_env();
+	JNIEnv *env = get_jni_env();
 	JavaVM *vm;
 	JavaVM *vm;
 	env->GetJavaVM(&vm);
 	env->GetJavaVM(&vm);
-	jobject activity_object = env->NewGlobalRef(android_api->godot_android_get_activity());
+	jobject activity_object = env->NewGlobalRef(static_cast<OS_Android *>(OS::get_singleton())->get_godot_java()->get_activity());
 
 
 	XrLoaderInitInfoAndroidKHR loader_init_info_android = {
 	XrLoaderInitInfoAndroidKHR loader_init_info_android = {
 		.type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR,
 		.type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR,
@@ -62,7 +64,7 @@ OpenXRAndroidExtension::OpenXRAndroidExtension(OpenXRAPI *p_openxr_api) :
 		.applicationVM = vm,
 		.applicationVM = vm,
 		.applicationContext = activity_object
 		.applicationContext = activity_object
 	};
 	};
-	xrInitializeLoaderKHR((const XrLoaderInitInfoBaseHeaderKHR *)&loader_init_info_android);
+	XrResult result = xrInitializeLoaderKHR((const XrLoaderInitInfoBaseHeaderKHR *)&loader_init_info_android);
 	ERR_FAIL_COND_MSG(XR_FAILED(result), "Failed to call xrInitializeLoaderKHR");
 	ERR_FAIL_COND_MSG(XR_FAILED(result), "Failed to call xrInitializeLoaderKHR");
 }
 }
 
 

+ 7 - 0
modules/openxr/extensions/openxr_android_extension.h

@@ -31,6 +31,7 @@
 #ifndef OPENXR_ANDROID_EXTENSION_H
 #ifndef OPENXR_ANDROID_EXTENSION_H
 #define OPENXR_ANDROID_EXTENSION_H
 #define OPENXR_ANDROID_EXTENSION_H
 
 
+#include "../util.h"
 #include "openxr_extension_wrapper.h"
 #include "openxr_extension_wrapper.h"
 
 
 class OpenXRAndroidExtension : public OpenXRExtensionWrapper {
 class OpenXRAndroidExtension : public OpenXRExtensionWrapper {
@@ -38,10 +39,16 @@ public:
 	static OpenXRAndroidExtension *get_singleton();
 	static OpenXRAndroidExtension *get_singleton();
 
 
 	OpenXRAndroidExtension(OpenXRAPI *p_openxr_api);
 	OpenXRAndroidExtension(OpenXRAPI *p_openxr_api);
+
+	virtual void on_before_instance_created() override;
+
 	virtual ~OpenXRAndroidExtension() override;
 	virtual ~OpenXRAndroidExtension() override;
 
 
 private:
 private:
 	static OpenXRAndroidExtension *singleton;
 	static OpenXRAndroidExtension *singleton;
+
+	// Initialize the loader
+	EXT_PROTO_XRRESULT_FUNC1(xrInitializeLoaderKHR, (const XrLoaderInitInfoBaseHeaderKHR *), loaderInitInfo)
 };
 };
 
 
 #endif // OPENXR_ANDROID_EXTENSION_H
 #endif // OPENXR_ANDROID_EXTENSION_H

+ 1 - 0
modules/openxr/extensions/openxr_extension_wrapper.h

@@ -66,6 +66,7 @@ public:
 	virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) { return p_next_pointer; }
 	virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) { return p_next_pointer; }
 	virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) { return p_next_pointer; }
 	virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) { return p_next_pointer; }
 
 
+	virtual void on_before_instance_created() {}
 	virtual void on_instance_created(const XrInstance p_instance) {}
 	virtual void on_instance_created(const XrInstance p_instance) {}
 	virtual void on_instance_destroyed() {}
 	virtual void on_instance_destroyed() {}
 	virtual void on_session_created(const XrSession p_instance) {}
 	virtual void on_session_created(const XrSession p_instance) {}

+ 5 - 62
modules/openxr/extensions/openxr_vulkan_extension.cpp

@@ -31,30 +31,12 @@
 #include "core/string/print_string.h"
 #include "core/string/print_string.h"
 
 
 #include "../extensions/openxr_vulkan_extension.h"
 #include "../extensions/openxr_vulkan_extension.h"
-#include "../openxr_api.h"
 #include "../openxr_util.h"
 #include "../openxr_util.h"
 #include "servers/rendering/renderer_rd/effects/copy_effects.h"
 #include "servers/rendering/renderer_rd/effects/copy_effects.h"
 #include "servers/rendering/renderer_rd/storage_rd/texture_storage.h"
 #include "servers/rendering/renderer_rd/storage_rd/texture_storage.h"
 #include "servers/rendering/rendering_server_globals.h"
 #include "servers/rendering/rendering_server_globals.h"
 #include "servers/rendering_server.h"
 #include "servers/rendering_server.h"
 
 
-// need to include Vulkan so we know of type definitions
-#define XR_USE_GRAPHICS_API_VULKAN
-
-#ifdef WINDOWS_ENABLED
-// Including windows.h here is absolutely evil, we shouldn't be doing this outside of platform
-// however due to the way the openxr headers are put together, we have no choice.
-#include <windows.h>
-#endif
-
-// include platform dependent structs
-#include <openxr/openxr_platform.h>
-
-PFN_xrGetVulkanGraphicsRequirements2KHR xrGetVulkanGraphicsRequirements2KHR_ptr = nullptr;
-PFN_xrCreateVulkanInstanceKHR xrCreateVulkanInstanceKHR_ptr = nullptr;
-PFN_xrGetVulkanGraphicsDevice2KHR xrGetVulkanGraphicsDevice2KHR_ptr = nullptr;
-PFN_xrCreateVulkanDeviceKHR xrCreateVulkanDeviceKHR_ptr = nullptr;
-
 OpenXRVulkanExtension::OpenXRVulkanExtension(OpenXRAPI *p_openxr_api) :
 OpenXRVulkanExtension::OpenXRVulkanExtension(OpenXRAPI *p_openxr_api) :
 		OpenXRGraphicsExtensionWrapper(p_openxr_api) {
 		OpenXRGraphicsExtensionWrapper(p_openxr_api) {
 	VulkanContext::set_vulkan_hooks(this);
 	VulkanContext::set_vulkan_hooks(this);
@@ -69,36 +51,15 @@ OpenXRVulkanExtension::~OpenXRVulkanExtension() {
 }
 }
 
 
 void OpenXRVulkanExtension::on_instance_created(const XrInstance p_instance) {
 void OpenXRVulkanExtension::on_instance_created(const XrInstance p_instance) {
-	XrResult result;
-
 	ERR_FAIL_NULL(openxr_api);
 	ERR_FAIL_NULL(openxr_api);
 
 
 	// Obtain pointers to functions we're accessing here, they are (not yet) part of core.
 	// Obtain pointers to functions we're accessing here, they are (not yet) part of core.
-	result = xrGetInstanceProcAddr(p_instance, "xrGetVulkanGraphicsRequirements2KHR", (PFN_xrVoidFunction *)&xrGetVulkanGraphicsRequirements2KHR_ptr);
-	if (XR_FAILED(result)) {
-		print_line("OpenXR: Failed to xrGetVulkanGraphicsRequirements2KHR entry point [", openxr_api->get_error_string(result), "]");
-	}
 
 
-	result = xrGetInstanceProcAddr(p_instance, "xrCreateVulkanInstanceKHR", (PFN_xrVoidFunction *)&xrCreateVulkanInstanceKHR_ptr);
-	if (XR_FAILED(result)) {
-		print_line("OpenXR: Failed to xrCreateVulkanInstanceKHR entry point [", openxr_api->get_error_string(result), "]");
-	}
-
-	result = xrGetInstanceProcAddr(p_instance, "xrGetVulkanGraphicsDevice2KHR", (PFN_xrVoidFunction *)&xrGetVulkanGraphicsDevice2KHR_ptr);
-	if (XR_FAILED(result)) {
-		print_line("OpenXR: Failed to xrGetVulkanGraphicsDevice2KHR entry point [", openxr_api->get_error_string(result), "]");
-	}
-
-	result = xrGetInstanceProcAddr(p_instance, "xrCreateVulkanDeviceKHR", (PFN_xrVoidFunction *)&xrCreateVulkanDeviceKHR_ptr);
-	if (XR_FAILED(result)) {
-		print_line("OpenXR: Failed to xrCreateVulkanDeviceKHR entry point [", openxr_api->get_error_string(result), "]");
-	}
-}
-
-XrResult OpenXRVulkanExtension::xrGetVulkanGraphicsRequirements2KHR(XrInstance p_instance, XrSystemId p_system_id, XrGraphicsRequirementsVulkanKHR *p_graphics_requirements) {
-	ERR_FAIL_NULL_V(xrGetVulkanGraphicsRequirements2KHR_ptr, XR_ERROR_HANDLE_INVALID);
-
-	return (*xrGetVulkanGraphicsRequirements2KHR_ptr)(p_instance, p_system_id, p_graphics_requirements);
+	EXT_INIT_XR_FUNC(xrGetVulkanGraphicsRequirements2KHR);
+	EXT_INIT_XR_FUNC(xrCreateVulkanInstanceKHR);
+	EXT_INIT_XR_FUNC(xrGetVulkanGraphicsDevice2KHR);
+	EXT_INIT_XR_FUNC(xrCreateVulkanDeviceKHR);
+	EXT_INIT_XR_FUNC(xrEnumerateSwapchainImages);
 }
 }
 
 
 bool OpenXRVulkanExtension::check_graphics_api_support(XrVersion p_desired_version) {
 bool OpenXRVulkanExtension::check_graphics_api_support(XrVersion p_desired_version) {
@@ -141,12 +102,6 @@ bool OpenXRVulkanExtension::check_graphics_api_support(XrVersion p_desired_versi
 	return true;
 	return true;
 }
 }
 
 
-XrResult OpenXRVulkanExtension::xrCreateVulkanInstanceKHR(XrInstance p_instance, const XrVulkanInstanceCreateInfoKHR *p_create_info, VkInstance *r_vulkan_instance, VkResult *r_vulkan_result) {
-	ERR_FAIL_NULL_V(xrCreateVulkanInstanceKHR_ptr, XR_ERROR_HANDLE_INVALID);
-
-	return (*xrCreateVulkanInstanceKHR_ptr)(p_instance, p_create_info, r_vulkan_instance, r_vulkan_result);
-}
-
 bool OpenXRVulkanExtension::create_vulkan_instance(const VkInstanceCreateInfo *p_vulkan_create_info, VkInstance *r_instance) {
 bool OpenXRVulkanExtension::create_vulkan_instance(const VkInstanceCreateInfo *p_vulkan_create_info, VkInstance *r_instance) {
 	// get the vulkan version we are creating
 	// get the vulkan version we are creating
 	uint32_t vulkan_version = p_vulkan_create_info->pApplicationInfo->apiVersion;
 	uint32_t vulkan_version = p_vulkan_create_info->pApplicationInfo->apiVersion;
@@ -195,12 +150,6 @@ bool OpenXRVulkanExtension::create_vulkan_instance(const VkInstanceCreateInfo *p
 	return true;
 	return true;
 }
 }
 
 
-XrResult OpenXRVulkanExtension::xrGetVulkanGraphicsDevice2KHR(XrInstance p_instance, const XrVulkanGraphicsDeviceGetInfoKHR *p_get_info, VkPhysicalDevice *r_vulkan_physical_device) {
-	ERR_FAIL_NULL_V(xrGetVulkanGraphicsDevice2KHR_ptr, XR_ERROR_HANDLE_INVALID);
-
-	return (*xrGetVulkanGraphicsDevice2KHR_ptr)(p_instance, p_get_info, r_vulkan_physical_device);
-}
-
 bool OpenXRVulkanExtension::get_physical_device(VkPhysicalDevice *r_device) {
 bool OpenXRVulkanExtension::get_physical_device(VkPhysicalDevice *r_device) {
 	ERR_FAIL_NULL_V(openxr_api, false);
 	ERR_FAIL_NULL_V(openxr_api, false);
 
 
@@ -222,12 +171,6 @@ bool OpenXRVulkanExtension::get_physical_device(VkPhysicalDevice *r_device) {
 	return true;
 	return true;
 }
 }
 
 
-XrResult OpenXRVulkanExtension::xrCreateVulkanDeviceKHR(XrInstance p_instance, const XrVulkanDeviceCreateInfoKHR *p_create_info, VkDevice *r_device, VkResult *r_result) {
-	ERR_FAIL_NULL_V(xrCreateVulkanDeviceKHR_ptr, XR_ERROR_HANDLE_INVALID);
-
-	return (*xrCreateVulkanDeviceKHR_ptr)(p_instance, p_create_info, r_device, r_result);
-}
-
 bool OpenXRVulkanExtension::create_vulkan_device(const VkDeviceCreateInfo *p_device_create_info, VkDevice *r_device) {
 bool OpenXRVulkanExtension::create_vulkan_device(const VkDeviceCreateInfo *p_device_create_info, VkDevice *r_device) {
 	ERR_FAIL_NULL_V(openxr_api, false);
 	ERR_FAIL_NULL_V(openxr_api, false);
 
 

+ 23 - 13
modules/openxr/extensions/openxr_vulkan_extension.h

@@ -36,16 +36,25 @@
 
 
 #include "drivers/vulkan/vulkan_context.h"
 #include "drivers/vulkan/vulkan_context.h"
 
 
-// Forward declare these so we don't need OpenXR headers where-ever this is included
-// Including OpenXR at this point gives loads and loads of compile issues especially
-// on Windows because windows.h is EVIL and really shouldn't be included outside of platform
-// but we really don't have a choice in the matter
+#include "../openxr_api.h"
+#include "../util.h"
 
 
-struct XrGraphicsRequirementsVulkanKHR;
-struct XrVulkanInstanceCreateInfoKHR;
-struct XrVulkanGraphicsDeviceGetInfoKHR;
-struct XrVulkanDeviceCreateInfoKHR;
-struct XrGraphicsBindingVulkanKHR;
+// need to include Vulkan so we know of type definitions
+#define XR_USE_GRAPHICS_API_VULKAN
+
+#ifdef WINDOWS_ENABLED
+// Including windows.h here is absolutely evil, we shouldn't be doing this outside of platform
+// however due to the way the openxr headers are put together, we have no choice.
+#include <windows.h>
+#endif
+
+#ifdef ANDROID_ENABLED
+// The jobject type from jni.h is used by openxr_platform.h on Android.
+#include <jni.h>
+#endif
+
+// include platform dependent structs
+#include <openxr/openxr_platform.h>
 
 
 class OpenXRVulkanExtension : public OpenXRGraphicsExtensionWrapper, VulkanHooks {
 class OpenXRVulkanExtension : public OpenXRGraphicsExtensionWrapper, VulkanHooks {
 public:
 public:
@@ -84,10 +93,11 @@ private:
 	uint32_t vulkan_queue_family_index = 0;
 	uint32_t vulkan_queue_family_index = 0;
 	uint32_t vulkan_queue_index = 0;
 	uint32_t vulkan_queue_index = 0;
 
 
-	XrResult xrGetVulkanGraphicsRequirements2KHR(XrInstance p_instance, XrSystemId p_system_id, XrGraphicsRequirementsVulkanKHR *p_graphics_requirements);
-	XrResult xrCreateVulkanInstanceKHR(XrInstance p_instance, const XrVulkanInstanceCreateInfoKHR *p_create_info, VkInstance *r_vulkan_instance, VkResult *r_vulkan_result);
-	XrResult xrGetVulkanGraphicsDevice2KHR(XrInstance p_instance, const XrVulkanGraphicsDeviceGetInfoKHR *p_get_info, VkPhysicalDevice *r_vulkan_physical_device);
-	XrResult xrCreateVulkanDeviceKHR(XrInstance p_instance, const XrVulkanDeviceCreateInfoKHR *p_create_info, VkDevice *r_device, VkResult *r_result);
+	EXT_PROTO_XRRESULT_FUNC3(xrGetVulkanGraphicsRequirements2KHR, (XrInstance), p_instance, (XrSystemId), p_system_id, (XrGraphicsRequirementsVulkanKHR *), p_graphics_requirements)
+	EXT_PROTO_XRRESULT_FUNC4(xrCreateVulkanInstanceKHR, (XrInstance), p_instance, (const XrVulkanInstanceCreateInfoKHR *), p_create_info, (VkInstance *), r_vulkan_instance, (VkResult *), r_vulkan_result)
+	EXT_PROTO_XRRESULT_FUNC3(xrGetVulkanGraphicsDevice2KHR, (XrInstance), p_instance, (const XrVulkanGraphicsDeviceGetInfoKHR *), p_get_info, (VkPhysicalDevice *), r_vulkan_physical_device)
+	EXT_PROTO_XRRESULT_FUNC4(xrCreateVulkanDeviceKHR, (XrInstance), p_instance, (const XrVulkanDeviceCreateInfoKHR *), p_create_info, (VkDevice *), r_device, (VkResult *), r_result)
+	EXT_PROTO_XRRESULT_FUNC4(xrEnumerateSwapchainImages, (XrSwapchain), p_swapchain, (uint32_t), p_image_capacity_input, (uint32_t *), p_image_count_output, (XrSwapchainImageBaseHeader *), p_images)
 };
 };
 
 
 #endif // OPENXR_VULKAN_EXTENSION_H
 #endif // OPENXR_VULKAN_EXTENSION_H

+ 105 - 0
modules/openxr/openxr_api.cpp

@@ -41,6 +41,7 @@
 #endif
 #endif
 
 
 #ifdef ANDROID_ENABLED
 #ifdef ANDROID_ENABLED
+#define OPENXR_LOADER_NAME "libopenxr_loader.so"
 #include "extensions/openxr_android_extension.h"
 #include "extensions/openxr_android_extension.h"
 #endif
 #endif
 
 
@@ -284,6 +285,9 @@ bool OpenXRAPI::create_instance() {
 		0, // runtimeVersion, from here will be set by our get call
 		0, // runtimeVersion, from here will be set by our get call
 		"" // runtimeName
 		"" // runtimeName
 	};
 	};
+
+	OPENXR_API_INIT_XR_FUNC_V(xrGetInstanceProperties);
+
 	result = xrGetInstanceProperties(instance, &instanceProps);
 	result = xrGetInstanceProperties(instance, &instanceProps);
 	if (XR_FAILED(result)) {
 	if (XR_FAILED(result)) {
 		// not fatal probably
 		// not fatal probably
@@ -992,9 +996,94 @@ bool OpenXRAPI::is_running() {
 	return running;
 	return running;
 }
 }
 
 
+bool OpenXRAPI::openxr_loader_init() {
+#ifdef ANDROID_ENABLED
+	ERR_FAIL_COND_V_MSG(openxr_loader_library_handle != nullptr, false, "OpenXR Loader library is already loaded.");
+
+	{
+		Error error_code = OS::get_singleton()->open_dynamic_library(OPENXR_LOADER_NAME, openxr_loader_library_handle);
+		ERR_FAIL_COND_V_MSG(error_code != OK, false, "OpenXR loader not found.");
+	}
+
+	{
+		Error error_code = OS::get_singleton()->get_dynamic_library_symbol_handle(openxr_loader_library_handle, "xrGetInstanceProcAddr", (void *&)xrGetInstanceProcAddr);
+		ERR_FAIL_COND_V_MSG(error_code != OK, false, "Symbol xrGetInstanceProcAddr not found in OpenXR Loader library.");
+	}
+#endif
+
+	// Resolve the symbols that don't require an instance
+	OPENXR_API_INIT_XR_FUNC_V(xrCreateInstance);
+	OPENXR_API_INIT_XR_FUNC_V(xrEnumerateApiLayerProperties);
+	OPENXR_API_INIT_XR_FUNC_V(xrEnumerateInstanceExtensionProperties);
+
+	return true;
+}
+
+bool OpenXRAPI::resolve_instance_openxr_symbols() {
+	ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, false);
+
+	OPENXR_API_INIT_XR_FUNC_V(xrAcquireSwapchainImage);
+	OPENXR_API_INIT_XR_FUNC_V(xrApplyHapticFeedback);
+	OPENXR_API_INIT_XR_FUNC_V(xrAttachSessionActionSets);
+	OPENXR_API_INIT_XR_FUNC_V(xrBeginFrame);
+	OPENXR_API_INIT_XR_FUNC_V(xrBeginSession);
+	OPENXR_API_INIT_XR_FUNC_V(xrCreateAction);
+	OPENXR_API_INIT_XR_FUNC_V(xrCreateActionSet);
+	OPENXR_API_INIT_XR_FUNC_V(xrCreateActionSpace);
+	OPENXR_API_INIT_XR_FUNC_V(xrCreateReferenceSpace);
+	OPENXR_API_INIT_XR_FUNC_V(xrCreateSession);
+	OPENXR_API_INIT_XR_FUNC_V(xrCreateSwapchain);
+	OPENXR_API_INIT_XR_FUNC_V(xrDestroyAction);
+	OPENXR_API_INIT_XR_FUNC_V(xrDestroyActionSet);
+	OPENXR_API_INIT_XR_FUNC_V(xrDestroyInstance);
+	OPENXR_API_INIT_XR_FUNC_V(xrDestroySession);
+	OPENXR_API_INIT_XR_FUNC_V(xrDestroySpace);
+	OPENXR_API_INIT_XR_FUNC_V(xrDestroySwapchain);
+	OPENXR_API_INIT_XR_FUNC_V(xrEndFrame);
+	OPENXR_API_INIT_XR_FUNC_V(xrEndSession);
+	OPENXR_API_INIT_XR_FUNC_V(xrEnumerateReferenceSpaces);
+	OPENXR_API_INIT_XR_FUNC_V(xrEnumerateSwapchainFormats);
+	OPENXR_API_INIT_XR_FUNC_V(xrEnumerateViewConfigurations);
+	OPENXR_API_INIT_XR_FUNC_V(xrEnumerateViewConfigurationViews);
+	OPENXR_API_INIT_XR_FUNC_V(xrGetActionStateBoolean);
+	OPENXR_API_INIT_XR_FUNC_V(xrGetActionStateFloat);
+	OPENXR_API_INIT_XR_FUNC_V(xrGetActionStateVector2f);
+	OPENXR_API_INIT_XR_FUNC_V(xrGetCurrentInteractionProfile);
+	OPENXR_API_INIT_XR_FUNC_V(xrGetSystem);
+	OPENXR_API_INIT_XR_FUNC_V(xrGetSystemProperties);
+	OPENXR_API_INIT_XR_FUNC_V(xrLocateViews);
+	OPENXR_API_INIT_XR_FUNC_V(xrLocateSpace);
+	OPENXR_API_INIT_XR_FUNC_V(xrPathToString);
+	OPENXR_API_INIT_XR_FUNC_V(xrPollEvent);
+	OPENXR_API_INIT_XR_FUNC_V(xrReleaseSwapchainImage);
+	OPENXR_API_INIT_XR_FUNC_V(xrResultToString);
+	OPENXR_API_INIT_XR_FUNC_V(xrStringToPath);
+	OPENXR_API_INIT_XR_FUNC_V(xrSuggestInteractionProfileBindings);
+	OPENXR_API_INIT_XR_FUNC_V(xrSyncActions);
+	OPENXR_API_INIT_XR_FUNC_V(xrWaitFrame);
+	OPENXR_API_INIT_XR_FUNC_V(xrWaitSwapchainImage);
+
+	return true;
+}
+
+XrResult OpenXRAPI::get_instance_proc_addr(const char *p_name, PFN_xrVoidFunction *p_addr) {
+	XrResult result = xrGetInstanceProcAddr(instance, p_name, p_addr);
+
+	if (result != XR_SUCCESS) {
+		String error_message = String("Symbol ") + p_name + " not found in OpenXR instance.";
+		ERR_FAIL_COND_V_MSG(true, result, error_message.utf8().get_data());
+	}
+
+	return result;
+}
+
 bool OpenXRAPI::initialize(const String &p_rendering_driver) {
 bool OpenXRAPI::initialize(const String &p_rendering_driver) {
 	ERR_FAIL_COND_V_MSG(instance != XR_NULL_HANDLE, false, "OpenXR instance was already created");
 	ERR_FAIL_COND_V_MSG(instance != XR_NULL_HANDLE, false, "OpenXR instance was already created");
 
 
+	if (!openxr_loader_init()) {
+		return false;
+	}
+
 	if (p_rendering_driver == "vulkan") {
 	if (p_rendering_driver == "vulkan") {
 #ifdef VULKAN_ENABLED
 #ifdef VULKAN_ENABLED
 		graphics_extension = memnew(OpenXRVulkanExtension(this));
 		graphics_extension = memnew(OpenXRVulkanExtension(this));
@@ -1017,6 +1106,10 @@ bool OpenXRAPI::initialize(const String &p_rendering_driver) {
 	}
 	}
 
 
 	// initialize
 	// initialize
+	for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
+		wrapper->on_before_instance_created();
+	}
+
 	if (!load_layer_properties()) {
 	if (!load_layer_properties()) {
 		destroy_instance();
 		destroy_instance();
 		return false;
 		return false;
@@ -1032,6 +1125,11 @@ bool OpenXRAPI::initialize(const String &p_rendering_driver) {
 		return false;
 		return false;
 	}
 	}
 
 
+	if (!resolve_instance_openxr_symbols()) {
+		destroy_instance();
+		return false;
+	}
+
 	if (!get_system_info()) {
 	if (!get_system_info()) {
 		destroy_instance();
 		destroy_instance();
 		return false;
 		return false;
@@ -1669,6 +1767,13 @@ OpenXRAPI::~OpenXRAPI() {
 		layer_properties = nullptr;
 		layer_properties = nullptr;
 	}
 	}
 
 
+#ifdef ANDROID_ENABLED
+	if (openxr_loader_library_handle) {
+		OS::get_singleton()->close_dynamic_library(openxr_loader_library_handle);
+		openxr_loader_library_handle = nullptr;
+	}
+#endif
+
 	singleton = nullptr;
 	singleton = nullptr;
 }
 }
 
 

+ 58 - 0
modules/openxr/openxr_api.h

@@ -50,6 +50,8 @@
 #include "extensions/openxr_composition_layer_provider.h"
 #include "extensions/openxr_composition_layer_provider.h"
 #include "extensions/openxr_extension_wrapper.h"
 #include "extensions/openxr_extension_wrapper.h"
 
 
+#include "util.h"
+
 // Note, OpenXR code that we wrote for our plugin makes use of C++20 notation for initialising structs which ensures zeroing out unspecified members.
 // Note, OpenXR code that we wrote for our plugin makes use of C++20 notation for initialising structs which ensures zeroing out unspecified members.
 // Godot is currently restricted to C++17 which doesn't allow this notation. Make sure critical fields are set.
 // Godot is currently restricted to C++17 which doesn't allow this notation. Make sure critical fields are set.
 
 
@@ -134,6 +136,61 @@ private:
 	bool load_supported_extensions();
 	bool load_supported_extensions();
 	bool is_extension_supported(const String &p_extension) const;
 	bool is_extension_supported(const String &p_extension) const;
 
 
+	bool openxr_loader_init();
+	bool resolve_instance_openxr_symbols();
+
+	void *openxr_loader_library_handle = nullptr;
+
+	// function pointers
+#ifdef ANDROID_ENABLED
+	// On non-Android platforms we use the OpenXR symbol linked into the engine binary.
+	PFN_xrGetInstanceProcAddr xrGetInstanceProcAddr = nullptr;
+#endif
+	EXT_PROTO_XRRESULT_FUNC3(xrAcquireSwapchainImage, (XrSwapchain), swapchain, (const XrSwapchainImageAcquireInfo *), acquireInfo, (uint32_t *), index)
+	EXT_PROTO_XRRESULT_FUNC3(xrApplyHapticFeedback, (XrSession), session, (const XrHapticActionInfo *), hapticActionInfo, (const XrHapticBaseHeader *), hapticFeedback)
+	EXT_PROTO_XRRESULT_FUNC2(xrAttachSessionActionSets, (XrSession), session, (const XrSessionActionSetsAttachInfo *), attachInfo)
+	EXT_PROTO_XRRESULT_FUNC2(xrBeginFrame, (XrSession), session, (const XrFrameBeginInfo *), frameBeginInfo)
+	EXT_PROTO_XRRESULT_FUNC2(xrBeginSession, (XrSession), session, (const XrSessionBeginInfo *), beginInfo)
+	EXT_PROTO_XRRESULT_FUNC3(xrCreateAction, (XrActionSet), actionSet, (const XrActionCreateInfo *), createInfo, (XrAction *), action)
+	EXT_PROTO_XRRESULT_FUNC3(xrCreateActionSet, (XrInstance), instance, (const XrActionSetCreateInfo *), createInfo, (XrActionSet *), actionSet)
+	EXT_PROTO_XRRESULT_FUNC3(xrCreateActionSpace, (XrSession), session, (const XrActionSpaceCreateInfo *), createInfo, (XrSpace *), space)
+	EXT_PROTO_XRRESULT_FUNC2(xrCreateInstance, (const XrInstanceCreateInfo *), createInfo, (XrInstance *), instance)
+	EXT_PROTO_XRRESULT_FUNC3(xrCreateReferenceSpace, (XrSession), session, (const XrReferenceSpaceCreateInfo *), createInfo, (XrSpace *), space)
+	EXT_PROTO_XRRESULT_FUNC3(xrCreateSession, (XrInstance), instance, (const XrSessionCreateInfo *), createInfo, (XrSession *), session)
+	EXT_PROTO_XRRESULT_FUNC3(xrCreateSwapchain, (XrSession), session, (const XrSwapchainCreateInfo *), createInfo, (XrSwapchain *), swapchain)
+	EXT_PROTO_XRRESULT_FUNC1(xrDestroyAction, (XrAction), action)
+	EXT_PROTO_XRRESULT_FUNC1(xrDestroyActionSet, (XrActionSet), actionSet)
+	EXT_PROTO_XRRESULT_FUNC1(xrDestroyInstance, (XrInstance), instance)
+	EXT_PROTO_XRRESULT_FUNC1(xrDestroySession, (XrSession), session)
+	EXT_PROTO_XRRESULT_FUNC1(xrDestroySpace, (XrSpace), space)
+	EXT_PROTO_XRRESULT_FUNC1(xrDestroySwapchain, (XrSwapchain), swapchain)
+	EXT_PROTO_XRRESULT_FUNC2(xrEndFrame, (XrSession), session, (const XrFrameEndInfo *), frameEndInfo)
+	EXT_PROTO_XRRESULT_FUNC1(xrEndSession, (XrSession), session)
+	EXT_PROTO_XRRESULT_FUNC3(xrEnumerateApiLayerProperties, (uint32_t), propertyCapacityInput, (uint32_t *), propertyCountOutput, (XrApiLayerProperties *), properties)
+	EXT_PROTO_XRRESULT_FUNC4(xrEnumerateInstanceExtensionProperties, (const char *), layerName, (uint32_t), propertyCapacityInput, (uint32_t *), propertyCountOutput, (XrExtensionProperties *), properties)
+	EXT_PROTO_XRRESULT_FUNC4(xrEnumerateReferenceSpaces, (XrSession), session, (uint32_t), spaceCapacityInput, (uint32_t *), spaceCountOutput, (XrReferenceSpaceType *), spaces)
+	EXT_PROTO_XRRESULT_FUNC4(xrEnumerateSwapchainFormats, (XrSession), session, (uint32_t), formatCapacityInput, (uint32_t *), formatCountOutput, (int64_t *), formats)
+	EXT_PROTO_XRRESULT_FUNC5(xrEnumerateViewConfigurations, (XrInstance), instance, (XrSystemId), systemId, (uint32_t), viewConfigurationTypeCapacityInput, (uint32_t *), viewConfigurationTypeCountOutput, (XrViewConfigurationType *), viewConfigurationTypes)
+	EXT_PROTO_XRRESULT_FUNC6(xrEnumerateViewConfigurationViews, (XrInstance), instance, (XrSystemId), systemId, (XrViewConfigurationType), viewConfigurationType, (uint32_t), viewCapacityInput, (uint32_t *), viewCountOutput, (XrViewConfigurationView *), views)
+	EXT_PROTO_XRRESULT_FUNC3(xrGetActionStateBoolean, (XrSession), session, (const XrActionStateGetInfo *), getInfo, (XrActionStateBoolean *), state)
+	EXT_PROTO_XRRESULT_FUNC3(xrGetActionStateFloat, (XrSession), session, (const XrActionStateGetInfo *), getInfo, (XrActionStateFloat *), state)
+	EXT_PROTO_XRRESULT_FUNC3(xrGetActionStateVector2f, (XrSession), session, (const XrActionStateGetInfo *), getInfo, (XrActionStateVector2f *), state)
+	EXT_PROTO_XRRESULT_FUNC3(xrGetCurrentInteractionProfile, (XrSession), session, (XrPath), topLevelUserPath, (XrInteractionProfileState *), interactionProfile)
+	EXT_PROTO_XRRESULT_FUNC2(xrGetInstanceProperties, (XrInstance), instance, (XrInstanceProperties *), instanceProperties)
+	EXT_PROTO_XRRESULT_FUNC3(xrGetSystem, (XrInstance), instance, (const XrSystemGetInfo *), getInfo, (XrSystemId *), systemId)
+	EXT_PROTO_XRRESULT_FUNC3(xrGetSystemProperties, (XrInstance), instance, (XrSystemId), systemId, (XrSystemProperties *), properties)
+	EXT_PROTO_XRRESULT_FUNC4(xrLocateSpace, (XrSpace), space, (XrSpace), baseSpace, (XrTime), time, (XrSpaceLocation *), location)
+	EXT_PROTO_XRRESULT_FUNC6(xrLocateViews, (XrSession), session, (const XrViewLocateInfo *), viewLocateInfo, (XrViewState *), viewState, (uint32_t), viewCapacityInput, (uint32_t *), viewCountOutput, (XrView *), views)
+	EXT_PROTO_XRRESULT_FUNC5(xrPathToString, (XrInstance), instance, (XrPath), path, (uint32_t), bufferCapacityInput, (uint32_t *), bufferCountOutput, (char *), buffer)
+	EXT_PROTO_XRRESULT_FUNC2(xrPollEvent, (XrInstance), instance, (XrEventDataBuffer *), eventData)
+	EXT_PROTO_XRRESULT_FUNC2(xrReleaseSwapchainImage, (XrSwapchain), swapchain, (const XrSwapchainImageReleaseInfo *), releaseInfo)
+	EXT_PROTO_XRRESULT_FUNC3(xrResultToString, (XrInstance), instance, (XrResult), value, (char *), buffer)
+	EXT_PROTO_XRRESULT_FUNC3(xrStringToPath, (XrInstance), instance, (const char *), pathString, (XrPath *), path)
+	EXT_PROTO_XRRESULT_FUNC2(xrSuggestInteractionProfileBindings, (XrInstance), instance, (const XrInteractionProfileSuggestedBinding *), suggestedBindings)
+	EXT_PROTO_XRRESULT_FUNC2(xrSyncActions, (XrSession), session, (const XrActionsSyncInfo *), syncInfo)
+	EXT_PROTO_XRRESULT_FUNC3(xrWaitFrame, (XrSession), session, (const XrFrameWaitInfo *), frameWaitInfo, (XrFrameState *), frameState)
+	EXT_PROTO_XRRESULT_FUNC2(xrWaitSwapchainImage, (XrSwapchain), swapchain, (const XrSwapchainImageWaitInfo *), waitInfo)
+
 	// instance
 	// instance
 	bool create_instance();
 	bool create_instance();
 	bool get_system_info();
 	bool get_system_info();
@@ -231,6 +288,7 @@ public:
 	static bool openxr_is_enabled(bool p_check_run_in_editor = true);
 	static bool openxr_is_enabled(bool p_check_run_in_editor = true);
 	static OpenXRAPI *get_singleton();
 	static OpenXRAPI *get_singleton();
 
 
+	XrResult get_instance_proc_addr(const char *p_name, PFN_xrVoidFunction *p_addr);
 	String get_error_string(XrResult result);
 	String get_error_string(XrResult result);
 	String get_swapchain_format_name(int64_t p_swapchain_format) const;
 	String get_swapchain_format_name(int64_t p_swapchain_format) const;
 
 

+ 110 - 0
modules/openxr/util.h

@@ -0,0 +1,110 @@
+/*************************************************************************/
+/*  util.h                                                               */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* 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:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* 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.                */
+/*************************************************************************/
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#define UNPACK(...) __VA_ARGS__
+
+#define INIT_XR_FUNC_V(openxr_api, name)                                                                              \
+	do {                                                                                                              \
+		XrResult get_instance_proc_addr_result;                                                                       \
+		get_instance_proc_addr_result = openxr_api->get_instance_proc_addr(#name, (PFN_xrVoidFunction *)&name##_ptr); \
+		ERR_FAIL_COND_V(XR_FAILED(get_instance_proc_addr_result), false);                                             \
+	} while (0)
+
+#define EXT_INIT_XR_FUNC_V(name) INIT_XR_FUNC_V(openxr_api, name)
+#define OPENXR_API_INIT_XR_FUNC_V(name) INIT_XR_FUNC_V(this, name)
+
+#define INIT_XR_FUNC(openxr_api, name)                                                                                \
+	do {                                                                                                              \
+		XrResult get_instance_proc_addr_result;                                                                       \
+		get_instance_proc_addr_result = openxr_api->get_instance_proc_addr(#name, (PFN_xrVoidFunction *)&name##_ptr); \
+		ERR_FAIL_COND(XR_FAILED(get_instance_proc_addr_result));                                                      \
+	} while (0)
+
+#define EXT_INIT_XR_FUNC(name) INIT_XR_FUNC(openxr_api, name)
+#define OPENXR_API_INIT_XR_FUNC(name) INIT_XR_FUNC(this, name)
+
+#define EXT_PROTO_XRRESULT_FUNC1(func_name, arg1_type, arg1)                \
+	PFN_##func_name func_name##_ptr = nullptr;                              \
+	XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type arg1) const { \
+		if (!func_name##_ptr) {                                             \
+			return XR_ERROR_HANDLE_INVALID;                                 \
+		}                                                                   \
+		return (*func_name##_ptr)(arg1);                                    \
+	}
+
+#define EXT_PROTO_XRRESULT_FUNC2(func_name, arg1_type, arg1, arg2_type, arg2)                      \
+	PFN_##func_name func_name##_ptr = nullptr;                                                     \
+	XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type arg1, UNPACK arg2_type arg2) const { \
+		if (!func_name##_ptr) {                                                                    \
+			return XR_ERROR_HANDLE_INVALID;                                                        \
+		}                                                                                          \
+		return (*func_name##_ptr)(arg1, arg2);                                                     \
+	}
+
+#define EXT_PROTO_XRRESULT_FUNC3(func_name, arg1_type, arg1, arg2_type, arg2, arg3_type, arg3)                            \
+	PFN_##func_name func_name##_ptr = nullptr;                                                                            \
+	XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type arg1, UNPACK arg2_type arg2, UNPACK arg3_type arg3) const { \
+		if (!func_name##_ptr) {                                                                                           \
+			return XR_ERROR_HANDLE_INVALID;                                                                               \
+		}                                                                                                                 \
+		return (*func_name##_ptr)(arg1, arg2, arg3);                                                                      \
+	}
+
+#define EXT_PROTO_XRRESULT_FUNC4(func_name, arg1_type, arg1, arg2_type, arg2, arg3_type, arg3, arg4_type, arg4)                                  \
+	PFN_##func_name func_name##_ptr = nullptr;                                                                                                   \
+	XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type arg1, UNPACK arg2_type arg2, UNPACK arg3_type arg3, UNPACK arg4_type arg4) const { \
+		if (!func_name##_ptr) {                                                                                                                  \
+			return XR_ERROR_HANDLE_INVALID;                                                                                                      \
+		}                                                                                                                                        \
+		return (*func_name##_ptr)(arg1, arg2, arg3, arg4);                                                                                       \
+	}
+
+#define EXT_PROTO_XRRESULT_FUNC5(func_name, arg1_type, arg1, arg2_type, arg2, arg3_type, arg3, arg4_type, arg4, arg5_type, arg5)                                        \
+	PFN_##func_name func_name##_ptr = nullptr;                                                                                                                          \
+	XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type arg1, UNPACK arg2_type arg2, UNPACK arg3_type arg3, UNPACK arg4_type arg4, UNPACK arg5_type arg5) const { \
+		if (!func_name##_ptr) {                                                                                                                                         \
+			return XR_ERROR_HANDLE_INVALID;                                                                                                                             \
+		}                                                                                                                                                               \
+		return (*func_name##_ptr)(arg1, arg2, arg3, arg4, arg5);                                                                                                        \
+	}
+
+#define EXT_PROTO_XRRESULT_FUNC6(func_name, arg1_type, arg1, arg2_type, arg2, arg3_type, arg3, arg4_type, arg4, arg5_type, arg5, arg6_type, arg6)                                              \
+	PFN_##func_name func_name##_ptr = nullptr;                                                                                                                                                 \
+	XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type arg1, UNPACK arg2_type arg2, UNPACK arg3_type arg3, UNPACK arg4_type arg4, UNPACK arg5_type arg5, UNPACK arg6_type arg6) const { \
+		if (!func_name##_ptr) {                                                                                                                                                                \
+			return XR_ERROR_HANDLE_INVALID;                                                                                                                                                    \
+		}                                                                                                                                                                                      \
+		return (*func_name##_ptr)(arg1, arg2, arg3, arg4, arg5, arg6);                                                                                                                         \
+	}
+
+#endif // UTIL_H