Blender  V2.93
GHOST_XrContext.cpp
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
23 #include <cassert>
24 #include <sstream>
25 #include <string>
26 #include <string_view>
27 
28 #include "GHOST_Types.h"
29 #include "GHOST_XrException.h"
30 #include "GHOST_XrSession.h"
31 #include "GHOST_Xr_intern.h"
32 
33 #include "GHOST_XrContext.h"
34 
36  XrInstance instance = XR_NULL_HANDLE;
37  XrInstanceProperties instance_properties = {};
38 
39  std::vector<XrExtensionProperties> extensions;
40  std::vector<XrApiLayerProperties> layers;
41 
42  static PFN_xrCreateDebugUtilsMessengerEXT s_xrCreateDebugUtilsMessengerEXT_fn;
43  static PFN_xrDestroyDebugUtilsMessengerEXT s_xrDestroyDebugUtilsMessengerEXT_fn;
44 
45  XrDebugUtilsMessengerEXT debug_messenger = XR_NULL_HANDLE;
46 };
47 
48 PFN_xrCreateDebugUtilsMessengerEXT OpenXRInstanceData::s_xrCreateDebugUtilsMessengerEXT_fn =
49  nullptr;
50 PFN_xrDestroyDebugUtilsMessengerEXT OpenXRInstanceData::s_xrDestroyDebugUtilsMessengerEXT_fn =
51  nullptr;
52 
53 GHOST_XrErrorHandlerFn GHOST_XrContext::s_error_handler = nullptr;
54 void *GHOST_XrContext::s_error_handler_customdata = nullptr;
55 
56 /* -------------------------------------------------------------------- */
61 GHOST_XrContext::GHOST_XrContext(const GHOST_XrContextCreateInfo *create_info)
62  : m_oxr(std::make_unique<OpenXRInstanceData>()),
63  m_debug(create_info->context_flag & GHOST_kXrContextDebug),
64  m_debug_time(create_info->context_flag & GHOST_kXrContextDebugTime)
65 {
66 }
67 
69 {
70  /* Destroy session data first. Otherwise xrDestroyInstance will implicitly do it, before the
71  * session had a chance to do so explicitly. */
72  m_session = nullptr;
73 
74  if (m_oxr->debug_messenger != XR_NULL_HANDLE) {
75  assert(m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn != nullptr);
76  m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn(m_oxr->debug_messenger);
77  }
78  if (m_oxr->instance != XR_NULL_HANDLE) {
79  CHECK_XR_ASSERT(xrDestroyInstance(m_oxr->instance));
80  m_oxr->instance = XR_NULL_HANDLE;
81  }
82 }
83 
84 void GHOST_XrContext::initialize(const GHOST_XrContextCreateInfo *create_info)
85 {
86  initApiLayers();
87  initExtensions();
88  if (isDebugMode()) {
89  printAvailableAPILayersAndExtensionsInfo();
90  }
91 
92  /* Multiple graphics binding extensions can be enabled, but only one will actually be used
93  * (determined later on). */
94  const std::vector<GHOST_TXrGraphicsBinding> graphics_binding_types =
95  determineGraphicsBindingTypesToEnable(create_info);
96 
97  assert(m_oxr->instance == XR_NULL_HANDLE);
98  createOpenXRInstance(graphics_binding_types);
99  storeInstanceProperties();
100 
101  /* Multiple bindings may be enabled. Now that we know the runtime in use, settle for one. */
102  m_gpu_binding_type = determineGraphicsBindingTypeToUse(graphics_binding_types);
103 
104  printInstanceInfo();
105  if (isDebugMode()) {
106  initDebugMessenger();
107  }
108 }
109 
110 void GHOST_XrContext::createOpenXRInstance(
111  const std::vector<GHOST_TXrGraphicsBinding> &graphics_binding_types)
112 {
113  XrInstanceCreateInfo create_info = {XR_TYPE_INSTANCE_CREATE_INFO};
114 
115  std::string("Blender").copy(create_info.applicationInfo.applicationName,
116  XR_MAX_APPLICATION_NAME_SIZE);
117  create_info.applicationInfo.apiVersion = XR_CURRENT_API_VERSION;
118 
119  getAPILayersToEnable(m_enabled_layers);
120  getExtensionsToEnable(graphics_binding_types, m_enabled_extensions);
121  create_info.enabledApiLayerCount = m_enabled_layers.size();
122  create_info.enabledApiLayerNames = m_enabled_layers.data();
123  create_info.enabledExtensionCount = m_enabled_extensions.size();
124  create_info.enabledExtensionNames = m_enabled_extensions.data();
125  if (isDebugMode()) {
126  printExtensionsAndAPILayersToEnable();
127  }
128 
129  CHECK_XR(xrCreateInstance(&create_info, &m_oxr->instance),
130  "Failed to connect to an OpenXR runtime.");
131 }
132 
133 void GHOST_XrContext::storeInstanceProperties()
134 {
135  const std::map<std::string, GHOST_TXrOpenXRRuntimeID> runtime_map = {
136  {"Monado(XRT) by Collabora et al", OPENXR_RUNTIME_MONADO},
137  {"Oculus", OPENXR_RUNTIME_OCULUS},
138  {"SteamVR/OpenXR", OPENXR_RUNTIME_STEAMVR},
139  {"Windows Mixed Reality Runtime", OPENXR_RUNTIME_WMR}};
140  decltype(runtime_map)::const_iterator runtime_map_iter;
141 
142  m_oxr->instance_properties.type = XR_TYPE_INSTANCE_PROPERTIES;
143  CHECK_XR(xrGetInstanceProperties(m_oxr->instance, &m_oxr->instance_properties),
144  "Failed to get OpenXR runtime information. Do you have an active runtime set up?");
145 
146  runtime_map_iter = runtime_map.find(m_oxr->instance_properties.runtimeName);
147  if (runtime_map_iter != runtime_map.end()) {
148  m_runtime_id = runtime_map_iter->second;
149  }
150 }
151  /* Create, Initialize and Destruct */
153 
154 /* -------------------------------------------------------------------- */
159 void GHOST_XrContext::printInstanceInfo()
160 {
161  assert(m_oxr->instance != XR_NULL_HANDLE);
162 
163  printf("Connected to OpenXR runtime: %s (Version %u.%u.%u)\n",
164  m_oxr->instance_properties.runtimeName,
165  XR_VERSION_MAJOR(m_oxr->instance_properties.runtimeVersion),
166  XR_VERSION_MINOR(m_oxr->instance_properties.runtimeVersion),
167  XR_VERSION_PATCH(m_oxr->instance_properties.runtimeVersion));
168 }
169 
170 void GHOST_XrContext::printAvailableAPILayersAndExtensionsInfo()
171 {
172  puts("Available OpenXR API-layers/extensions:");
173  for (XrApiLayerProperties &layer_info : m_oxr->layers) {
174  printf("Layer: %s\n", layer_info.layerName);
175  }
176  for (XrExtensionProperties &ext_info : m_oxr->extensions) {
177  printf("Extension: %s\n", ext_info.extensionName);
178  }
179 }
180 
181 void GHOST_XrContext::printExtensionsAndAPILayersToEnable()
182 {
183  for (const char *layer_name : m_enabled_layers) {
184  printf("Enabling OpenXR API-Layer: %s\n", layer_name);
185  }
186  for (const char *ext_name : m_enabled_extensions) {
187  printf("Enabling OpenXR Extension: %s\n", ext_name);
188  }
189 }
190 
191 static XrBool32 debug_messenger_func(XrDebugUtilsMessageSeverityFlagsEXT /*messageSeverity*/,
192  XrDebugUtilsMessageTypeFlagsEXT /*messageTypes*/,
193  const XrDebugUtilsMessengerCallbackDataEXT *callbackData,
194  void * /*userData*/)
195 {
196  puts("OpenXR Debug Message:");
197  puts(callbackData->message);
198  return XR_FALSE; /* OpenXR spec suggests always returning false. */
199 }
200 
201 void GHOST_XrContext::initDebugMessenger()
202 {
203  XrDebugUtilsMessengerCreateInfoEXT create_info = {XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT};
204 
205  /* Extension functions need to be obtained through xrGetInstanceProcAddr(). */
206  if (XR_FAILED(xrGetInstanceProcAddr(
207  m_oxr->instance,
208  "xrCreateDebugUtilsMessengerEXT",
209  (PFN_xrVoidFunction *)&m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn)) ||
210  XR_FAILED(xrGetInstanceProcAddr(
211  m_oxr->instance,
212  "xrDestroyDebugUtilsMessengerEXT",
213  (PFN_xrVoidFunction *)&m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn))) {
214  m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn = nullptr;
215  m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn = nullptr;
216 
217  fprintf(stderr,
218  "Could not use XR_EXT_debug_utils to enable debug prints. Not a fatal error, "
219  "continuing without the messenger.\n");
220  return;
221  }
222 
223  create_info.messageSeverities = XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
224  XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
225  XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
226  XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
227  create_info.messageTypes = XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
228  XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
229  XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
230  create_info.userCallback = debug_messenger_func;
231 
232  if (XR_FAILED(m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn(
233  m_oxr->instance, &create_info, &m_oxr->debug_messenger))) {
234  fprintf(stderr,
235  "Failed to create OpenXR debug messenger. Not a fatal error, continuing without the "
236  "messenger.\n");
237  return;
238  }
239 }
240  /* Debug Printing */
242 
243 /* -------------------------------------------------------------------- */
249 {
250  GHOST_XrError error;
251 
252  error.user_message = exception->m_msg;
253  error.customdata = s_error_handler_customdata;
254 
255  if (isDebugMode()) {
256  fprintf(stderr,
257  "Error: \t%s\n\tOpenXR error value: %i\n",
258  error.user_message,
259  exception->m_result);
260  }
261 
262  /* Potentially destroys GHOST_XrContext */
263  s_error_handler(&error);
264 }
265 
266 void GHOST_XrContext::setErrorHandler(GHOST_XrErrorHandlerFn handler_fn, void *customdata)
267 {
268  s_error_handler = handler_fn;
269  s_error_handler_customdata = customdata;
270 }
271  /* Error handling */
273 
274 /* -------------------------------------------------------------------- */
282 void GHOST_XrContext::initExtensionsEx(std::vector<XrExtensionProperties> &extensions,
283  const char *layer_name)
284 {
285  uint32_t extension_count = 0;
286 
287  /* Get count for array creation/init first. */
288  CHECK_XR(xrEnumerateInstanceExtensionProperties(layer_name, 0, &extension_count, nullptr),
289  "Failed to query OpenXR runtime information. Do you have an active runtime set up?");
290 
291  if (extension_count == 0) {
292  /* Extensions are optional, can successfully exit. */
293  return;
294  }
295 
296  for (uint32_t i = 0; i < extension_count; i++) {
297  XrExtensionProperties ext = {XR_TYPE_EXTENSION_PROPERTIES};
298  extensions.push_back(ext);
299  }
300 
301  /* Actually get the extensions. */
302  CHECK_XR(xrEnumerateInstanceExtensionProperties(
303  layer_name, extension_count, &extension_count, extensions.data()),
304  "Failed to query OpenXR runtime information. Do you have an active runtime set up?");
305 }
306 
307 void GHOST_XrContext::initExtensions()
308 {
309  initExtensionsEx(m_oxr->extensions, nullptr);
310 }
311 
312 void GHOST_XrContext::initApiLayers()
313 {
314  uint32_t layer_count = 0;
315 
316  /* Get count for array creation/init first. */
317  CHECK_XR(xrEnumerateApiLayerProperties(0, &layer_count, nullptr),
318  "Failed to query OpenXR runtime information. Do you have an active runtime set up?");
319 
320  if (layer_count == 0) {
321  /* Layers are optional, can safely exit. */
322  return;
323  }
324 
325  m_oxr->layers = std::vector<XrApiLayerProperties>(layer_count);
326  for (XrApiLayerProperties &layer : m_oxr->layers) {
327  layer.type = XR_TYPE_API_LAYER_PROPERTIES;
328  }
329 
330  /* Actually get the layers. */
331  CHECK_XR(xrEnumerateApiLayerProperties(layer_count, &layer_count, m_oxr->layers.data()),
332  "Failed to query OpenXR runtime information. Do you have an active runtime set up?");
333  for (XrApiLayerProperties &layer : m_oxr->layers) {
334  /* Each layer may have own extensions. */
335  initExtensionsEx(m_oxr->extensions, layer.layerName);
336  }
337 }
338 
339 static bool openxr_layer_is_available(const std::vector<XrApiLayerProperties> &layers_info,
340  const std::string &layer_name)
341 {
342  for (const XrApiLayerProperties &layer_info : layers_info) {
343  if (layer_info.layerName == layer_name) {
344  return true;
345  }
346  }
347 
348  return false;
349 }
350 
352  const std::vector<XrExtensionProperties> &extensions_info,
353  const std::string_view &extension_name)
354 {
355  for (const XrExtensionProperties &ext_info : extensions_info) {
356  if (ext_info.extensionName == extension_name) {
357  return true;
358  }
359  }
360 
361  return false;
362 }
363 
367 void GHOST_XrContext::getAPILayersToEnable(std::vector<const char *> &r_ext_names)
368 {
369  static std::vector<std::string> try_layers;
370 
371  try_layers.clear();
372 
373  if (isDebugMode()) {
374  try_layers.push_back("XR_APILAYER_LUNARG_core_validation");
375  }
376 
377  r_ext_names.reserve(try_layers.size());
378 
379  for (const std::string &layer : try_layers) {
380  if (openxr_layer_is_available(m_oxr->layers, layer)) {
381  r_ext_names.push_back(layer.c_str());
382  }
383  }
384 }
385 
386 static const char *openxr_ext_name_from_wm_gpu_binding(GHOST_TXrGraphicsBinding binding)
387 {
388  switch (binding) {
389  case GHOST_kXrGraphicsOpenGL:
390  return XR_KHR_OPENGL_ENABLE_EXTENSION_NAME;
391 #ifdef WIN32
392  case GHOST_kXrGraphicsD3D11:
393  return XR_KHR_D3D11_ENABLE_EXTENSION_NAME;
394 #endif
395  case GHOST_kXrGraphicsUnknown:
396  assert(!"Could not identify graphics binding to choose.");
397  return nullptr;
398  }
399 
400  return nullptr;
401 }
402 
406 void GHOST_XrContext::getExtensionsToEnable(
407  const std::vector<GHOST_TXrGraphicsBinding> &graphics_binding_types,
408  std::vector<const char *> &r_ext_names)
409 {
410  std::vector<std::string_view> try_ext;
411 
412  /* Try enabling debug extension. */
413  if (isDebugMode()) {
414  try_ext.push_back(XR_EXT_DEBUG_UTILS_EXTENSION_NAME);
415  }
416 
417  r_ext_names.reserve(try_ext.size() + graphics_binding_types.size());
418 
419  /* Add graphics binding extensions (may be multiple ones, we'll settle for one to use later, once
420  * we have more info about the runtime). */
421  for (GHOST_TXrGraphicsBinding type : graphics_binding_types) {
422  const char *gpu_binding = openxr_ext_name_from_wm_gpu_binding(type);
423  assert(openxr_extension_is_available(m_oxr->extensions, gpu_binding));
424  r_ext_names.push_back(gpu_binding);
425  }
426 
427  for (const std::string_view &ext : try_ext) {
428  if (openxr_extension_is_available(m_oxr->extensions, ext)) {
429  r_ext_names.push_back(ext.data());
430  }
431  }
432 }
433 
438 std::vector<GHOST_TXrGraphicsBinding> GHOST_XrContext::determineGraphicsBindingTypesToEnable(
439  const GHOST_XrContextCreateInfo *create_info)
440 {
441  std::vector<GHOST_TXrGraphicsBinding> result;
442  assert(create_info->gpu_binding_candidates != NULL);
443  assert(create_info->gpu_binding_candidates_count > 0);
444 
445  for (uint32_t i = 0; i < create_info->gpu_binding_candidates_count; i++) {
446  assert(create_info->gpu_binding_candidates[i] != GHOST_kXrGraphicsUnknown);
447  const char *ext_name = openxr_ext_name_from_wm_gpu_binding(
448  create_info->gpu_binding_candidates[i]);
449  if (openxr_extension_is_available(m_oxr->extensions, ext_name)) {
450  result.push_back(create_info->gpu_binding_candidates[i]);
451  }
452  }
453 
454  if (result.empty()) {
455  throw GHOST_XrException("No supported graphics binding found.");
456  }
457 
458  return result;
459 }
460 
461 GHOST_TXrGraphicsBinding GHOST_XrContext::determineGraphicsBindingTypeToUse(
462  const std::vector<GHOST_TXrGraphicsBinding> &enabled_types)
463 {
464  /* Return the first working type. */
465  for (GHOST_TXrGraphicsBinding type : enabled_types) {
466 #ifdef WIN32
467  /* The SteamVR OpenGL backend fails currently. Disable it and allow falling back to the DirectX
468  * one. */
469  if ((m_runtime_id == OPENXR_RUNTIME_STEAMVR) && (type == GHOST_kXrGraphicsOpenGL)) {
470  continue;
471  }
472 #endif
473 
474  assert(type != GHOST_kXrGraphicsUnknown);
475  return type;
476  }
477 
478  throw GHOST_XrException("Failed to determine a graphics binding to use.");
479 }
480  /* OpenXR API-Layers and Extensions */
482 
483 /* -------------------------------------------------------------------- */
489 void GHOST_XrContext::startSession(const GHOST_XrSessionBeginInfo *begin_info)
490 {
491  m_custom_funcs.session_exit_fn = begin_info->exit_fn;
492  m_custom_funcs.session_exit_customdata = begin_info->exit_customdata;
493 
494  if (m_session == nullptr) {
495  m_session = std::make_unique<GHOST_XrSession>(*this);
496  }
497  m_session->start(begin_info);
498 }
499 
501 {
502  if (m_session) {
503  if (m_session->isRunning()) {
504  m_session->requestEnd();
505  }
506  else {
507  m_session = nullptr;
508  }
509  }
510 }
511 
513 {
514  return m_session && m_session->isRunning();
515 }
516 
517 void GHOST_XrContext::drawSessionViews(void *draw_customdata)
518 {
519  m_session->draw(draw_customdata);
520 }
521 
525 void GHOST_XrContext::handleSessionStateChange(const XrEventDataSessionStateChanged &lifecycle)
526 {
527  if (m_session &&
528  m_session->handleStateChangeEvent(lifecycle) == GHOST_XrSession::SESSION_DESTROY) {
529  m_session = nullptr;
530  }
531 }
532  /* Session Management */
534 
535 /* -------------------------------------------------------------------- */
541 void GHOST_XrContext::setGraphicsContextBindFuncs(GHOST_XrGraphicsContextBindFn bind_fn,
542  GHOST_XrGraphicsContextUnbindFn unbind_fn)
543 {
544  if (m_session) {
545  m_session->unbindGraphicsContext();
546  }
547  m_custom_funcs.gpu_ctx_bind_fn = bind_fn;
548  m_custom_funcs.gpu_ctx_unbind_fn = unbind_fn;
549 }
550 
551 void GHOST_XrContext::setDrawViewFunc(GHOST_XrDrawViewFn draw_view_fn)
552 {
553  m_custom_funcs.draw_view_fn = draw_view_fn;
554 }
555 
557 {
558  /* Must only be called after the session was started */
559  assert(m_session);
560  return m_session->needsUpsideDownDrawing();
561 }
562  /* Public Accessors and Mutators */
564 
565 /* -------------------------------------------------------------------- */
571 {
572  return m_runtime_id;
573 }
574 
576 {
577  return m_custom_funcs;
578 }
579 
580 GHOST_TXrGraphicsBinding GHOST_XrContext::getGraphicsBindingType() const
581 {
582  return m_gpu_binding_type;
583 }
584 
586 {
587  return m_oxr->instance;
588 }
589 
591 {
592  return m_debug;
593 }
594 
596 {
597  return m_debug_time;
598 }
599  /* Ghost Internal Accessors and Mutators */
static XrBool32 debug_messenger_func(XrDebugUtilsMessageSeverityFlagsEXT, XrDebugUtilsMessageTypeFlagsEXT, const XrDebugUtilsMessengerCallbackDataEXT *callbackData, void *)
static bool openxr_layer_is_available(const std::vector< XrApiLayerProperties > &layers_info, const std::string &layer_name)
static const char * openxr_ext_name_from_wm_gpu_binding(GHOST_TXrGraphicsBinding binding)
static bool openxr_extension_is_available(const std::vector< XrExtensionProperties > &extensions_info, const std::string_view &extension_name)
GHOST_TXrOpenXRRuntimeID
@ OPENXR_RUNTIME_MONADO
@ OPENXR_RUNTIME_OCULUS
@ OPENXR_RUNTIME_STEAMVR
@ OPENXR_RUNTIME_WMR
#define CHECK_XR_ASSERT(call)
#define CHECK_XR(call, error_msg)
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
XrInstance getInstance() const
GHOST_TXrOpenXRRuntimeID getOpenXRRuntimeID() const
void setGraphicsContextBindFuncs(GHOST_XrGraphicsContextBindFn bind_fn, GHOST_XrGraphicsContextUnbindFn unbind_fn) override
GHOST_TXrGraphicsBinding getGraphicsBindingType() const
void drawSessionViews(void *draw_customdata) override
void handleSessionStateChange(const XrEventDataSessionStateChanged &lifecycle)
const GHOST_XrCustomFuncs & getCustomFuncs() const
void dispatchErrorMessage(const class GHOST_XrException *exception) const override
bool isDebugTimeMode() const
void setDrawViewFunc(GHOST_XrDrawViewFn draw_view_fn) override
bool needsUpsideDownDrawing() const override
bool isSessionRunning() const override
void startSession(const GHOST_XrSessionBeginInfo *begin_info) override
void endSession() override
GHOST_XrContext(const GHOST_XrContextCreateInfo *create_info)
bool isDebugMode() const
void initialize(const GHOST_XrContextCreateInfo *create_info)
static void setErrorHandler(GHOST_XrErrorHandlerFn handler_fn, void *customdata)
static void error(const char *str)
Definition: meshlaplacian.c:65
unsigned int uint32_t
Definition: stdint.h:83
GHOST_XrDrawViewFn draw_view_fn
GHOST_XrSessionExitFn session_exit_fn
GHOST_XrGraphicsContextUnbindFn gpu_ctx_unbind_fn
GHOST_XrGraphicsContextBindFn gpu_ctx_bind_fn
XrDebugUtilsMessengerEXT debug_messenger
XrInstanceProperties instance_properties
std::vector< XrExtensionProperties > extensions
static PFN_xrDestroyDebugUtilsMessengerEXT s_xrDestroyDebugUtilsMessengerEXT_fn
std::vector< XrApiLayerProperties > layers
static PFN_xrCreateDebugUtilsMessengerEXT s_xrCreateDebugUtilsMessengerEXT_fn