General minor fixes and cleanup
[blender.git] / intern / ghost / intern / GHOST_XrContext.cpp
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
17 /** \file
18  * \ingroup GHOST
19  *
20  * Abstraction for XR (VR, AR, MR, ..) access via OpenXR.
21  */
22
23 #include <cassert>
24 #include <string>
25
26 #include "GHOST_Types.h"
27 #include "GHOST_Xr_intern.h"
28 #include "GHOST_XrSession.h"
29
30 #include "GHOST_XrContext.h"
31
32 struct OpenXRInstanceData {
33   XrInstance instance{XR_NULL_HANDLE};
34
35   std::vector<XrExtensionProperties> extensions;
36   std::vector<XrApiLayerProperties> layers;
37
38   static PFN_xrCreateDebugUtilsMessengerEXT s_xrCreateDebugUtilsMessengerEXT_fn;
39   static PFN_xrDestroyDebugUtilsMessengerEXT s_xrDestroyDebugUtilsMessengerEXT_fn;
40
41   XrDebugUtilsMessengerEXT debug_messenger{XR_NULL_HANDLE};
42 };
43
44 PFN_xrCreateDebugUtilsMessengerEXT OpenXRInstanceData::s_xrCreateDebugUtilsMessengerEXT_fn =
45     nullptr;
46 PFN_xrDestroyDebugUtilsMessengerEXT OpenXRInstanceData::s_xrDestroyDebugUtilsMessengerEXT_fn =
47     nullptr;
48
49 /* -------------------------------------------------------------------- */
50 /** \name Create, Initialize and Destruct
51  *
52  * \{ */
53
54 GHOST_XrContext::GHOST_XrContext(const GHOST_XrContextCreateInfo *create_info)
55     : m_oxr(new OpenXRInstanceData()), m_debug(create_info->context_flag & GHOST_kXrContextDebug)
56 {
57 }
58 GHOST_XrContext::~GHOST_XrContext()
59 {
60   if (m_oxr->debug_messenger != XR_NULL_HANDLE) {
61     assert(m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn != nullptr);
62     m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn(m_oxr->debug_messenger);
63   }
64   if (m_oxr->instance != XR_NULL_HANDLE) {
65     xrDestroyInstance(m_oxr->instance);
66   }
67 }
68
69 GHOST_TSuccess GHOST_XrContext::initialize(const GHOST_XrContextCreateInfo *create_info)
70 {
71   if (!enumerateApiLayers() || !enumerateExtensions()) {
72     return GHOST_kFailure;
73   }
74   XR_DEBUG_ONLY_CALL(this, printAvailableAPILayersAndExtensionsInfo());
75
76   m_gpu_binding_type = determineGraphicsBindingTypeToEnable(create_info);
77
78   assert(m_oxr->instance == XR_NULL_HANDLE);
79   createOpenXRInstance();
80   printInstanceInfo();
81   XR_DEBUG_ONLY_CALL(this, initDebugMessenger());
82
83   return GHOST_kSuccess;
84 }
85
86 void GHOST_XrContext::createOpenXRInstance()
87 {
88   XrInstanceCreateInfo create_info{XR_TYPE_INSTANCE_CREATE_INFO};
89
90   std::string("Blender").copy(create_info.applicationInfo.applicationName,
91                               XR_MAX_APPLICATION_NAME_SIZE);
92   create_info.applicationInfo.apiVersion = XR_CURRENT_API_VERSION;
93
94   getAPILayersToEnable(m_enabled_layers);
95   getExtensionsToEnable(m_enabled_extensions);
96   create_info.enabledApiLayerCount = m_enabled_layers.size();
97   create_info.enabledApiLayerNames = m_enabled_layers.data();
98   create_info.enabledExtensionCount = m_enabled_extensions.size();
99   create_info.enabledExtensionNames = m_enabled_extensions.data();
100   XR_DEBUG_ONLY_CALL(this, printExtensionsAndAPILayersToEnable());
101
102   xrCreateInstance(&create_info, &m_oxr->instance);
103 }
104
105 /** \} */ /* Create, Initialize and Destruct */
106
107 /* -------------------------------------------------------------------- */
108 /** \name Debug Printing
109  *
110  * \{ */
111
112 void GHOST_XrContext::printInstanceInfo()
113 {
114   assert(m_oxr->instance != XR_NULL_HANDLE);
115
116   XrInstanceProperties instance_properties{XR_TYPE_INSTANCE_PROPERTIES};
117   xrGetInstanceProperties(m_oxr->instance, &instance_properties);
118
119   printf("Connected to OpenXR runtime: %s (Version %i.%i.%i)\n",
120          instance_properties.runtimeName,
121          XR_VERSION_MAJOR(instance_properties.runtimeVersion),
122          XR_VERSION_MINOR(instance_properties.runtimeVersion),
123          XR_VERSION_PATCH(instance_properties.runtimeVersion));
124 }
125
126 void GHOST_XrContext::printAvailableAPILayersAndExtensionsInfo()
127 {
128   puts("Available OpenXR API-layers/extensions:");
129   for (XrApiLayerProperties &layer_info : m_oxr->layers) {
130     printf("Layer: %s\n", layer_info.layerName);
131   }
132   for (XrExtensionProperties &ext_info : m_oxr->extensions) {
133     printf("Extension: %s\n", ext_info.extensionName);
134   }
135 }
136
137 void GHOST_XrContext::printExtensionsAndAPILayersToEnable()
138 {
139   for (const char *layer_name : m_enabled_layers) {
140     printf("Enabling OpenXR API-Layer: %s\n", layer_name);
141   }
142   for (const char *ext_name : m_enabled_extensions) {
143     printf("Enabling OpenXR Extension: %s\n", ext_name);
144   }
145 }
146
147 static XrBool32 debug_messenger_func(XrDebugUtilsMessageSeverityFlagsEXT /*messageSeverity*/,
148                                      XrDebugUtilsMessageTypeFlagsEXT /*messageTypes*/,
149                                      const XrDebugUtilsMessengerCallbackDataEXT *callbackData,
150                                      void * /*userData*/)
151 {
152   puts("OpenXR Debug Message:");
153   puts(callbackData->message);
154   return XR_FALSE;  // OpenXR spec suggests always returning false.
155 }
156
157 void GHOST_XrContext::initDebugMessenger()
158 {
159   XrDebugUtilsMessengerCreateInfoEXT create_info{XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT};
160
161   /* Extension functions need to be obtained through xrGetInstanceProcAddr */
162   if (XR_FAILED(xrGetInstanceProcAddr(
163           m_oxr->instance,
164           "xrCreateDebugUtilsMessengerEXT",
165           (PFN_xrVoidFunction *)&m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn)) ||
166       XR_FAILED(xrGetInstanceProcAddr(
167           m_oxr->instance,
168           "xrDestroyDebugUtilsMessengerEXT",
169           (PFN_xrVoidFunction *)&m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn))) {
170     fprintf(stderr, "Could not use XR_EXT_debug_utils to enable debug prints.\n");
171     m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn = nullptr;
172     m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn = nullptr;
173     return;
174   }
175
176   create_info.messageSeverities = XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
177                                   XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
178                                   XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
179                                   XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
180   create_info.messageTypes = XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
181                              XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
182                              XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
183   create_info.userCallback = debug_messenger_func;
184
185   m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn(
186       m_oxr->instance, &create_info, &m_oxr->debug_messenger);
187 }
188
189 /** \} */ /* Debug Printing */
190
191 /* -------------------------------------------------------------------- */
192 /** \name OpenXR API-Layers and Extensions
193  *
194  * \{ */
195
196 /**
197  * \param layer_name May be NULL for extensions not belonging to a specific layer.
198  */
199 GHOST_TSuccess GHOST_XrContext::enumerateExtensionsEx(
200     std::vector<XrExtensionProperties> &extensions, const char *layer_name)
201 {
202   uint32_t extension_count = 0;
203
204   /* Get count for array creation/init first. */
205   if (XR_FAILED(
206           xrEnumerateInstanceExtensionProperties(layer_name, 0, &extension_count, nullptr))) {
207     return GHOST_kFailure;
208   }
209
210   if (extension_count == 0) {
211     /* Extensions are optional, can successfully exit. */
212     return GHOST_kSuccess;
213   }
214
215   for (uint32_t i = 0; i < extension_count; i++) {
216     XrExtensionProperties ext{XR_TYPE_EXTENSION_PROPERTIES};
217     extensions.push_back(ext);
218   }
219
220   /* Actually get the extensions. */
221   xrEnumerateInstanceExtensionProperties(
222       layer_name, extension_count, &extension_count, extensions.data());
223
224   return GHOST_kSuccess;
225 }
226 GHOST_TSuccess GHOST_XrContext::enumerateExtensions()
227 {
228   return enumerateExtensionsEx(m_oxr->extensions, nullptr);
229 }
230
231 GHOST_TSuccess GHOST_XrContext::enumerateApiLayers()
232 {
233   uint32_t layer_count = 0;
234
235   /* Get count for array creation/init first. */
236   if (XR_FAILED(xrEnumerateApiLayerProperties(0, &layer_count, nullptr))) {
237     return GHOST_kFailure;
238   }
239
240   if (layer_count == 0) {
241     /* Layers are optional, can safely exit. */
242     return GHOST_kSuccess;
243   }
244
245   m_oxr->layers = std::vector<XrApiLayerProperties>(layer_count);
246   for (XrApiLayerProperties &layer : m_oxr->layers) {
247     layer.type = XR_TYPE_API_LAYER_PROPERTIES;
248   }
249
250   /* Actually get the layers. */
251   xrEnumerateApiLayerProperties(layer_count, &layer_count, m_oxr->layers.data());
252   for (XrApiLayerProperties &layer : m_oxr->layers) {
253     /* Each layer may have own extensions */
254     enumerateExtensionsEx(m_oxr->extensions, layer.layerName);
255   }
256
257   return GHOST_kSuccess;
258 }
259
260 static bool openxr_layer_is_available(const std::vector<XrApiLayerProperties> layers_info,
261                                       const std::string &layer_name)
262 {
263   for (const XrApiLayerProperties &layer_info : layers_info) {
264     if (layer_info.layerName == layer_name) {
265       return true;
266     }
267   }
268
269   return false;
270 }
271 static bool openxr_extension_is_available(const std::vector<XrExtensionProperties> extensions_info,
272                                           const std::string &extension_name)
273 {
274   for (const XrExtensionProperties &ext_info : extensions_info) {
275     if (ext_info.extensionName == extension_name) {
276       return true;
277     }
278   }
279
280   return false;
281 }
282
283 /**
284  * Gather an array of names for the API-layers to enable.
285  */
286 void GHOST_XrContext::getAPILayersToEnable(std::vector<const char *> &r_ext_names)
287 {
288   static std::vector<std::string> try_layers;
289
290   XR_DEBUG_ONLY_CALL(this, try_layers.push_back("XR_APILAYER_LUNARG_core_validation"));
291
292   r_ext_names.reserve(try_layers.size());
293
294   for (const std::string &layer : try_layers) {
295     if (openxr_layer_is_available(m_oxr->layers, layer)) {
296       r_ext_names.push_back(layer.c_str());
297     }
298   }
299 }
300
301 static const char *openxr_ext_name_from_wm_gpu_binding(GHOST_TXrGraphicsBinding binding)
302 {
303   switch (binding) {
304     case GHOST_kXrGraphicsOpenGL:
305       return XR_KHR_OPENGL_ENABLE_EXTENSION_NAME;
306 #ifdef WIN32
307     case GHOST_kXrGraphicsD3D11:
308       return XR_KHR_D3D11_ENABLE_EXTENSION_NAME;
309 #endif
310     case GHOST_kXrGraphicsUnknown:
311       assert(false);
312       return nullptr;
313   }
314
315   return nullptr;
316 }
317
318 /**
319  * Gather an array of names for the extensions to enable.
320  */
321 void GHOST_XrContext::getExtensionsToEnable(std::vector<const char *> &r_ext_names)
322 {
323   assert(m_gpu_binding_type != GHOST_kXrGraphicsUnknown);
324
325   const char *gpu_binding = openxr_ext_name_from_wm_gpu_binding(m_gpu_binding_type);
326   static std::vector<std::string> try_ext;
327
328   /* Try enabling debug extension */
329   XR_DEBUG_ONLY_CALL(this, try_ext.push_back(XR_EXT_DEBUG_UTILS_EXTENSION_NAME));
330
331   r_ext_names.reserve(try_ext.size() + 1); /* + 1 for graphics binding extension. */
332
333   /* Add graphics binding extension. */
334   assert(gpu_binding);
335   assert(openxr_extension_is_available(m_oxr->extensions, gpu_binding));
336   r_ext_names.push_back(gpu_binding);
337
338   for (const std::string &ext : try_ext) {
339     if (openxr_extension_is_available(m_oxr->extensions, ext)) {
340       r_ext_names.push_back(ext.c_str());
341     }
342   }
343 }
344
345 /**
346  * Decide which graphics binding extension to use based on
347  * #GHOST_XrContextCreateInfo.gpu_binding_candidates and available extensions.
348  */
349 GHOST_TXrGraphicsBinding GHOST_XrContext::determineGraphicsBindingTypeToEnable(
350     const GHOST_XrContextCreateInfo *create_info)
351 {
352   assert(create_info->gpu_binding_candidates != NULL);
353   assert(create_info->gpu_binding_candidates_count > 0);
354
355   for (uint32_t i = 0; i < create_info->gpu_binding_candidates_count; i++) {
356     assert(create_info->gpu_binding_candidates[i] != GHOST_kXrGraphicsUnknown);
357     const char *ext_name = openxr_ext_name_from_wm_gpu_binding(
358         create_info->gpu_binding_candidates[i]);
359     if (openxr_extension_is_available(m_oxr->extensions, ext_name)) {
360       return create_info->gpu_binding_candidates[i];
361     }
362   }
363
364   return GHOST_kXrGraphicsUnknown;
365 }
366
367 /** \} */ /* OpenXR API-Layers and Extensions */
368
369 /* -------------------------------------------------------------------- */
370 /** \name Session management
371  *
372  * Manage session lifetime and delegate public calls to #GHOST_XrSession.
373  * \{ */
374
375 void GHOST_XrContext::startSession(const GHOST_XrSessionBeginInfo *begin_info)
376 {
377   if (m_session == nullptr) {
378     m_session = std::unique_ptr<GHOST_XrSession>(new GHOST_XrSession(this));
379   }
380
381   m_session->start(begin_info);
382 }
383 void GHOST_XrContext::endSession()
384 {
385   m_session->end();
386   m_session = nullptr;
387 }
388
389 bool GHOST_XrContext::isSessionRunning() const
390 {
391   return m_session && m_session->isRunning();
392 }
393
394 void GHOST_XrContext::drawSessionViews(void *draw_customdata)
395 {
396   m_session->draw(draw_customdata);
397 }
398
399 /**
400  * Delegates event to session, allowing context to destruct the session if needed.
401  */
402 void GHOST_XrContext::handleSessionStateChange(const XrEventDataSessionStateChanged *lifecycle)
403 {
404   if (m_session &&
405       m_session->handleStateChangeEvent(lifecycle) == GHOST_XrSession::SESSION_DESTROY) {
406     m_session = nullptr;
407   }
408 }
409
410 /** \} */ /* Session Management */
411
412 /* -------------------------------------------------------------------- */
413 /** \name Public Accessors and Mutators
414  *
415  * Public as in, exposed in the Ghost API.
416  * \{ */
417
418 /**
419  * Set context for binding and unbinding a graphics context for a session. The binding callback
420  * may create a new context thereby. In fact that's the sole reason for this callback approach to
421  * binding. Just make sure to have an unbind function set that properly destructs.
422  *
423  * \param bind_fn Function to retrieve (possibly create) a graphics context.
424  * \param unbind_fn Function to release (possibly free) a graphics context.
425  */
426 void GHOST_XrContext::setGraphicsContextBindFuncs(GHOST_XrGraphicsContextBindFn bind_fn,
427                                                   GHOST_XrGraphicsContextUnbindFn unbind_fn)
428 {
429   if (m_session) {
430     m_session->unbindGraphicsContext();
431   }
432   m_custom_funcs.gpu_ctx_bind_fn = bind_fn;
433   m_custom_funcs.gpu_ctx_unbind_fn = unbind_fn;
434 }
435
436 void GHOST_XrContext::setDrawViewFunc(GHOST_XrDrawViewFn draw_view_fn)
437 {
438   m_custom_funcs.draw_view_fn = draw_view_fn;
439 }
440
441 /** \} */ /* Public Accessors and Mutators */
442
443 /* -------------------------------------------------------------------- */
444 /** \name Ghost Internal Accessors and Mutators
445  *
446  * \{ */
447
448 const GHOST_XrCustomFuncs *GHOST_XrContext::getCustomFuncs() const
449 {
450   return &m_custom_funcs;
451 }
452
453 GHOST_TXrGraphicsBinding GHOST_XrContext::getGraphicsBindingType() const
454 {
455   return m_gpu_binding_type;
456 }
457
458 XrInstance GHOST_XrContext::getInstance() const
459 {
460   return m_oxr->instance;
461 }
462
463 bool GHOST_XrContext::isDebugMode() const
464 {
465   return m_debug;
466 }
467
468 /** \} */ /* Ghost Internal Accessors and Mutators */