1// Copyright (c) 2017-2023, The Khronos Group Inc.
2// Copyright (c) 2017-2019 Valve Corporation
3// Copyright (c) 2017-2019 LunarG, Inc.
4//
5// SPDX-License-Identifier: Apache-2.0 OR MIT
6//
7// Initial Author: Mark Young <marky@lunarg.com>
8//
9
10#include "runtime_interface.hpp"
11
12#include "manifest_file.hpp"
13#include "loader_interfaces.h"
14#include "loader_logger.hpp"
15#include "loader_platform.hpp"
16#include "xr_generated_dispatch_table.h"
17
18#include <openxr/openxr.h>
19
20#include <cstring>
21#include <memory>
22#include <mutex>
23#include <string>
24#include <unordered_map>
25#include <utility>
26#include <vector>
27
28#ifdef XR_USE_PLATFORM_ANDROID
29#include "android_utilities.h"
30#include <android/asset_manager_jni.h>
31#include <json/value.h>
32#endif // XR_USE_PLATFORM_ANDROID
33
34#ifdef XR_KHR_LOADER_INIT_SUPPORT
35namespace {
36/*!
37 * Stores a copy of the data passed to the xrInitializeLoaderKHR function in a singleton.
38 */
39class LoaderInitData {
40 public:
41 /*!
42 * Singleton accessor.
43 */
44 static LoaderInitData& instance() {
45 static LoaderInitData obj;
46 return obj;
47 }
48
49#ifdef XR_USE_PLATFORM_ANDROID
50 /*!
51 * Type alias for the platform-specific structure type.
52 */
53 using StructType = XrLoaderInitInfoAndroidKHR;
54 /*!
55 * Native library path.
56 */
57 std::string _native_library_path;
58 /*!
59 * Android asset manager.
60 */
61 AAssetManager* _android_asset_manager;
62#endif
63
64 /*!
65 * Get our copy of the data, casted to pass to the runtime's matching method.
66 */
67 const XrLoaderInitInfoBaseHeaderKHR* getParam() const { return reinterpret_cast<const XrLoaderInitInfoBaseHeaderKHR*>(&_data); }
68
69 /*!
70 * Get the data via its real structure type.
71 */
72 const StructType& getData() const { return _data; }
73
74 /*!
75 * Has this been correctly initialized?
76 */
77 bool initialized() const noexcept { return _initialized; }
78
79 /*!
80 * Initialize loader data - called by InitializeLoader() and thus ultimately by the loader's xrInitializeLoaderKHR
81 * implementation. Each platform that needs this extension will provide an implementation of this.
82 */
83 XrResult initialize(const XrLoaderInitInfoBaseHeaderKHR* info);
84
85 private:
86 //! Private constructor, forces use of singleton accessor.
87 LoaderInitData() = default;
88 //! Platform-specific init data
89 StructType _data = {};
90 //! Flag for indicating whether _data is valid.
91 bool _initialized = false;
92};
93
94#ifdef XR_USE_PLATFORM_ANDROID
95// Check and copy the Android-specific init data.
96XrResult LoaderInitData::initialize(const XrLoaderInitInfoBaseHeaderKHR* info) {
97 if (info->type != XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR) {
98 return XR_ERROR_VALIDATION_FAILURE;
99 }
100 auto cast_info = reinterpret_cast<XrLoaderInitInfoAndroidKHR const*>(info);
101
102 if (cast_info->applicationVM == nullptr) {
103 return XR_ERROR_VALIDATION_FAILURE;
104 }
105 if (cast_info->applicationContext == nullptr) {
106 return XR_ERROR_VALIDATION_FAILURE;
107 }
108 _data = *cast_info;
109 jni::init((jni::JavaVM*)_data.applicationVM);
110 _data.next = nullptr;
111 JNIEnv* Env;
112 ((jni::JavaVM*)(cast_info->applicationVM))->AttachCurrentThread(&Env, nullptr);
113 const jclass contextClass = Env->GetObjectClass((jobject)_data.applicationContext);
114
115 const jmethodID getAssetsMethod = Env->GetMethodID(contextClass, "getAssets", "()Landroid/content/res/AssetManager;");
116 const jobject AssetManagerObject = Env->CallObjectMethod((jobject)_data.applicationContext, getAssetsMethod);
117 _android_asset_manager = AAssetManager_fromJava(Env, AssetManagerObject);
118
119 const jmethodID getApplicationContextMethod =
120 Env->GetMethodID(contextClass, "getApplicationContext", "()Landroid/content/Context;");
121 const jobject contextObject = Env->CallObjectMethod((jobject)_data.applicationContext, getApplicationContextMethod);
122 const jmethodID getApplicationInfoMethod =
123 Env->GetMethodID(contextClass, "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
124 const jobject applicationInfoObject = Env->CallObjectMethod(contextObject, getApplicationInfoMethod);
125 const jfieldID nativeLibraryDirField =
126 Env->GetFieldID(Env->GetObjectClass(applicationInfoObject), "nativeLibraryDir", "Ljava/lang/String;");
127 const jobject nativeLibraryDirObject = Env->GetObjectField(applicationInfoObject, nativeLibraryDirField);
128 const jmethodID getBytesMethod =
129 Env->GetMethodID(Env->GetObjectClass(nativeLibraryDirObject), "getBytes", "(Ljava/lang/String;)[B");
130 const auto bytesObject =
131 static_cast<jbyteArray>(Env->CallObjectMethod(nativeLibraryDirObject, getBytesMethod, Env->NewStringUTF("UTF-8")));
132 const size_t length = Env->GetArrayLength(bytesObject);
133 const jbyte* const bytes = Env->GetByteArrayElements(bytesObject, nullptr);
134 _native_library_path = std::string(reinterpret_cast<const char*>(bytes), length);
135 _initialized = true;
136 return XR_SUCCESS;
137}
138#endif // XR_USE_PLATFORM_ANDROID
139} // namespace
140
141XrResult InitializeLoader(const XrLoaderInitInfoBaseHeaderKHR* loaderInitInfo) {
142 return LoaderInitData::instance().initialize(loaderInitInfo);
143}
144
145std::string GetAndroidNativeLibraryDir() { return LoaderInitData::instance()._native_library_path; }
146
147void* Android_Get_Asset_Manager() { return LoaderInitData::instance()._android_asset_manager; }
148
149#endif // XR_KHR_LOADER_INIT_SUPPORT
150
151#ifdef XR_USE_PLATFORM_ANDROID
152XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest) {
153 using wrap::android::content::Context;
154 auto& initData = LoaderInitData::instance();
155 if (!initData.initialized()) {
156 return XR_ERROR_INITIALIZATION_FAILED;
157 }
158 auto context = Context(reinterpret_cast<jobject>(initData.getData().applicationContext));
159 if (context.isNull()) {
160 return XR_ERROR_INITIALIZATION_FAILED;
161 }
162 Json::Value virtualManifest;
163 if (0 != openxr_android::getActiveRuntimeVirtualManifest(context, virtualManifest)) {
164 return XR_ERROR_INITIALIZATION_FAILED;
165 }
166 out_manifest = virtualManifest;
167 return XR_SUCCESS;
168}
169#endif // XR_USE_PLATFORM_ANDROID
170
171XrResult RuntimeInterface::TryLoadingSingleRuntime(const std::string& openxr_command,
172 std::unique_ptr<RuntimeManifestFile>& manifest_file) {
173 LoaderPlatformLibraryHandle runtime_library = LoaderPlatformLibraryOpen(manifest_file->LibraryPath());
174 if (nullptr == runtime_library) {
175 std::string library_message = LoaderPlatformLibraryOpenError(manifest_file->LibraryPath());
176 std::string warning_message = "RuntimeInterface::LoadRuntime skipping manifest file ";
177 warning_message += manifest_file->Filename();
178 warning_message += ", failed to load with message \"";
179 warning_message += library_message;
180 warning_message += "\"";
181 LoaderLogger::LogErrorMessage(openxr_command, warning_message);
182 return XR_ERROR_FILE_ACCESS_ERROR;
183 }
184#ifdef XR_KHR_LOADER_INIT_SUPPORT
185 if (!LoaderInitData::instance().initialized()) {
186 LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntime skipping manifest file " +
187 manifest_file->Filename() +
188 " because xrInitializeLoaderKHR was not yet called.");
189
190 LoaderPlatformLibraryClose(runtime_library);
191 return XR_ERROR_VALIDATION_FAILURE;
192 }
193 bool forwardedInitLoader = false;
194 {
195 // If we have xrInitializeLoaderKHR exposed as an export, forward call to it.
196 const auto function_name = manifest_file->GetFunctionName("xrInitializeLoaderKHR");
197 auto initLoader =
198 reinterpret_cast<PFN_xrInitializeLoaderKHR>(LoaderPlatformLibraryGetProcAddr(runtime_library, function_name));
199 if (initLoader != nullptr) {
200 // we found the entry point one way or another.
201 LoaderLogger::LogInfoMessage(openxr_command,
202 "RuntimeInterface::LoadRuntime forwarding xrInitializeLoaderKHR call to runtime before "
203 "calling xrNegotiateLoaderRuntimeInterface.");
204 XrResult res = initLoader(LoaderInitData::instance().getParam());
205 if (!XR_SUCCEEDED(res)) {
206 LoaderLogger::LogErrorMessage(openxr_command,
207 "RuntimeInterface::LoadRuntime forwarded call to xrInitializeLoaderKHR failed.");
208
209 LoaderPlatformLibraryClose(runtime_library);
210 return res;
211 }
212 forwardedInitLoader = true;
213 }
214 }
215#endif
216
217 // Get and settle on an runtime interface version (using any provided name if required).
218 std::string function_name = manifest_file->GetFunctionName("xrNegotiateLoaderRuntimeInterface");
219 auto negotiate =
220 reinterpret_cast<PFN_xrNegotiateLoaderRuntimeInterface>(LoaderPlatformLibraryGetProcAddr(runtime_library, function_name));
221
222 // Loader info for negotiation
223 XrNegotiateLoaderInfo loader_info = {};
224 loader_info.structType = XR_LOADER_INTERFACE_STRUCT_LOADER_INFO;
225 loader_info.structVersion = XR_LOADER_INFO_STRUCT_VERSION;
226 loader_info.structSize = sizeof(XrNegotiateLoaderInfo);
227 loader_info.minInterfaceVersion = 1;
228 loader_info.maxInterfaceVersion = XR_CURRENT_LOADER_RUNTIME_VERSION;
229 loader_info.minApiVersion = XR_MAKE_VERSION(1, 0, 0);
230 loader_info.maxApiVersion = XR_MAKE_VERSION(1, 0x3ff, 0xfff); // Maximum allowed version for this major version.
231
232 // Set up the runtime return structure
233 XrNegotiateRuntimeRequest runtime_info = {};
234 runtime_info.structType = XR_LOADER_INTERFACE_STRUCT_RUNTIME_REQUEST;
235 runtime_info.structVersion = XR_RUNTIME_INFO_STRUCT_VERSION;
236 runtime_info.structSize = sizeof(XrNegotiateRuntimeRequest);
237
238 // Skip calling the negotiate function and fail if the function pointer
239 // could not get loaded
240 XrResult res = XR_ERROR_RUNTIME_FAILURE;
241 if (nullptr != negotiate) {
242 res = negotiate(&loader_info, &runtime_info);
243 }
244 // If we supposedly succeeded, but got a nullptr for GetInstanceProcAddr
245 // then something still went wrong, so return with an error.
246 if (XR_SUCCEEDED(res)) {
247 uint32_t runtime_major = XR_VERSION_MAJOR(runtime_info.runtimeApiVersion);
248 uint32_t runtime_minor = XR_VERSION_MINOR(runtime_info.runtimeApiVersion);
249 uint32_t loader_major = XR_VERSION_MAJOR(XR_CURRENT_API_VERSION);
250 if (nullptr == runtime_info.getInstanceProcAddr) {
251 std::string error_message = "RuntimeInterface::LoadRuntime skipping manifest file ";
252 error_message += manifest_file->Filename();
253 error_message += ", negotiation succeeded but returned NULL getInstanceProcAddr";
254 LoaderLogger::LogErrorMessage(openxr_command, error_message);
255 res = XR_ERROR_FILE_CONTENTS_INVALID;
256 } else if (0 >= runtime_info.runtimeInterfaceVersion ||
257 XR_CURRENT_LOADER_RUNTIME_VERSION < runtime_info.runtimeInterfaceVersion) {
258 std::string error_message = "RuntimeInterface::LoadRuntime skipping manifest file ";
259 error_message += manifest_file->Filename();
260 error_message += ", negotiation succeeded but returned invalid interface version";
261 LoaderLogger::LogErrorMessage(openxr_command, error_message);
262 res = XR_ERROR_FILE_CONTENTS_INVALID;
263 } else if (runtime_major != loader_major || (runtime_major == 0 && runtime_minor == 0)) {
264 std::string error_message = "RuntimeInterface::LoadRuntime skipping manifest file ";
265 error_message += manifest_file->Filename();
266 error_message += ", OpenXR version returned not compatible with this loader";
267 LoaderLogger::LogErrorMessage(openxr_command, error_message);
268 res = XR_ERROR_FILE_CONTENTS_INVALID;
269 }
270 }
271#ifdef XR_KHR_LOADER_INIT_SUPPORT
272 if (XR_SUCCEEDED(res) && !forwardedInitLoader) {
273 // Forward initialize loader call, where possible and if we did not do so before.
274 PFN_xrVoidFunction initializeVoid = nullptr;
275 PFN_xrInitializeLoaderKHR initialize = nullptr;
276
277 // Now we may try asking xrGetInstanceProcAddr
278 if (XR_SUCCEEDED(runtime_info.getInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", &initializeVoid))) {
279 if (initializeVoid == nullptr) {
280 LoaderLogger::LogErrorMessage(openxr_command,
281 "RuntimeInterface::LoadRuntime got success from xrGetInstanceProcAddr "
282 "for xrInitializeLoaderKHR, but output a null pointer.");
283 res = XR_ERROR_RUNTIME_FAILURE;
284 } else {
285 initialize = reinterpret_cast<PFN_xrInitializeLoaderKHR>(initializeVoid);
286 }
287 }
288 if (initialize != nullptr) {
289 // we found the entry point one way or another.
290 LoaderLogger::LogInfoMessage(openxr_command,
291 "RuntimeInterface::LoadRuntime forwarding xrInitializeLoaderKHR call to runtime after "
292 "calling xrNegotiateLoaderRuntimeInterface.");
293 res = initialize(LoaderInitData::instance().getParam());
294 if (!XR_SUCCEEDED(res)) {
295 LoaderLogger::LogErrorMessage(openxr_command,
296 "RuntimeInterface::LoadRuntime forwarded call to xrInitializeLoaderKHR failed.");
297 }
298 }
299 }
300#endif
301 if (XR_FAILED(res)) {
302 std::string warning_message = "RuntimeInterface::LoadRuntime skipping manifest file ";
303 warning_message += manifest_file->Filename();
304 warning_message += ", negotiation failed with error ";
305 warning_message += std::to_string(res);
306 LoaderLogger::LogErrorMessage(openxr_command, warning_message);
307 LoaderPlatformLibraryClose(runtime_library);
308 return res;
309 }
310
311 std::string info_message = "RuntimeInterface::LoadRuntime succeeded loading runtime defined in manifest file ";
312 info_message += manifest_file->Filename();
313 info_message += " using interface version ";
314 info_message += std::to_string(runtime_info.runtimeInterfaceVersion);
315 info_message += " and OpenXR API version ";
316 info_message += std::to_string(XR_VERSION_MAJOR(runtime_info.runtimeApiVersion));
317 info_message += ".";
318 info_message += std::to_string(XR_VERSION_MINOR(runtime_info.runtimeApiVersion));
319 LoaderLogger::LogInfoMessage(openxr_command, info_message);
320
321 // Use this runtime
322 GetInstance().reset(new RuntimeInterface(runtime_library, runtime_info.getInstanceProcAddr));
323
324 // Grab the list of extensions this runtime supports for easy filtering after the
325 // xrCreateInstance call
326 std::vector<std::string> supported_extensions;
327 std::vector<XrExtensionProperties> extension_properties;
328 GetInstance()->GetInstanceExtensionProperties(extension_properties);
329 supported_extensions.reserve(extension_properties.size());
330 for (XrExtensionProperties ext_prop : extension_properties) {
331 supported_extensions.emplace_back(ext_prop.extensionName);
332 }
333 GetInstance()->SetSupportedExtensions(supported_extensions);
334
335 return XR_SUCCESS;
336}
337
338XrResult RuntimeInterface::LoadRuntime(const std::string& openxr_command) {
339 // If something's already loaded, we're done here.
340 if (GetInstance() != nullptr) {
341 return XR_SUCCESS;
342 }
343#ifdef XR_KHR_LOADER_INIT_SUPPORT
344
345 if (!LoaderInitData::instance().initialized()) {
346 LoaderLogger::LogErrorMessage(
347 openxr_command, "RuntimeInterface::LoadRuntime cannot run because xrInitializeLoaderKHR was not successfully called.");
348 return XR_ERROR_INITIALIZATION_FAILED;
349 }
350#endif // XR_KHR_LOADER_INIT_SUPPORT
351
352 std::vector<std::unique_ptr<RuntimeManifestFile>> runtime_manifest_files = {};
353
354 // Find the available runtimes which we may need to report information for.
355 XrResult last_error = RuntimeManifestFile::FindManifestFiles(runtime_manifest_files);
356 if (XR_FAILED(last_error)) {
357 LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntimes - unknown error");
358 } else {
359 last_error = XR_ERROR_RUNTIME_UNAVAILABLE;
360 for (std::unique_ptr<RuntimeManifestFile>& manifest_file : runtime_manifest_files) {
361 last_error = RuntimeInterface::TryLoadingSingleRuntime(openxr_command, manifest_file);
362 if (XR_SUCCEEDED(last_error)) {
363 break;
364 }
365 }
366 }
367
368 // Unsuccessful in loading any runtime, throw the runtime unavailable message.
369 if (XR_FAILED(last_error)) {
370 LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntimes - failed to load a runtime");
371 last_error = XR_ERROR_RUNTIME_UNAVAILABLE;
372 }
373
374 return last_error;
375}
376
377void RuntimeInterface::UnloadRuntime(const std::string& openxr_command) {
378 if (GetInstance()) {
379 LoaderLogger::LogInfoMessage(openxr_command, "RuntimeInterface::UnloadRuntime - Unloading RuntimeInterface");
380 GetInstance().reset();
381 }
382}
383
384XrResult RuntimeInterface::GetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function) {
385 return GetInstance()->_get_instance_proc_addr(instance, name, function);
386}
387
388const XrGeneratedDispatchTable* RuntimeInterface::GetDispatchTable(XrInstance instance) {
389 XrGeneratedDispatchTable* table = nullptr;
390 std::lock_guard<std::mutex> mlock(GetInstance()->_dispatch_table_mutex);
391 auto it = GetInstance()->_dispatch_table_map.find(instance);
392 if (it != GetInstance()->_dispatch_table_map.end()) {
393 table = it->second.get();
394 }
395 return table;
396}
397
398const XrGeneratedDispatchTable* RuntimeInterface::GetDebugUtilsMessengerDispatchTable(XrDebugUtilsMessengerEXT messenger) {
399 XrInstance runtime_instance = XR_NULL_HANDLE;
400 {
401 std::lock_guard<std::mutex> mlock(GetInstance()->_messenger_to_instance_mutex);
402 auto it = GetInstance()->_messenger_to_instance_map.find(messenger);
403 if (it != GetInstance()->_messenger_to_instance_map.end()) {
404 runtime_instance = it->second;
405 }
406 }
407 return GetDispatchTable(runtime_instance);
408}
409
410RuntimeInterface::RuntimeInterface(LoaderPlatformLibraryHandle runtime_library, PFN_xrGetInstanceProcAddr get_instance_proc_addr)
411 : _runtime_library(runtime_library), _get_instance_proc_addr(get_instance_proc_addr) {}
412
413RuntimeInterface::~RuntimeInterface() {
414 std::string info_message = "RuntimeInterface being destroyed.";
415 LoaderLogger::LogInfoMessage("", info_message);
416 {
417 std::lock_guard<std::mutex> mlock(_dispatch_table_mutex);
418 _dispatch_table_map.clear();
419 }
420 LoaderPlatformLibraryClose(_runtime_library);
421}
422
423void RuntimeInterface::GetInstanceExtensionProperties(std::vector<XrExtensionProperties>& extension_properties) {
424 std::vector<XrExtensionProperties> runtime_extension_properties;
425 PFN_xrEnumerateInstanceExtensionProperties rt_xrEnumerateInstanceExtensionProperties;
426 _get_instance_proc_addr(XR_NULL_HANDLE, "xrEnumerateInstanceExtensionProperties",
427 reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrEnumerateInstanceExtensionProperties));
428 uint32_t count = 0;
429 uint32_t count_output = 0;
430 // Get the count from the runtime
431 rt_xrEnumerateInstanceExtensionProperties(nullptr, count, &count_output, nullptr);
432 if (count_output > 0) {
433 XrExtensionProperties example_properties{};
434 example_properties.type = XR_TYPE_EXTENSION_PROPERTIES;
435 runtime_extension_properties.resize(count_output, example_properties);
436 count = count_output;
437 rt_xrEnumerateInstanceExtensionProperties(nullptr, count, &count_output, runtime_extension_properties.data());
438 }
439 size_t ext_count = runtime_extension_properties.size();
440 size_t props_count = extension_properties.size();
441 for (size_t ext = 0; ext < ext_count; ++ext) {
442 bool found = false;
443 for (size_t prop = 0; prop < props_count; ++prop) {
444 // If we find it, then make sure the spec version matches that of the runtime instead of the
445 // layer.
446 if (strcmp(extension_properties[prop].extensionName, runtime_extension_properties[ext].extensionName) == 0) {
447 // Make sure the spec version used is the runtime's
448 extension_properties[prop].extensionVersion = runtime_extension_properties[ext].extensionVersion;
449 found = true;
450 break;
451 }
452 }
453 if (!found) {
454 extension_properties.push_back(runtime_extension_properties[ext]);
455 }
456 }
457}
458
459XrResult RuntimeInterface::CreateInstance(const XrInstanceCreateInfo* info, XrInstance* instance) {
460 XrResult res = XR_SUCCESS;
461 bool create_succeeded = false;
462 PFN_xrCreateInstance rt_xrCreateInstance;
463 _get_instance_proc_addr(XR_NULL_HANDLE, "xrCreateInstance", reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrCreateInstance));
464 res = rt_xrCreateInstance(info, instance);
465 if (XR_SUCCEEDED(res)) {
466 create_succeeded = true;
467 std::unique_ptr<XrGeneratedDispatchTable> dispatch_table(new XrGeneratedDispatchTable());
468 GeneratedXrPopulateDispatchTable(dispatch_table.get(), *instance, _get_instance_proc_addr);
469 std::lock_guard<std::mutex> mlock(_dispatch_table_mutex);
470 _dispatch_table_map[*instance] = std::move(dispatch_table);
471 }
472
473 // If the failure occurred during the populate, clean up the instance we had picked up from the runtime
474 if (XR_FAILED(res) && create_succeeded) {
475 PFN_xrDestroyInstance rt_xrDestroyInstance;
476 _get_instance_proc_addr(*instance, "xrDestroyInstance", reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrDestroyInstance));
477 rt_xrDestroyInstance(*instance);
478 *instance = XR_NULL_HANDLE;
479 }
480
481 return res;
482}
483
484XrResult RuntimeInterface::DestroyInstance(XrInstance instance) {
485 if (XR_NULL_HANDLE != instance) {
486 // Destroy the dispatch table for this instance first
487 {
488 std::lock_guard<std::mutex> mlock(_dispatch_table_mutex);
489 auto map_iter = _dispatch_table_map.find(instance);
490 if (map_iter != _dispatch_table_map.end()) {
491 _dispatch_table_map.erase(map_iter);
492 }
493 }
494 // Now delete the instance
495 PFN_xrDestroyInstance rt_xrDestroyInstance;
496 _get_instance_proc_addr(instance, "xrDestroyInstance", reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrDestroyInstance));
497 rt_xrDestroyInstance(instance);
498 }
499 return XR_SUCCESS;
500}
501
502bool RuntimeInterface::TrackDebugMessenger(XrInstance instance, XrDebugUtilsMessengerEXT messenger) {
503 std::lock_guard<std::mutex> mlock(_messenger_to_instance_mutex);
504 _messenger_to_instance_map[messenger] = instance;
505 return true;
506}
507
508void RuntimeInterface::ForgetDebugMessenger(XrDebugUtilsMessengerEXT messenger) {
509 if (XR_NULL_HANDLE != messenger) {
510 std::lock_guard<std::mutex> mlock(_messenger_to_instance_mutex);
511 _messenger_to_instance_map.erase(messenger);
512 }
513}
514
515void RuntimeInterface::SetSupportedExtensions(std::vector<std::string>& supported_extensions) {
516 _supported_extensions = supported_extensions;
517}
518
519bool RuntimeInterface::SupportsExtension(const std::string& extension_name) {
520 bool found_prop = false;
521 for (const std::string& supported_extension : _supported_extensions) {
522 if (supported_extension == extension_name) {
523 found_prop = true;
524 break;
525 }
526 }
527 return found_prop;
528}
529