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 Authors: Mark Young <marky@lunarg.com>, Dave Houlton <daveh@lunarg.com>
8//
9
10#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
11#define _CRT_SECURE_NO_WARNINGS
12#endif // defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
13
14#include "api_layer_interface.hpp"
15#include "exception_handling.hpp"
16#include "hex_and_handles.h"
17#include "loader_instance.hpp"
18#include "loader_logger_recorders.hpp"
19#include "loader_logger.hpp"
20#include "loader_platform.hpp"
21#include "runtime_interface.hpp"
22#include "xr_generated_dispatch_table.h"
23#include "xr_generated_loader.hpp"
24
25#include <openxr/openxr.h>
26
27#include <cstring>
28#include <memory>
29#include <mutex>
30#include <sstream>
31#include <string>
32#include <utility>
33#include <vector>
34
35// Global loader lock to:
36// 1. Ensure ActiveLoaderInstance get and set operations are done atomically.
37// 2. Ensure RuntimeInterface isn't used to unload the runtime while the runtime is in use.
38static std::mutex &GetGlobalLoaderMutex() {
39 static std::mutex loader_mutex;
40 return loader_mutex;
41}
42
43// Prototypes for the debug utils calls used internally.
44static XRAPI_ATTR XrResult XRAPI_CALL LoaderTrampolineCreateDebugUtilsMessengerEXT(
45 XrInstance instance, const XrDebugUtilsMessengerCreateInfoEXT *createInfo, XrDebugUtilsMessengerEXT *messenger);
46static XRAPI_ATTR XrResult XRAPI_CALL LoaderTrampolineDestroyDebugUtilsMessengerEXT(XrDebugUtilsMessengerEXT messenger);
47
48// Terminal functions needed by xrCreateInstance.
49static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermGetInstanceProcAddr(XrInstance, const char *, PFN_xrVoidFunction *);
50static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermCreateInstance(const XrInstanceCreateInfo *, XrInstance *);
51static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermCreateApiLayerInstance(const XrInstanceCreateInfo *,
52 const struct XrApiLayerCreateInfo *, XrInstance *);
53static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermSetDebugUtilsObjectNameEXT(XrInstance, const XrDebugUtilsObjectNameInfoEXT *);
54static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermCreateDebugUtilsMessengerEXT(XrInstance,
55 const XrDebugUtilsMessengerCreateInfoEXT *,
56 XrDebugUtilsMessengerEXT *);
57static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermDestroyDebugUtilsMessengerEXT(XrDebugUtilsMessengerEXT);
58static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermSubmitDebugUtilsMessageEXT(
59 XrInstance instance, XrDebugUtilsMessageSeverityFlagsEXT messageSeverity, XrDebugUtilsMessageTypeFlagsEXT messageTypes,
60 const XrDebugUtilsMessengerCallbackDataEXT *callbackData);
61static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrGetInstanceProcAddr(XrInstance instance, const char *name,
62 PFN_xrVoidFunction *function);
63
64// Utility template function meant to validate if a fixed size string contains
65// a null-terminator.
66template <size_t max_length>
67inline bool IsMissingNullTerminator(const char (&str)[max_length]) {
68 for (size_t index = 0; index < max_length; ++index) {
69 if (str[index] == '\0') {
70 return false;
71 }
72 }
73 return true;
74}
75
76// ---- Core 1.0 manual loader trampoline functions
77#ifdef XR_KHR_LOADER_INIT_SUPPORT // platforms that support XR_KHR_loader_init.
78XRAPI_ATTR XrResult XRAPI_CALL LoaderXrInitializeLoaderKHR(const XrLoaderInitInfoBaseHeaderKHR *loaderInitInfo) XRLOADER_ABI_TRY {
79 LoaderLogger::LogVerboseMessage("xrInitializeLoaderKHR", "Entering loader trampoline");
80 return InitializeLoader(loaderInitInfo);
81}
82XRLOADER_ABI_CATCH_FALLBACK
83#endif
84
85static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrEnumerateApiLayerProperties(uint32_t propertyCapacityInput,
86 uint32_t *propertyCountOutput,
87 XrApiLayerProperties *properties) XRLOADER_ABI_TRY {
88 LoaderLogger::LogVerboseMessage("xrEnumerateApiLayerProperties", "Entering loader trampoline");
89
90 // Make sure only one thread is attempting to read the JSON files at a time.
91 std::unique_lock<std::mutex> loader_lock(GetGlobalLoaderMutex());
92
93 XrResult result = ApiLayerInterface::GetApiLayerProperties("xrEnumerateApiLayerProperties", propertyCapacityInput,
94 propertyCountOutput, properties);
95 if (XR_FAILED(result)) {
96 LoaderLogger::LogErrorMessage("xrEnumerateApiLayerProperties", "Failed ApiLayerInterface::GetApiLayerProperties");
97 }
98
99 return result;
100}
101XRLOADER_ABI_CATCH_FALLBACK
102
103static XRAPI_ATTR XrResult XRAPI_CALL
104LoaderXrEnumerateInstanceExtensionProperties(const char *layerName, uint32_t propertyCapacityInput, uint32_t *propertyCountOutput,
105 XrExtensionProperties *properties) XRLOADER_ABI_TRY {
106 bool just_layer_properties = false;
107 LoaderLogger::LogVerboseMessage("xrEnumerateInstanceExtensionProperties", "Entering loader trampoline");
108
109 // "Independent of elementCapacityInput or elements parameters, elementCountOutput must be a valid pointer,
110 // and the function sets elementCountOutput." - 2.11
111 if (nullptr == propertyCountOutput) {
112 return XR_ERROR_VALIDATION_FAILURE;
113 }
114
115 if (nullptr != layerName && 0 != strlen(layerName)) {
116 // Application is only interested in layer's properties, not all of them.
117 just_layer_properties = true;
118 }
119
120 std::vector<XrExtensionProperties> extension_properties = {};
121 XrResult result;
122
123 {
124 // Make sure the runtime isn't unloaded while this call is in progress.
125 std::unique_lock<std::mutex> loader_lock(GetGlobalLoaderMutex());
126
127 // Get the layer extension properties
128 result = ApiLayerInterface::GetInstanceExtensionProperties("xrEnumerateInstanceExtensionProperties", layerName,
129 extension_properties);
130 if (XR_SUCCEEDED(result) && !just_layer_properties) {
131 // If not specific to a layer, get the runtime extension properties
132 result = RuntimeInterface::LoadRuntime("xrEnumerateInstanceExtensionProperties");
133 if (XR_SUCCEEDED(result)) {
134 RuntimeInterface::GetRuntime().GetInstanceExtensionProperties(extension_properties);
135 } else {
136 LoaderLogger::LogErrorMessage("xrEnumerateInstanceExtensionProperties",
137 "Failed to find default runtime with RuntimeInterface::LoadRuntime()");
138 }
139 }
140 }
141
142 if (XR_FAILED(result)) {
143 LoaderLogger::LogErrorMessage("xrEnumerateInstanceExtensionProperties", "Failed querying extension properties");
144 return result;
145 }
146
147 // If this is not in reference to a specific layer, then add the loader-specific extension properties as well.
148 // These are extensions that the loader directly supports.
149 if (!just_layer_properties) {
150 for (const XrExtensionProperties &loader_prop : LoaderInstance::LoaderSpecificExtensions()) {
151 bool found_prop = false;
152 for (XrExtensionProperties &existing_prop : extension_properties) {
153 if (0 == strcmp(existing_prop.extensionName, loader_prop.extensionName)) {
154 found_prop = true;
155 // Use the loader version if it is newer
156 if (existing_prop.extensionVersion < loader_prop.extensionVersion) {
157 existing_prop.extensionVersion = loader_prop.extensionVersion;
158 }
159 break;
160 }
161 }
162 // Only add extensions not supported by the loader
163 if (!found_prop) {
164 extension_properties.push_back(loader_prop);
165 }
166 }
167 }
168
169 auto num_extension_properties = static_cast<uint32_t>(extension_properties.size());
170 if (propertyCapacityInput == 0) {
171 *propertyCountOutput = num_extension_properties;
172 } else if (nullptr != properties) {
173 if (propertyCapacityInput < num_extension_properties) {
174 *propertyCountOutput = num_extension_properties;
175 LoaderLogger::LogValidationErrorMessage("VUID-xrEnumerateInstanceExtensionProperties-propertyCountOutput-parameter",
176 "xrEnumerateInstanceExtensionProperties", "insufficient space in array");
177 return XR_ERROR_SIZE_INSUFFICIENT;
178 }
179
180 uint32_t num_to_copy = num_extension_properties;
181 // Determine how many extension properties we can copy over
182 if (propertyCapacityInput < num_to_copy) {
183 num_to_copy = propertyCapacityInput;
184 }
185 bool properties_valid = true;
186 for (uint32_t prop = 0; prop < propertyCapacityInput && prop < extension_properties.size(); ++prop) {
187 if (XR_TYPE_EXTENSION_PROPERTIES != properties[prop].type) {
188 properties_valid = false;
189 LoaderLogger::LogValidationErrorMessage("VUID-XrExtensionProperties-type-type",
190 "xrEnumerateInstanceExtensionProperties", "unknown type in properties");
191 }
192 if (properties_valid) {
193 properties[prop] = extension_properties[prop];
194 }
195 }
196 if (!properties_valid) {
197 LoaderLogger::LogValidationErrorMessage("VUID-xrEnumerateInstanceExtensionProperties-properties-parameter",
198 "xrEnumerateInstanceExtensionProperties", "invalid properties");
199 return XR_ERROR_VALIDATION_FAILURE;
200 }
201 if (nullptr != propertyCountOutput) {
202 *propertyCountOutput = num_to_copy;
203 }
204 } else {
205 // incoming_count is not 0 BUT the properties is NULL
206 return XR_ERROR_VALIDATION_FAILURE;
207 }
208 LoaderLogger::LogVerboseMessage("xrEnumerateInstanceExtensionProperties", "Completed loader trampoline");
209 return XR_SUCCESS;
210}
211XRLOADER_ABI_CATCH_FALLBACK
212
213static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrCreateInstance(const XrInstanceCreateInfo *info,
214 XrInstance *instance) XRLOADER_ABI_TRY {
215 LoaderLogger::LogVerboseMessage("xrCreateInstance", "Entering loader trampoline");
216 if (nullptr == info) {
217 LoaderLogger::LogValidationErrorMessage("VUID-xrCreateInstance-info-parameter", "xrCreateInstance", "must be non-NULL");
218 return XR_ERROR_VALIDATION_FAILURE;
219 }
220 // If application requested OpenXR API version is higher than the loader version, then we need to throw
221 // an error.
222 uint16_t app_major = XR_VERSION_MAJOR(info->applicationInfo.apiVersion); // NOLINT
223 uint16_t app_minor = XR_VERSION_MINOR(info->applicationInfo.apiVersion); // NOLINT
224 uint16_t loader_major = XR_VERSION_MAJOR(XR_CURRENT_API_VERSION); // NOLINT
225 uint16_t loader_minor = XR_VERSION_MINOR(XR_CURRENT_API_VERSION); // NOLINT
226 if (app_major > loader_major || (app_major == loader_major && app_minor > loader_minor)) {
227 std::ostringstream oss;
228 oss << "xrCreateInstance called with invalid API version " << app_major << "." << app_minor
229 << ". Max supported version is " << loader_major << "." << loader_minor;
230 LoaderLogger::LogErrorMessage("xrCreateInstance", oss.str());
231 return XR_ERROR_API_VERSION_UNSUPPORTED;
232 }
233
234 if (nullptr == instance) {
235 LoaderLogger::LogValidationErrorMessage("VUID-xrCreateInstance-instance-parameter", "xrCreateInstance", "must be non-NULL");
236 return XR_ERROR_VALIDATION_FAILURE;
237 }
238
239 // Make sure the ActiveLoaderInstance::IsAvailable check is done atomically with RuntimeInterface::LoadRuntime.
240 std::unique_lock<std::mutex> instance_lock(GetGlobalLoaderMutex());
241
242 // Check if there is already an XrInstance that is alive. If so, another instance cannot be created.
243 // The loader does not support multiple simultaneous instances because the loader is intended to be
244 // usable by apps using future OpenXR APIs (through xrGetInstanceProcAddr). Because the loader would
245 // not be aware of new handle types, it would not be able to look up the appropriate dispatch table
246 // in some cases.
247 if (ActiveLoaderInstance::IsAvailable()) { // If there is an XrInstance already alive.
248 LoaderLogger::LogErrorMessage("xrCreateInstance", "Loader does not support simultaneous XrInstances");
249 return XR_ERROR_LIMIT_REACHED;
250 }
251
252 std::vector<std::unique_ptr<ApiLayerInterface>> api_layer_interfaces;
253 XrResult result;
254
255 // Make sure only one thread is attempting to read the JSON files and use the instance.
256 {
257 // Load the available runtime
258 result = RuntimeInterface::LoadRuntime("xrCreateInstance");
259 if (XR_FAILED(result)) {
260 LoaderLogger::LogErrorMessage("xrCreateInstance", "Failed loading runtime information");
261 } else {
262 // Load the appropriate layers
263 result = ApiLayerInterface::LoadApiLayers("xrCreateInstance", info->enabledApiLayerCount, info->enabledApiLayerNames,
264 api_layer_interfaces);
265 if (XR_FAILED(result)) {
266 LoaderLogger::LogErrorMessage("xrCreateInstance", "Failed loading layer information");
267 }
268 }
269 }
270
271 // Create the loader instance (only send down first runtime interface)
272 LoaderInstance *loader_instance = nullptr;
273 if (XR_SUCCEEDED(result)) {
274 std::unique_ptr<LoaderInstance> owned_loader_instance;
275 result = LoaderInstance::CreateInstance(LoaderXrTermGetInstanceProcAddr, LoaderXrTermCreateInstance,
276 LoaderXrTermCreateApiLayerInstance, std::move(api_layer_interfaces), info,
277 &owned_loader_instance);
278 if (XR_SUCCEEDED(result)) {
279 loader_instance = owned_loader_instance.get();
280 result = ActiveLoaderInstance::Set(std::move(owned_loader_instance), "xrCreateInstance");
281 }
282 }
283
284 if (XR_SUCCEEDED(result)) {
285 // Create a debug utils messenger if the create structure is in the "next" chain
286 const auto *next_header = reinterpret_cast<const XrBaseInStructure *>(info->next);
287 const XrDebugUtilsMessengerCreateInfoEXT *dbg_utils_create_info = nullptr;
288 while (next_header != nullptr) {
289 if (next_header->type == XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT) {
290 LoaderLogger::LogInfoMessage("xrCreateInstance", "Found XrDebugUtilsMessengerCreateInfoEXT in \'next\' chain.");
291 dbg_utils_create_info = reinterpret_cast<const XrDebugUtilsMessengerCreateInfoEXT *>(next_header);
292 XrDebugUtilsMessengerEXT messenger;
293 result = LoaderTrampolineCreateDebugUtilsMessengerEXT(loader_instance->GetInstanceHandle(), dbg_utils_create_info,
294 &messenger);
295 if (XR_FAILED(result)) {
296 return XR_ERROR_VALIDATION_FAILURE;
297 }
298 loader_instance->SetDefaultDebugUtilsMessenger(messenger);
299 break;
300 }
301 next_header = reinterpret_cast<const XrBaseInStructure *>(next_header->next);
302 }
303 }
304
305 if (XR_FAILED(result)) {
306 // Ensure the global loader instance and runtime are destroyed if something went wrong.
307 ActiveLoaderInstance::Remove();
308 RuntimeInterface::UnloadRuntime("xrCreateInstance");
309 LoaderLogger::LogErrorMessage("xrCreateInstance", "xrCreateInstance failed");
310 } else {
311 *instance = loader_instance->GetInstanceHandle();
312 LoaderLogger::LogVerboseMessage("xrCreateInstance", "Completed loader trampoline");
313 }
314
315 return result;
316}
317XRLOADER_ABI_CATCH_FALLBACK
318
319static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrDestroyInstance(XrInstance instance) XRLOADER_ABI_TRY {
320 LoaderLogger::LogVerboseMessage("xrDestroyInstance", "Entering loader trampoline");
321 // Runtimes may detect XR_NULL_HANDLE provided as a required handle parameter and return XR_ERROR_HANDLE_INVALID. - 2.9
322 if (XR_NULL_HANDLE == instance) {
323 LoaderLogger::LogErrorMessage("xrDestroyInstance", "Instance handle is XR_NULL_HANDLE.");
324 return XR_ERROR_HANDLE_INVALID;
325 }
326
327 // Make sure the runtime isn't unloaded while it is being used by xrEnumerateInstanceExtensionProperties.
328 std::unique_lock<std::mutex> loader_lock(GetGlobalLoaderMutex());
329
330 LoaderInstance *loader_instance;
331 XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrDestroyInstance");
332 if (XR_FAILED(result)) {
333 return result;
334 }
335
336 const std::unique_ptr<XrGeneratedDispatchTable> &dispatch_table = loader_instance->DispatchTable();
337
338 // If we allocated a default debug utils messenger, free it
339 XrDebugUtilsMessengerEXT messenger = loader_instance->DefaultDebugUtilsMessenger();
340 if (messenger != XR_NULL_HANDLE) {
341 LoaderTrampolineDestroyDebugUtilsMessengerEXT(messenger);
342 }
343
344 // Now destroy the instance
345 if (XR_FAILED(dispatch_table->DestroyInstance(instance))) {
346 LoaderLogger::LogErrorMessage("xrDestroyInstance", "Unknown error occurred calling down chain");
347 }
348
349 // Get rid of the loader instance. This will make it possible to create another instance in the future.
350 ActiveLoaderInstance::Remove();
351
352 // Lock the instance create/destroy mutex
353 LoaderLogger::LogVerboseMessage("xrDestroyInstance", "Completed loader trampoline");
354
355 // Finally, unload the runtime if necessary
356 RuntimeInterface::UnloadRuntime("xrDestroyInstance");
357
358 return XR_SUCCESS;
359}
360XRLOADER_ABI_CATCH_FALLBACK
361
362// ---- Core 1.0 manual loader terminator functions
363
364// Validate that the applicationInfo structure in the XrInstanceCreateInfo is valid.
365static XrResult ValidateApplicationInfo(const XrApplicationInfo &info) {
366 if (IsMissingNullTerminator<XR_MAX_APPLICATION_NAME_SIZE>(info.applicationName)) {
367 LoaderLogger::LogValidationErrorMessage("VUID-XrApplicationInfo-applicationName-parameter", "xrCreateInstance",
368 "application name missing NULL terminator.");
369 return XR_ERROR_NAME_INVALID;
370 }
371 if (IsMissingNullTerminator<XR_MAX_ENGINE_NAME_SIZE>(info.engineName)) {
372 LoaderLogger::LogValidationErrorMessage("VUID-XrApplicationInfo-engineName-parameter", "xrCreateInstance",
373 "engine name missing NULL terminator.");
374 return XR_ERROR_NAME_INVALID;
375 }
376 if (strlen(info.applicationName) == 0) {
377 LoaderLogger::LogErrorMessage("xrCreateInstance",
378 "VUID-XrApplicationInfo-engineName-parameter: application name can not be empty.");
379 return XR_ERROR_NAME_INVALID;
380 }
381 return XR_SUCCESS;
382}
383
384// Validate that the XrInstanceCreateInfo is valid
385static XrResult ValidateInstanceCreateInfo(const XrInstanceCreateInfo *info) {
386 // Should have a valid 'type'
387 if (XR_TYPE_INSTANCE_CREATE_INFO != info->type) {
388 LoaderLogger::LogValidationErrorMessage("VUID-XrInstanceCreateInfo-type-type", "xrCreateInstance",
389 "expected XR_TYPE_INSTANCE_CREATE_INFO.");
390 return XR_ERROR_VALIDATION_FAILURE;
391 }
392 // Flags must be 0
393 if (0 != info->createFlags) {
394 LoaderLogger::LogValidationErrorMessage("VUID-XrInstanceCreateInfo-createFlags-zerobitmask", "xrCreateInstance",
395 "flags must be 0.");
396 return XR_ERROR_VALIDATION_FAILURE;
397 }
398 // ApplicationInfo struct must be valid
399 XrResult result = ValidateApplicationInfo(info->applicationInfo);
400 if (XR_FAILED(result)) {
401 LoaderLogger::LogValidationErrorMessage("VUID-XrInstanceCreateInfo-applicationInfo-parameter", "xrCreateInstance",
402 "info->applicationInfo is not valid.");
403 return result;
404 }
405 // VUID-XrInstanceCreateInfo-enabledApiLayerNames-parameter already tested in LoadApiLayers()
406 if ((info->enabledExtensionCount != 0u) && nullptr == info->enabledExtensionNames) {
407 LoaderLogger::LogValidationErrorMessage("VUID-XrInstanceCreateInfo-enabledExtensionNames-parameter", "xrCreateInstance",
408 "enabledExtensionCount is non-0 but array is NULL");
409 return XR_ERROR_VALIDATION_FAILURE;
410 }
411 return XR_SUCCESS;
412}
413
414static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermCreateInstance(const XrInstanceCreateInfo *createInfo,
415 XrInstance *instance) XRLOADER_ABI_TRY {
416 LoaderLogger::LogVerboseMessage("xrCreateInstance", "Entering loader terminator");
417 XrResult result = ValidateInstanceCreateInfo(createInfo);
418 if (XR_FAILED(result)) {
419 LoaderLogger::LogValidationErrorMessage("VUID-xrCreateInstance-info-parameter", "xrCreateInstance",
420 "something wrong with XrInstanceCreateInfo contents");
421 return result;
422 }
423 result = RuntimeInterface::GetRuntime().CreateInstance(createInfo, instance);
424 LoaderLogger::LogVerboseMessage("xrCreateInstance", "Completed loader terminator");
425 return result;
426}
427XRLOADER_ABI_CATCH_FALLBACK
428
429static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermCreateApiLayerInstance(const XrInstanceCreateInfo *info,
430 const struct XrApiLayerCreateInfo * /*apiLayerInfo*/,
431 XrInstance *instance) {
432 return LoaderXrTermCreateInstance(info, instance);
433}
434
435static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermDestroyInstance(XrInstance instance) XRLOADER_ABI_TRY {
436 LoaderLogger::LogVerboseMessage("xrDestroyInstance", "Entering loader terminator");
437 LoaderLogger::GetInstance().RemoveLogRecordersForXrInstance(instance);
438 XrResult result = RuntimeInterface::GetRuntime().DestroyInstance(instance);
439 LoaderLogger::LogVerboseMessage("xrDestroyInstance", "Completed loader terminator");
440 return result;
441}
442XRLOADER_ABI_CATCH_FALLBACK
443
444static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermGetInstanceProcAddr(XrInstance instance, const char *name,
445 PFN_xrVoidFunction *function) XRLOADER_ABI_TRY {
446 // A few instance commands need to go through a loader terminator.
447 // Otherwise, go directly to the runtime version of the command if it exists.
448 // But first set the function pointer to NULL so that the fall-through below actually works.
449 *function = nullptr;
450
451 // NOTE: ActiveLoaderInstance cannot be used in this function because it is called before an instance is made active.
452
453 if (0 == strcmp(name, "xrGetInstanceProcAddr")) {
454 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermGetInstanceProcAddr);
455 } else if (0 == strcmp(name, "xrCreateInstance")) {
456 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermCreateInstance);
457 } else if (0 == strcmp(name, "xrDestroyInstance")) {
458 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermDestroyInstance);
459 } else if (0 == strcmp(name, "xrSetDebugUtilsObjectNameEXT")) {
460 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermSetDebugUtilsObjectNameEXT);
461 } else if (0 == strcmp(name, "xrCreateDebugUtilsMessengerEXT")) {
462 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermCreateDebugUtilsMessengerEXT);
463 } else if (0 == strcmp(name, "xrDestroyDebugUtilsMessengerEXT")) {
464 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermDestroyDebugUtilsMessengerEXT);
465 } else if (0 == strcmp(name, "xrSubmitDebugUtilsMessageEXT")) {
466 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermSubmitDebugUtilsMessageEXT);
467 } else if (0 == strcmp(name, "xrCreateApiLayerInstance")) {
468 // Special layer version of xrCreateInstance terminator. If we get called this by a layer,
469 // we simply re-direct the information back into the standard xrCreateInstance terminator.
470 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermCreateApiLayerInstance);
471 }
472
473 if (nullptr != *function) {
474 return XR_SUCCESS;
475 }
476
477 return RuntimeInterface::GetInstanceProcAddr(instance, name, function);
478}
479XRLOADER_ABI_CATCH_FALLBACK
480
481// ---- Extension manual loader trampoline functions
482
483static XRAPI_ATTR XrResult XRAPI_CALL
484LoaderTrampolineCreateDebugUtilsMessengerEXT(XrInstance instance, const XrDebugUtilsMessengerCreateInfoEXT *createInfo,
485 XrDebugUtilsMessengerEXT *messenger) XRLOADER_ABI_TRY {
486 LoaderLogger::LogVerboseMessage("xrCreateDebugUtilsMessengerEXT", "Entering loader trampoline");
487
488 if (instance == XR_NULL_HANDLE) {
489 LoaderLogger::LogErrorMessage("xrCreateDebugUtilsMessengerEXT", "Instance handle is XR_NULL_HANDLE.");
490 return XR_ERROR_HANDLE_INVALID;
491 }
492
493 LoaderInstance *loader_instance;
494 XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrCreateDebugUtilsMessengerEXT");
495 if (XR_FAILED(result)) {
496 return result;
497 }
498
499 result = loader_instance->DispatchTable()->CreateDebugUtilsMessengerEXT(instance, createInfo, messenger);
500 LoaderLogger::LogVerboseMessage("xrCreateDebugUtilsMessengerEXT", "Completed loader trampoline");
501 return result;
502}
503XRLOADER_ABI_CATCH_BAD_ALLOC_OOM XRLOADER_ABI_CATCH_FALLBACK
504
505 static XRAPI_ATTR XrResult XRAPI_CALL
506 LoaderTrampolineDestroyDebugUtilsMessengerEXT(XrDebugUtilsMessengerEXT messenger) XRLOADER_ABI_TRY {
507 // TODO: get instance from messenger in loader
508 // Also, is the loader really doing all this every call?
509 LoaderLogger::LogVerboseMessage("xrDestroyDebugUtilsMessengerEXT", "Entering loader trampoline");
510
511 if (messenger == XR_NULL_HANDLE) {
512 LoaderLogger::LogErrorMessage("xrDestroyDebugUtilsMessengerEXT", "Messenger handle is XR_NULL_HANDLE.");
513 return XR_ERROR_HANDLE_INVALID;
514 }
515
516 LoaderInstance *loader_instance;
517 XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrDestroyDebugUtilsMessengerEXT");
518 if (XR_FAILED(result)) {
519 return result;
520 }
521
522 result = loader_instance->DispatchTable()->DestroyDebugUtilsMessengerEXT(messenger);
523 LoaderLogger::LogVerboseMessage("xrDestroyDebugUtilsMessengerEXT", "Completed loader trampoline");
524 return result;
525}
526XRLOADER_ABI_CATCH_FALLBACK
527
528static XRAPI_ATTR XrResult XRAPI_CALL
529LoaderTrampolineSessionBeginDebugUtilsLabelRegionEXT(XrSession session, const XrDebugUtilsLabelEXT *labelInfo) XRLOADER_ABI_TRY {
530 if (session == XR_NULL_HANDLE) {
531 LoaderLogger::LogErrorMessage("xrSessionBeginDebugUtilsLabelRegionEXT", "Session handle is XR_NULL_HANDLE.");
532 return XR_ERROR_HANDLE_INVALID;
533 }
534
535 if (nullptr == labelInfo) {
536 LoaderLogger::LogValidationErrorMessage("VUID-xrSessionBeginDebugUtilsLabelRegionEXT-labelInfo-parameter",
537 "xrSessionBeginDebugUtilsLabelRegionEXT", "labelInfo must be non-NULL",
538 {XrSdkLogObjectInfo{session, XR_OBJECT_TYPE_SESSION}});
539 return XR_ERROR_VALIDATION_FAILURE;
540 }
541
542 LoaderInstance *loader_instance;
543 XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrSessionBeginDebugUtilsLabelRegionEXT");
544 if (XR_FAILED(result)) {
545 return result;
546 }
547 LoaderLogger::GetInstance().BeginLabelRegion(session, labelInfo);
548 const std::unique_ptr<XrGeneratedDispatchTable> &dispatch_table = loader_instance->DispatchTable();
549 if (nullptr != dispatch_table->SessionBeginDebugUtilsLabelRegionEXT) {
550 return dispatch_table->SessionBeginDebugUtilsLabelRegionEXT(session, labelInfo);
551 }
552 return XR_SUCCESS;
553}
554XRLOADER_ABI_CATCH_FALLBACK
555
556static XRAPI_ATTR XrResult XRAPI_CALL LoaderTrampolineSessionEndDebugUtilsLabelRegionEXT(XrSession session) XRLOADER_ABI_TRY {
557 if (session == XR_NULL_HANDLE) {
558 LoaderLogger::LogErrorMessage("xrSessionEndDebugUtilsLabelRegionEXT", "Session handle is XR_NULL_HANDLE.");
559 return XR_ERROR_HANDLE_INVALID;
560 }
561
562 LoaderInstance *loader_instance;
563 XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrSessionEndDebugUtilsLabelRegionEXT");
564 if (XR_FAILED(result)) {
565 return result;
566 }
567
568 LoaderLogger::GetInstance().EndLabelRegion(session);
569 const std::unique_ptr<XrGeneratedDispatchTable> &dispatch_table = loader_instance->DispatchTable();
570 if (nullptr != dispatch_table->SessionEndDebugUtilsLabelRegionEXT) {
571 return dispatch_table->SessionEndDebugUtilsLabelRegionEXT(session);
572 }
573 return XR_SUCCESS;
574}
575XRLOADER_ABI_CATCH_FALLBACK
576
577static XRAPI_ATTR XrResult XRAPI_CALL
578LoaderTrampolineSessionInsertDebugUtilsLabelEXT(XrSession session, const XrDebugUtilsLabelEXT *labelInfo) XRLOADER_ABI_TRY {
579 if (session == XR_NULL_HANDLE) {
580 LoaderLogger::LogErrorMessage("xrSessionInsertDebugUtilsLabelEXT", "Session handle is XR_NULL_HANDLE.");
581 return XR_ERROR_HANDLE_INVALID;
582 }
583
584 LoaderInstance *loader_instance;
585 XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrSessionInsertDebugUtilsLabelEXT");
586 if (XR_FAILED(result)) {
587 return result;
588 }
589
590 if (nullptr == labelInfo) {
591 LoaderLogger::LogValidationErrorMessage("VUID-xrSessionInsertDebugUtilsLabelEXT-labelInfo-parameter",
592 "xrSessionInsertDebugUtilsLabelEXT", "labelInfo must be non-NULL",
593 {XrSdkLogObjectInfo{session, XR_OBJECT_TYPE_SESSION}});
594 return XR_ERROR_VALIDATION_FAILURE;
595 }
596
597 LoaderLogger::GetInstance().InsertLabel(session, labelInfo);
598
599 const std::unique_ptr<XrGeneratedDispatchTable> &dispatch_table = loader_instance->DispatchTable();
600 if (nullptr != dispatch_table->SessionInsertDebugUtilsLabelEXT) {
601 return dispatch_table->SessionInsertDebugUtilsLabelEXT(session, labelInfo);
602 }
603
604 return XR_SUCCESS;
605}
606XRLOADER_ABI_CATCH_FALLBACK
607
608// No-op trampoline needed for xrGetInstanceProcAddr. Work done in terminator.
609static XRAPI_ATTR XrResult XRAPI_CALL
610LoaderTrampolineSetDebugUtilsObjectNameEXT(XrInstance instance, const XrDebugUtilsObjectNameInfoEXT *nameInfo) XRLOADER_ABI_TRY {
611 LoaderInstance *loader_instance;
612 XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrSetDebugUtilsObjectNameEXT");
613 if (XR_SUCCEEDED(result)) {
614 result = loader_instance->DispatchTable()->SetDebugUtilsObjectNameEXT(instance, nameInfo);
615 }
616 return result;
617}
618XRLOADER_ABI_CATCH_FALLBACK
619
620// No-op trampoline needed for xrGetInstanceProcAddr. Work done in terminator.
621static XRAPI_ATTR XrResult XRAPI_CALL LoaderTrampolineSubmitDebugUtilsMessageEXT(
622 XrInstance instance, XrDebugUtilsMessageSeverityFlagsEXT messageSeverity, XrDebugUtilsMessageTypeFlagsEXT messageTypes,
623 const XrDebugUtilsMessengerCallbackDataEXT *callbackData) XRLOADER_ABI_TRY {
624 LoaderInstance *loader_instance;
625 XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrSubmitDebugUtilsMessageEXT");
626 if (XR_SUCCEEDED(result)) {
627 result =
628 loader_instance->DispatchTable()->SubmitDebugUtilsMessageEXT(instance, messageSeverity, messageTypes, callbackData);
629 }
630 return result;
631}
632XRLOADER_ABI_CATCH_FALLBACK
633
634// ---- Extension manual loader terminator functions
635
636XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermCreateDebugUtilsMessengerEXT(XrInstance instance,
637 const XrDebugUtilsMessengerCreateInfoEXT *createInfo,
638 XrDebugUtilsMessengerEXT *messenger) XRLOADER_ABI_TRY {
639 LoaderLogger::LogVerboseMessage("xrCreateDebugUtilsMessengerEXT", "Entering loader terminator");
640 if (nullptr == messenger) {
641 LoaderLogger::LogValidationErrorMessage("VUID-xrCreateDebugUtilsMessengerEXT-messenger-parameter",
642 "xrCreateDebugUtilsMessengerEXT", "invalid messenger pointer");
643 return XR_ERROR_VALIDATION_FAILURE;
644 }
645 const XrGeneratedDispatchTable *dispatch_table = RuntimeInterface::GetDispatchTable(instance);
646 XrResult result = XR_SUCCESS;
647 // This extension is supported entirely by the loader which means the runtime may or may not support it.
648 if (nullptr != dispatch_table->CreateDebugUtilsMessengerEXT) {
649 result = dispatch_table->CreateDebugUtilsMessengerEXT(instance, createInfo, messenger);
650 } else {
651 // Just allocate a character so we have a unique value
652 char *temp_mess_ptr = new char;
653 *messenger = reinterpret_cast<XrDebugUtilsMessengerEXT>(temp_mess_ptr);
654 }
655 if (XR_SUCCEEDED(result)) {
656 LoaderLogger::GetInstance().AddLogRecorderForXrInstance(instance, MakeDebugUtilsLoaderLogRecorder(createInfo, *messenger));
657 RuntimeInterface::GetRuntime().TrackDebugMessenger(instance, *messenger);
658 }
659 LoaderLogger::LogVerboseMessage("xrCreateDebugUtilsMessengerEXT", "Completed loader terminator");
660 return result;
661}
662XRLOADER_ABI_CATCH_FALLBACK
663
664XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermDestroyDebugUtilsMessengerEXT(XrDebugUtilsMessengerEXT messenger) XRLOADER_ABI_TRY {
665 LoaderLogger::LogVerboseMessage("xrDestroyDebugUtilsMessengerEXT", "Entering loader terminator");
666 const XrGeneratedDispatchTable *dispatch_table = RuntimeInterface::GetDebugUtilsMessengerDispatchTable(messenger);
667 XrResult result = XR_SUCCESS;
668 LoaderLogger::GetInstance().RemoveLogRecorder(MakeHandleGeneric(messenger));
669 RuntimeInterface::GetRuntime().ForgetDebugMessenger(messenger);
670 // This extension is supported entirely by the loader which means the runtime may or may not support it.
671 if (nullptr != dispatch_table->DestroyDebugUtilsMessengerEXT) {
672 result = dispatch_table->DestroyDebugUtilsMessengerEXT(messenger);
673 } else {
674 // Delete the character we would've created
675 delete (reinterpret_cast<char *>(MakeHandleGeneric(messenger)));
676 }
677 LoaderLogger::LogVerboseMessage("xrDestroyDebugUtilsMessengerEXT", "Completed loader terminator");
678 return result;
679}
680XRLOADER_ABI_CATCH_FALLBACK
681
682XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermSubmitDebugUtilsMessageEXT(
683 XrInstance instance, XrDebugUtilsMessageSeverityFlagsEXT messageSeverity, XrDebugUtilsMessageTypeFlagsEXT messageTypes,
684 const XrDebugUtilsMessengerCallbackDataEXT *callbackData) XRLOADER_ABI_TRY {
685 LoaderLogger::LogVerboseMessage("xrSubmitDebugUtilsMessageEXT", "Entering loader terminator");
686 const XrGeneratedDispatchTable *dispatch_table = RuntimeInterface::GetDispatchTable(instance);
687 XrResult result = XR_SUCCESS;
688 if (nullptr != dispatch_table->SubmitDebugUtilsMessageEXT) {
689 result = dispatch_table->SubmitDebugUtilsMessageEXT(instance, messageSeverity, messageTypes, callbackData);
690 } else {
691 // Only log the message from the loader if the runtime doesn't support this extension. If we did,
692 // then the user would receive multiple instances of the same message.
693 LoaderLogger::GetInstance().LogDebugUtilsMessage(messageSeverity, messageTypes, callbackData);
694 }
695 LoaderLogger::LogVerboseMessage("xrSubmitDebugUtilsMessageEXT", "Completed loader terminator");
696 return result;
697}
698XRLOADER_ABI_CATCH_FALLBACK
699
700XRAPI_ATTR XrResult XRAPI_CALL
701LoaderXrTermSetDebugUtilsObjectNameEXT(XrInstance instance, const XrDebugUtilsObjectNameInfoEXT *nameInfo) XRLOADER_ABI_TRY {
702 LoaderLogger::LogVerboseMessage("xrSetDebugUtilsObjectNameEXT", "Entering loader terminator");
703 const XrGeneratedDispatchTable *dispatch_table = RuntimeInterface::GetDispatchTable(instance);
704 XrResult result = XR_SUCCESS;
705 if (nullptr != dispatch_table->SetDebugUtilsObjectNameEXT) {
706 result = dispatch_table->SetDebugUtilsObjectNameEXT(instance, nameInfo);
707 }
708 LoaderLogger::GetInstance().AddObjectName(nameInfo->objectHandle, nameInfo->objectType, nameInfo->objectName);
709 LoaderLogger::LogVerboseMessage("xrSetDebugUtilsObjectNameEXT", "Completed loader terminator");
710 return result;
711}
712XRLOADER_ABI_CATCH_FALLBACK
713
714XRAPI_ATTR XrResult XRAPI_CALL LoaderXrGetInstanceProcAddr(XrInstance instance, const char *name,
715 PFN_xrVoidFunction *function) XRLOADER_ABI_TRY {
716 if (nullptr == function) {
717 LoaderLogger::LogValidationErrorMessage("VUID-xrGetInstanceProcAddr-function-parameter", "xrGetInstanceProcAddr",
718 "Invalid Function pointer");
719 return XR_ERROR_VALIDATION_FAILURE;
720 }
721
722 if (nullptr == name) {
723 LoaderLogger::LogValidationErrorMessage("VUID-xrGetInstanceProcAddr-function-parameter", "xrGetInstanceProcAddr",
724 "Invalid Name pointer");
725 return XR_ERROR_VALIDATION_FAILURE;
726 }
727
728 // Initialize the function to nullptr in case it does not get caught in a known case
729 *function = nullptr;
730
731 LoaderInstance *loader_instance = nullptr;
732 if (instance == XR_NULL_HANDLE) {
733 // Null instance is allowed for a few specific API entry points, otherwise return error
734 if (strcmp(name, "xrCreateInstance") != 0 && strcmp(name, "xrEnumerateApiLayerProperties") != 0 &&
735 strcmp(name, "xrEnumerateInstanceExtensionProperties") != 0 && strcmp(name, "xrInitializeLoaderKHR") != 0) {
736 // TODO why is xrGetInstanceProcAddr not listed in here?
737 std::string error_str = "XR_NULL_HANDLE for instance but query for ";
738 error_str += name;
739 error_str += " requires a valid instance";
740 LoaderLogger::LogValidationErrorMessage("VUID-xrGetInstanceProcAddr-instance-parameter", "xrGetInstanceProcAddr",
741 error_str);
742 return XR_ERROR_HANDLE_INVALID;
743 }
744 } else {
745 // non null instance passed in, it should be our current instance
746 XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrGetInstanceProcAddr");
747 if (XR_FAILED(result)) {
748 return result;
749 }
750 if (loader_instance->GetInstanceHandle() != instance) {
751 return XR_ERROR_HANDLE_INVALID;
752 }
753 }
754
755 // These functions must always go through the loader's implementation (trampoline).
756 if (strcmp(name, "xrGetInstanceProcAddr") == 0) {
757 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrGetInstanceProcAddr);
758 return XR_SUCCESS;
759 } else if (strcmp(name, "xrInitializeLoaderKHR") == 0) {
760#ifdef XR_KHR_LOADER_INIT_SUPPORT
761 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrInitializeLoaderKHR);
762 return XR_SUCCESS;
763#else
764 return XR_ERROR_FUNCTION_UNSUPPORTED;
765#endif
766 } else if (strcmp(name, "xrEnumerateApiLayerProperties") == 0) {
767 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrEnumerateApiLayerProperties);
768 return XR_SUCCESS;
769 } else if (strcmp(name, "xrEnumerateInstanceExtensionProperties") == 0) {
770 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrEnumerateInstanceExtensionProperties);
771 return XR_SUCCESS;
772 } else if (strcmp(name, "xrCreateInstance") == 0) {
773 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrCreateInstance);
774 return XR_SUCCESS;
775 } else if (strcmp(name, "xrDestroyInstance") == 0) {
776 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrDestroyInstance);
777 return XR_SUCCESS;
778 }
779
780 // XR_EXT_debug_utils is built into the loader and handled partly through the xrGetInstanceProcAddress terminator,
781 // but the check to see if the extension is enabled must be done here where ActiveLoaderInstance is safe to use.
782 if (*function == nullptr) {
783 if (strcmp(name, "xrCreateDebugUtilsMessengerEXT") == 0) {
784 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineCreateDebugUtilsMessengerEXT);
785 } else if (strcmp(name, "xrDestroyDebugUtilsMessengerEXT") == 0) {
786 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineDestroyDebugUtilsMessengerEXT);
787 } else if (strcmp(name, "xrSessionBeginDebugUtilsLabelRegionEXT") == 0) {
788 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineSessionBeginDebugUtilsLabelRegionEXT);
789 } else if (strcmp(name, "xrSessionEndDebugUtilsLabelRegionEXT") == 0) {
790 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineSessionEndDebugUtilsLabelRegionEXT);
791 } else if (strcmp(name, "xrSessionInsertDebugUtilsLabelEXT") == 0) {
792 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineSessionInsertDebugUtilsLabelEXT);
793 } else if (strcmp(name, "xrSetDebugUtilsObjectNameEXT") == 0) {
794 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineSetDebugUtilsObjectNameEXT);
795 } else if (strcmp(name, "xrSubmitDebugUtilsMessageEXT") == 0) {
796 *function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineSubmitDebugUtilsMessageEXT);
797 }
798
799 if (*function != nullptr && !loader_instance->ExtensionIsEnabled("XR_EXT_debug_utils")) {
800 // The function matches one of the XR_EXT_debug_utils functions but the extension is not enabled.
801 *function = nullptr;
802 return XR_ERROR_FUNCTION_UNSUPPORTED;
803 }
804 }
805
806 if (*function != nullptr) {
807 // The loader has a trampoline or implementation of this function.
808 return XR_SUCCESS;
809 }
810
811 // If the function is not supported by the loader, call down to the next layer.
812 return loader_instance->GetInstanceProcAddr(name, function);
813}
814XRLOADER_ABI_CATCH_FALLBACK
815
816// Exported loader functions
817//
818// The application might override these by exporting the same symbols and so we can't use these
819// symbols anywhere in the loader code, and instead the internal non exported functions that these
820// stubs call should be used internally.
821LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateApiLayerProperties(uint32_t propertyCapacityInput,
822 uint32_t *propertyCountOutput,
823 XrApiLayerProperties *properties) {
824 return LoaderXrEnumerateApiLayerProperties(propertyCapacityInput, propertyCountOutput, properties);
825}
826
827LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateInstanceExtensionProperties(const char *layerName,
828 uint32_t propertyCapacityInput,
829 uint32_t *propertyCountOutput,
830 XrExtensionProperties *properties) {
831 return LoaderXrEnumerateInstanceExtensionProperties(layerName, propertyCapacityInput, propertyCountOutput, properties);
832}
833
834LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrCreateInstance(const XrInstanceCreateInfo *info, XrInstance *instance) {
835 return LoaderXrCreateInstance(info, instance);
836}
837
838LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrDestroyInstance(XrInstance instance) { return LoaderXrDestroyInstance(instance); }
839
840LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetInstanceProcAddr(XrInstance instance, const char *name,
841 PFN_xrVoidFunction *function) {
842 return LoaderXrGetInstanceProcAddr(instance, name, function);
843}
844
845#ifdef XR_KHR_LOADER_INIT_SUPPORT
846LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrInitializeLoaderKHR(const XrLoaderInitInfoBaseHeaderKHR *loaderInitInfo) {
847 return LoaderXrInitializeLoaderKHR(loaderInitInfo);
848}
849#endif
850