Path: blob/master/thirdparty/openxr/src/loader/runtime_interface.cpp
9917 views
// Copyright (c) 2017-2025 The Khronos Group Inc.1// Copyright (c) 2017-2019 Valve Corporation2// Copyright (c) 2017-2019 LunarG, Inc.3//4// SPDX-License-Identifier: Apache-2.0 OR MIT5//6// Initial Author: Mark Young <[email protected]>7//89#include "runtime_interface.hpp"1011#include <openxr/openxr.h>12#include <openxr/openxr_loader_negotiation.h>1314#include "manifest_file.hpp"15#include "loader_init_data.hpp"16#include "loader_logger.hpp"17#include "loader_platform.hpp"18#include "xr_generated_dispatch_table_core.h"1920#include <cstring>21#include <memory>22#include <mutex>23#include <string>24#include <unordered_map>25#include <utility>26#include <vector>2728#ifdef XR_USE_PLATFORM_ANDROID29#include <json/value.h>3031// Needed for the loader init struct32#include <xr_dependencies.h>33#include <openxr/openxr_platform.h>34#endif // XR_USE_PLATFORM_ANDROID3536#if defined(XR_KHR_LOADER_INIT_SUPPORT) && defined(XR_USE_PLATFORM_ANDROID)37XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest) {38using wrap::android::content::Context;39auto& initData = LoaderInitData::instance();40if (!initData.initialized()) {41return XR_ERROR_INITIALIZATION_FAILED;42}43auto context = Context(reinterpret_cast<jobject>(initData.getData().applicationContext));44if (context.isNull()) {45return XR_ERROR_INITIALIZATION_FAILED;46}47Json::Value virtualManifest;48if (0 != openxr_android::getActiveRuntimeVirtualManifest(context, virtualManifest)) {49return XR_ERROR_INITIALIZATION_FAILED;50}51out_manifest = virtualManifest;52return XR_SUCCESS;53}54#endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT)5556XrResult RuntimeInterface::TryLoadingSingleRuntime(const std::string& openxr_command,57std::unique_ptr<RuntimeManifestFile>& manifest_file) {58LoaderPlatformLibraryHandle runtime_library = LoaderPlatformLibraryOpen(manifest_file->LibraryPath());59if (nullptr == runtime_library) {60std::string library_message = LoaderPlatformLibraryOpenError(manifest_file->LibraryPath());61std::string warning_message = "RuntimeInterface::LoadRuntime skipping manifest file ";62warning_message += manifest_file->Filename();63warning_message += ", failed to load with message \"";64warning_message += library_message;65warning_message += "\"";66LoaderLogger::LogErrorMessage(openxr_command, warning_message);67return XR_ERROR_FILE_ACCESS_ERROR;68}69#ifdef XR_KHR_LOADER_INIT_SUPPORT70if (!LoaderInitData::instance().initialized()) {71LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntime skipping manifest file " +72manifest_file->Filename() +73" because xrInitializeLoaderKHR was not yet called.");7475LoaderPlatformLibraryClose(runtime_library);76return XR_ERROR_VALIDATION_FAILURE;77}78bool forwardedInitLoader = false;79{80// If we have xrInitializeLoaderKHR exposed as an export, forward call to it.81const auto function_name = manifest_file->GetFunctionName("xrInitializeLoaderKHR");82auto initLoader =83reinterpret_cast<PFN_xrInitializeLoaderKHR>(LoaderPlatformLibraryGetProcAddr(runtime_library, function_name));84if (initLoader != nullptr) {85// we found the entry point one way or another.86LoaderLogger::LogInfoMessage(openxr_command,87"RuntimeInterface::LoadRuntime forwarding xrInitializeLoaderKHR call to runtime before "88"calling xrNegotiateLoaderRuntimeInterface.");89XrResult res = initLoader(LoaderInitData::instance().getParam());90if (!XR_SUCCEEDED(res)) {91LoaderLogger::LogErrorMessage(openxr_command,92"RuntimeInterface::LoadRuntime forwarded call to xrInitializeLoaderKHR failed.");9394LoaderPlatformLibraryClose(runtime_library);95return res;96}97forwardedInitLoader = true;98}99}100#endif101102// Get and settle on an runtime interface version (using any provided name if required).103std::string function_name = manifest_file->GetFunctionName("xrNegotiateLoaderRuntimeInterface");104auto negotiate =105reinterpret_cast<PFN_xrNegotiateLoaderRuntimeInterface>(LoaderPlatformLibraryGetProcAddr(runtime_library, function_name));106107// Loader info for negotiation108XrNegotiateLoaderInfo loader_info = {};109loader_info.structType = XR_LOADER_INTERFACE_STRUCT_LOADER_INFO;110loader_info.structVersion = XR_LOADER_INFO_STRUCT_VERSION;111loader_info.structSize = sizeof(XrNegotiateLoaderInfo);112loader_info.minInterfaceVersion = 1;113loader_info.maxInterfaceVersion = XR_CURRENT_LOADER_RUNTIME_VERSION;114loader_info.minApiVersion = XR_MAKE_VERSION(1, 0, 0);115loader_info.maxApiVersion = XR_MAKE_VERSION(1, 0x3ff, 0xfff); // Maximum allowed version for this major version.116117// Set up the runtime return structure118XrNegotiateRuntimeRequest runtime_info = {};119runtime_info.structType = XR_LOADER_INTERFACE_STRUCT_RUNTIME_REQUEST;120runtime_info.structVersion = XR_RUNTIME_INFO_STRUCT_VERSION;121runtime_info.structSize = sizeof(XrNegotiateRuntimeRequest);122123// Skip calling the negotiate function and fail if the function pointer124// could not get loaded125XrResult res = XR_ERROR_RUNTIME_FAILURE;126if (nullptr != negotiate) {127res = negotiate(&loader_info, &runtime_info);128} else {129std::string error_message = "RuntimeInterface::LoadRuntime failed to find negotiate function ";130error_message += function_name;131LoaderLogger::LogErrorMessage(openxr_command, error_message);132}133// If we supposedly succeeded, but got a nullptr for GetInstanceProcAddr134// then something still went wrong, so return with an error.135if (XR_SUCCEEDED(res)) {136uint32_t runtime_major = XR_VERSION_MAJOR(runtime_info.runtimeApiVersion);137uint32_t runtime_minor = XR_VERSION_MINOR(runtime_info.runtimeApiVersion);138uint32_t loader_major = XR_VERSION_MAJOR(XR_CURRENT_API_VERSION);139if (nullptr == runtime_info.getInstanceProcAddr) {140std::string error_message = "RuntimeInterface::LoadRuntime skipping manifest file ";141error_message += manifest_file->Filename();142error_message += ", negotiation succeeded but returned NULL getInstanceProcAddr";143LoaderLogger::LogErrorMessage(openxr_command, error_message);144res = XR_ERROR_FILE_CONTENTS_INVALID;145} else if (0 >= runtime_info.runtimeInterfaceVersion ||146XR_CURRENT_LOADER_RUNTIME_VERSION < runtime_info.runtimeInterfaceVersion) {147std::string error_message = "RuntimeInterface::LoadRuntime skipping manifest file ";148error_message += manifest_file->Filename();149error_message += ", negotiation succeeded but returned invalid interface version";150LoaderLogger::LogErrorMessage(openxr_command, error_message);151res = XR_ERROR_FILE_CONTENTS_INVALID;152} else if (runtime_major != loader_major || (runtime_major == 0 && runtime_minor == 0)) {153std::string error_message = "RuntimeInterface::LoadRuntime skipping manifest file ";154error_message += manifest_file->Filename();155error_message += ", OpenXR version returned not compatible with this loader";156LoaderLogger::LogErrorMessage(openxr_command, error_message);157res = XR_ERROR_FILE_CONTENTS_INVALID;158}159}160#ifdef XR_KHR_LOADER_INIT_SUPPORT161if (XR_SUCCEEDED(res) && !forwardedInitLoader) {162// Forward initialize loader call, where possible and if we did not do so before.163PFN_xrVoidFunction initializeVoid = nullptr;164PFN_xrInitializeLoaderKHR initialize = nullptr;165166// Now we may try asking xrGetInstanceProcAddr167if (XR_SUCCEEDED(runtime_info.getInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", &initializeVoid))) {168if (initializeVoid == nullptr) {169LoaderLogger::LogErrorMessage(openxr_command,170"RuntimeInterface::LoadRuntime got success from xrGetInstanceProcAddr "171"for xrInitializeLoaderKHR, but output a null pointer.");172res = XR_ERROR_RUNTIME_FAILURE;173} else {174initialize = reinterpret_cast<PFN_xrInitializeLoaderKHR>(initializeVoid);175}176}177if (initialize != nullptr) {178// we found the entry point one way or another.179LoaderLogger::LogInfoMessage(openxr_command,180"RuntimeInterface::LoadRuntime forwarding xrInitializeLoaderKHR call to runtime after "181"calling xrNegotiateLoaderRuntimeInterface.");182res = initialize(LoaderInitData::instance().getParam());183if (!XR_SUCCEEDED(res)) {184LoaderLogger::LogErrorMessage(openxr_command,185"RuntimeInterface::LoadRuntime forwarded call to xrInitializeLoaderKHR failed.");186}187}188}189#endif190if (XR_FAILED(res)) {191std::string warning_message = "RuntimeInterface::LoadRuntime skipping manifest file ";192warning_message += manifest_file->Filename();193warning_message += ", negotiation failed with error ";194warning_message += std::to_string(res);195LoaderLogger::LogErrorMessage(openxr_command, warning_message);196LoaderPlatformLibraryClose(runtime_library);197return res;198}199200std::string info_message = "RuntimeInterface::LoadRuntime succeeded loading runtime defined in manifest file ";201info_message += manifest_file->Filename();202info_message += " using interface version ";203info_message += std::to_string(runtime_info.runtimeInterfaceVersion);204info_message += " and OpenXR API version ";205info_message += std::to_string(XR_VERSION_MAJOR(runtime_info.runtimeApiVersion));206info_message += ".";207info_message += std::to_string(XR_VERSION_MINOR(runtime_info.runtimeApiVersion));208LoaderLogger::LogInfoMessage(openxr_command, info_message);209210// Use this runtime211GetInstance().reset(new RuntimeInterface(runtime_library, runtime_info.getInstanceProcAddr));212213// Grab the list of extensions this runtime supports for easy filtering after the214// xrCreateInstance call215std::vector<std::string> supported_extensions;216std::vector<XrExtensionProperties> extension_properties;217GetInstance()->GetInstanceExtensionProperties(extension_properties);218supported_extensions.reserve(extension_properties.size());219for (XrExtensionProperties ext_prop : extension_properties) {220supported_extensions.emplace_back(ext_prop.extensionName);221}222GetInstance()->SetSupportedExtensions(supported_extensions);223224return XR_SUCCESS;225}226227XrResult RuntimeInterface::LoadRuntime(const std::string& openxr_command) {228// If something's already loaded, we're done here.229if (GetInstance() != nullptr) {230return XR_SUCCESS;231}232#ifdef XR_KHR_LOADER_INIT_SUPPORT233if (!LoaderInitData::instance().initialized()) {234LoaderLogger::LogErrorMessage(235openxr_command, "RuntimeInterface::LoadRuntime cannot run because xrInitializeLoaderKHR was not successfully called.");236return XR_ERROR_INITIALIZATION_FAILED;237}238#endif // XR_KHR_LOADER_INIT_SUPPORT239240std::vector<std::unique_ptr<RuntimeManifestFile>> runtime_manifest_files = {};241242// Find the available runtimes which we may need to report information for.243XrResult last_error = RuntimeManifestFile::FindManifestFiles(openxr_command, runtime_manifest_files);244if (XR_FAILED(last_error)) {245LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntimes - unknown error");246} else {247last_error = XR_ERROR_RUNTIME_UNAVAILABLE;248for (std::unique_ptr<RuntimeManifestFile>& manifest_file : runtime_manifest_files) {249last_error = RuntimeInterface::TryLoadingSingleRuntime(openxr_command, manifest_file);250if (XR_SUCCEEDED(last_error)) {251break;252}253}254}255256// Unsuccessful in loading any runtime, throw the runtime unavailable message.257if (XR_FAILED(last_error)) {258LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntimes - failed to load a runtime");259last_error = XR_ERROR_RUNTIME_UNAVAILABLE;260}261262return last_error;263}264265void RuntimeInterface::UnloadRuntime(const std::string& openxr_command) {266if (GetInstance()) {267LoaderLogger::LogInfoMessage(openxr_command, "RuntimeInterface::UnloadRuntime - Unloading RuntimeInterface");268GetInstance().reset();269}270}271272XrResult RuntimeInterface::GetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function) {273return GetInstance()->_get_instance_proc_addr(instance, name, function);274}275276const XrGeneratedDispatchTableCore* RuntimeInterface::GetDispatchTable(XrInstance instance) {277XrGeneratedDispatchTableCore* table = nullptr;278std::lock_guard<std::mutex> mlock(GetInstance()->_dispatch_table_mutex);279auto it = GetInstance()->_dispatch_table_map.find(instance);280if (it != GetInstance()->_dispatch_table_map.end()) {281table = it->second.get();282}283return table;284}285286const XrGeneratedDispatchTableCore* RuntimeInterface::GetDebugUtilsMessengerDispatchTable(XrDebugUtilsMessengerEXT messenger) {287XrInstance runtime_instance = XR_NULL_HANDLE;288{289std::lock_guard<std::mutex> mlock(GetInstance()->_messenger_to_instance_mutex);290auto it = GetInstance()->_messenger_to_instance_map.find(messenger);291if (it != GetInstance()->_messenger_to_instance_map.end()) {292runtime_instance = it->second;293}294}295return GetDispatchTable(runtime_instance);296}297298RuntimeInterface::RuntimeInterface(LoaderPlatformLibraryHandle runtime_library, PFN_xrGetInstanceProcAddr get_instance_proc_addr)299: _runtime_library(runtime_library), _get_instance_proc_addr(get_instance_proc_addr) {}300301RuntimeInterface::~RuntimeInterface() {302std::string info_message = "RuntimeInterface being destroyed.";303LoaderLogger::LogInfoMessage("", info_message);304{305std::lock_guard<std::mutex> mlock(_dispatch_table_mutex);306_dispatch_table_map.clear();307}308LoaderPlatformLibraryClose(_runtime_library);309}310311void RuntimeInterface::GetInstanceExtensionProperties(std::vector<XrExtensionProperties>& extension_properties) {312std::vector<XrExtensionProperties> runtime_extension_properties;313PFN_xrEnumerateInstanceExtensionProperties rt_xrEnumerateInstanceExtensionProperties;314_get_instance_proc_addr(XR_NULL_HANDLE, "xrEnumerateInstanceExtensionProperties",315reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrEnumerateInstanceExtensionProperties));316uint32_t count = 0;317uint32_t count_output = 0;318// Get the count from the runtime319rt_xrEnumerateInstanceExtensionProperties(nullptr, count, &count_output, nullptr);320if (count_output > 0) {321XrExtensionProperties example_properties{};322example_properties.type = XR_TYPE_EXTENSION_PROPERTIES;323runtime_extension_properties.resize(count_output, example_properties);324count = count_output;325rt_xrEnumerateInstanceExtensionProperties(nullptr, count, &count_output, runtime_extension_properties.data());326}327size_t ext_count = runtime_extension_properties.size();328size_t props_count = extension_properties.size();329for (size_t ext = 0; ext < ext_count; ++ext) {330bool found = false;331for (size_t prop = 0; prop < props_count; ++prop) {332// If we find it, then make sure the spec version matches that of the runtime instead of the333// layer.334if (strcmp(extension_properties[prop].extensionName, runtime_extension_properties[ext].extensionName) == 0) {335// Make sure the spec version used is the runtime's336extension_properties[prop].extensionVersion = runtime_extension_properties[ext].extensionVersion;337found = true;338break;339}340}341if (!found) {342extension_properties.push_back(runtime_extension_properties[ext]);343}344}345}346347XrResult RuntimeInterface::CreateInstance(const XrInstanceCreateInfo* info, XrInstance* instance) {348XrResult res = XR_SUCCESS;349bool create_succeeded = false;350PFN_xrCreateInstance rt_xrCreateInstance;351_get_instance_proc_addr(XR_NULL_HANDLE, "xrCreateInstance", reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrCreateInstance));352res = rt_xrCreateInstance(info, instance);353if (XR_SUCCEEDED(res)) {354create_succeeded = true;355std::unique_ptr<XrGeneratedDispatchTableCore> dispatch_table(new XrGeneratedDispatchTableCore());356GeneratedXrPopulateDispatchTableCore(dispatch_table.get(), *instance, _get_instance_proc_addr);357std::lock_guard<std::mutex> mlock(_dispatch_table_mutex);358_dispatch_table_map[*instance] = std::move(dispatch_table);359}360361// If the failure occurred during the populate, clean up the instance we had picked up from the runtime362if (XR_FAILED(res) && create_succeeded) {363PFN_xrDestroyInstance rt_xrDestroyInstance;364_get_instance_proc_addr(*instance, "xrDestroyInstance", reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrDestroyInstance));365rt_xrDestroyInstance(*instance);366*instance = XR_NULL_HANDLE;367}368369return res;370}371372XrResult RuntimeInterface::DestroyInstance(XrInstance instance) {373if (XR_NULL_HANDLE != instance) {374// Destroy the dispatch table for this instance first375{376std::lock_guard<std::mutex> mlock(_dispatch_table_mutex);377auto map_iter = _dispatch_table_map.find(instance);378if (map_iter != _dispatch_table_map.end()) {379_dispatch_table_map.erase(map_iter);380}381}382// Now delete the instance383PFN_xrDestroyInstance rt_xrDestroyInstance;384_get_instance_proc_addr(instance, "xrDestroyInstance", reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrDestroyInstance));385rt_xrDestroyInstance(instance);386}387return XR_SUCCESS;388}389390bool RuntimeInterface::TrackDebugMessenger(XrInstance instance, XrDebugUtilsMessengerEXT messenger) {391std::lock_guard<std::mutex> mlock(_messenger_to_instance_mutex);392_messenger_to_instance_map[messenger] = instance;393return true;394}395396void RuntimeInterface::ForgetDebugMessenger(XrDebugUtilsMessengerEXT messenger) {397if (XR_NULL_HANDLE != messenger) {398std::lock_guard<std::mutex> mlock(_messenger_to_instance_mutex);399_messenger_to_instance_map.erase(messenger);400}401}402403void RuntimeInterface::SetSupportedExtensions(std::vector<std::string>& supported_extensions) {404_supported_extensions = supported_extensions;405}406407bool RuntimeInterface::SupportsExtension(const std::string& extension_name) {408bool found_prop = false;409for (const std::string& supported_extension : _supported_extensions) {410if (supported_extension == extension_name) {411found_prop = true;412break;413}414}415return found_prop;416}417418419