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 |
35 | namespace { |
36 | /*! |
37 | * Stores a copy of the data passed to the xrInitializeLoaderKHR function in a singleton. |
38 | */ |
39 | class 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. |
96 | XrResult 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 | |
141 | XrResult InitializeLoader(const XrLoaderInitInfoBaseHeaderKHR* loaderInitInfo) { |
142 | return LoaderInitData::instance().initialize(loaderInitInfo); |
143 | } |
144 | |
145 | std::string GetAndroidNativeLibraryDir() { return LoaderInitData::instance()._native_library_path; } |
146 | |
147 | void* 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 |
152 | XrResult 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 | |
171 | XrResult 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 | |
338 | XrResult 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 | |
377 | void 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 | |
384 | XrResult RuntimeInterface::GetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function) { |
385 | return GetInstance()->_get_instance_proc_addr(instance, name, function); |
386 | } |
387 | |
388 | const 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 | |
398 | const 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 | |
410 | RuntimeInterface::RuntimeInterface(LoaderPlatformLibraryHandle runtime_library, PFN_xrGetInstanceProcAddr get_instance_proc_addr) |
411 | : _runtime_library(runtime_library), _get_instance_proc_addr(get_instance_proc_addr) {} |
412 | |
413 | RuntimeInterface::~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 | |
423 | void 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 | |
459 | XrResult 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 | |
484 | XrResult 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 | |
502 | bool 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 | |
508 | void 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 | |
515 | void RuntimeInterface::SetSupportedExtensions(std::vector<std::string>& supported_extensions) { |
516 | _supported_extensions = supported_extensions; |
517 | } |
518 | |
519 | bool 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 | |