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