Path: blob/master/thirdparty/openxr/src/loader/runtime_interface.cpp
20879 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 "loader_properties.hpp"19#include "xr_generated_dispatch_table_core.h"2021#include <cstring>22#include <memory>23#include <mutex>24#include <string>25#include <unordered_map>26#include <utility>27#include <vector>2829#if defined(XR_USE_PLATFORM_ANDROID) && defined(XR_HAS_REQUIRED_PLATFORM_LOADER_INIT_STRUCT)30#include <json/value.h>3132XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest) {33using wrap::android::content::Context;34auto& initData = LoaderInitData::instance();35if (!initData.initialized()) {36return XR_ERROR_INITIALIZATION_FAILED;37}38auto context = Context(reinterpret_cast<jobject>(initData.getPlatformData().applicationContext));39if (context.isNull()) {40return XR_ERROR_INITIALIZATION_FAILED;41}42Json::Value virtualManifest;43if (0 != openxr_android::getActiveRuntimeVirtualManifest(context, virtualManifest)) {44return XR_ERROR_INITIALIZATION_FAILED;45}46out_manifest = virtualManifest;47return XR_SUCCESS;48}49#endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_HAS_REQUIRED_PLATFORM_LOADER_INIT_STRUCT)5051XrResult RuntimeInterface::TryLoadingSingleRuntime(const std::string& openxr_command,52std::unique_ptr<RuntimeManifestFile>& manifest_file) {53LoaderPlatformLibraryHandle runtime_library = LoaderPlatformLibraryOpen(manifest_file->LibraryPath());54if (nullptr == runtime_library) {55std::string library_message = LoaderPlatformLibraryOpenError(manifest_file->LibraryPath());56std::string warning_message = "RuntimeInterface::LoadRuntime skipping manifest file ";57warning_message += manifest_file->Filename();58warning_message += ", failed to load with message \"";59warning_message += library_message;60warning_message += "\"";61LoaderLogger::LogErrorMessage(openxr_command, warning_message);62return XR_ERROR_FILE_ACCESS_ERROR;63}6465#if defined(XR_HAS_REQUIRED_PLATFORM_LOADER_INIT_STRUCT)66if (!LoaderInitData::instance().initialized()) {67LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntime skipping manifest file " +68manifest_file->Filename() +69" because xrInitializeLoaderKHR was not yet called.");7071LoaderPlatformLibraryClose(runtime_library);72return XR_ERROR_VALIDATION_FAILURE;73}74#endif // defined(XR_HAS_REQUIRED_PLATFORM_LOADER_INIT_STRUCT)7576bool forwardedInitLoader = false;77if (LoaderInitData::instance().initialized() && LoaderInitData::instance().getPlatformParam() != nullptr) {78// If we have xrInitializeLoaderKHR exposed as an export, forward call to it.79const auto function_name = manifest_file->GetFunctionName("xrInitializeLoaderKHR");80auto initLoader =81reinterpret_cast<PFN_xrInitializeLoaderKHR>(LoaderPlatformLibraryGetProcAddr(runtime_library, function_name));8283if (initLoader != nullptr) {84// we found the entry point one way or another.85LoaderLogger::LogInfoMessage(openxr_command,86"RuntimeInterface::LoadRuntime forwarding xrInitializeLoaderKHR call to runtime before "87"calling xrNegotiateLoaderRuntimeInterface.");88XrResult res = initLoader(LoaderInitData::instance().getPlatformParam());89if (!XR_SUCCEEDED(res)) {90LoaderLogger::LogErrorMessage(openxr_command,91"RuntimeInterface::LoadRuntime forwarded call to xrInitializeLoaderKHR failed.");9293LoaderPlatformLibraryClose(runtime_library);94return res;95}96forwardedInitLoader = true;97}98}99100// Get and settle on an runtime interface version (using any provided name if required).101std::string function_name = manifest_file->GetFunctionName("xrNegotiateLoaderRuntimeInterface");102auto negotiate =103reinterpret_cast<PFN_xrNegotiateLoaderRuntimeInterface>(LoaderPlatformLibraryGetProcAddr(runtime_library, function_name));104105// Loader info for negotiation106XrNegotiateLoaderInfo loader_info = {};107loader_info.structType = XR_LOADER_INTERFACE_STRUCT_LOADER_INFO;108loader_info.structVersion = XR_LOADER_INFO_STRUCT_VERSION;109loader_info.structSize = sizeof(XrNegotiateLoaderInfo);110loader_info.minInterfaceVersion = 1;111loader_info.maxInterfaceVersion = XR_CURRENT_LOADER_RUNTIME_VERSION;112loader_info.minApiVersion = XR_MAKE_VERSION(1, 0, 0);113loader_info.maxApiVersion = XR_MAKE_VERSION(1, 0x3ff, 0xfff); // Maximum allowed version for this major version.114115// Set up the runtime return structure116XrNegotiateRuntimeRequest runtime_info = {};117runtime_info.structType = XR_LOADER_INTERFACE_STRUCT_RUNTIME_REQUEST;118runtime_info.structVersion = XR_RUNTIME_INFO_STRUCT_VERSION;119runtime_info.structSize = sizeof(XrNegotiateRuntimeRequest);120121// Skip calling the negotiate function and fail if the function pointer122// could not get loaded123XrResult res = XR_ERROR_RUNTIME_FAILURE;124if (nullptr != negotiate) {125res = negotiate(&loader_info, &runtime_info);126} else {127std::string error_message = "RuntimeInterface::LoadRuntime failed to find negotiate function ";128error_message += function_name;129LoaderLogger::LogErrorMessage(openxr_command, error_message);130}131// If we supposedly succeeded, but got a nullptr for GetInstanceProcAddr132// then something still went wrong, so return with an error.133if (XR_SUCCEEDED(res)) {134uint32_t runtime_major = XR_VERSION_MAJOR(runtime_info.runtimeApiVersion);135uint32_t runtime_minor = XR_VERSION_MINOR(runtime_info.runtimeApiVersion);136uint32_t loader_major = XR_VERSION_MAJOR(XR_CURRENT_API_VERSION);137if (nullptr == runtime_info.getInstanceProcAddr) {138std::string error_message = "RuntimeInterface::LoadRuntime skipping manifest file ";139error_message += manifest_file->Filename();140error_message += ", negotiation succeeded but returned NULL getInstanceProcAddr";141LoaderLogger::LogErrorMessage(openxr_command, error_message);142res = XR_ERROR_FILE_CONTENTS_INVALID;143} else if (0 >= runtime_info.runtimeInterfaceVersion ||144XR_CURRENT_LOADER_RUNTIME_VERSION < runtime_info.runtimeInterfaceVersion) {145std::string error_message = "RuntimeInterface::LoadRuntime skipping manifest file ";146error_message += manifest_file->Filename();147error_message += ", negotiation succeeded but returned invalid interface version";148LoaderLogger::LogErrorMessage(openxr_command, error_message);149res = XR_ERROR_FILE_CONTENTS_INVALID;150} else if (runtime_major != loader_major || (runtime_major == 0 && runtime_minor == 0)) {151std::string error_message = "RuntimeInterface::LoadRuntime skipping manifest file ";152error_message += manifest_file->Filename();153error_message += ", OpenXR version returned not compatible with this loader";154LoaderLogger::LogErrorMessage(openxr_command, error_message);155res = XR_ERROR_FILE_CONTENTS_INVALID;156}157}158159if (XR_SUCCEEDED(res) && !forwardedInitLoader && LoaderInitData::instance().getPlatformParam() != nullptr) {160// Forward initialize loader call, where possible and if we did not do so before.161PFN_xrVoidFunction initializeVoid = nullptr;162PFN_xrInitializeLoaderKHR initialize = nullptr;163164// xrInitializeLoaderKHR was not exposed as an export, so now we may try asking with xrGetInstanceProcAddr165if (XR_SUCCEEDED(runtime_info.getInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", &initializeVoid))) {166if (initializeVoid == nullptr) {167LoaderLogger::LogErrorMessage(openxr_command,168"RuntimeInterface::LoadRuntime got success from xrGetInstanceProcAddr "169"for xrInitializeLoaderKHR, but output a null pointer.");170res = XR_ERROR_RUNTIME_FAILURE;171} else {172initialize = reinterpret_cast<PFN_xrInitializeLoaderKHR>(initializeVoid);173}174}175if (initialize != nullptr) {176// we found the entry point one way or another.177LoaderLogger::LogInfoMessage(openxr_command,178"RuntimeInterface::LoadRuntime forwarding xrInitializeLoaderKHR call to runtime after "179"calling xrNegotiateLoaderRuntimeInterface.");180res = initialize(LoaderInitData::instance().getPlatformParam());181if (!XR_SUCCEEDED(res)) {182LoaderLogger::LogErrorMessage(openxr_command,183"RuntimeInterface::LoadRuntime forwarded call to xrInitializeLoaderKHR failed.");184}185}186}187188if (XR_FAILED(res)) {189std::string warning_message = "RuntimeInterface::LoadRuntime skipping manifest file ";190warning_message += manifest_file->Filename();191warning_message += ", negotiation failed with error ";192warning_message += std::to_string(res);193LoaderLogger::LogErrorMessage(openxr_command, warning_message);194LoaderPlatformLibraryClose(runtime_library);195return res;196}197198std::string info_message = "RuntimeInterface::LoadRuntime succeeded loading runtime defined in manifest file ";199info_message += manifest_file->Filename();200info_message += " using interface version ";201info_message += std::to_string(runtime_info.runtimeInterfaceVersion);202info_message += " and OpenXR API version ";203info_message += std::to_string(XR_VERSION_MAJOR(runtime_info.runtimeApiVersion));204info_message += ".";205info_message += std::to_string(XR_VERSION_MINOR(runtime_info.runtimeApiVersion));206LoaderLogger::LogInfoMessage(openxr_command, info_message);207208// Use this runtime209GetInstance().reset(new RuntimeInterface(runtime_library, runtime_info.getInstanceProcAddr));210211// Grab the list of extensions this runtime supports for easy filtering after the212// xrCreateInstance call213std::vector<std::string> supported_extensions;214std::vector<XrExtensionProperties> extension_properties;215GetInstance()->GetInstanceExtensionProperties(extension_properties);216supported_extensions.reserve(extension_properties.size());217for (XrExtensionProperties ext_prop : extension_properties) {218supported_extensions.emplace_back(ext_prop.extensionName);219}220GetInstance()->SetSupportedExtensions(supported_extensions);221222return XR_SUCCESS;223}224225XrResult RuntimeInterface::LoadRuntime(const std::string& openxr_command) {226// If something's already loaded, we're done here.227if (GetInstance() != nullptr) {228return XR_SUCCESS;229}230231#if defined(XR_HAS_REQUIRED_PLATFORM_LOADER_INIT_STRUCT)232if (!LoaderInitData::instance().initialized()) {233LoaderLogger::LogErrorMessage(234openxr_command, "RuntimeInterface::LoadRuntime cannot run because xrInitializeLoaderKHR was not successfully called.");235return XR_ERROR_INITIALIZATION_FAILED;236}237#endif // XR_HAS_REQUIRED_PLATFORM_LOADER_INIT_STRUCT238239std::vector<std::unique_ptr<RuntimeManifestFile>> runtime_manifest_files = {};240241// Find the available runtimes which we may need to report information for.242XrResult last_error = RuntimeManifestFile::FindManifestFiles(openxr_command, runtime_manifest_files);243if (XR_FAILED(last_error)) {244LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntimes - unknown error");245} else {246last_error = XR_ERROR_RUNTIME_UNAVAILABLE;247for (std::unique_ptr<RuntimeManifestFile>& manifest_file : runtime_manifest_files) {248last_error = RuntimeInterface::TryLoadingSingleRuntime(openxr_command, manifest_file);249if (XR_SUCCEEDED(last_error)) {250break;251}252}253}254255// Unsuccessful in loading any runtime, throw the runtime unavailable message.256if (XR_FAILED(last_error)) {257LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntimes - failed to load a runtime");258last_error = XR_ERROR_RUNTIME_UNAVAILABLE;259}260261return last_error;262}263264void RuntimeInterface::UnloadRuntime(const std::string& openxr_command) {265if (GetInstance()) {266LoaderLogger::LogInfoMessage(openxr_command, "RuntimeInterface::UnloadRuntime - Unloading RuntimeInterface");267GetInstance().reset();268}269}270271XrResult RuntimeInterface::GetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function) {272return GetInstance()->_get_instance_proc_addr(instance, name, function);273}274275const XrGeneratedDispatchTableCore* RuntimeInterface::GetDispatchTable(XrInstance instance) {276XrGeneratedDispatchTableCore* table = nullptr;277std::lock_guard<std::mutex> mlock(GetInstance()->_dispatch_table_mutex);278auto it = GetInstance()->_dispatch_table_map.find(instance);279if (it != GetInstance()->_dispatch_table_map.end()) {280table = it->second.get();281}282return table;283}284285const XrGeneratedDispatchTableCore* RuntimeInterface::GetDebugUtilsMessengerDispatchTable(XrDebugUtilsMessengerEXT messenger) {286XrInstance runtime_instance = XR_NULL_HANDLE;287{288std::lock_guard<std::mutex> mlock(GetInstance()->_messenger_to_instance_mutex);289auto it = GetInstance()->_messenger_to_instance_map.find(messenger);290if (it != GetInstance()->_messenger_to_instance_map.end()) {291runtime_instance = it->second;292}293}294return GetDispatchTable(runtime_instance);295}296297RuntimeInterface::RuntimeInterface(LoaderPlatformLibraryHandle runtime_library, PFN_xrGetInstanceProcAddr get_instance_proc_addr)298: _runtime_library(runtime_library), _get_instance_proc_addr(get_instance_proc_addr) {}299300RuntimeInterface::~RuntimeInterface() {301std::string info_message = "RuntimeInterface being destroyed.";302LoaderLogger::LogInfoMessage("", info_message);303{304std::lock_guard<std::mutex> mlock(_dispatch_table_mutex);305_dispatch_table_map.clear();306}307LoaderPlatformLibraryClose(_runtime_library);308}309310void RuntimeInterface::GetInstanceExtensionProperties(std::vector<XrExtensionProperties>& extension_properties) {311std::vector<XrExtensionProperties> runtime_extension_properties;312PFN_xrEnumerateInstanceExtensionProperties rt_xrEnumerateInstanceExtensionProperties;313_get_instance_proc_addr(XR_NULL_HANDLE, "xrEnumerateInstanceExtensionProperties",314reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrEnumerateInstanceExtensionProperties));315uint32_t count = 0;316uint32_t count_output = 0;317// Get the count from the runtime318rt_xrEnumerateInstanceExtensionProperties(nullptr, count, &count_output, nullptr);319if (count_output > 0) {320XrExtensionProperties example_properties{};321example_properties.type = XR_TYPE_EXTENSION_PROPERTIES;322runtime_extension_properties.resize(count_output, example_properties);323count = count_output;324rt_xrEnumerateInstanceExtensionProperties(nullptr, count, &count_output, runtime_extension_properties.data());325}326size_t ext_count = runtime_extension_properties.size();327size_t props_count = extension_properties.size();328for (size_t ext = 0; ext < ext_count; ++ext) {329bool found = false;330for (size_t prop = 0; prop < props_count; ++prop) {331// If we find it, then make sure the spec version matches that of the runtime instead of the332// layer.333if (strcmp(extension_properties[prop].extensionName, runtime_extension_properties[ext].extensionName) == 0) {334// Make sure the spec version used is the runtime's335extension_properties[prop].extensionVersion = runtime_extension_properties[ext].extensionVersion;336found = true;337break;338}339}340if (!found) {341extension_properties.push_back(runtime_extension_properties[ext]);342}343}344}345346XrResult RuntimeInterface::CreateInstance(const XrInstanceCreateInfo* info, XrInstance* instance) {347XrResult res = XR_SUCCESS;348bool create_succeeded = false;349PFN_xrCreateInstance rt_xrCreateInstance;350_get_instance_proc_addr(XR_NULL_HANDLE, "xrCreateInstance", reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrCreateInstance));351res = rt_xrCreateInstance(info, instance);352if (XR_SUCCEEDED(res)) {353create_succeeded = true;354std::unique_ptr<XrGeneratedDispatchTableCore> dispatch_table(new XrGeneratedDispatchTableCore());355GeneratedXrPopulateDispatchTableCore(dispatch_table.get(), *instance, _get_instance_proc_addr);356std::lock_guard<std::mutex> mlock(_dispatch_table_mutex);357_dispatch_table_map[*instance] = std::move(dispatch_table);358}359360// If the failure occurred during the populate, clean up the instance we had picked up from the runtime361if (XR_FAILED(res) && create_succeeded) {362PFN_xrDestroyInstance rt_xrDestroyInstance;363_get_instance_proc_addr(*instance, "xrDestroyInstance", reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrDestroyInstance));364rt_xrDestroyInstance(*instance);365*instance = XR_NULL_HANDLE;366}367368return res;369}370371XrResult RuntimeInterface::DestroyInstance(XrInstance instance) {372if (XR_NULL_HANDLE != instance) {373// Destroy the dispatch table for this instance first374{375std::lock_guard<std::mutex> mlock(_dispatch_table_mutex);376auto map_iter = _dispatch_table_map.find(instance);377if (map_iter != _dispatch_table_map.end()) {378_dispatch_table_map.erase(map_iter);379}380}381// Now delete the instance382PFN_xrDestroyInstance rt_xrDestroyInstance;383_get_instance_proc_addr(instance, "xrDestroyInstance", reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrDestroyInstance));384rt_xrDestroyInstance(instance);385}386return XR_SUCCESS;387}388389bool RuntimeInterface::TrackDebugMessenger(XrInstance instance, XrDebugUtilsMessengerEXT messenger) {390std::lock_guard<std::mutex> mlock(_messenger_to_instance_mutex);391_messenger_to_instance_map[messenger] = instance;392return true;393}394395void RuntimeInterface::ForgetDebugMessenger(XrDebugUtilsMessengerEXT messenger) {396if (XR_NULL_HANDLE != messenger) {397std::lock_guard<std::mutex> mlock(_messenger_to_instance_mutex);398_messenger_to_instance_map.erase(messenger);399}400}401402void RuntimeInterface::SetSupportedExtensions(std::vector<std::string>& supported_extensions) {403_supported_extensions = supported_extensions;404}405406bool RuntimeInterface::SupportsExtension(const std::string& extension_name) {407bool found_prop = false;408for (const std::string& supported_extension : _supported_extensions) {409if (supported_extension == extension_name) {410found_prop = true;411break;412}413}414return found_prop;415}416417418