Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/openxr/src/loader/runtime_interface.cpp
9917 views
1
// Copyright (c) 2017-2025 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 <[email protected]>
8
//
9
10
#include "runtime_interface.hpp"
11
12
#include <openxr/openxr.h>
13
#include <openxr/openxr_loader_negotiation.h>
14
15
#include "manifest_file.hpp"
16
#include "loader_init_data.hpp"
17
#include "loader_logger.hpp"
18
#include "loader_platform.hpp"
19
#include "xr_generated_dispatch_table_core.h"
20
21
#include <cstring>
22
#include <memory>
23
#include <mutex>
24
#include <string>
25
#include <unordered_map>
26
#include <utility>
27
#include <vector>
28
29
#ifdef XR_USE_PLATFORM_ANDROID
30
#include <json/value.h>
31
32
// Needed for the loader init struct
33
#include <xr_dependencies.h>
34
#include <openxr/openxr_platform.h>
35
#endif // XR_USE_PLATFORM_ANDROID
36
37
#if defined(XR_KHR_LOADER_INIT_SUPPORT) && defined(XR_USE_PLATFORM_ANDROID)
38
XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest) {
39
using wrap::android::content::Context;
40
auto& initData = LoaderInitData::instance();
41
if (!initData.initialized()) {
42
return XR_ERROR_INITIALIZATION_FAILED;
43
}
44
auto context = Context(reinterpret_cast<jobject>(initData.getData().applicationContext));
45
if (context.isNull()) {
46
return XR_ERROR_INITIALIZATION_FAILED;
47
}
48
Json::Value virtualManifest;
49
if (0 != openxr_android::getActiveRuntimeVirtualManifest(context, virtualManifest)) {
50
return XR_ERROR_INITIALIZATION_FAILED;
51
}
52
out_manifest = virtualManifest;
53
return XR_SUCCESS;
54
}
55
#endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT)
56
57
XrResult RuntimeInterface::TryLoadingSingleRuntime(const std::string& openxr_command,
58
std::unique_ptr<RuntimeManifestFile>& manifest_file) {
59
LoaderPlatformLibraryHandle runtime_library = LoaderPlatformLibraryOpen(manifest_file->LibraryPath());
60
if (nullptr == runtime_library) {
61
std::string library_message = LoaderPlatformLibraryOpenError(manifest_file->LibraryPath());
62
std::string warning_message = "RuntimeInterface::LoadRuntime skipping manifest file ";
63
warning_message += manifest_file->Filename();
64
warning_message += ", failed to load with message \"";
65
warning_message += library_message;
66
warning_message += "\"";
67
LoaderLogger::LogErrorMessage(openxr_command, warning_message);
68
return XR_ERROR_FILE_ACCESS_ERROR;
69
}
70
#ifdef XR_KHR_LOADER_INIT_SUPPORT
71
if (!LoaderInitData::instance().initialized()) {
72
LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntime skipping manifest file " +
73
manifest_file->Filename() +
74
" because xrInitializeLoaderKHR was not yet called.");
75
76
LoaderPlatformLibraryClose(runtime_library);
77
return XR_ERROR_VALIDATION_FAILURE;
78
}
79
bool forwardedInitLoader = false;
80
{
81
// If we have xrInitializeLoaderKHR exposed as an export, forward call to it.
82
const auto function_name = manifest_file->GetFunctionName("xrInitializeLoaderKHR");
83
auto initLoader =
84
reinterpret_cast<PFN_xrInitializeLoaderKHR>(LoaderPlatformLibraryGetProcAddr(runtime_library, function_name));
85
if (initLoader != nullptr) {
86
// we found the entry point one way or another.
87
LoaderLogger::LogInfoMessage(openxr_command,
88
"RuntimeInterface::LoadRuntime forwarding xrInitializeLoaderKHR call to runtime before "
89
"calling xrNegotiateLoaderRuntimeInterface.");
90
XrResult res = initLoader(LoaderInitData::instance().getParam());
91
if (!XR_SUCCEEDED(res)) {
92
LoaderLogger::LogErrorMessage(openxr_command,
93
"RuntimeInterface::LoadRuntime forwarded call to xrInitializeLoaderKHR failed.");
94
95
LoaderPlatformLibraryClose(runtime_library);
96
return res;
97
}
98
forwardedInitLoader = true;
99
}
100
}
101
#endif
102
103
// Get and settle on an runtime interface version (using any provided name if required).
104
std::string function_name = manifest_file->GetFunctionName("xrNegotiateLoaderRuntimeInterface");
105
auto negotiate =
106
reinterpret_cast<PFN_xrNegotiateLoaderRuntimeInterface>(LoaderPlatformLibraryGetProcAddr(runtime_library, function_name));
107
108
// Loader info for negotiation
109
XrNegotiateLoaderInfo loader_info = {};
110
loader_info.structType = XR_LOADER_INTERFACE_STRUCT_LOADER_INFO;
111
loader_info.structVersion = XR_LOADER_INFO_STRUCT_VERSION;
112
loader_info.structSize = sizeof(XrNegotiateLoaderInfo);
113
loader_info.minInterfaceVersion = 1;
114
loader_info.maxInterfaceVersion = XR_CURRENT_LOADER_RUNTIME_VERSION;
115
loader_info.minApiVersion = XR_MAKE_VERSION(1, 0, 0);
116
loader_info.maxApiVersion = XR_MAKE_VERSION(1, 0x3ff, 0xfff); // Maximum allowed version for this major version.
117
118
// Set up the runtime return structure
119
XrNegotiateRuntimeRequest runtime_info = {};
120
runtime_info.structType = XR_LOADER_INTERFACE_STRUCT_RUNTIME_REQUEST;
121
runtime_info.structVersion = XR_RUNTIME_INFO_STRUCT_VERSION;
122
runtime_info.structSize = sizeof(XrNegotiateRuntimeRequest);
123
124
// Skip calling the negotiate function and fail if the function pointer
125
// could not get loaded
126
XrResult res = XR_ERROR_RUNTIME_FAILURE;
127
if (nullptr != negotiate) {
128
res = negotiate(&loader_info, &runtime_info);
129
} else {
130
std::string error_message = "RuntimeInterface::LoadRuntime failed to find negotiate function ";
131
error_message += function_name;
132
LoaderLogger::LogErrorMessage(openxr_command, error_message);
133
}
134
// If we supposedly succeeded, but got a nullptr for GetInstanceProcAddr
135
// then something still went wrong, so return with an error.
136
if (XR_SUCCEEDED(res)) {
137
uint32_t runtime_major = XR_VERSION_MAJOR(runtime_info.runtimeApiVersion);
138
uint32_t runtime_minor = XR_VERSION_MINOR(runtime_info.runtimeApiVersion);
139
uint32_t loader_major = XR_VERSION_MAJOR(XR_CURRENT_API_VERSION);
140
if (nullptr == runtime_info.getInstanceProcAddr) {
141
std::string error_message = "RuntimeInterface::LoadRuntime skipping manifest file ";
142
error_message += manifest_file->Filename();
143
error_message += ", negotiation succeeded but returned NULL getInstanceProcAddr";
144
LoaderLogger::LogErrorMessage(openxr_command, error_message);
145
res = XR_ERROR_FILE_CONTENTS_INVALID;
146
} else if (0 >= runtime_info.runtimeInterfaceVersion ||
147
XR_CURRENT_LOADER_RUNTIME_VERSION < runtime_info.runtimeInterfaceVersion) {
148
std::string error_message = "RuntimeInterface::LoadRuntime skipping manifest file ";
149
error_message += manifest_file->Filename();
150
error_message += ", negotiation succeeded but returned invalid interface version";
151
LoaderLogger::LogErrorMessage(openxr_command, error_message);
152
res = XR_ERROR_FILE_CONTENTS_INVALID;
153
} else if (runtime_major != loader_major || (runtime_major == 0 && runtime_minor == 0)) {
154
std::string error_message = "RuntimeInterface::LoadRuntime skipping manifest file ";
155
error_message += manifest_file->Filename();
156
error_message += ", OpenXR version returned not compatible with this loader";
157
LoaderLogger::LogErrorMessage(openxr_command, error_message);
158
res = XR_ERROR_FILE_CONTENTS_INVALID;
159
}
160
}
161
#ifdef XR_KHR_LOADER_INIT_SUPPORT
162
if (XR_SUCCEEDED(res) && !forwardedInitLoader) {
163
// Forward initialize loader call, where possible and if we did not do so before.
164
PFN_xrVoidFunction initializeVoid = nullptr;
165
PFN_xrInitializeLoaderKHR initialize = nullptr;
166
167
// Now we may try asking xrGetInstanceProcAddr
168
if (XR_SUCCEEDED(runtime_info.getInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", &initializeVoid))) {
169
if (initializeVoid == nullptr) {
170
LoaderLogger::LogErrorMessage(openxr_command,
171
"RuntimeInterface::LoadRuntime got success from xrGetInstanceProcAddr "
172
"for xrInitializeLoaderKHR, but output a null pointer.");
173
res = XR_ERROR_RUNTIME_FAILURE;
174
} else {
175
initialize = reinterpret_cast<PFN_xrInitializeLoaderKHR>(initializeVoid);
176
}
177
}
178
if (initialize != nullptr) {
179
// we found the entry point one way or another.
180
LoaderLogger::LogInfoMessage(openxr_command,
181
"RuntimeInterface::LoadRuntime forwarding xrInitializeLoaderKHR call to runtime after "
182
"calling xrNegotiateLoaderRuntimeInterface.");
183
res = initialize(LoaderInitData::instance().getParam());
184
if (!XR_SUCCEEDED(res)) {
185
LoaderLogger::LogErrorMessage(openxr_command,
186
"RuntimeInterface::LoadRuntime forwarded call to xrInitializeLoaderKHR failed.");
187
}
188
}
189
}
190
#endif
191
if (XR_FAILED(res)) {
192
std::string warning_message = "RuntimeInterface::LoadRuntime skipping manifest file ";
193
warning_message += manifest_file->Filename();
194
warning_message += ", negotiation failed with error ";
195
warning_message += std::to_string(res);
196
LoaderLogger::LogErrorMessage(openxr_command, warning_message);
197
LoaderPlatformLibraryClose(runtime_library);
198
return res;
199
}
200
201
std::string info_message = "RuntimeInterface::LoadRuntime succeeded loading runtime defined in manifest file ";
202
info_message += manifest_file->Filename();
203
info_message += " using interface version ";
204
info_message += std::to_string(runtime_info.runtimeInterfaceVersion);
205
info_message += " and OpenXR API version ";
206
info_message += std::to_string(XR_VERSION_MAJOR(runtime_info.runtimeApiVersion));
207
info_message += ".";
208
info_message += std::to_string(XR_VERSION_MINOR(runtime_info.runtimeApiVersion));
209
LoaderLogger::LogInfoMessage(openxr_command, info_message);
210
211
// Use this runtime
212
GetInstance().reset(new RuntimeInterface(runtime_library, runtime_info.getInstanceProcAddr));
213
214
// Grab the list of extensions this runtime supports for easy filtering after the
215
// xrCreateInstance call
216
std::vector<std::string> supported_extensions;
217
std::vector<XrExtensionProperties> extension_properties;
218
GetInstance()->GetInstanceExtensionProperties(extension_properties);
219
supported_extensions.reserve(extension_properties.size());
220
for (XrExtensionProperties ext_prop : extension_properties) {
221
supported_extensions.emplace_back(ext_prop.extensionName);
222
}
223
GetInstance()->SetSupportedExtensions(supported_extensions);
224
225
return XR_SUCCESS;
226
}
227
228
XrResult RuntimeInterface::LoadRuntime(const std::string& openxr_command) {
229
// If something's already loaded, we're done here.
230
if (GetInstance() != nullptr) {
231
return XR_SUCCESS;
232
}
233
#ifdef XR_KHR_LOADER_INIT_SUPPORT
234
if (!LoaderInitData::instance().initialized()) {
235
LoaderLogger::LogErrorMessage(
236
openxr_command, "RuntimeInterface::LoadRuntime cannot run because xrInitializeLoaderKHR was not successfully called.");
237
return XR_ERROR_INITIALIZATION_FAILED;
238
}
239
#endif // XR_KHR_LOADER_INIT_SUPPORT
240
241
std::vector<std::unique_ptr<RuntimeManifestFile>> runtime_manifest_files = {};
242
243
// Find the available runtimes which we may need to report information for.
244
XrResult last_error = RuntimeManifestFile::FindManifestFiles(openxr_command, runtime_manifest_files);
245
if (XR_FAILED(last_error)) {
246
LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntimes - unknown error");
247
} else {
248
last_error = XR_ERROR_RUNTIME_UNAVAILABLE;
249
for (std::unique_ptr<RuntimeManifestFile>& manifest_file : runtime_manifest_files) {
250
last_error = RuntimeInterface::TryLoadingSingleRuntime(openxr_command, manifest_file);
251
if (XR_SUCCEEDED(last_error)) {
252
break;
253
}
254
}
255
}
256
257
// Unsuccessful in loading any runtime, throw the runtime unavailable message.
258
if (XR_FAILED(last_error)) {
259
LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntimes - failed to load a runtime");
260
last_error = XR_ERROR_RUNTIME_UNAVAILABLE;
261
}
262
263
return last_error;
264
}
265
266
void RuntimeInterface::UnloadRuntime(const std::string& openxr_command) {
267
if (GetInstance()) {
268
LoaderLogger::LogInfoMessage(openxr_command, "RuntimeInterface::UnloadRuntime - Unloading RuntimeInterface");
269
GetInstance().reset();
270
}
271
}
272
273
XrResult RuntimeInterface::GetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function) {
274
return GetInstance()->_get_instance_proc_addr(instance, name, function);
275
}
276
277
const XrGeneratedDispatchTableCore* RuntimeInterface::GetDispatchTable(XrInstance instance) {
278
XrGeneratedDispatchTableCore* table = nullptr;
279
std::lock_guard<std::mutex> mlock(GetInstance()->_dispatch_table_mutex);
280
auto it = GetInstance()->_dispatch_table_map.find(instance);
281
if (it != GetInstance()->_dispatch_table_map.end()) {
282
table = it->second.get();
283
}
284
return table;
285
}
286
287
const XrGeneratedDispatchTableCore* RuntimeInterface::GetDebugUtilsMessengerDispatchTable(XrDebugUtilsMessengerEXT messenger) {
288
XrInstance runtime_instance = XR_NULL_HANDLE;
289
{
290
std::lock_guard<std::mutex> mlock(GetInstance()->_messenger_to_instance_mutex);
291
auto it = GetInstance()->_messenger_to_instance_map.find(messenger);
292
if (it != GetInstance()->_messenger_to_instance_map.end()) {
293
runtime_instance = it->second;
294
}
295
}
296
return GetDispatchTable(runtime_instance);
297
}
298
299
RuntimeInterface::RuntimeInterface(LoaderPlatformLibraryHandle runtime_library, PFN_xrGetInstanceProcAddr get_instance_proc_addr)
300
: _runtime_library(runtime_library), _get_instance_proc_addr(get_instance_proc_addr) {}
301
302
RuntimeInterface::~RuntimeInterface() {
303
std::string info_message = "RuntimeInterface being destroyed.";
304
LoaderLogger::LogInfoMessage("", info_message);
305
{
306
std::lock_guard<std::mutex> mlock(_dispatch_table_mutex);
307
_dispatch_table_map.clear();
308
}
309
LoaderPlatformLibraryClose(_runtime_library);
310
}
311
312
void RuntimeInterface::GetInstanceExtensionProperties(std::vector<XrExtensionProperties>& extension_properties) {
313
std::vector<XrExtensionProperties> runtime_extension_properties;
314
PFN_xrEnumerateInstanceExtensionProperties rt_xrEnumerateInstanceExtensionProperties;
315
_get_instance_proc_addr(XR_NULL_HANDLE, "xrEnumerateInstanceExtensionProperties",
316
reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrEnumerateInstanceExtensionProperties));
317
uint32_t count = 0;
318
uint32_t count_output = 0;
319
// Get the count from the runtime
320
rt_xrEnumerateInstanceExtensionProperties(nullptr, count, &count_output, nullptr);
321
if (count_output > 0) {
322
XrExtensionProperties example_properties{};
323
example_properties.type = XR_TYPE_EXTENSION_PROPERTIES;
324
runtime_extension_properties.resize(count_output, example_properties);
325
count = count_output;
326
rt_xrEnumerateInstanceExtensionProperties(nullptr, count, &count_output, runtime_extension_properties.data());
327
}
328
size_t ext_count = runtime_extension_properties.size();
329
size_t props_count = extension_properties.size();
330
for (size_t ext = 0; ext < ext_count; ++ext) {
331
bool found = false;
332
for (size_t prop = 0; prop < props_count; ++prop) {
333
// If we find it, then make sure the spec version matches that of the runtime instead of the
334
// layer.
335
if (strcmp(extension_properties[prop].extensionName, runtime_extension_properties[ext].extensionName) == 0) {
336
// Make sure the spec version used is the runtime's
337
extension_properties[prop].extensionVersion = runtime_extension_properties[ext].extensionVersion;
338
found = true;
339
break;
340
}
341
}
342
if (!found) {
343
extension_properties.push_back(runtime_extension_properties[ext]);
344
}
345
}
346
}
347
348
XrResult RuntimeInterface::CreateInstance(const XrInstanceCreateInfo* info, XrInstance* instance) {
349
XrResult res = XR_SUCCESS;
350
bool create_succeeded = false;
351
PFN_xrCreateInstance rt_xrCreateInstance;
352
_get_instance_proc_addr(XR_NULL_HANDLE, "xrCreateInstance", reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrCreateInstance));
353
res = rt_xrCreateInstance(info, instance);
354
if (XR_SUCCEEDED(res)) {
355
create_succeeded = true;
356
std::unique_ptr<XrGeneratedDispatchTableCore> dispatch_table(new XrGeneratedDispatchTableCore());
357
GeneratedXrPopulateDispatchTableCore(dispatch_table.get(), *instance, _get_instance_proc_addr);
358
std::lock_guard<std::mutex> mlock(_dispatch_table_mutex);
359
_dispatch_table_map[*instance] = std::move(dispatch_table);
360
}
361
362
// If the failure occurred during the populate, clean up the instance we had picked up from the runtime
363
if (XR_FAILED(res) && create_succeeded) {
364
PFN_xrDestroyInstance rt_xrDestroyInstance;
365
_get_instance_proc_addr(*instance, "xrDestroyInstance", reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrDestroyInstance));
366
rt_xrDestroyInstance(*instance);
367
*instance = XR_NULL_HANDLE;
368
}
369
370
return res;
371
}
372
373
XrResult RuntimeInterface::DestroyInstance(XrInstance instance) {
374
if (XR_NULL_HANDLE != instance) {
375
// Destroy the dispatch table for this instance first
376
{
377
std::lock_guard<std::mutex> mlock(_dispatch_table_mutex);
378
auto map_iter = _dispatch_table_map.find(instance);
379
if (map_iter != _dispatch_table_map.end()) {
380
_dispatch_table_map.erase(map_iter);
381
}
382
}
383
// Now delete the instance
384
PFN_xrDestroyInstance rt_xrDestroyInstance;
385
_get_instance_proc_addr(instance, "xrDestroyInstance", reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrDestroyInstance));
386
rt_xrDestroyInstance(instance);
387
}
388
return XR_SUCCESS;
389
}
390
391
bool RuntimeInterface::TrackDebugMessenger(XrInstance instance, XrDebugUtilsMessengerEXT messenger) {
392
std::lock_guard<std::mutex> mlock(_messenger_to_instance_mutex);
393
_messenger_to_instance_map[messenger] = instance;
394
return true;
395
}
396
397
void RuntimeInterface::ForgetDebugMessenger(XrDebugUtilsMessengerEXT messenger) {
398
if (XR_NULL_HANDLE != messenger) {
399
std::lock_guard<std::mutex> mlock(_messenger_to_instance_mutex);
400
_messenger_to_instance_map.erase(messenger);
401
}
402
}
403
404
void RuntimeInterface::SetSupportedExtensions(std::vector<std::string>& supported_extensions) {
405
_supported_extensions = supported_extensions;
406
}
407
408
bool RuntimeInterface::SupportsExtension(const std::string& extension_name) {
409
bool found_prop = false;
410
for (const std::string& supported_extension : _supported_extensions) {
411
if (supported_extension == extension_name) {
412
found_prop = true;
413
break;
414
}
415
}
416
return found_prop;
417
}
418
419