HoviTron Video Pipeline
WindowOpenXR.cpp
1/* ----------------------
2* Copyright 2023 Université Libre de Bruxelles(ULB), Universidad Politécnica de Madrid(UPM), CREAL, Deutsches Zentrum für Luft - und Raumfahrt(DLR)
3
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License.
6* You may obtain a copy of the License at < http://www.apache.org/licenses/LICENSE-2.0>
7
8* Unless required by applicable law or agreed to in writing, software
9* distributed under the License is distributed on an "AS IS" BASIS,
10* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11* See the License for the specific language governing permissionsand
12* limitations under the License.
13---------------------- */
14
15
16#include "WindowOpenXR.h"
17
23
24#include "openXRHelp.h"
25#include "SwapchainOpenXR.h"
26
27#include <iostream>
28#include <sstream>
29#include <thread>
30
31#include <optional>
32
33#define _USE_MATH_DEFINES
34#include <math.h>
35
36#ifdef WIN32
37#ifdef HVT_UDP_CONTROL
38#define _WINSOCKAPI_
39#endif
40#include<Windows.h>
41
42#endif
43
44#include <chrono>
45//#include <chrono_io>
46//D(
47//typedef std::chrono::high_resolution_clock Clock;
48//using std::chrono::milliseconds;
49//using std::chrono::duration_cast;
50double count = 0;
51
52std::chrono::milliseconds totalChrono{0};
53//)
54
55#ifdef __ANDROID__
56static const char* kTAG = "window";
57#define LOGI(...) \
58 ((void)__android_log_print(ANDROID_LOG_INFO, kTAG, __VA_ARGS__))
59#define LOGW(...) \
60 ((void)__android_log_print(ANDROID_LOG_WARN, kTAG, __VA_ARGS__))
61#define LOGE(...) \
62 ((void)__android_log_print(ANDROID_LOG_ERROR, kTAG, __VA_ARGS__))
63
64void handle_cmd_w(android_app* app, int32_t cmd) {
65 switch (cmd) {
66 case APP_CMD_INIT_WINDOW:
67 // The window is being shown, get it ready.
68 //initialize(app);
69 LOGI("window init");
70 break;
71 case APP_CMD_TERM_WINDOW:
72 // The window is being hidden or closed, clean it up.
73 //terminate();
74 LOGI("app out");
75 break;
76 case APP_CMD_WINDOW_RESIZED:
77 LOGI("need resize");
78
79 break;
80 case APP_CMD_INPUT_CHANGED:
81 LOGI("Got an input !");
82 break;
83 default:
84 LOGI("test", cmd);
85 }
86}
87OpenXRWindow::OpenXRWindow(android_app* appAndroid)
88{
89 this->appAndroid = appAndroid;
90}
91#endif // __ANDROID__
92
93
94glm::mat3x3 rotMatFromEuler(glm::vec3 & r) {
95 /*(
96 , , 0.f,
97 , , 0.f,
98 0.f, 0.f, 1.f);*/
99 /*
100 return cv::Matx33f(
101 cos(r[1]) + cos(r[2]), -sin(r[2]), sin(r[1]),
102 sin(r[2]), cos(r[0]) + cos(r[2]), -sin(r[0]),
103 -sin(r[1]), sin(r[0]), cos(r[0]) + cos(r[1])
104 );*/
105 glm::mat3x3 res = { cos(r[1])+cos(r[2]),sin(r[2]),-sin(r[1]) , -sin(r[2]),cos(r[0]) + cos(r[2]),sin(r[0]) , sin(r[1]),-sin(r[0]),cos(r[0]) + cos(r[1]) };
106 return res;
107};
108
109
111{
112 currentTranslation = {0,0,0};
113 currentRotation = {0,0,0};
114 initialised = true;
115
116
117#ifndef __ANDROID__
118 if (mirrorActivated) {
119 glfwInit();
120 glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
121 glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
122 glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE);
123 glfwWindowHint(GLFW_DECORATED, GLFW_TRUE);
124 window = glfwCreateWindow(width, height, "Mirror", nullptr, nullptr);
125 glfwSetWindowUserPointer(window, this);
126 auto funKey = [](GLFWwindow* w, int key, int scancode, int action, int mods) {
127 static_cast<WindowOpenXR*>(glfwGetWindowUserPointer(w))->inputKeyCallback(w, key, scancode, action, mods);
128 };
129 glfwSetKeyCallback(window, funKey);
130 }
131#endif
132
133 initOpenXR();
134}
135
137{
138
139 graphicsBinding = wrapper->getGraphicsBinding();
140 XrSessionCreateInfo createInfoSession{ XR_TYPE_SESSION_CREATE_INFO };
141 createInfoSession.next = &graphicsBinding;
142 createInfoSession.systemId = systemId;
143
144 PRINT("Before session creation");
145 CHECK_XRCMD(xrCreateSession(xrInstance, &createInfoSession, &xrSession));
146 PRINT("Session created !");
147
148 LogReferenceSpaces();
149
150 //if we want to create actions, change it here
151
152 //create XRSpace
153 CreateSpaces();
154
155 PRINT("Space created!");
156 //create Swapchain
157
159 CreateSwapChain();
160 if (mirrorActivated) {
161 createMirrorSwapchain();
162 }
163
164}
165
167 return true;
168}
169
170
172{
173 /*
174 for (Swapchain swapchain : xrSwapchains) {
175 xrDestroySwapchain(swapchain.handle);
176 }*/
177 for (int i = 0; i < swapchains.size(); i++) {
178 swapchains[i]->cleanup();
179 }
180 swapchains.clear();
181
182 for (XrSpace visualizedSpace : xrVisualizedSpaces) {
183 xrDestroySpace(visualizedSpace);
184 }
185
186 if (refSpace != XR_NULL_HANDLE) {
187 xrDestroySpace(refSpace);
188 }
189
190 xrDestroySession(xrSession);
191
192 wrapper->endCleanUp();
193
194 xrDestroyInstance(xrInstance);
195
196#ifdef __ANDROID__
197 appAndroid->activity->vm->DetachCurrentThread();
198#endif //
199#ifndef __ANDROID__
200 if (mirrorActivated) {
201 glfwDestroyWindow(window);
202 glfwTerminate();
203 }
204#endif //
205}
206
207void WindowOpenXR::cleanUpMirrorSwapchain() {
208 if (mirrorActivated) {
209 for (auto sem : mirrorAvailableSemaphore) {
210 wrapper->context.device.destroySemaphore(sem);
211 }
212 for (auto sem : mirrorFinnishedSemaphore) {
213 wrapper->context.device.destroySemaphore(sem);
214 }
215 mirrorImages.clear();
216 mirrorAvailableSemaphore.clear();
217 mirrorFinnishedSemaphore.clear();
218 wrapper->context.device.destroySwapchainKHR(mirrorSwapchain);
219 }
220}
221
222
223void WindowOpenXR::recreateMirror() {
224 int width = 0, height = 0;
225 glfwGetFramebufferSize(window, &width, &height);
226 if (! (width == 0 || height == 0)) {
227 vkDeviceWaitIdle(wrapper->context.device);
228 cleanUpMirrorSwapchain();
229 createMirrorSwapchain();
230 vkDeviceWaitIdle(wrapper->context.device);
231 }
232}
234{
235 if (mirrorActivated) {
236 cleanUpMirrorSwapchain();
237 VkInstance instance = static_cast<VkInstance>(wrapper->context.instance);
238 vkDestroySurfaceKHR(instance, surface, nullptr);
239 }
240}
241
243{
244#ifdef __ANDROID__
245 //For the moment the loop is on the mainOculus when using the standalone mode
246#else
247 bool requestRestart = false;
248 while (!requestRestart) {
249#ifndef __ANDROID__
250 if (mirrorActivated) {
251 glfwPollEvents();
252 if (glfwWindowShouldClose(window)) {
253 xrRequestExitSession(xrSession);
254 }
255 }
256#endif //
257 bool exitRenderLoop = false;
258 pollEvents(&exitRenderLoop, &requestRestart);
259 if (exitRenderLoop) {
260 break;
261 }
262 if (isSessionRunning()) {
263 if (pauseRendering) {
264 std::this_thread::sleep_for(std::chrono::milliseconds(1000));
265 pauseRendering = false;
266 }
267 else {
268 renderFrame(vulkanDrawing);
269 }
270 }
271 else {
272 // Throttle loop since xrWaitFrame won't be called.
273 std::this_thread::sleep_for(std::chrono::milliseconds(250));
274 }
275
276
277 }
278#endif
279}
280
282{
283#ifndef __ANDROID__
284 if (mirrorActivated) {
285 if (auto res = glfwCreateWindowSurface(wrapper->context.instance, window, nullptr, &surface); res != VK_SUCCESS) {
286 throw std::runtime_error("failed to create window surface! (" + std::to_string(res) + ")");
287 }
288 }
289#endif
290}
291
292std::vector<const char*> WindowOpenXR::getRequiredExtensions()
293{
294 auto extensions = instanceExtensions;
295
296#ifndef __ANDROID__
297 PFN_xrGetVulkanInstanceExtensionsKHR pfnGetVulkanInstanceExtensionsKHR = nullptr;
298 CHECK_XRCMD(xrGetInstanceProcAddr(xrInstance, "xrGetVulkanInstanceExtensionsKHR",
299 reinterpret_cast<PFN_xrVoidFunction*>(&pfnGetVulkanInstanceExtensionsKHR)));
300
301 uint32_t extensionNamesSize = 0;
302 CHECK_XRCMD(pfnGetVulkanInstanceExtensionsKHR(xrInstance, systemId, 0, &extensionNamesSize, nullptr));
303
304 extensionNames = std::vector<char>(extensionNamesSize);
305 CHECK_XRCMD(pfnGetVulkanInstanceExtensionsKHR(xrInstance, systemId, extensionNamesSize, &extensionNamesSize,
306 &extensionNames[0]));
307
308
309 std::vector<const char*> requestedExt;
310
311 for (int i = 0; i < extensionNames.size(); i++) {
312 extensions.push_back(&extensionNames[i]);
313 while (i < extensionNames.size()) {
314 if (extensionNames[i] == ' ') {
315 extensionNames[i] = '\0';
316 break;
317 }
318 i++;
319 }
320 }
321 //extensions.resize(extensions.size() - 1);
322#endif
323 if (mirrorActivated) {
324 uint32_t glfwExtensionCount = 0;
325 const char** glfwExtensions;
326 glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
327 std::vector<const char*> mirrorExtensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
328
329 for (auto glfwext : mirrorExtensions) {
330 bool found = false;
331 for (auto ext : extensions) {
332 if (std::strcmp(glfwext, ext) == 0) {
333 found = true;
334 break;
335 }
336 }
337 if (!found) {
338 extensions.push_back(glfwext);
339 }
340 }
341 }
342
344 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
345 }
346 else {
347 extensions.push_back("VK_EXT_debug_report");
348 extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
349 }
350 return extensions;
351 }
352
353 VkBool32 WindowOpenXR::isDeviceSupportingSufaceKHR(VkPhysicalDevice device, int i)
354 {
355 if (!mirrorActivated) {
356 return false;
357 }
358 else {
359 VkBool32 presentSupport = false;
360 vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
361 return presentSupport;
362 }
363
364 }
365
367 {
368 PFN_xrGetVulkanDeviceExtensionsKHR pfnGetVulkanDeviceExtensionsKHR = nullptr;
369 CHECK_XRCMD(xrGetInstanceProcAddr(xrInstance, "xrGetVulkanDeviceExtensionsKHR",
370 reinterpret_cast<PFN_xrVoidFunction*>(&pfnGetVulkanDeviceExtensionsKHR)));
371
372 uint32_t deviceExtensionNamesSize = 0;
373 CHECK_XRCMD(pfnGetVulkanDeviceExtensionsKHR(xrInstance, systemId, 0, &deviceExtensionNamesSize, nullptr));
374 deviceExtension = std::vector<char>(deviceExtensionNamesSize);
375 CHECK_XRCMD(pfnGetVulkanDeviceExtensionsKHR(xrInstance, systemId, deviceExtensionNamesSize,
376 &deviceExtensionNamesSize, &deviceExtension[0]));
377
378 std::vector<const char*> extensions = additionalDeviceExtensions;
379 for (int i = 0; i < deviceExtension.size(); i++) {
380 extensions.push_back(&deviceExtension[i]);
381 while (i < deviceExtension.size()) {
382 if (deviceExtension[i] == ' ') {
383 deviceExtension[i] = '\0';
384 break;
385 }
386 i++;
387 }
388 }
389
390
391
392 return extensions;
393
394 //return deviceExtensions;
395
396
397 }
398
400 {
401
403 throw std::runtime_error("not autorised with open xr implementation");
404 return details;
405 }
406
407
408 void WindowOpenXR::getFrameBufferSize(int* w, int* h, vk::PhysicalDevice & pDevice) {
409
410 }
411
413 {
414 return false;
415 }
416
417
418
419
420
422 {
424 }
425
427 {
428 }
429
430 std::vector<vk::Image> WindowOpenXR::getBlitDestinations(int view, int elem)
431 {
432 auto res = std::vector<vk::Image>();
433 if (mirrorActivated && view == 0 && elem < mirrorSwapchainSize && mirrorShouldRender) {
434 res.push_back(mirrorImages[elem]);
435 }
436 return res;
437 }
438
439 std::vector<vk::Extent2D> WindowOpenXR::getBlitExtentDestinations(int view, int elem)
440 {
441 auto res = std::vector<vk::Extent2D>();
442 if (mirrorActivated && view == 0 && elem < mirrorSwapchainSize && mirrorShouldRender) {
443 res.push_back(vk::Extent2D{ width,height });
444 }
445 return res;
446 }
447
448 std::vector<vk::Semaphore> WindowOpenXR::getSemaphoreWait(int view, int elem)
449 {
450 auto res = std::vector<vk::Semaphore>();
451 if (mirrorActivated && view == 0 && elem < mirrorSwapchainSize) {
452 res.push_back(mirrorAvailableSemaphore[elem]);
453 }
454 return res;
455 }
456
457 std::vector<vk::Semaphore> WindowOpenXR::getSemaphoreSignal(int view, int elem)
458 {
459 auto res = std::vector<vk::Semaphore>();
460 if (mirrorActivated && view == 0 && elem < mirrorSwapchainSize) {
461 res.push_back(mirrorFinnishedSemaphore[elem]);
462 }
463 return res;
464 }
465
466 void WindowOpenXR::initOpenXR() {
467#ifdef __ANDROID__
468 PFN_xrInitializeLoaderKHR initializeLoader = nullptr;
469 if (XR_SUCCEEDED(
470 xrGetInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", (PFN_xrVoidFunction*)(&initializeLoader)))) {
471 XrLoaderInitInfoAndroidKHR loaderInitInfoAndroid;
472 memset(&loaderInitInfoAndroid, 0, sizeof(loaderInitInfoAndroid));
473 loaderInitInfoAndroid.type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR;
474 loaderInitInfoAndroid.next = NULL;
475 loaderInitInfoAndroid.applicationVM = appAndroid->activity->vm;
476 loaderInitInfoAndroid.applicationContext = appAndroid->activity->clazz;
477 initializeLoader((const XrLoaderInitInfoBaseHeaderKHR*)&loaderInitInfoAndroid);
478 LOGI("loader");
479 }
480 else {
481 LOGI("This is a failure");
482 }
483 LOGI("OpenXR");
484
485
486 //Instance
487 XrInstanceCreateInfoAndroidKHR instanceCreateInfoAndroid = { XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR };
488 instanceCreateInfoAndroid.applicationVM = appAndroid->activity->vm;
489 instanceCreateInfoAndroid.applicationActivity = appAndroid->activity->clazz;
490
491 XrInstanceCreateInfo createInfo{ XR_TYPE_INSTANCE_CREATE_INFO };
492 std::vector<const char*> extensions = { XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME, XR_KHR_VULKAN_ENABLE_EXTENSION_NAME };
493#else
494 checkExtensions();
495 XrInstanceCreateInfo createInfo{ XR_TYPE_INSTANCE_CREATE_INFO };
496 std::vector<const char*> extensions = { XR_KHR_VULKAN_ENABLE_EXTENSION_NAME };
497 if (depthExtensionAvailable) {
498 PRINT("Depth extension is now activated !\n");
499 extensions.push_back(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME);
500 }
501 if (quadViewExtensionAvailable) {
502 PRINT("VARJO quad view extension is now activated !\n");
503 extensions.push_back(XR_VARJO_QUAD_VIEWS_EXTENSION_NAME);
504 separateFromWindowDim = false;
505 }
506#endif //
507
508#ifdef __ANDROID__
509 createInfo.next = (XrBaseInStructure*)&instanceCreateInfoAndroid;
510#else
511 createInfo.next = nullptr;
512#endif //
513
514 createInfo.enabledExtensionCount = (uint32_t)extensions.size();
515 createInfo.enabledExtensionNames = extensions.data();
516 strcpy(createInfo.applicationInfo.applicationName, "RVSVulkan_OpenXR");
517 createInfo.applicationInfo.apiVersion = XR_CURRENT_API_VERSION;
518
519 std::vector<const char*> layers = {};
520 if (validationXR) { //TODO check if supported
521 layers.push_back("XR_LUNARG_core_validation");
522 }
523
524 createInfo.enabledApiLayerCount = (uint32_t)layers.size();
525 createInfo.enabledApiLayerNames = layers.data();
526 if (XR_SUCCEEDED(xrCreateInstance(&createInfo, &xrInstance))) {
527 PRINT("Success");
528 }
529 else {
530 PRINT("Error Instance");
531 exit(-1);
532 }
533 XrSystemGetInfo systemInfo{ XR_TYPE_SYSTEM_GET_INFO };
534 systemInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; //The code is for the oculus
535 CHECK_XRCMD(xrGetSystem(xrInstance, &systemInfo, &systemId));
536 PRINT("%s", Fmt("Using system %d for form factor %s", systemId, to_string(systemInfo.formFactor)).c_str());
537 CHECK(xrInstance != XR_NULL_HANDLE);
538 CHECK(systemId != XR_NULL_SYSTEM_ID);
539
540
541 selectViewConfiguration();
542 selectBlendMode();
543 logViewConfiguration(xrInstance, systemId);
544
545 XrGraphicsRequirementsVulkan2KHR graphicsRequirements{ XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN2_KHR };
546 CHECK_XRCMD(GetVulkanGraphicsRequirements2KHR(xrInstance, systemId, &graphicsRequirements));
547
548 uint32_t layerCount;
549 vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
550
551
552
553
554 }
555
556
557
558
559 void WindowOpenXR::LogReferenceSpaces() {
560 CHECK(xrSession != XR_NULL_HANDLE);
561
562 uint32_t spaceCount;
563 CHECK_XRCMD(xrEnumerateReferenceSpaces(xrSession, 0, &spaceCount, nullptr));
564 std::vector<XrReferenceSpaceType> spaces(spaceCount);
565 CHECK_XRCMD(xrEnumerateReferenceSpaces(xrSession, spaceCount, &spaceCount, spaces.data()));
566
567 PRINT("%s", Fmt("Available reference spaces: %d", spaceCount).c_str());
568 for (XrReferenceSpaceType space : spaces) {
569 PRINT("%s", Fmt(" Name: %s", to_string(space)).c_str());
570 }
571 }
572
573
574 void WindowOpenXR::CreateSpaces() {
575 //visual + ref
576 //--------------visual space
577 CHECK(xrSession != XR_NULL_HANDLE);
578 //check wich one we need
579 //std::string visualizedSpaces[] = {"ViewFront", "Local", "Stage", "StageLeft", "StageRight", "StageLeftRotated",
580 // "StageRightRotated"};
581
582 //for (const auto& visualizedSpace : visualizedSpaces) {
583 XrReferenceSpaceCreateInfo referenceSpaceCreateInfo{ XR_TYPE_REFERENCE_SPACE_CREATE_INFO };
584 XrPosef identity{};
585 identity.orientation.w = 1;
586 referenceSpaceCreateInfo.poseInReferenceSpace = identity;
587 referenceSpaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
588 //XrReferenceSpaceCreateInfo referenceSpaceCreateInfo = GetXrReferenceSpaceCreateInfo(visualizedSpace);
589 XrSpace space;
590 XrResult res = xrCreateReferenceSpace(xrSession, &referenceSpaceCreateInfo, &space);
591 if (XR_SUCCEEDED(res)) {
592 xrVisualizedSpaces.push_back(space);
593 }
594 else {
595 PRINT("%s", Fmt("Failed to create reference space %s with error %d", "Local", res).c_str());
596 }
597 //--------------------appSpace
598
599 XrResult res2 = xrCreateReferenceSpace(xrSession, &referenceSpaceCreateInfo, &refSpace);
600 if (!XR_SUCCEEDED(res2)) {
601 PRINT("%s", Fmt("Failed to create reference space %s with error %d", "Local", res2).c_str());
602 }
603
604 //}
605 }
606 void WindowOpenXR::logViewConfiguration(XrInstance & xrInstance, XrSystemId & systemId) {
607 uint32_t viewConfigTypeCount;
608 CHECK_XRCMD(xrEnumerateViewConfigurations(xrInstance, systemId, 0, &viewConfigTypeCount, nullptr));
609 std::vector<XrViewConfigurationType> viewConfigTypes(viewConfigTypeCount);
610 CHECK_XRCMD(xrEnumerateViewConfigurations(xrInstance, systemId, viewConfigTypeCount,
611 &viewConfigTypeCount,
612 viewConfigTypes.data()));
613 CHECK((uint32_t)viewConfigTypes.size() == viewConfigTypeCount);
614 PRINT("%s", Fmt("Available View Configuration Types: (%d)", viewConfigTypeCount).c_str());
615 for (XrViewConfigurationType viewConfigType : viewConfigTypes) {
616 PRINT("%s", Fmt(" View Configuration Type: %s %s", to_string(viewConfigType),
617 viewConfigType == xrViewConfType ? "(Selected)" : "").c_str());
618
619 XrViewConfigurationProperties viewConfigProperties{ XR_TYPE_VIEW_CONFIGURATION_PROPERTIES };
620 CHECK_XRCMD(xrGetViewConfigurationProperties(xrInstance, systemId, viewConfigType,
621 &viewConfigProperties));
622
623 PRINT("%s",
624 Fmt(" View configuration FovMutable=%s",
625 viewConfigProperties.fovMutable == XR_TRUE ? "True" : "False").c_str());
626
627 uint32_t viewCount;
628 CHECK_XRCMD(
629 xrEnumerateViewConfigurationViews(xrInstance, systemId, viewConfigType, 0, &viewCount,
630 nullptr));
631 if (viewCount > 0) {
632 std::vector<XrViewConfigurationView> views(viewCount, { XR_TYPE_VIEW_CONFIGURATION_VIEW });
633 CHECK_XRCMD(
634 xrEnumerateViewConfigurationViews(xrInstance, systemId, viewConfigType, viewCount,
635 &viewCount, views.data()));
636
637 for (uint32_t i = 0; i < views.size(); i++) {
638 const XrViewConfigurationView& view = views[i];
639
640 PRINT("%s", Fmt(" View [%d]: Recommended Width=%d Height=%d SampleCount=%d", i,
641 view.recommendedImageRectWidth, view.recommendedImageRectHeight,
642 view.recommendedSwapchainSampleCount).c_str());
643 PRINT("%s",
644 Fmt(" View [%d]: Maximum Width=%d Height=%d SampleCount=%d", i,
645 view.maxImageRectWidth,
646 view.maxImageRectHeight, view.maxSwapchainSampleCount).c_str());
647 }
648 }
649 else {
650 PRINT("%s", Fmt("Empty view configuration type").c_str());
651 }
652
653 uint32_t count;
654 CHECK_XRCMD(xrEnumerateEnvironmentBlendModes(xrInstance, systemId, viewConfigType, 0, &count, nullptr));
655 CHECK(count > 0);
656
657 PRINT("%s", Fmt("Available Environment Blend Mode count : (%d)", count).c_str());
658
659 std::vector<XrEnvironmentBlendMode> blendModes(count);
660 CHECK_XRCMD(xrEnumerateEnvironmentBlendModes(xrInstance, systemId, viewConfigType, count, &count, blendModes.data()));
661
662 bool blendModeFound = false;
663 for (XrEnvironmentBlendMode mode : blendModes) {
664 const bool blendModeMatch = (mode == xrBlendMode);
665 PRINT("%s", Fmt("Environment Blend Mode (%s) : %s", to_string(mode), blendModeMatch ? "(Selected)" : "").c_str());
666 blendModeFound |= blendModeMatch;
667 }
668 CHECK(blendModeFound);
669
670 }
671
672 }
673
674 XrResult WindowOpenXR::GetVulkanGraphicsRequirements2KHR(XrInstance instance, XrSystemId systemId,
675 XrGraphicsRequirementsVulkan2KHR * graphicsRequirements) {
676 PFN_xrGetVulkanGraphicsRequirementsKHR pfnGetVulkanGraphicsRequirementsKHR = nullptr;
677 CHECK_XRCMD(xrGetInstanceProcAddr(instance, "xrGetVulkanGraphicsRequirementsKHR",
678 reinterpret_cast<PFN_xrVoidFunction*>(&pfnGetVulkanGraphicsRequirementsKHR)));
679
680 XrGraphicsRequirementsVulkanKHR legacyRequirements{ XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR };
681 CHECK_XRCMD(pfnGetVulkanGraphicsRequirementsKHR(instance, systemId, &legacyRequirements));
682
683 graphicsRequirements->maxApiVersionSupported = legacyRequirements.maxApiVersionSupported;
684 graphicsRequirements->minApiVersionSupported = legacyRequirements.minApiVersionSupported;
685
686 return XR_SUCCESS;
687 }
688
689 XrResult GetVulkanGraphicsDevice2KHR(XrInstance instance, const XrVulkanGraphicsDeviceGetInfoKHR * getInfo,
690 VkPhysicalDevice * vulkanPhysicalDevice) {
691 PFN_xrGetVulkanGraphicsDeviceKHR pfnGetVulkanGraphicsDeviceKHR = nullptr;
692 CHECK_XRCMD(xrGetInstanceProcAddr(instance, "xrGetVulkanGraphicsDeviceKHR",
693 reinterpret_cast<PFN_xrVoidFunction*>(&pfnGetVulkanGraphicsDeviceKHR)));
694
695 if (getInfo->next != nullptr) {
696 return XR_ERROR_FEATURE_UNSUPPORTED;
697 }
698
699 CHECK_XRCMD(pfnGetVulkanGraphicsDeviceKHR(instance, getInfo->systemId, getInfo->vulkanInstance, vulkanPhysicalDevice));
700
701 return XR_SUCCESS;
702 }
703
704
705 void WindowOpenXR::getVulkanPhysicalDevice(VkInstance & vkInstance, VkPhysicalDevice * physicalDevice) {
706 XrVulkanGraphicsDeviceGetInfoKHR deviceGetInfo{ XR_TYPE_VULKAN_GRAPHICS_DEVICE_GET_INFO_KHR };
707 deviceGetInfo.systemId = systemId;
708 deviceGetInfo.vulkanInstance = vkInstance;
709 CHECK_XRCMD(GetVulkanGraphicsDevice2KHR(xrInstance, &deviceGetInfo, physicalDevice));
710 }
711
712 XrResult CreateVulkanDeviceKHR(XrInstance instance, const XrVulkanDeviceCreateInfoKHR * createInfo,
713 VkDevice * vulkanDevice, VkResult * vulkanResult) {
714 PFN_xrCreateVulkanDeviceKHR pfnCreateVulkanDeviceKHR = nullptr;
715 CHECK_XRCMD(xrGetInstanceProcAddr(instance, "xrCreateVulkanDeviceKHR",
716 reinterpret_cast<PFN_xrVoidFunction*>(&pfnCreateVulkanDeviceKHR)));
717
718 return pfnCreateVulkanDeviceKHR(instance, createInfo, vulkanDevice, vulkanResult);
719 }
720
721 void WindowOpenXR::createVulkanDevice(VkPhysicalDevice & physicalDevice, VkDeviceCreateInfo & info, VkDevice * device) {
722 XrVulkanDeviceCreateInfoKHR deviceCreateInfo{ XR_TYPE_VULKAN_DEVICE_CREATE_INFO_KHR };
723 deviceCreateInfo.systemId = systemId;
724 deviceCreateInfo.pfnGetInstanceProcAddr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(&vkGetInstanceProcAddr);
725 deviceCreateInfo.vulkanCreateInfo = &info;
726 deviceCreateInfo.vulkanPhysicalDevice = physicalDevice;
727 deviceCreateInfo.vulkanAllocator = nullptr;
728 VkResult err;
729 CHECK_XRCMD(CreateVulkanDeviceKHR(xrInstance, &deviceCreateInfo, device, &err));
730 if (err != VK_SUCCESS) {
731 throw std::runtime_error("Error creating the device");
732 }
733 }
734 int64_t SelectColorSwapchainFormat(const std::vector<int64_t>&runtimeFormats) {
735 // List of supported color swapchain formats.
736 constexpr int64_t SupportedColorSwapchainFormats[] = { VK_FORMAT_R8G8B8A8_UNORM };//VK_FORMAT_R8G8B8A8_UNORM,VK_FORMAT_B8G8R8A8_UNORM,VK_FORMAT_R32G32B32A32_SFLOAT,VK_FORMAT_R32G32B32_SFLOAT,VK_FORMAT_R8G8B8A8_SRGB };//{VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_R8G8B8A8_SRGB,
737 // VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM};
738
739 auto swapchainFormatIt =
740 std::find_first_of(runtimeFormats.begin(), runtimeFormats.end(), std::begin(SupportedColorSwapchainFormats),
741 std::end(SupportedColorSwapchainFormats));
742 if (swapchainFormatIt == runtimeFormats.end()) {
743 THROW("No runtime swapchain format supported for color swapchain");
744 }
745
746 return *swapchainFormatIt;
747 }
748 int64_t SelectDepthSwapchainFormat(const std::vector<int64_t>& runtimeFormats, const std::vector<vk::Format>& supportedFormats) {
749 for(auto f : supportedFormats) {
750 auto it = std::find_if(runtimeFormats.begin(), runtimeFormats.end(), [&](int64_t format){
751 return format == (int64_t)f;
752 });
753 if(it != runtimeFormats.end()){
754 return *it;
755 }
756 }
757 THROW("No runtime swapchain format supported for depth swapchain");
758 }
759 void WindowOpenXR::CreateSwapChain() {
760
761 // Read graphics properties for preferred swapchain length and logging.
762 XrSystemProperties systemProperties{ XR_TYPE_SYSTEM_PROPERTIES };
763 CHECK_XRCMD(xrGetSystemProperties(xrInstance, systemId, &systemProperties));
764
765 // Log system properties.
766 PRINT("%s", Fmt("System Properties: Name=%s VendorId=%d", systemProperties.systemName, systemProperties.vendorId).c_str());
767 PRINT("%s", Fmt("System Graphics Properties: MaxWidth=%d MaxHeight=%d MaxLayers=%d",
768 systemProperties.graphicsProperties.maxSwapchainImageWidth,
769 systemProperties.graphicsProperties.maxSwapchainImageHeight,
770 systemProperties.graphicsProperties.maxLayerCount).c_str());
771 PRINT("%s", Fmt("System Tracking Properties: OrientationTracking=%s PositionTracking=%s",
772 systemProperties.trackingProperties.orientationTracking == XR_TRUE ? "True" : "False",
773 systemProperties.trackingProperties.positionTracking == XR_TRUE ? "True" : "False").c_str());
774
775 // Query and cache view configuration views.
776 uint32_t viewCount;
777 CHECK_XRCMD(xrEnumerateViewConfigurationViews(xrInstance, systemId, xrViewConfType, 0, &viewCount, nullptr));
778 xrConfViews.resize(viewCount, { XR_TYPE_VIEW_CONFIGURATION_VIEW });
779 CHECK_XRCMD(xrEnumerateViewConfigurationViews(xrInstance, systemId, xrViewConfType, viewCount, &viewCount,
780 xrConfViews.data()));
781
782 // create v
783 // Create and cache view buffer for xrLocateViews later.
784 xrViews.resize(viewCount, { XR_TYPE_VIEW });
785
786 // Create the swapchain and get the images.
787 if (viewCount > 0) {
788 // Select a swapchain format.
789 uint32_t swapchainFormatCount;
790 CHECK_XRCMD(xrEnumerateSwapchainFormats(xrSession, 0, &swapchainFormatCount, nullptr));
791 std::vector<int64_t> swapchainFormats(swapchainFormatCount);
792 CHECK_XRCMD(xrEnumerateSwapchainFormats(xrSession, (uint32_t)swapchainFormats.size(), &swapchainFormatCount,
793 swapchainFormats.data()));
794 CHECK(swapchainFormatCount == swapchainFormats.size());
795 colorSwapchainFormat = SelectColorSwapchainFormat(swapchainFormats);
796 if (depthExtensionAvailable) {
797 depthSwapchainFormat = SelectDepthSwapchainFormat(swapchainFormats, wrapper->getRenderPassSupportedDepthFormats());
798 }
799 // Print swapchain formats and the selected one.
800 {
801 std::string swapchainFormatsString;
802 for (int64_t format : swapchainFormats) {
803 const bool selected = format == colorSwapchainFormat;
804 swapchainFormatsString += " ";
805 if (selected) {
806 swapchainFormatsString += "[";
807 }
808 swapchainFormatsString += std::to_string(format);
809 if (selected) {
810 swapchainFormatsString += "]";
811 }
812 }
813 PRINT("%s", Fmt("Swapchain Formats: %s", swapchainFormatsString.c_str()).c_str());
814 }
815
816 // Create a swapchain for each view.
817 swapchains.resize(viewCount);
818 translationsOMAF.resize(viewCount);
819 translationsOpenXR.resize(viewCount);
820
821 if (depthExtensionAvailable) {
822 depthSwapchains.resize(viewCount);
823 }
824 for (uint32_t i = 0; i < viewCount; i++) {
825 swapchains[i] = std::unique_ptr<SwapchainOpenXR>(new SwapchainOpenXR(colorSwapchainFormat, xrConfViews[i], this, wrapper, &xrSession));
826 if (depthExtensionAvailable) {
827 depthSwapchains[i] = std::unique_ptr<SwapchainOpenXR>(new SwapchainOpenXR(depthSwapchainFormat, xrConfViews[i], this, wrapper, &xrSession));
828 }
829 }
830 }
831 }
832void WindowOpenXR::createMirrorSwapchain()
833{
834 vk::SwapchainCreateInfoKHR mirrorCreateInfo{};
835 //vk::Format colorFormat = static_cast<vk::Format>(colorSwapchainFormat);
836 auto availableFormats = wrapper->context.physicalDevice.getSurfaceFormatsKHR(surface);
837 auto colorFormat = availableFormats[0];
838 for (auto form : availableFormats) {
839 if (form.format == vk::Format::eR8G8B8Unorm) {
840 colorFormat = form;
841 }
842 }
843 vk::PresentModeKHR presentMode = vk::PresentModeKHR::eMailbox;
844
845 int width, height;
846 glfwGetFramebufferSize(window, &width, &height);
847 vk::Extent2D extent{ (uint32_t) width, (uint32_t) height};
848
849 this->width = width;
850 this->height = height;
851
853 uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
854
855 int queueFamilyIndexCount = 0;
856 auto imageSharingMode = vk::SharingMode::eExclusive;
857 if (indices.graphicsFamily != indices.presentFamily) {
858 imageSharingMode = vk::SharingMode::eConcurrent;
859 queueFamilyIndexCount = 2;
860 }
861
862 vk::SwapchainCreateInfoKHR swapChainCreateInfo(
863 vk::SwapchainCreateFlagsKHR(), //flags
864 static_cast<vk::SurfaceKHR>(surface), //vk::surface
865 mirrorSwapchainSize, //numberOfImage
866 colorFormat.format, //format
867 colorFormat.colorSpace, //colorSpace
868 extent, //vk::Extend2D
869 1, //imageArrayLayers
870 vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eInputAttachment | vk::ImageUsageFlagBits::eTransferDst, //imageUsage
871 vk::SharingMode::eExclusive, //imageSharingMode
872 queueFamilyIndexCount, //Queue family index count
873 queueFamilyIndexCount > 0 ? queueFamilyIndices : nullptr, //Queue family indices
874 vk::SurfaceTransformFlagBitsKHR::eIdentity, //preTransform
875 vk::CompositeAlphaFlagBitsKHR::eOpaque, //compositeAlpha //TODO check Alpha
876 presentMode, //presentMode
877 true, //clipped
878 nullptr); //oldSwapchain
879
880 mirrorSwapchain = wrapper->context.device.createSwapchainKHR(swapChainCreateInfo);
881 mirrorImages = wrapper->context.device.getSwapchainImagesKHR(mirrorSwapchain);
882 vk::SemaphoreCreateInfo semaphoreInfo;
883 for (int i = 0; i < mirrorSwapchainSize; i++) {
884 mirrorAvailableSemaphore.push_back(wrapper->context.device.createSemaphore(semaphoreInfo));
885 mirrorFinnishedSemaphore.push_back(wrapper->context.device.createSemaphore(semaphoreInfo));
886 }
887}
888// Return event if one is available, otherwise return null.
889const XrEventDataBaseHeader* WindowOpenXR::TryReadNextEvent() {
890 // It is sufficient to clear the just the XrEventDataBuffer header to
891 // XR_TYPE_EVENT_DATA_BUFFER
892 XrEventDataBaseHeader* baseHeader = reinterpret_cast<XrEventDataBaseHeader*>(&eventDataBuffer);
893 *baseHeader = {XR_TYPE_EVENT_DATA_BUFFER};
894 const XrResult xr = xrPollEvent(xrInstance, &eventDataBuffer);
895 if (xr == XR_SUCCESS) {
896 if (baseHeader->type == XR_TYPE_EVENT_DATA_EVENTS_LOST) {
897 const XrEventDataEventsLost* const eventsLost = reinterpret_cast<const XrEventDataEventsLost*>(baseHeader);
898 PRINT("%s", Fmt("%d events lost", eventsLost).c_str());
899 }
900
901 return baseHeader;
902 }
903 if (xr == XR_EVENT_UNAVAILABLE) {
904 return nullptr;
905 }
906 THROW_XR(xr, "xrPollEvent");
907}
908
909void WindowOpenXR::setMirror(bool mirrorActivated)
910{
911#ifdef __ANDROID__
912 throw std::runtime_error("Not supported when the headset is in standalone mode");
913#endif //
914 this->mirrorActivated = mirrorActivated;
915}
916
918{
919 if (depthExtensionAvailable) {
920 return true;
921 }
922 else {
923 return false;
924 }
925}
926
927
928void WindowOpenXR::pollEvents(bool *exitRenderLoop, bool *requestRestart) {
929 *exitRenderLoop = *requestRestart = false;
930
931 // Process all pending messages.
932 while (const XrEventDataBaseHeader* event = TryReadNextEvent()) {
933 switch (event->type) {
934 case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: {
935 const auto& instanceLossPending = *reinterpret_cast<const XrEventDataInstanceLossPending*>(event);
936 PRINT("%s", Fmt("XrEventDataInstanceLossPending by %lld", instanceLossPending.lossTime).c_str());
937 *exitRenderLoop = true;
938 *requestRestart = true;
939 return;
940 }
941 case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: {
942 auto sessionStateChangedEvent = *reinterpret_cast<const XrEventDataSessionStateChanged*>(event);
943 PRINT("sesion state change");
944 HandleSessionStateChangedEvent(sessionStateChangedEvent, exitRenderLoop, requestRestart);
945 //TODO
946 break;
947 }
948 case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED:
949 //no in
950 break;
951 case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING:
952 default: {
953 PRINT( "%s", Fmt("Ignoring event type %d", event->type).c_str());
954 break;
955 }
956 }
957 }
958}
959void WindowOpenXR::HandleSessionStateChangedEvent(const XrEventDataSessionStateChanged& stateChangedEvent, bool* exitRenderLoop,
960 bool* requestRestart) {
961 const XrSessionState oldState = xrSessionState;
962 xrSessionState = stateChangedEvent.state;
963
964 PRINT("%s", Fmt("XrEventDataSessionStateChanged: state %s->%s session=%lld time=%lld", to_string(oldState),
965 to_string(xrSessionState), stateChangedEvent.session, stateChangedEvent.time).c_str());
966
967 if ((stateChangedEvent.session != XR_NULL_HANDLE) && (stateChangedEvent.session != xrSession)) {
968 PRINT("%s", "XrEventDataSessionStateChanged for unknown session");
969 return;
970 }
971
972 switch (xrSessionState) {
973 case XR_SESSION_STATE_READY: {
974 CHECK(xrSession != XR_NULL_HANDLE);
975 XrSessionBeginInfo sessionBeginInfo{XR_TYPE_SESSION_BEGIN_INFO};
976 sessionBeginInfo.primaryViewConfigurationType = xrViewConfType;
977 CHECK_XRCMD(xrBeginSession(xrSession, &sessionBeginInfo));
978 runningSession = true;
979 break;
980 }
981 case XR_SESSION_STATE_STOPPING: {
982 CHECK(xrSession != XR_NULL_HANDLE);
983 runningSession = false;
984 CHECK_XRCMD(xrEndSession(xrSession))
985 break;
986 }
987 case XR_SESSION_STATE_EXITING: {
988 *exitRenderLoop = true;
989 // Do not attempt to restart because user closed this session.
990 *requestRestart = false;
991 break;
992 }
993 case XR_SESSION_STATE_LOSS_PENDING: {
994 *exitRenderLoop = true;
995 // Poll for a new instance.
996 *requestRestart = true;
997 break;
998 }
999 default:
1000 break;
1001 }
1002}
1003
1004void WindowOpenXR::selectViewConfiguration()
1005{
1006 uint32_t viewConfigTypeCount;
1007 CHECK_XRCMD(xrEnumerateViewConfigurations(xrInstance, systemId, 0, &viewConfigTypeCount, nullptr));
1008 std::vector<XrViewConfigurationType> viewConfigTypes(viewConfigTypeCount);
1009 CHECK_XRCMD(xrEnumerateViewConfigurations(xrInstance, systemId, viewConfigTypeCount,
1010 &viewConfigTypeCount,
1011 viewConfigTypes.data()));
1012 CHECK((uint32_t)viewConfigTypes.size() == viewConfigTypeCount);
1013 bool foundConfigurationType = false;
1014 for (auto supportedConf : supportedViewConfigurations) {
1015 for (XrViewConfigurationType viewConfigType : viewConfigTypes) {
1016 if (supportedConf == viewConfigType && !((viewConfigType == XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO) && !quadViewExtensionAvailable )) {
1017 //in the case where the quad view etension has not been selected, this view configuration type should not be selected
1018 xrViewConfType = supportedConf;
1019 }
1020 }
1021 }
1022}
1023
1024void WindowOpenXR::selectBlendMode()
1025{
1026 /*uint32_t viewConfigTypeCount;
1027 CHECK_XRCMD(xrEnumerateViewConfigurations(xrInstance, systemId, 0, &viewConfigTypeCount, nullptr));
1028 std::vector<XrViewConfigurationType> viewConfigTypes(viewConfigTypeCount);
1029 CHECK_XRCMD(xrEnumerateViewConfigurations(xrInstance, systemId, viewConfigTypeCount,
1030 &viewConfigTypeCount,
1031 viewConfigTypes.data()));
1032 CHECK((uint32_t)viewConfigTypes.size() == viewConfigTypeCount);*/
1033 bool blendModeFound = false;
1034 //for (XrViewConfigurationType viewConfigType : viewConfigTypes) {
1035 uint32_t count;
1036 CHECK_XRCMD(xrEnumerateEnvironmentBlendModes(xrInstance, systemId, xrViewConfType, 0, &count, nullptr));
1037 CHECK(count > 0);
1038 std::vector<XrEnvironmentBlendMode> blendModes(count);
1039 CHECK_XRCMD(xrEnumerateEnvironmentBlendModes(xrInstance, systemId, xrViewConfType, count, &count, blendModes.data()));
1040
1041
1042 for (XrEnvironmentBlendMode mode : blendModes) {
1043 for (auto supportedBlend : this->supportedBlendModes) {
1044 if (supportedBlend == mode && !blendModeFound) {
1045 xrBlendMode = mode;
1046 blendModeFound = true;
1047 break;
1048 }
1049 }
1050 }
1051 CHECK(blendModeFound);
1052 //}
1053}
1054
1055void WindowOpenXR::checkExtensions()
1056{
1057 PRINT("\n Check for Openxr Extensions:");
1058 uint32_t instanceExtensionCount;
1059 CHECK_XRCMD(xrEnumerateInstanceExtensionProperties(nullptr, 0, &instanceExtensionCount, nullptr));
1060
1061 std::vector<XrExtensionProperties> extensions(instanceExtensionCount);
1062 for (XrExtensionProperties& extension : extensions) {
1063 extension.type = XR_TYPE_EXTENSION_PROPERTIES;
1064 }
1065
1066 CHECK_XRCMD(xrEnumerateInstanceExtensionProperties(nullptr, (uint32_t)extensions.size(), &instanceExtensionCount,
1067 extensions.data()));
1068
1069 for (auto& ext : extensions) {
1070 if (strcmp(ext.extensionName , XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME) == 0) {
1071 depthExtensionAvailable = true;
1072 PRINT("Depth extension available !");
1073 }
1074 if (strcmp(ext.extensionName , XR_VARJO_QUAD_VIEWS_EXTENSION_NAME) == 0) {
1075 quadViewExtensionAvailable = true;
1076 PRINT("Varjo quad view extension available !" );
1077 }
1078 D(PRINT("found extension: %s", ext.extensionName));
1079 }
1080 PRINT("\n");
1081}
1082
1083#ifndef __ANDROID__
1084void WindowOpenXR::inputKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mode)
1085{
1086 const int inputAction = GLFW_PRESS;
1087 if (key == GLFW_KEY_SPACE && action == inputAction)
1088 {
1089 printCoordinates();
1090 }
1091 else if ((key == GLFW_KEY_0 || key == GLFW_KEY_KP_0) && action == inputAction) {
1092 resetOrigin();
1093 }
1094 else if (key == GLFW_KEY_ESCAPE) {
1095 xrRequestExitSession(xrSession);
1096 }
1097 else if (key == GLFW_KEY_F3) {
1099 }
1100 else if (key == GLFW_KEY_F9) {
1101 this->pauseRendering = true;
1102 PRINT("pause rendering for test");
1103 }
1104}
1105#endif
1106
1107void WindowOpenXR::renderFrame(VulkanDrawing* vulkanDrawing) {
1108 XrFrameWaitInfo frameWaitInfo{XR_TYPE_FRAME_WAIT_INFO};
1109 XrFrameState frameState{XR_TYPE_FRAME_STATE};
1110 CHECK_XRCMD(xrWaitFrame(xrSession, &frameWaitInfo, &frameState));
1111
1112 XrFrameBeginInfo frameBeginInfo{XR_TYPE_FRAME_BEGIN_INFO};
1113 CHECK_XRCMD(xrBeginFrame(xrSession, &frameBeginInfo));
1114
1115
1116
1117 std::vector<XrCompositionLayerProjectionView> projectionLayerViews;
1118 std::vector<XrCompositionLayerDepthInfoKHR> depthProjectionLayerViews;
1119 std::vector<XrCompositionLayerBaseHeader*> layers;
1120 XrCompositionLayerProjection layer{ XR_TYPE_COMPOSITION_LAYER_PROJECTION };
1121
1122 if (!mirrorActivated) {
1123 checkKeyboard();
1124 }
1125
1126 if (frameState.shouldRender == XR_TRUE) {
1127
1128 //if (RenderLayer(frameState.predictedDisplayTime, projectionLayerViews, layer)) {
1129 XrResult res;
1130 bool renderOk = true;
1131
1132 XrViewState viewState{XR_TYPE_VIEW_STATE};
1133 uint32_t viewCapacityInput = (uint32_t)xrViews.size();
1134 uint32_t viewCountOutput;
1135
1136 XrViewLocateInfo viewLocateInfo{XR_TYPE_VIEW_LOCATE_INFO};
1137 viewLocateInfo.viewConfigurationType = xrViewConfType;
1138 viewLocateInfo.displayTime = frameState.predictedDisplayTime;
1139 viewLocateInfo.space = refSpace;
1140
1141 res = xrLocateViews(xrSession, &viewLocateInfo, &viewState, viewCapacityInput, &viewCountOutput, xrViews.data());
1142 if (!((viewState.viewStateFlags & XR_VIEW_STATE_POSITION_VALID_BIT) == 0 ||
1143 (viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT) == 0)) {
1144 //valid pose
1145 //D(
1146 count++;
1147 auto startTime = std::chrono::high_resolution_clock::now();
1148 //)
1149
1150 CHECK(viewCountOutput == viewCapacityInput);
1151 CHECK(viewCountOutput == xrConfViews.size());
1152 CHECK(viewCountOutput == swapchains.size());
1153 if (depthExtensionAvailable) {
1154 CHECK(viewCountOutput == depthSwapchains.size());
1155 }
1156
1157 projectionLayerViews.resize(viewCountOutput);
1158 if (depthExtensionAvailable) {
1159 depthProjectionLayerViews.resize(viewCountOutput);
1160 }
1161
1162 std::vector<vk::Fence> fences;
1163 std::vector<uint32_t> indexesSwap;
1164 std::vector<uint32_t> indexesSwapDepth;
1165 //-------------------------------------
1166 // Render view to the appropriate part of the swapchain image.
1167 for (uint32_t i = 0; i < viewCountOutput; i++) {
1168 // Each view has a separate swapchain which is acquired, rendered to, and released.
1169 auto viewSwapchainHandle = swapchains[i]->handle;//swapchains[i]->getSwapchainStruct();
1170 //std::variant<XrSwapchain,vk::SwapchainKHR> depthHandle;
1171 /*
1172 */
1173 uint32_t swapchainImageIndex;
1174 std::optional<vk::Semaphore> semaphore;
1175 std::tie(swapchainImageIndex, semaphore) = swapchains[i]->acquireImage();
1176 indexesSwap.push_back(swapchainImageIndex);
1177 std::vector<vk::Semaphore> semaphoresList;
1178 if (semaphore.has_value()) {
1179 semaphoresList.push_back(semaphore.value());
1180 }
1181 projectionLayerViews[i] = { XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW };
1182 projectionLayerViews[i].pose = xrViews[i].pose;
1183 projectionLayerViews[i].fov = xrViews[i].fov;
1184 projectionLayerViews[i].subImage.swapchain = std::get<XrSwapchain>(viewSwapchainHandle);
1185 projectionLayerViews[i].subImage.imageRect.offset = { 0, 0 };
1186 projectionLayerViews[i].subImage.imageRect.extent.width = swapchains[i]->swapchainExtent.width;
1187 projectionLayerViews[i].subImage.imageRect.extent.height = swapchains[i]->swapchainExtent.height;
1188
1189 uint32_t depthSwapchainImageIndex = UINT_MAX;
1190 if (depthExtensionAvailable) {
1191 std::optional<vk::Semaphore> dSemaphore;
1192 std::tie(depthSwapchainImageIndex, dSemaphore) = depthSwapchains[i]->acquireImage();
1193 indexesSwapDepth.push_back(depthSwapchainImageIndex);
1194 if (dSemaphore.has_value()) {
1195 semaphoresList.push_back(dSemaphore.value());
1196 }
1197 assert(depthSwapchainImageIndex == swapchainImageIndex);
1198 depthProjectionLayerViews[i] = {XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR};
1199 depthProjectionLayerViews[i].minDepth = 1;
1200 depthProjectionLayerViews[i].maxDepth = 0;
1201 //reverse depth so min value is set to far and max to near
1202 depthProjectionLayerViews[i].nearZ = wrapper->params.farPlaneHeadset;
1203 depthProjectionLayerViews[i].farZ = wrapper->params.nearPlaneHeadset;
1204 //--------
1205 depthProjectionLayerViews[i].subImage.swapchain = std::get<XrSwapchain>(depthSwapchains[i]->handle);
1206 depthProjectionLayerViews[i].subImage.imageArrayIndex = 0;
1207 depthProjectionLayerViews[i].subImage.imageRect.offset = { 0,0 };
1208 depthProjectionLayerViews[i].subImage.imageRect.extent = { (int) depthSwapchains[i]->swapchainExtent.width, (int) depthSwapchains[i]->swapchainExtent.height };
1209 projectionLayerViews[i].next = &(depthProjectionLayerViews[i]);
1210 }
1211 else {
1212 projectionLayerViews[i].next = nullptr;
1213 }
1214
1215 translationsOpenXR[i] = { xrViews[i].pose.position.x ,xrViews[i].pose.position.y,xrViews[i].pose.position.z };
1216 translationsOMAF[i] = conversionMat * translationsOpenXR[i];
1217
1218#ifdef HVT_UDP_CONTROL
1219 mutex.lock();
1220#endif
1221 this->currentTranslation = deltaQuat*(translationsOpenXR[i] + deltaPos);
1222
1223#ifdef HVT_UDP_CONTROL
1224 mutex.unlock();
1225#endif
1226
1227 auto rot = XrQuaternionToGLMQuat(xrViews[i].pose.orientation);
1228 this->setRotation(rot, i);
1229 glm::vec4 fov(xrViews[i].fov.angleLeft, xrViews[i].fov.angleRight, xrViews[i].fov.angleUp,xrViews[i].fov.angleDown);
1231
1232 uint32_t mirrorIndex = 0;
1233 mirrorShouldRender = true;
1234 if (mirrorActivated && i == 0 && swapchainImageIndex == currentMirror) {
1235
1236 int width = 0, height = 0;
1237 glfwGetFramebufferSize(window, &width, &height);
1238
1239 if (width == 0 || height == 0) {
1240 mirrorShouldRender = false;
1241 }else{
1242 try {
1243 vk::Result acquireResult;
1244 std::tie(acquireResult, mirrorIndex) = wrapper->getContext()->device.acquireNextImageKHR(mirrorSwapchain, std::numeric_limits<uint64_t>::max(), mirrorAvailableSemaphore[currentMirror], nullptr);
1245
1246 if (acquireResult != vk::Result::eSuccess && acquireResult != vk::Result::eSuboptimalKHR) {
1247 throw std::runtime_error("failed to acquire swap chain image!");
1248 }
1249 }
1250 catch (vk::OutOfDateKHRError e) {
1251 //vulkan hpp raise an exception when vk::Result::eErrorOutOfDateKHR, no need to check for it in return code above
1252 recreateMirror();
1253 mirrorShouldRender = false;
1254 }
1255 }
1256 if (mirrorShouldRender) {
1257 semaphoresList.push_back(mirrorAvailableSemaphore[currentMirror]);
1258 }
1259
1260 }
1261
1262 std::tuple<int, uint32_t> idImage = std::make_pair(i,swapchainImageIndex);
1263 vk::Fence renderfinnished;
1264 std::vector<vk::Semaphore> sem;
1265 if (mirrorActivated && i == 0 && swapchainImageIndex == currentMirror && mirrorShouldRender) {
1266 sem.push_back(mirrorFinnishedSemaphore[currentMirror]);
1267 }
1268
1269 if (depthExtensionAvailable) {
1270 renderfinnished = vulkanDrawing->submitDrawCall(idImage, semaphoresList, sem ,depthSwapchainImageIndex);
1271 }
1272 else {
1273 renderfinnished = vulkanDrawing->submitDrawCall(idImage, semaphoresList, sem);
1274 }
1275
1276 fences.push_back(renderfinnished);
1277
1278 if (mirrorActivated && i == 0 && swapchainImageIndex == currentMirror && mirrorShouldRender) {
1279 vk::PresentInfoKHR presentInfo{ 1,
1280 &mirrorFinnishedSemaphore[currentMirror],
1281 1,
1282 &mirrorSwapchain,
1283 &mirrorIndex,
1284 nullptr };
1285 try {
1286 vk::Result resultPresent = wrapper->getContext()->presentQueue.presentKHR(presentInfo);
1287
1288 if (resultPresent == vk::Result::eSuboptimalKHR) {
1289 recreateMirror();
1290 }
1291 else if (resultPresent != vk::Result::eSuccess) {
1292 throw std::runtime_error("failed to present swap chain image!");
1293 }
1294 }
1295 catch (vk::OutOfDateKHRError) {
1296 //vulkan hpp raise an exception when vk::Result::eErrorOutOfDateKHR, no need to check for it in return code above
1297 recreateMirror();
1298 }
1299 currentMirror = (currentMirror + 1) % mirrorSwapchainSize;
1300 }
1301
1302
1303
1304 }
1305 for (uint32_t i = 0; i < viewCountOutput; i++) {
1306 assert(fences.size() == viewCountOutput);
1307 assert(indexesSwap.size() == viewCountOutput);
1308
1309 swapchains[i]->presentImage(indexesSwap[i], fences[i]);
1310 if (depthExtensionAvailable) {
1311 assert(indexesSwapDepth.size() == viewCountOutput);
1312 depthSwapchains[i]->presentImage(indexesSwapDepth[i], fences[i]);
1313 }
1314 }
1315
1316 layer.space = refSpace;
1317 layer.viewCount = (uint32_t)projectionLayerViews.size();
1318 layer.views = projectionLayerViews.data();
1319
1320 layers.push_back(reinterpret_cast<XrCompositionLayerBaseHeader*>(&layer));
1321 //D(
1322 auto end = std::chrono::high_resolution_clock::now();
1323 auto t = std::chrono::duration_cast<std::chrono::milliseconds>(end - startTime);
1324 totalChrono += t;
1325 if (count == 100) {
1326 auto res = totalChrono / (count);
1327 std::cout << res.count() << std::endl;
1328 totalChrono = std::chrono::milliseconds( 0 );
1329 count = 0;
1330 }
1331 //)
1332
1333 }
1334 }
1335
1336 XrFrameEndInfo frameEndInfo{XR_TYPE_FRAME_END_INFO};
1337 frameEndInfo.displayTime = frameState.predictedDisplayTime;
1338 frameEndInfo.environmentBlendMode = xrBlendMode;
1339 frameEndInfo.layerCount = (uint32_t)layers.size();
1340 frameEndInfo.layers = layers.data();
1341 CHECK_XRCMD(xrEndFrame(xrSession, &frameEndInfo));
1342
1343 depthProjectionLayerViews.clear();
1344 projectionLayerViews.clear();
1345
1346
1347}
1348
1350 return runningSession;
1351}
1352
1353void WindowOpenXR::setRotation(glm::quat & qR, int view) {
1354
1355 //Math from https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
1356
1357
1358
1359 assert(quaternionsList.size() == rotationsOMAF.size());
1360 assert(quaternionsList.size() == rotationsOpenXR.size());
1361 if (quaternionsList.size() != swapchains.size()) {
1362 const int s = swapchains.size();
1363 quaternionsList.resize(s);
1364 rotationsOMAF.resize(s);
1365 rotationsOpenXR.resize(s);
1366 }
1367
1368 quaternionsList[view] = qR;
1369
1370#ifdef HVT_UDP_CONTROL
1371 mutex.lock();
1372#endif
1373 auto q = deltaQuat * qR;
1374
1375#ifdef HVT_UDP_CONTROL
1376 mutex.unlock();
1377#endif
1378
1379 //roll
1380 double siny_cosp = 2 * (q.w * q.z + q.x * q.y);
1381 double cosy_cosp = 1 - 2 * (q.y * q.y + q.z * q.z);
1382 rotationsOMAF[view][0] = -1 * std::atan2(siny_cosp, cosy_cosp) * (180.0 / M_PI);
1383 rotationsOpenXR[view][2] = std::atan2(siny_cosp, cosy_cosp) * (180.0 / M_PI);
1384
1385 // pitch
1386 double sinp = 2 * (q.w * q.y - q.z * q.x);
1387 if (std::abs(sinp) >= 1) {
1388 rotationsOMAF[view][2] = std::copysign(M_PI / 2, sinp) * (180.0 / M_PI); // use 90 degrees if out of range
1389 rotationsOpenXR[view][1] = std::copysign(M_PI / 2, sinp) * (180.0 / M_PI); // use 90 degrees if out of range
1390 }
1391 else {
1392 rotationsOMAF[view][2] = std::asin(sinp) * (180.0 / M_PI);
1393 rotationsOpenXR[view][1] = std::asin(sinp) * (180.0 / M_PI);
1394 }
1395
1396 // Roll
1397 double sinr_cosp = 2 * (q.w * q.x + q.y * q.z);
1398 double cosr_cosp = 1 - 2 * (q.x * q.x + q.y * q.y);
1399 rotationsOMAF[view][1] = -1 * std::atan2(sinr_cosp, cosr_cosp) * (180.0 / M_PI);
1400 rotationsOpenXR[view][0] = std::atan2(sinr_cosp, cosr_cosp) * (180.0 / M_PI);
1401
1402 currentRotation = rotationsOpenXR[view];
1403
1404
1405}
1406
1407
1408
1409vk::Image WindowOpenXR::getSwapchainImage(int view, int index) {
1410 assert(view < swapchains.size());
1411 return swapchains[view]->getSwapchainImage(index);//swapChainImagesXr[view][index].image;
1412}
1413
1414
1415void WindowOpenXR::checkKeyboard() {
1416#if WIN32
1417 //Windows only
1418 SHORT keyState0 = GetAsyncKeyState('0');
1419 bool isToggled0 = keyState0 & 1;
1420 bool isDown0 = keyState0 & 0x8000;
1421
1422 SHORT keyStateNum0 = GetAsyncKeyState(VK_NUMPAD0);
1423 auto isToggledNum0 = keyStateNum0 & 1;
1424 bool isDownNum0 = keyStateNum0 & 0x8000;
1425
1426 SHORT keyStateSpace = GetAsyncKeyState(VK_SPACE);
1427 bool isToggledSpace = keyStateSpace & 1;
1428 bool isDownSpace = keyStateSpace & 0x8000;
1429
1430 SHORT keyStateEscape = GetAsyncKeyState(VK_ESCAPE);
1431 bool isToggledEscape = keyStateEscape & 1;
1432
1433 SHORT keyStateF2 = GetAsyncKeyState(VK_F2);
1434 bool isToggledF2 = keyStateF2 & 1;
1435
1436 SHORT keyStateF3 = GetAsyncKeyState(VK_F3);
1437 bool isToggledF3 = keyStateF3 & 1;
1438
1439#else
1440 //need to test for posix systems
1441 bool isToggled0 = false;
1442 bool isToggledSpace = false;
1443 bool isDown0 = false;
1444 bool isDownSpace = false;
1445 bool isToggledNum0 = false;
1446 bool isDownNum0 = false;
1447 bool isToggledEscape = false;
1448 bool isToggledF2 = false;
1449 bool isToggledF3 = false;
1450
1451
1452 //TODO fix that for LINUX
1453 //maybe:
1454 //https://stackoverflow.com/questions/29335758/using-kbhit-and-getch-on-linux
1455 /*
1456 if (kbhit()) {
1457 auto c = getch();
1458 if (c == ' ') {
1459 isDownSpace = true;
1460 isToggledSpace = true;
1461 }
1462 else if (c == '0') {
1463 isDown0 = true;
1464 isToggled0 = true;
1465 }
1466 }*/
1467#endif
1468
1469 if ((isDown0 && isToggled0) || (isDownNum0 && isToggledNum0)) {
1470 resetOrigin();
1471 }
1472
1473 if (isDownSpace && isToggledSpace) {
1474 printCoordinates();
1475 }
1476
1477 if (isToggledEscape) {
1478 xrRequestExitSession(xrSession);
1479 }
1480
1481 /*
1482 if (isToggledF2) {
1483 wrapper->saveConfigurationsToDisk();
1484 }*/
1485
1486 if (isToggledF3) {
1488 }
1489}
1490
1492{
1493 glm::vec3 averagePos = { 0,0,0 };
1494 for (int i = 0; i < translationsOpenXR.size(); i++) {
1495 averagePos += translationsOpenXR[i];
1496 }
1497 averagePos /= translationsOpenXR.size();
1498 glm::quat q = quaternionsList[quaternionsList.size() - 1];
1499
1500#ifdef HVT_UDP_CONTROL
1501 mutex.lock();
1502#endif
1503
1504 deltaPos = -1.0f * averagePos;
1505 deltaQuat = glm::inverse(q);
1506
1507#ifdef HVT_UDP_CONTROL
1508 mutex.unlock();
1509#endif
1510
1511}
1512
1513void WindowOpenXR::printCoordinates()
1514{
1515 std::cout << "--------------" << std::endl;
1516 for (int i = 0; i < 2; i++) {
1517 std::cout << "View: " << i << std::endl;
1518 std::cout << "Translation OpenXR: " << glm::to_string(translationsOpenXR[i]) << "Rotation OpenXR : " << glm::to_string(rotationsOpenXR[i]) << std::endl;
1519 std::cout << "Translation OMAF: " << glm::to_string(translationsOMAF[i]) << "Rotation OMAF : " << glm::to_string(rotationsOMAF[i]) << std::endl;
1520 std::cout << "Test: " << glm::to_string(conversionMat * rotationsOMAF[i]) << std::endl;
1521 }
1522}
File that contains the SwapchainOpenXR class.
File that contain the VulkanContext class to manage Vulkan Instance, Physical device,...
Contains the class that manage the drawing process (manage and record command buffer,...
Class that contains helper functions for Vulkan.
file that contains the VulkanWrapper class that manages the classes related to Vulkan code and ease t...
Class that take care of the tasks linked to OpenXR (input and display), is used when HMD support is r...
Class that encapsulate an OpenXR Swapchain and functions associated to it.
vk::PhysicalDevice physicalDevice
Definition: VulkanContext.h:85
vk::Instance instance
Definition: VulkanContext.h:83
vk::Device device
Definition: VulkanContext.h:87
vk::Queue presentQueue
Definition: VulkanContext.h:91
QueueFamilyIndices findQueueFamilies(vk::PhysicalDevice device)
The class that manages the drawing operation (manage and record command buffers).
Definition: VulkanDrawing.h:46
vk::Fence submitDrawCall(std::tuple< int, uint32_t > idImage, const std::vector< vk::Semaphore > &imageAvailableSemaphores, std::vector< vk::Semaphore > &signalSemaphore, uint32_t depthIndex=UINT_MAX)
void loadConfigurationsFromDisk()
bool framebufferResized
std::vector< vk::Format > getRenderPassSupportedDepthFormats() const
VulkanContext context
void updateSpaceTransform(glm::vec3 translation, glm::vec3 rotation)
RenderingParameters params
VulkanContext * getContext()
glm::vec3 currentTranslation
std::vector< std::unique_ptr< SwapchainAbstract > > depthSwapchains
glm::vec3 currentRotation
bool separateFromWindowDim
std::vector< std::unique_ptr< SwapchainAbstract > > swapchains
VulkanWrapper * wrapper
const bool enableValidationLayers
Class that take care of the tasks linked to OpenXR (input and display), is used when HMD support is r...
Definition: WindowOpenXR.h:52
std::vector< vk::Extent2D > getBlitExtentDestinations(int view, int elem) override
void checkForCorrectSize()
void setMirror(bool mirrorActivated)
virtual std::vector< vk::Semaphore > getSemaphoreWait(int view, int elem) override
void createVulkanDevice(VkPhysicalDevice &physicalDevice, VkDeviceCreateInfo &info, VkDevice *device)
virtual std::vector< vk::Semaphore > getSemaphoreSignal(int view, int elem) override
void getVulkanPhysicalDevice(VkInstance &vkInstance, VkPhysicalDevice *physicalDevice)
void cleanUpSurface() override
void continueInit() override
bool isSessionRunning()
void initWindow() override
bool isDepthRecquired() override
std::vector< const char * > getRequiredDeviceExtensions() override
vk::Image getSwapchainImage(int view, int index) override
void cleanUp() override
void mainLoop(VulkanDrawing *vulkanDrawing) override
std::vector< vk::Image > getBlitDestinations(int view, int elem) override
void setRotation(glm::quat &q, int view)
void createSurface() override
void framebufferResizeCallback()
const bool useOpenXR() override
VkBool32 isDeviceSupportingSufaceKHR(VkPhysicalDevice device, int i) override
void getFrameBufferSize(int *w, int *h, vk::PhysicalDevice &pDevice) override
bool isSynchroWithSemaphore() override
void resetOrigin() override
std::vector< const char * > getRequiredExtensions() override
SwapChainSupportDetails querySwapChainSupport(vk::PhysicalDevice device) override
void pollEvents(bool *exitRenderLoop, bool *requestRestart)
file that contains the common include for the Vulkan part
Some useful functions from the openXR sample (hello_xr)
Struct to encapsulate the indice of the queues families.
Definition: VulkanContext.h:38
std::optional< uint32_t > graphicsFamily
Definition: VulkanContext.h:40
std::optional< uint32_t > presentFamily
Definition: VulkanContext.h:42
Struct that contains the capability for the sapchain, the formats and the present mode supported.
Definition: commonVulkan.h:87