summaryrefslogtreecommitdiff
path: root/thirdparty/openxr/src/loader/runtime_interface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/openxr/src/loader/runtime_interface.cpp')
-rw-r--r--thirdparty/openxr/src/loader/runtime_interface.cpp493
1 files changed, 493 insertions, 0 deletions
diff --git a/thirdparty/openxr/src/loader/runtime_interface.cpp b/thirdparty/openxr/src/loader/runtime_interface.cpp
new file mode 100644
index 0000000000..1a35ba013a
--- /dev/null
+++ b/thirdparty/openxr/src/loader/runtime_interface.cpp
@@ -0,0 +1,493 @@
+// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2019 Valve Corporation
+// Copyright (c) 2017-2019 LunarG, Inc.
+//
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+//
+// Initial Author: Mark Young <marky@lunarg.com>
+//
+
+#include "runtime_interface.hpp"
+
+#include "manifest_file.hpp"
+#include "loader_interfaces.h"
+#include "loader_logger.hpp"
+#include "loader_platform.hpp"
+#include "xr_generated_dispatch_table.h"
+
+#include <openxr/openxr.h>
+
+#include <cstring>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#ifdef XR_USE_PLATFORM_ANDROID
+#include "android_utilities.h"
+#include <json/value.h>
+#endif // XR_USE_PLATFORM_ANDROID
+
+#ifdef XR_KHR_LOADER_INIT_SUPPORT
+namespace {
+/*!
+ * Stores a copy of the data passed to the xrInitializeLoaderKHR function in a singleton.
+ */
+class LoaderInitData {
+ public:
+ /*!
+ * Singleton accessor.
+ */
+ static LoaderInitData& instance() {
+ static LoaderInitData obj;
+ return obj;
+ }
+
+#ifdef XR_USE_PLATFORM_ANDROID
+ /*!
+ * Type alias for the platform-specific structure type.
+ */
+ using StructType = XrLoaderInitInfoAndroidKHR;
+#endif
+
+ /*!
+ * Get our copy of the data, casted to pass to the runtime's matching method.
+ */
+ const XrLoaderInitInfoBaseHeaderKHR* getParam() const { return reinterpret_cast<const XrLoaderInitInfoBaseHeaderKHR*>(&_data); }
+
+ /*!
+ * Get the data via its real structure type.
+ */
+ const StructType& getData() const { return _data; }
+
+ /*!
+ * Has this been correctly initialized?
+ */
+ bool initialized() const noexcept { return _initialized; }
+
+ /*!
+ * Initialize loader data - called by InitializeLoader() and thus ultimately by the loader's xrInitializeLoaderKHR
+ * implementation. Each platform that needs this extension will provide an implementation of this.
+ */
+ XrResult initialize(const XrLoaderInitInfoBaseHeaderKHR* info);
+
+ private:
+ //! Private constructor, forces use of singleton accessor.
+ LoaderInitData() = default;
+ //! Platform-specific init data
+ StructType _data = {};
+ //! Flag for indicating whether _data is valid.
+ bool _initialized = false;
+};
+
+#ifdef XR_USE_PLATFORM_ANDROID
+// Check and copy the Android-specific init data.
+XrResult LoaderInitData::initialize(const XrLoaderInitInfoBaseHeaderKHR* info) {
+ if (info->type != XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR) {
+ return XR_ERROR_VALIDATION_FAILURE;
+ }
+ auto cast_info = reinterpret_cast<XrLoaderInitInfoAndroidKHR const*>(info);
+
+ if (cast_info->applicationVM == nullptr) {
+ return XR_ERROR_VALIDATION_FAILURE;
+ }
+ if (cast_info->applicationContext == nullptr) {
+ return XR_ERROR_VALIDATION_FAILURE;
+ }
+ _data = *cast_info;
+ jni::init((jni::JavaVM*)_data.applicationVM);
+ _data.next = nullptr;
+ _initialized = true;
+ return XR_SUCCESS;
+}
+#endif // XR_USE_PLATFORM_ANDROID
+} // namespace
+
+XrResult InitializeLoader(const XrLoaderInitInfoBaseHeaderKHR* loaderInitInfo) {
+ return LoaderInitData::instance().initialize(loaderInitInfo);
+}
+
+#endif // XR_KHR_LOADER_INIT_SUPPORT
+
+#ifdef XR_USE_PLATFORM_ANDROID
+XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest) {
+ using wrap::android::content::Context;
+ auto& initData = LoaderInitData::instance();
+ if (!initData.initialized()) {
+ return XR_ERROR_INITIALIZATION_FAILED;
+ }
+ auto context = Context(reinterpret_cast<jobject>(initData.getData().applicationContext));
+ if (context.isNull()) {
+ return XR_ERROR_INITIALIZATION_FAILED;
+ }
+ Json::Value virtualManifest;
+ if (0 != openxr_android::getActiveRuntimeVirtualManifest(context, virtualManifest)) {
+ return XR_ERROR_INITIALIZATION_FAILED;
+ }
+ out_manifest = virtualManifest;
+ return XR_SUCCESS;
+}
+#endif // XR_USE_PLATFORM_ANDROID
+
+XrResult RuntimeInterface::TryLoadingSingleRuntime(const std::string& openxr_command,
+ std::unique_ptr<RuntimeManifestFile>& manifest_file) {
+ LoaderPlatformLibraryHandle runtime_library = LoaderPlatformLibraryOpen(manifest_file->LibraryPath());
+ if (nullptr == runtime_library) {
+ std::string library_message = LoaderPlatformLibraryOpenError(manifest_file->LibraryPath());
+ std::string warning_message = "RuntimeInterface::LoadRuntime skipping manifest file ";
+ warning_message += manifest_file->Filename();
+ warning_message += ", failed to load with message \"";
+ warning_message += library_message;
+ warning_message += "\"";
+ LoaderLogger::LogErrorMessage(openxr_command, warning_message);
+ return XR_ERROR_FILE_ACCESS_ERROR;
+ }
+#ifdef XR_KHR_LOADER_INIT_SUPPORT
+ if (!LoaderInitData::instance().initialized()) {
+ LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntime skipping manifest file " +
+ manifest_file->Filename() +
+ " because xrInitializeLoaderKHR was not yet called.");
+
+ LoaderPlatformLibraryClose(runtime_library);
+ return XR_ERROR_VALIDATION_FAILURE;
+ }
+ bool forwardedInitLoader = false;
+ {
+ // If we have xrInitializeLoaderKHR exposed as an export, forward call to it.
+ const auto function_name = manifest_file->GetFunctionName("xrInitializeLoaderKHR");
+ auto initLoader =
+ reinterpret_cast<PFN_xrInitializeLoaderKHR>(LoaderPlatformLibraryGetProcAddr(runtime_library, function_name));
+ if (initLoader != nullptr) {
+ // we found the entry point one way or another.
+ LoaderLogger::LogInfoMessage(openxr_command,
+ "RuntimeInterface::LoadRuntime forwarding xrInitializeLoaderKHR call to runtime before "
+ "calling xrNegotiateLoaderRuntimeInterface.");
+ XrResult res = initLoader(LoaderInitData::instance().getParam());
+ if (!XR_SUCCEEDED(res)) {
+ LoaderLogger::LogErrorMessage(openxr_command,
+ "RuntimeInterface::LoadRuntime forwarded call to xrInitializeLoaderKHR failed.");
+
+ LoaderPlatformLibraryClose(runtime_library);
+ return res;
+ }
+ forwardedInitLoader = true;
+ }
+ }
+#endif
+
+ // Get and settle on an runtime interface version (using any provided name if required).
+ std::string function_name = manifest_file->GetFunctionName("xrNegotiateLoaderRuntimeInterface");
+ auto negotiate =
+ reinterpret_cast<PFN_xrNegotiateLoaderRuntimeInterface>(LoaderPlatformLibraryGetProcAddr(runtime_library, function_name));
+
+ // Loader info for negotiation
+ XrNegotiateLoaderInfo loader_info = {};
+ loader_info.structType = XR_LOADER_INTERFACE_STRUCT_LOADER_INFO;
+ loader_info.structVersion = XR_LOADER_INFO_STRUCT_VERSION;
+ loader_info.structSize = sizeof(XrNegotiateLoaderInfo);
+ loader_info.minInterfaceVersion = 1;
+ loader_info.maxInterfaceVersion = XR_CURRENT_LOADER_RUNTIME_VERSION;
+ loader_info.minApiVersion = XR_MAKE_VERSION(1, 0, 0);
+ loader_info.maxApiVersion = XR_MAKE_VERSION(1, 0x3ff, 0xfff); // Maximum allowed version for this major version.
+
+ // Set up the runtime return structure
+ XrNegotiateRuntimeRequest runtime_info = {};
+ runtime_info.structType = XR_LOADER_INTERFACE_STRUCT_RUNTIME_REQUEST;
+ runtime_info.structVersion = XR_RUNTIME_INFO_STRUCT_VERSION;
+ runtime_info.structSize = sizeof(XrNegotiateRuntimeRequest);
+
+ // Skip calling the negotiate function and fail if the function pointer
+ // could not get loaded
+ XrResult res = XR_ERROR_RUNTIME_FAILURE;
+ if (nullptr != negotiate) {
+ res = negotiate(&loader_info, &runtime_info);
+ }
+ // If we supposedly succeeded, but got a nullptr for GetInstanceProcAddr
+ // then something still went wrong, so return with an error.
+ if (XR_SUCCEEDED(res)) {
+ uint32_t runtime_major = XR_VERSION_MAJOR(runtime_info.runtimeApiVersion);
+ uint32_t runtime_minor = XR_VERSION_MINOR(runtime_info.runtimeApiVersion);
+ uint32_t loader_major = XR_VERSION_MAJOR(XR_CURRENT_API_VERSION);
+ if (nullptr == runtime_info.getInstanceProcAddr) {
+ std::string error_message = "RuntimeInterface::LoadRuntime skipping manifest file ";
+ error_message += manifest_file->Filename();
+ error_message += ", negotiation succeeded but returned NULL getInstanceProcAddr";
+ LoaderLogger::LogErrorMessage(openxr_command, error_message);
+ res = XR_ERROR_FILE_CONTENTS_INVALID;
+ } else if (0 >= runtime_info.runtimeInterfaceVersion ||
+ XR_CURRENT_LOADER_RUNTIME_VERSION < runtime_info.runtimeInterfaceVersion) {
+ std::string error_message = "RuntimeInterface::LoadRuntime skipping manifest file ";
+ error_message += manifest_file->Filename();
+ error_message += ", negotiation succeeded but returned invalid interface version";
+ LoaderLogger::LogErrorMessage(openxr_command, error_message);
+ res = XR_ERROR_FILE_CONTENTS_INVALID;
+ } else if (runtime_major != loader_major || (runtime_major == 0 && runtime_minor == 0)) {
+ std::string error_message = "RuntimeInterface::LoadRuntime skipping manifest file ";
+ error_message += manifest_file->Filename();
+ error_message += ", OpenXR version returned not compatible with this loader";
+ LoaderLogger::LogErrorMessage(openxr_command, error_message);
+ res = XR_ERROR_FILE_CONTENTS_INVALID;
+ }
+ }
+#ifdef XR_KHR_LOADER_INIT_SUPPORT
+ if (XR_SUCCEEDED(res) && !forwardedInitLoader) {
+ // Forward initialize loader call, where possible and if we did not do so before.
+ PFN_xrVoidFunction initializeVoid = nullptr;
+ PFN_xrInitializeLoaderKHR initialize = nullptr;
+
+ // Now we may try asking xrGetInstanceProcAddr
+ if (XR_SUCCEEDED(runtime_info.getInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", &initializeVoid))) {
+ if (initializeVoid == nullptr) {
+ LoaderLogger::LogErrorMessage(openxr_command,
+ "RuntimeInterface::LoadRuntime got success from xrGetInstanceProcAddr "
+ "for xrInitializeLoaderKHR, but output a null pointer.");
+ res = XR_ERROR_RUNTIME_FAILURE;
+ } else {
+ initialize = reinterpret_cast<PFN_xrInitializeLoaderKHR>(initializeVoid);
+ }
+ }
+ if (initialize != nullptr) {
+ // we found the entry point one way or another.
+ LoaderLogger::LogInfoMessage(openxr_command,
+ "RuntimeInterface::LoadRuntime forwarding xrInitializeLoaderKHR call to runtime after "
+ "calling xrNegotiateLoaderRuntimeInterface.");
+ res = initialize(LoaderInitData::instance().getParam());
+ if (!XR_SUCCEEDED(res)) {
+ LoaderLogger::LogErrorMessage(openxr_command,
+ "RuntimeInterface::LoadRuntime forwarded call to xrInitializeLoaderKHR failed.");
+ }
+ }
+ }
+#endif
+ if (XR_FAILED(res)) {
+ std::string warning_message = "RuntimeInterface::LoadRuntime skipping manifest file ";
+ warning_message += manifest_file->Filename();
+ warning_message += ", negotiation failed with error ";
+ warning_message += std::to_string(res);
+ LoaderLogger::LogErrorMessage(openxr_command, warning_message);
+ LoaderPlatformLibraryClose(runtime_library);
+ return res;
+ }
+
+ std::string info_message = "RuntimeInterface::LoadRuntime succeeded loading runtime defined in manifest file ";
+ info_message += manifest_file->Filename();
+ info_message += " using interface version ";
+ info_message += std::to_string(runtime_info.runtimeInterfaceVersion);
+ info_message += " and OpenXR API version ";
+ info_message += std::to_string(XR_VERSION_MAJOR(runtime_info.runtimeApiVersion));
+ info_message += ".";
+ info_message += std::to_string(XR_VERSION_MINOR(runtime_info.runtimeApiVersion));
+ LoaderLogger::LogInfoMessage(openxr_command, info_message);
+
+ // Use this runtime
+ GetInstance().reset(new RuntimeInterface(runtime_library, runtime_info.getInstanceProcAddr));
+
+ // Grab the list of extensions this runtime supports for easy filtering after the
+ // xrCreateInstance call
+ std::vector<std::string> supported_extensions;
+ std::vector<XrExtensionProperties> extension_properties;
+ GetInstance()->GetInstanceExtensionProperties(extension_properties);
+ supported_extensions.reserve(extension_properties.size());
+ for (XrExtensionProperties ext_prop : extension_properties) {
+ supported_extensions.emplace_back(ext_prop.extensionName);
+ }
+ GetInstance()->SetSupportedExtensions(supported_extensions);
+
+ return XR_SUCCESS;
+}
+
+XrResult RuntimeInterface::LoadRuntime(const std::string& openxr_command) {
+ // If something's already loaded, we're done here.
+ if (GetInstance() != nullptr) {
+ return XR_SUCCESS;
+ }
+#ifdef XR_KHR_LOADER_INIT_SUPPORT
+
+ if (!LoaderInitData::instance().initialized()) {
+ LoaderLogger::LogErrorMessage(
+ openxr_command, "RuntimeInterface::LoadRuntime cannot run because xrInitializeLoaderKHR was not successfully called.");
+ return XR_ERROR_INITIALIZATION_FAILED;
+ }
+#endif // XR_KHR_LOADER_INIT_SUPPORT
+
+ std::vector<std::unique_ptr<RuntimeManifestFile>> runtime_manifest_files = {};
+
+ // Find the available runtimes which we may need to report information for.
+ XrResult last_error = RuntimeManifestFile::FindManifestFiles(runtime_manifest_files);
+ if (XR_FAILED(last_error)) {
+ LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntimes - unknown error");
+ } else {
+ last_error = XR_ERROR_RUNTIME_UNAVAILABLE;
+ for (std::unique_ptr<RuntimeManifestFile>& manifest_file : runtime_manifest_files) {
+ last_error = RuntimeInterface::TryLoadingSingleRuntime(openxr_command, manifest_file);
+ if (XR_SUCCEEDED(last_error)) {
+ break;
+ }
+ }
+ }
+
+ // Unsuccessful in loading any runtime, throw the runtime unavailable message.
+ if (XR_FAILED(last_error)) {
+ LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntimes - failed to load a runtime");
+ last_error = XR_ERROR_RUNTIME_UNAVAILABLE;
+ }
+
+ return last_error;
+}
+
+void RuntimeInterface::UnloadRuntime(const std::string& openxr_command) {
+ if (GetInstance()) {
+ LoaderLogger::LogInfoMessage(openxr_command, "RuntimeInterface::UnloadRuntime - Unloading RuntimeInterface");
+ GetInstance().reset();
+ }
+}
+
+XrResult RuntimeInterface::GetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function) {
+ return GetInstance()->_get_instance_proc_addr(instance, name, function);
+}
+
+const XrGeneratedDispatchTable* RuntimeInterface::GetDispatchTable(XrInstance instance) {
+ XrGeneratedDispatchTable* table = nullptr;
+ std::lock_guard<std::mutex> mlock(GetInstance()->_dispatch_table_mutex);
+ auto it = GetInstance()->_dispatch_table_map.find(instance);
+ if (it != GetInstance()->_dispatch_table_map.end()) {
+ table = it->second.get();
+ }
+ return table;
+}
+
+const XrGeneratedDispatchTable* RuntimeInterface::GetDebugUtilsMessengerDispatchTable(XrDebugUtilsMessengerEXT messenger) {
+ XrInstance runtime_instance = XR_NULL_HANDLE;
+ {
+ std::lock_guard<std::mutex> mlock(GetInstance()->_messenger_to_instance_mutex);
+ auto it = GetInstance()->_messenger_to_instance_map.find(messenger);
+ if (it != GetInstance()->_messenger_to_instance_map.end()) {
+ runtime_instance = it->second;
+ }
+ }
+ return GetDispatchTable(runtime_instance);
+}
+
+RuntimeInterface::RuntimeInterface(LoaderPlatformLibraryHandle runtime_library, PFN_xrGetInstanceProcAddr get_instance_proc_addr)
+ : _runtime_library(runtime_library), _get_instance_proc_addr(get_instance_proc_addr) {}
+
+RuntimeInterface::~RuntimeInterface() {
+ std::string info_message = "RuntimeInterface being destroyed.";
+ LoaderLogger::LogInfoMessage("", info_message);
+ {
+ std::lock_guard<std::mutex> mlock(_dispatch_table_mutex);
+ _dispatch_table_map.clear();
+ }
+ LoaderPlatformLibraryClose(_runtime_library);
+}
+
+void RuntimeInterface::GetInstanceExtensionProperties(std::vector<XrExtensionProperties>& extension_properties) {
+ std::vector<XrExtensionProperties> runtime_extension_properties;
+ PFN_xrEnumerateInstanceExtensionProperties rt_xrEnumerateInstanceExtensionProperties;
+ _get_instance_proc_addr(XR_NULL_HANDLE, "xrEnumerateInstanceExtensionProperties",
+ reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrEnumerateInstanceExtensionProperties));
+ uint32_t count = 0;
+ uint32_t count_output = 0;
+ // Get the count from the runtime
+ rt_xrEnumerateInstanceExtensionProperties(nullptr, count, &count_output, nullptr);
+ if (count_output > 0) {
+ runtime_extension_properties.resize(count_output);
+ count = count_output;
+ for (XrExtensionProperties& ext_prop : runtime_extension_properties) {
+ ext_prop.type = XR_TYPE_EXTENSION_PROPERTIES;
+ ext_prop.next = nullptr;
+ }
+ rt_xrEnumerateInstanceExtensionProperties(nullptr, count, &count_output, runtime_extension_properties.data());
+ }
+ size_t ext_count = runtime_extension_properties.size();
+ size_t props_count = extension_properties.size();
+ for (size_t ext = 0; ext < ext_count; ++ext) {
+ bool found = false;
+ for (size_t prop = 0; prop < props_count; ++prop) {
+ // If we find it, then make sure the spec version matches that of the runtime instead of the
+ // layer.
+ if (strcmp(extension_properties[prop].extensionName, runtime_extension_properties[ext].extensionName) == 0) {
+ // Make sure the spec version used is the runtime's
+ extension_properties[prop].extensionVersion = runtime_extension_properties[ext].extensionVersion;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ extension_properties.push_back(runtime_extension_properties[ext]);
+ }
+ }
+}
+
+XrResult RuntimeInterface::CreateInstance(const XrInstanceCreateInfo* info, XrInstance* instance) {
+ XrResult res = XR_SUCCESS;
+ bool create_succeeded = false;
+ PFN_xrCreateInstance rt_xrCreateInstance;
+ _get_instance_proc_addr(XR_NULL_HANDLE, "xrCreateInstance", reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrCreateInstance));
+ res = rt_xrCreateInstance(info, instance);
+ if (XR_SUCCEEDED(res)) {
+ create_succeeded = true;
+ std::unique_ptr<XrGeneratedDispatchTable> dispatch_table(new XrGeneratedDispatchTable());
+ GeneratedXrPopulateDispatchTable(dispatch_table.get(), *instance, _get_instance_proc_addr);
+ std::lock_guard<std::mutex> mlock(_dispatch_table_mutex);
+ _dispatch_table_map[*instance] = std::move(dispatch_table);
+ }
+
+ // If the failure occurred during the populate, clean up the instance we had picked up from the runtime
+ if (XR_FAILED(res) && create_succeeded) {
+ PFN_xrDestroyInstance rt_xrDestroyInstance;
+ _get_instance_proc_addr(*instance, "xrDestroyInstance", reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrDestroyInstance));
+ rt_xrDestroyInstance(*instance);
+ *instance = XR_NULL_HANDLE;
+ }
+
+ return res;
+}
+
+XrResult RuntimeInterface::DestroyInstance(XrInstance instance) {
+ if (XR_NULL_HANDLE != instance) {
+ // Destroy the dispatch table for this instance first
+ {
+ std::lock_guard<std::mutex> mlock(_dispatch_table_mutex);
+ auto map_iter = _dispatch_table_map.find(instance);
+ if (map_iter != _dispatch_table_map.end()) {
+ _dispatch_table_map.erase(map_iter);
+ }
+ }
+ // Now delete the instance
+ PFN_xrDestroyInstance rt_xrDestroyInstance;
+ _get_instance_proc_addr(instance, "xrDestroyInstance", reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrDestroyInstance));
+ rt_xrDestroyInstance(instance);
+ }
+ return XR_SUCCESS;
+}
+
+bool RuntimeInterface::TrackDebugMessenger(XrInstance instance, XrDebugUtilsMessengerEXT messenger) {
+ std::lock_guard<std::mutex> mlock(_messenger_to_instance_mutex);
+ _messenger_to_instance_map[messenger] = instance;
+ return true;
+}
+
+void RuntimeInterface::ForgetDebugMessenger(XrDebugUtilsMessengerEXT messenger) {
+ if (XR_NULL_HANDLE != messenger) {
+ std::lock_guard<std::mutex> mlock(_messenger_to_instance_mutex);
+ _messenger_to_instance_map.erase(messenger);
+ }
+}
+
+void RuntimeInterface::SetSupportedExtensions(std::vector<std::string>& supported_extensions) {
+ _supported_extensions = supported_extensions;
+}
+
+bool RuntimeInterface::SupportsExtension(const std::string& extension_name) {
+ bool found_prop = false;
+ for (const std::string& supported_extension : _supported_extensions) {
+ if (supported_extension == extension_name) {
+ found_prop = true;
+ break;
+ }
+ }
+ return found_prop;
+}