UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
openxr_gl.cpp
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2010-2023 CESNET, z. s. p. o.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37#ifdef HAVE_CONFIG_H
38# include "config.h"
39# include "config_unix.h"
40# include "config_win32.h"
41#endif //HAVE_CONFIG_H
42
43#include <chrono>
44#include <thread>
45#include <vector>
46#include <condition_variable>
47#include <queue>
48
49#include <assert.h>
50#ifdef __linux__
51# include <X11/Xlib.h>
52# include <GL/glew.h>
53# include <GL/glx.h>
54# define XR_USE_PLATFORM_XLIB
55# define XR_USE_GRAPHICS_API_OPENGL
56#elif defined _WIN32
57# define XR_USE_PLATFORM_WIN32
58# define XR_USE_GRAPHICS_API_OPENGL
59#endif
60#include <openxr/openxr.h>
61#include <openxr/openxr_platform.h>
62
63#include <glm/glm.hpp>
64#include <glm/gtc/matrix_transform.hpp>
65#include <glm/gtc/type_ptr.hpp>
66#include <glm/gtc/quaternion.hpp>
67
68#include "debug.h"
69#include "host.h"
70#include "keyboard_control.h" // K_*
71#include "lib_common.h"
72#include "messaging.h"
73#include "module.h"
74#include "video.h"
75#include "video_display.h"
77
78#include "opengl_utils.hpp"
79#include "opengl_panorama.hpp"
80#include "sdl_window.hpp"
82
83#define MAX_BUFFER_SIZE 3
84
86public:
88 XrSystemId systemId,
89 const void *xrGraphicsBindingOpenGL)
90 {
91 XrSessionCreateInfo session_create_info = {};
92 session_create_info.type = XR_TYPE_SESSION_CREATE_INFO;
93 session_create_info.next = xrGraphicsBindingOpenGL;
94 session_create_info.systemId = systemId;
95
96 XrResult result;
97
98 PFN_xrGetOpenGLGraphicsRequirementsKHR pfnGetOpenGLGraphicsRequirementsKHR = nullptr;
99 result = xrGetInstanceProcAddr(instance, "xrGetOpenGLGraphicsRequirementsKHR",
100 reinterpret_cast<PFN_xrVoidFunction*>(&pfnGetOpenGLGraphicsRequirementsKHR));
101
102 XrGraphicsRequirementsOpenGLKHR graphicsRequirements{};
103 graphicsRequirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR;
104 result = pfnGetOpenGLGraphicsRequirementsKHR(instance, systemId, &graphicsRequirements);
105
106 result = xrCreateSession(instance, &session_create_info, &session);
107 if(!XR_SUCCEEDED(result)){
108 throw std::runtime_error("Failed to create OpenXR session!");
109 }
110 }
111
113 xrDestroySession(session);
114 }
115
116 XrSession get(){ return session; }
117
118 void begin(){
119 XrSessionBeginInfo session_begin_info;
120 session_begin_info.type = XR_TYPE_SESSION_BEGIN_INFO;
121 session_begin_info.next = NULL;
122 session_begin_info.primaryViewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
123 XrResult result = xrBeginSession(session, &session_begin_info);
124 if(!XR_SUCCEEDED(result)){
125 throw std::runtime_error("Failed to begin OpenXR session!");
126 }
127 }
128private:
129 XrSession session;
130};
131
133public:
135 uint32_t properties_count = 0;
136 XrResult result = xrEnumerateInstanceExtensionProperties(nullptr,
137 properties_count,
138 &properties_count,
139 nullptr);
140 if(!XR_SUCCEEDED(result)){
141 log_msg(LOG_LEVEL_WARNING, "Failed to check XR_KHR_OPENGL availability!\n");
142 } else {
143 std::vector<XrExtensionProperties> props;
144 props.resize(properties_count);
145 for(auto& prop : props){
146 prop.type = XR_TYPE_EXTENSION_PROPERTIES;
147 prop.next = nullptr;
148 }
149 result = xrEnumerateInstanceExtensionProperties(nullptr,
150 properties_count,
151 &properties_count,
152 props.data());
153
154 bool found = false;
155 for(const auto& prop : props){
156 if(strcmp(prop.extensionName, XR_KHR_OPENGL_ENABLE_EXTENSION_NAME) == 0){
157 found = true;
158 break;
159 }
160 }
161 if(!found){
162 throw std::runtime_error("OpenXR runtime does not support OpenGL interop!");
163 }
164 }
165 const char* const enabledExtensions[] = {XR_KHR_OPENGL_ENABLE_EXTENSION_NAME};
166
167 XrInstanceCreateInfo instanceCreateInfo;
168 instanceCreateInfo.type = XR_TYPE_INSTANCE_CREATE_INFO;
169 instanceCreateInfo.next = NULL;
170 instanceCreateInfo.createFlags = 0;
171 instanceCreateInfo.enabledExtensionCount = 1;
172 instanceCreateInfo.enabledExtensionNames = enabledExtensions;
173 instanceCreateInfo.enabledApiLayerCount = 0;
174 strcpy(instanceCreateInfo.applicationInfo.applicationName, "UltraGrid OpenXR gl display");
175 strcpy(instanceCreateInfo.applicationInfo.engineName, "");
176 instanceCreateInfo.applicationInfo.applicationVersion = 1;
177 instanceCreateInfo.applicationInfo.engineVersion = 0;
178 instanceCreateInfo.applicationInfo.apiVersion = XR_CURRENT_API_VERSION;
179
180 result = xrCreateInstance(&instanceCreateInfo, &instance);
181 if(!XR_SUCCEEDED(result)){
182 throw std::runtime_error("Failed to create OpenXR instance!");
183 }
184
185 XrInstanceProperties instanceProperties;
186 instanceProperties.type = XR_TYPE_INSTANCE_PROPERTIES;
187 instanceProperties.next = NULL;
188
189 result = xrGetInstanceProperties(instance, &instanceProperties);
190
191 if(XR_SUCCEEDED(result)){
192 printf("Runtime Name: %s\n", instanceProperties.runtimeName);
193 printf("Runtime Version: %d.%d.%d\n",
194 XR_VERSION_MAJOR(instanceProperties.runtimeVersion),
195 XR_VERSION_MINOR(instanceProperties.runtimeVersion),
196 XR_VERSION_PATCH(instanceProperties.runtimeVersion));
197 }
198
199
200 }
201
203 xrDestroyInstance(instance);
204 }
205
210
211 XrInstance get() { return instance; }
212private:
213 XrInstance instance;
214};
215
217public:
218 Openxr_swapchain() = default;
219
220 Openxr_swapchain(XrSession session, const XrSwapchainCreateInfo *info) :
222 {
223 xrCreateSwapchain(session, info, &swapchain);
224 }
225
227 int64_t swapchain_format,
228 uint32_t w,
229 uint32_t h) :
231 {
232 XrSwapchainCreateInfo swapchain_create_info;
233 swapchain_create_info.type = XR_TYPE_SWAPCHAIN_CREATE_INFO;
234 swapchain_create_info.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT |
235 XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
236 swapchain_create_info.createFlags = 0;
237 swapchain_create_info.format = swapchain_format;
238 swapchain_create_info.sampleCount = 1;
239 swapchain_create_info.width = w;
240 swapchain_create_info.height = h;
241 swapchain_create_info.faceCount = 1;
242 swapchain_create_info.arraySize = 1;
243 swapchain_create_info.mipCount = 1;
244 swapchain_create_info.next = nullptr;
245
246 XrResult result = xrCreateSwapchain(session, &swapchain_create_info, &swapchain);
247
248 if(!XR_SUCCEEDED(result)){
249 throw std::runtime_error("Failed to create OpenXR swapchain!");
250 }
251 }
252
254 if(swapchain != XR_NULL_HANDLE){
255 xrDestroySwapchain(swapchain);
256 }
257 }
258
261 swap(o);
262 }
265
266 uint32_t get_length(){
267 uint32_t len = 0;
268 XrResult result = xrEnumerateSwapchainImages(swapchain, 0, &len, nullptr);
269 if(!XR_SUCCEEDED(result)){
270 throw std::runtime_error("Failed to enumerate swapchain images\n");
271 }
272
273 return len;
274 }
275
276 void swap(Openxr_swapchain& o) noexcept{
277 std::swap(swapchain, o.swapchain);
278 std::swap(session, o.session);
279 }
280
281 XrSwapchain get(){ return swapchain; }
282
283private:
284 XrSwapchain swapchain = XR_NULL_HANDLE;
285 XrSession session = XR_NULL_HANDLE;
286};
287
289public:
290 Gl_interop_swapchain(XrSession session, const XrSwapchainCreateInfo *info) :
291 xr_swapchain(session, info)
292 {
293 init();
294 }
295
296 Gl_interop_swapchain(XrSession session,
297 int64_t swapchain_format,
298 uint32_t w,
299 uint32_t h) :
300 xr_swapchain(session, swapchain_format, w, h)
301 {
302 init();
303 }
304
305 GLuint get_texture(size_t idx){
306 return images[idx].image;
307 }
308
310 return framebuffers[idx];
311 }
312
313 XrSwapchain get() { return xr_swapchain.get(); }
314
319
320private:
321 void init(){
322 uint32_t length = xr_swapchain.get_length();
323 images.resize(length);
324 framebuffers.resize(length);
325
326 for(auto& image : images){
327 image.type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR;
328 image.next = nullptr;
329 }
330
331 XrResult result = xrEnumerateSwapchainImages(xr_swapchain.get(),
332 length, &length,
333 (XrSwapchainImageBaseHeader *)(images.data()));
334
335 if(!XR_SUCCEEDED(result)){
336 throw std::runtime_error("Failed to enumerate swapchain images");
337 }
338
339 for(size_t i = 0; i < length; i++){
340 framebuffers[i].attach_texture(get_texture(i));
341 }
342 }
343
345 std::vector<Framebuffer> framebuffers;
346 std::vector<XrSwapchainImageOpenGLKHR> images;
347};
348
350public:
351 Openxr_local_space(XrSession session){
352 XrPosef origin{};
353 origin.orientation.x = 0.0;
354 origin.orientation.y = 0.0;
355 origin.orientation.z = 0.0;
356 origin.orientation.w = 1.0;
357
358 XrReferenceSpaceCreateInfo space_create_info;
359 space_create_info.type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
360 space_create_info.next = NULL;
361 space_create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
362 space_create_info.poseInReferenceSpace = origin;
363
364 XrResult result = xrCreateReferenceSpace(session, &space_create_info, &space);
365 if(!XR_SUCCEEDED(result)){
366 throw std::runtime_error("Failed to create OpenXR reference space!");
367 }
368 }
369
371 xrDestroySpace(space);
372 }
373
378
379 XrSpace get() { return space; }
380
381private:
382 XrSpace space;
383};
384
387 XrSystemId system_id;
388 //Openxr_session session;
389};
390
394
397
399
400 std::chrono::steady_clock::time_point last_frame;
401
402 std::mutex lock;
403 std::condition_variable frame_consumed_cv;
404 std::condition_variable new_frame_ready_cv;
405 std::queue<video_frame *> frame_queue;
406
407 std::vector<video_frame *> free_frame_pool;
408 std::condition_variable free_frame_ready_cv;
409
410 std::vector<video_frame *> dispose_frame_pool;
411};
412
413static std::vector<XrViewConfigurationView> get_views(Openxr_state& xr_state){
414 unsigned view_count;
415 XrResult result = xrEnumerateViewConfigurationViews(xr_state.instance.get(),
416 xr_state.system_id,
417 XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO,
418 0,
419 &view_count,
420 nullptr);
421
422 if(!XR_SUCCEEDED(result)){
423 throw std::runtime_error("Failed to enumerate view configuration views!");
424 }
425
426 std::vector<XrViewConfigurationView> config_views(view_count);
427 for(auto& view : config_views) view.type = XR_TYPE_VIEW_CONFIGURATION_VIEW;
428
429 result = xrEnumerateViewConfigurationViews(xr_state.instance.get(),
430 xr_state.system_id,
431 XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO,
432 view_count,
433 &view_count,
434 config_views.data());
435
436 if(!XR_SUCCEEDED(result)){
437 throw std::runtime_error("Failed to enumerate view configuration views!");
438 }
439
440 return config_views;
441}
442
443static std::vector<int64_t> get_swapchain_formats(XrSession session){
444 XrResult result;
445 unsigned swapchain_format_count;
446 result = xrEnumerateSwapchainFormats(session,
447 0,
448 &swapchain_format_count,
449 nullptr);
450
451 printf("Runtime supports %d swapchain formats\n", swapchain_format_count);
452 std::vector<int64_t> swapchain_formats(swapchain_format_count);
453 result = xrEnumerateSwapchainFormats(session,
454 swapchain_format_count,
455 &swapchain_format_count,
456 swapchain_formats.data());
457
458 if(!XR_SUCCEEDED(result)){
459 throw std::runtime_error("Failed to enumerate swapchain formats!");
460 }
461
462 return swapchain_formats;
463}
464
465static glm::mat4 get_proj_mat(const XrFovf& fov, float zNear, float zFar){
466 glm::mat4 res{};
467
468 float tanLeft = glm::tan(fov.angleLeft);
469 float tanRight = glm::tan(fov.angleRight);
470 float tanUp = glm::tan(fov.angleUp);
471 float tanDown = glm::tan(fov.angleDown);
472
473 res[0][0] = 2.f / (tanRight - tanLeft);
474 res[1][1] = 2.f / (tanUp - tanDown);
475 res[2][0] = (tanRight + tanLeft) / (tanRight - tanLeft);
476 res[2][1] = (tanUp + tanDown) / (tanUp - tanDown);
477 res[2][2] = -(zFar + zNear) / (zFar - zNear);
478 res[2][3] = -1;
479 res[3][2] = -(2.f * zFar * zNear) / (zFar - zNear);
480
481 return res;
482}
483
484static int64_t select_swapchain_fmt(const std::vector<int64_t>& swapchain_formats){
485 const int64_t preferred_formats[] = {
486 GL_RGBA8_EXT,
487 GL_SRGB8_ALPHA8_EXT
488 };
489
490 for(auto pref : preferred_formats){
491 for(auto supported_fmt : swapchain_formats){
492 if(pref == supported_fmt){
493 return pref;
494 }
495 }
496 }
497
498 return 0;
499}
500
501static void map_new_buffer(struct video_frame *f){
502 glBufferData(GL_PIXEL_UNPACK_BUFFER, f->tiles[0].data_len, 0, GL_STREAM_DRAW);
503 f->tiles[0].data = (char *) glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
504 GLenum ret = glGetError();
505 if(ret != GL_NO_ERROR){
506 std::cerr << "Error mapping: " << ret << std::endl;
507 }
508 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
509}
510
512 GlBuffer *pbo = static_cast<GlBuffer *>(f->callbacks.dispose_udata);
513 if(!pbo){
514 return;
515 }
516 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo->get());
517 if(f->tiles[0].data){
518 glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
519 }
521}
522
523static void delete_frame(video_frame *f){
524 GlBuffer *pbo = static_cast<GlBuffer *>(f->callbacks.dispose_udata);
525 vf_free(f);
526
527 if(!pbo){
528 return;
529 }
530 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo->get());
531 if(f->tiles[0].data){
532 glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
533 }
534
535 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
536 delete pbo;
537}
538
541 GlBuffer *pbo = new GlBuffer();
542 buffer->callbacks.dispose_udata = pbo;
543 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo->get());
544
545 map_new_buffer(buffer);
546
547 return buffer;
548}
549
550static void worker(state_xrgl *s){
553 std::unique_lock<std::mutex> lk(s->lock);
554 for(size_t i = 0; i < MAX_BUFFER_SIZE; i++){
555 video_frame *buf = vf_alloc(1);
556 GlBuffer *pbo = new GlBuffer();
557 buf->callbacks.dispose_udata = pbo;
558 s->free_frame_pool.push_back(buf);
559 }
560 lk.unlock();
561 s->free_frame_ready_cv.notify_all();
562
563 bool running = true;
564 lk.lock();
565 while(running){
566 PROFILE_DETAIL("Wait for frame");
567 s->new_frame_ready_cv.wait(lk,
568 [s]{
569 return !s->frame_queue.empty() || !s->dispose_frame_pool.empty();
570 });
571 if(!s->dispose_frame_pool.empty()){
572 PROFILE_DETAIL("Process disposed frames");
573 for(video_frame *f : s->dispose_frame_pool){
575 recycle_frame(f);
576 s->free_frame_pool.push_back(f);
577 } else {
578 delete_frame(f);
579
580 struct video_frame *buffer = allocate_frame(s);
581 s->free_frame_pool.push_back(buffer);
582 }
583 }
584 s->dispose_frame_pool.clear();
585 s->free_frame_ready_cv.notify_all();
586 }
587
588 if(!s->frame_queue.empty()){
589 PROFILE_DETAIL("put_frame");
590 video_frame *frame = s->frame_queue.front();
591 s->frame_queue.pop();
592 lk.unlock();
593 if(!frame){
594 SDL_Event event;
595 event.type = SDL_QUIT;
596 SDL_PushEvent(&event);
597 running = false;
598 } else {
599 s->frame_consumed_cv.notify_one();
601
602 lk.lock();
603 s->dispose_frame_pool.push_back(frame);
604 }
605 }
606 }
607}
608
609static void display_xrgl_run(void *state){
611
612 state_xrgl *s = static_cast<state_xrgl *>(state);
613
614 //Make sure the worker context is initialized before starting worker
617
618 std::thread worker_thread(worker, s);
619
620#ifdef __linux__
621 XrGraphicsBindingOpenGLXlibKHR graphics_binding_gl = {};
622 graphics_binding_gl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR;
623 graphics_binding_gl.xDisplay = nullptr;
624
625 s->window.getXlibHandles(&graphics_binding_gl.xDisplay,
626 &graphics_binding_gl.glxContext,
627 &graphics_binding_gl.glxDrawable);
628
629 Openxr_session session(s->xr_state.instance.get(),
631 &graphics_binding_gl);
632#elif defined _WIN32
633 XrGraphicsBindingOpenGLWin32KHR graphics_binding_gl = {};
634 graphics_binding_gl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR;
635 graphics_binding_gl.next = nullptr;
636
638
639 graphics_binding_gl.hDC = wglGetCurrentDC();
640 graphics_binding_gl.hGLRC = wglGetCurrentContext();
641
642 Openxr_session session(s->xr_state.instance.get(),
644 &graphics_binding_gl);
645
646#else
647 //TODO: Implement GL OpenXR binding for other platforms
648 log_msg(LOG_LEVEL_ERROR, "OpenXR and OpenGL binding not implemented on this platform!\n");
649 return;
650#endif // defined __linux__
651
652 std::vector<XrViewConfigurationView> config_views = get_views(s->xr_state);
653
654 Openxr_local_space space(session.get());
655
656 session.begin();
657
658 std::vector<int64_t> swapchain_fmts = get_swapchain_formats(session.get());
659
660 int64_t selected_swapchain_fmt = select_swapchain_fmt(swapchain_fmts);
661
662 std::vector<Gl_interop_swapchain> swapchains;
663 for(const auto& view : config_views){
664 swapchains.emplace_back(session.get(),
665 selected_swapchain_fmt,
666 view.recommendedImageRectWidth,
667 view.recommendedImageRectHeight);
668 }
669
670 size_t view_count = config_views.size();
671 std::vector<XrCompositionLayerProjectionView> projection_views(view_count);
672
673
674 XrCompositionLayerProjection projection_layer;
675 projection_layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION;
676 projection_layer.next = nullptr;
677 projection_layer.layerFlags = 0;
678 projection_layer.space = space.get();
679 projection_layer.viewCount = view_count;
680 projection_layer.views = projection_views.data();
681
682 std::vector<XrView> views(view_count);
683
684 for(auto& view : views){
685 view.type = XR_TYPE_VIEW;
686 view.next = nullptr;
687 }
688
689 for(unsigned i = 0; i < view_count; i++){
690 projection_views[i].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
691 projection_views[i].next = nullptr;
692 projection_views[i].subImage.swapchain = swapchains[i].get();
693 projection_views[i].subImage.imageArrayIndex = 0;
694 projection_views[i].subImage.imageRect.offset.x = 0;
695 projection_views[i].subImage.imageRect.offset.y = 0;
696 projection_views[i].subImage.imageRect.extent.width =
697 config_views[i].recommendedImageRectWidth;
698 projection_views[i].subImage.imageRect.extent.height =
699 config_views[i].recommendedImageRectHeight;
700
701 }
702
703 bool running = true;
704 if(selected_swapchain_fmt == GL_SRGB8_ALPHA8_EXT){
705 /* Convert to sRGB to correctly display on the HMD.
706 * This however breaks the preview window colors, because
707 * the framebuffer is directly copied into preview window
708 * using glBlitNamedFramebuffer
709 */
710 glEnable(GL_FRAMEBUFFER_SRGB);
711 }
712
713 glm::mat4 view_reset_rot = glm::mat4(1.0f);
714
715 while(running){
716 XrResult result;
717
718 XrFrameState frame_state{};
719 frame_state.type = XR_TYPE_FRAME_STATE;
720 frame_state.next = nullptr;
721
722 XrFrameWaitInfo frame_wait_info{};
723 frame_wait_info.type = XR_TYPE_FRAME_WAIT_INFO;
724 frame_wait_info.next = nullptr;
725
726 PROFILE_DETAIL("wait frame");
727 result = xrWaitFrame(session.get(), &frame_wait_info, &frame_state);
728 if (!XR_SUCCEEDED(result)){
729 log_msg(LOG_LEVEL_ERROR, "Failed to xrWaitFrame\n");
730 break;
731 }
732
733 bool reset_view = false;
734
735 SDL_Event event;
736 while(SDL_PollEvent(&event)){
737 switch(event.type){
738 case SDL_QUIT:
739 running = false;
740 break;
741 case SDL_WINDOWEVENT:
742 if(event.window.event == SDL_WINDOWEVENT_RESIZED){
743 s->window.width = event.window.data1;
744 s->window.height = event.window.data2;
745 }
746 break;
747 case SDL_KEYDOWN:
748 case SDL_KEYUP:
749 switch(event.key.keysym.sym){
750 case SDLK_q:
751 running = false;
752 break;
753 case SDLK_v:
754 if(event.type == SDL_KEYUP){
755 reset_view = true;
756 }
757 break;
758 }
759 break;
760 default:
761 break;
762 }
763 }
764
765 while(true){
766 XrEventDataBuffer xr_event{};
767 xr_event.type = XR_TYPE_EVENT_DATA_BUFFER;
768 xr_event.next = nullptr;
769
770 result = xrPollEvent(s->xr_state.instance.get(), &xr_event);
771 if(result != XR_SUCCESS){
772 break;
773 }
774
775 switch(xr_event.type){
776 case XR_TYPE_EVENT_DATA_EVENTS_LOST:
777 log_msg(LOG_LEVEL_WARNING, "OpenXR events lost!\n");
778 break;
779 case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING:
780 log_msg(LOG_LEVEL_WARNING, "OpenXR instance loss pending!\n");
781 running = false;
782 break;
783 case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED:
784 {
785 XrEventDataSessionStateChanged* event = (XrEventDataSessionStateChanged*) &xr_event;
786 if(event->state >= XR_SESSION_STATE_STOPPING){
787 log_msg(LOG_LEVEL_NOTICE, "Received event requesting stop\n");
788 running = false;
789 }
790 break;
791 }
792 default:
793 log_msg(LOG_LEVEL_NOTICE, "Unhandled event %d\n", xr_event.type);
794 break;
795 }
796
797 }
798
799 XrViewLocateInfo view_locate_info;
800 view_locate_info.type = XR_TYPE_VIEW_LOCATE_INFO;
801 view_locate_info.displayTime = frame_state.predictedDisplayTime;
802 view_locate_info.viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; //Assuming stereo view exists. TODO: do it properly and enumerate types first
803 view_locate_info.space = space.get();
804
805 XrViewState view_state;
806 view_state.type = XR_TYPE_VIEW_STATE;
807 view_state.next = nullptr;
808
809 uint32_t located_views = 0;
810 result = xrLocateViews(session.get(),
811 &view_locate_info,
812 &view_state,
813 view_count,
814 &located_views,
815 views.data());
816
817 if (!XR_SUCCEEDED(result)){
818 log_msg(LOG_LEVEL_ERROR, "Failed to locate views!\n");
819 break;
820 }
821
822 /*
823 printf("View: %f %f %f %f, %f %f %f, fov = %f %f %f %f\n",
824 views[1].pose.orientation.x,
825 views[1].pose.orientation.y,
826 views[1].pose.orientation.z,
827 views[1].pose.orientation.w,
828 views[1].pose.position.x,
829 views[1].pose.position.y,
830 views[1].pose.position.z,
831 views[1].fov.angleLeft,
832 views[1].fov.angleRight,
833 views[1].fov.angleUp,
834 views[1].fov.angleDown);
835 */
836
837 XrFrameBeginInfo frame_begin_info;
838 frame_begin_info.type = XR_TYPE_FRAME_BEGIN_INFO;
839 frame_begin_info.next = nullptr;
840
841 PROFILE_DETAIL("begin frame");
842 result = xrBeginFrame(session.get(), &frame_begin_info);
843 if (!XR_SUCCEEDED(result)){
844 log_msg(LOG_LEVEL_ERROR, "Failed to begin frame!\n");
845 break;
846 }
847
848 for(unsigned i = 0; i < view_count; i++){
849 PROFILE_DETAIL("render view");
850
851 XrSwapchainImageAcquireInfo swapchain_image_acquire_info;
852 swapchain_image_acquire_info.type = XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO;
853 swapchain_image_acquire_info.next = nullptr;
854
855 uint32_t buf_idx;
856 result = xrAcquireSwapchainImage(
857 swapchains[i].get(),
858 &swapchain_image_acquire_info,
859 &buf_idx);
860
861 if(!XR_SUCCEEDED(result)){
862 log_msg(LOG_LEVEL_ERROR, "Failed to acquire swapchain image!\n");
863 break;
864 }
865
866 XrSwapchainImageWaitInfo swapchain_image_wait_info;
867 swapchain_image_wait_info.type = XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO;
868 swapchain_image_wait_info.next = nullptr;
869 swapchain_image_wait_info.timeout = 1000;
870
871 result = xrWaitSwapchainImage(swapchains[i].get(), &swapchain_image_wait_info);
872 if(!XR_SUCCEEDED(result)){
873 log_msg(LOG_LEVEL_ERROR, "failed to wait for swapchain image!\n");
874 break;
875 }
876
877 projection_views[i].pose = views[i].pose;
878 projection_views[i].fov = views[i].fov;
879 unsigned w = config_views[i].recommendedImageRectWidth;
880 unsigned h = config_views[i].recommendedImageRectHeight;
881
882 glm::mat4 projMat = get_proj_mat(views[i].fov, 0.05f, 100.f);
883 //glm::mat4 projMat = glm::perspective(glm::radians(70.f), (float) w /h, 0.1f, 300.f);
884 const auto& rot = views[i].pose.orientation;
885 glm::mat4 viewMat = glm::mat4_cast(glm::quat(rot.w, rot.x, rot.y, rot.z));
886 if(reset_view){
887 reset_view = false;
888 view_reset_rot = viewMat;
889 }
890 glm::mat4 pvMat = projMat * glm::inverse(viewMat) * view_reset_rot;
891
892 auto framebuffer = swapchains[i].get_framebuffer(buf_idx).get();
893 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
894 glClear(GL_COLOR_BUFFER_BIT);
895
896 s->scene.render(w, h, pvMat);
897
898 glBindFramebuffer(GL_FRAMEBUFFER, 0);
899
900 if(i % 2){
901 int dstX0;
902 int dstY0;
903 int dstX1;
904 int dstY1;
905 if(w * s->window.height > s->window.width * h){
906 dstX0 = 0;
907 dstX1 = s->window.width;
908
909 int height = (s->window.width * h) / w;
910 dstY0 = (s->window.height - height) / 2;
911 dstY1 = dstY0 + height;
912 } else {
913 int width = (s->window.height * w) / h;
914 dstX0 = (s->window.width - width) / 2;
915 dstX1 = dstX0 + width;
916
917 dstY0 = 0;
918 dstY1 = s->window.height;
919
920 }
921 glClear(GL_COLOR_BUFFER_BIT);
922 glBlitNamedFramebuffer((GLuint)framebuffer, // readFramebuffer
923 (GLuint)0, // backbuffer // drawFramebuffer
924 (GLint)0, // srcX0
925 (GLint)0, // srcY0
926 (GLint)w, // srcX1
927 (GLint)h, // srcY1
928 (GLint)dstX0, // dstX0
929 (GLint)dstY0, // dstY0
930 (GLint)dstX1, // dstX1
931 (GLint)dstY1, // dstY1
932 (GLbitfield)GL_COLOR_BUFFER_BIT, // mask
933 (GLenum)GL_LINEAR); // filter
934
935 SDL_GL_SwapWindow(s->window.sdl_window);
936 }
937
938 PROFILE_DETAIL("glFinish");
939 glFinish();
940
941 PROFILE_DETAIL("release swapchain");
942 XrSwapchainImageReleaseInfo swapchain_image_release_info;
943 swapchain_image_release_info.type = XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO;
944 swapchain_image_release_info.next = nullptr;
945
946 result = xrReleaseSwapchainImage(
947 swapchains[i].get(),
948 &swapchain_image_release_info);
949
950 if (!XR_SUCCEEDED(result)){
951 log_msg(LOG_LEVEL_ERROR, "Failed to release swapchain image!\n");
952 break;
953 }
954 }
955
956 const XrCompositionLayerBaseHeader *composition_layers = (const XrCompositionLayerBaseHeader *) &projection_layer;
957 XrFrameEndInfo frame_end_info;
958 frame_end_info.type = XR_TYPE_FRAME_END_INFO;
959 frame_end_info.displayTime = frame_state.predictedDisplayTime;
960 frame_end_info.layerCount = 1;
961 frame_end_info.layers = &composition_layers;
962 frame_end_info.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
963 frame_end_info.next = nullptr;
964
965 PROFILE_DETAIL("End Frame");
966 result = xrEndFrame(session.get(), &frame_end_info);
967 if (!XR_SUCCEEDED(result)){
968 log_msg(LOG_LEVEL_ERROR, "Failed to end frame!\n");
969 break;
970 }
971
972 }
973
974 exit_uv(0);
975 worker_thread.join();
976}
977
978static void * display_xrgl_init(struct module *parent, const char *fmt, unsigned int flags) {
979 UNUSED(parent);
980 UNUSED(fmt);
981 UNUSED(flags);
982 state_xrgl *s = new state_xrgl();
983
984 XrSystemGetInfo system_get_info;
985 system_get_info.type = XR_TYPE_SYSTEM_GET_INFO;
986 system_get_info.next = nullptr;
987 system_get_info.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
988
989 XrResult result = xrGetSystem(s->xr_state.instance.get(),
990 &system_get_info,
991 &s->xr_state.system_id);
992
993 if(!XR_SUCCEEDED(result)){
994 throw std::runtime_error("No available device found!");
995 }
996
999
1000 return s;
1001}
1002
1003static void display_xrgl_done(void *state) {
1004 state_xrgl *s = static_cast<state_xrgl *>(state);
1005
1006 delete s;
1007}
1008
1009static struct video_frame * display_xrgl_getf(void *state) {
1010 struct state_xrgl *s = static_cast<state_xrgl *>(state);
1011
1012 std::unique_lock<std::mutex> lock(s->lock);
1013
1014 while (true) {
1015 s->free_frame_ready_cv.wait(lock, [s]{return s->free_frame_pool.size() > 0;});
1016 struct video_frame *buffer = s->free_frame_pool.back();
1017 s->free_frame_pool.pop_back();
1019 return buffer;
1020 } else {
1021 s->dispose_frame_pool.push_back(buffer);
1022 s->new_frame_ready_cv.notify_one();
1023 }
1024 }
1025}
1026
1027static bool display_xrgl_putf(void *state, struct video_frame *frame, long long nonblock) {
1028 struct state_xrgl *s = static_cast<state_xrgl *>(state);
1029
1030 std::unique_lock<std::mutex> lk(s->lock);
1031
1032 if(!frame) {
1033 s->frame_queue.push(frame);
1034 lk.unlock();
1035 s->new_frame_ready_cv.notify_one();
1036 return true;
1037 }
1038
1039 if (nonblock == PUTF_DISCARD) {
1040 s->dispose_frame_pool.push_back(frame);
1041 s->new_frame_ready_cv.notify_one();
1042 return true;
1043 }
1044 if (s->frame_queue.size() >= MAX_BUFFER_SIZE && nonblock != PUTF_BLOCKING) {
1045 s->dispose_frame_pool.push_back(frame);
1046 s->new_frame_ready_cv.notify_one();
1047 return false;
1048 }
1049 s->frame_consumed_cv.wait(lk, [s]{return s->frame_queue.size() < MAX_BUFFER_SIZE;});
1050 s->frame_queue.push(frame);
1051
1052 lk.unlock();
1053 s->new_frame_ready_cv.notify_one();
1054
1055 return true;
1056}
1057
1058static bool display_xrgl_reconfigure(void *state, struct video_desc desc) {
1059 state_xrgl *s = static_cast<state_xrgl *>(state);
1060
1061 s->current_desc = desc;
1062 return true;
1063}
1064
1065static bool display_xrgl_get_property(void *state, int property, void *val, size_t *len) {
1066 auto s = static_cast<state_xrgl *>(state);
1067
1068 enum interlacing_t supported_il_modes[] = {PROGRESSIVE};
1069 int rgb_shift[] = {0, 8, 16};
1070
1071 switch (property) {
1073 auto codecs = s->scene.get_codecs();
1074 size_t codecs_size = codecs.size() * sizeof(codec_t);
1075 if(codecs_size <= *len) {
1076 memcpy(val, codecs.data(), codecs_size);
1077 } else {
1078 return false;
1079 }
1080
1081 *len = codecs_size;
1082 break;
1083 }
1085 if(sizeof(rgb_shift) > *len) {
1086 return false;
1087 }
1088 memcpy(val, rgb_shift, sizeof(rgb_shift));
1089 *len = sizeof(rgb_shift);
1090 break;
1092 *(int *) val = PITCH_DEFAULT;
1093 *len = sizeof(int);
1094 break;
1096 if(sizeof(supported_il_modes) <= *len) {
1097 memcpy(val, supported_il_modes, sizeof(supported_il_modes));
1098 } else {
1099 return false;
1100 }
1101 *len = sizeof(supported_il_modes);
1102 break;
1103 default:
1104 return false;
1105 }
1106 return true;
1107}
1108
1109static void display_xrgl_probe(struct device_info **available_cards, int *count, void (**deleter)(void *)){
1110 UNUSED(deleter);
1111 *count = 0;
1112 *available_cards = nullptr;
1113
1114 XrSystemGetInfo systemGetInfo;
1115 systemGetInfo.type = XR_TYPE_SYSTEM_GET_INFO;
1116 systemGetInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
1117 systemGetInfo.next = NULL;
1118
1119 XrSystemId systemId;
1120
1121 XrSystemProperties systemProperties;
1122 systemProperties.type = XR_TYPE_SYSTEM_PROPERTIES;
1123 systemProperties.next = NULL;
1124 systemProperties.graphicsProperties = {};
1125 systemProperties.trackingProperties = {};
1126
1127 try{
1129
1130 XrResult result = xrGetSystem(instance.get(), &systemGetInfo, &systemId);
1131 if(!XR_SUCCEEDED(result)){
1132 return;
1133 }
1134
1135 result = xrGetSystemProperties(instance.get(), systemId, &systemProperties);
1136 if(!XR_SUCCEEDED(result)){
1137 return;
1138 }
1139 } catch(const std::exception& e){
1140 std::cout << e.what() << std::endl;
1141 return;
1142 }
1143
1144 *available_cards = (struct device_info *) calloc(1, sizeof(struct device_info));
1145 *count = 1;
1146 snprintf((*available_cards)[0].dev, sizeof((*available_cards)[0].dev), ":system=%lu", systemId);
1147 snprintf((*available_cards)[0].name, sizeof((*available_cards)[0].name), "OpenXr: %s", systemProperties.systemName);
1148 (*available_cards)[0].repeatable = false;
1149}
1150
1151
1165
#define UNUSED(x)
void ** state
struct video_frame long long nonblock
int void * val
struct video_desc desc
struct video_frame * frame
const char * fmt
int void size_t * len
const char unsigned int flags
int void(** deleter)(void *))
int property
struct video_frame * vf_alloc(int count)
Allocates blank video frame.
void vf_free(struct video_frame *buf)
Frees video_frame structure.
struct video_desc video_desc_from_frame(const struct video_frame *frame)
Returns struct video_desc from video frame.
struct video_frame * vf_alloc_desc(struct video_desc desc)
Allocates video frame accordig given desc.
std::vector< XrSwapchainImageOpenGLKHR > images
std::vector< Framebuffer > framebuffers
XrSwapchain get()
Gl_interop_swapchain(Gl_interop_swapchain &&)=default
Gl_interop_swapchain(const Gl_interop_swapchain &)=delete
Gl_interop_swapchain & operator=(const Gl_interop_swapchain &)=delete
GLuint get_texture(size_t idx)
Framebuffer & get_framebuffer(size_t idx)
Openxr_swapchain xr_swapchain
Gl_interop_swapchain & operator=(Gl_interop_swapchain &&)=default
Gl_interop_swapchain(XrSession session, const XrSwapchainCreateInfo *info)
Gl_interop_swapchain(XrSession session, int64_t swapchain_format, uint32_t w, uint32_t h)
GLuint get()
XrInstance instance
XrInstance get()
Openxr_instance & operator=(Openxr_instance &&)=delete
Openxr_instance(const Openxr_instance &)=delete
Openxr_instance(Openxr_instance &&)=delete
Openxr_instance & operator=(const Openxr_instance &)=delete
Openxr_local_space(XrSession session)
Openxr_local_space(Openxr_local_space &&)=delete
Openxr_local_space & operator=(const Openxr_local_space &)=delete
Openxr_local_space(const Openxr_local_space &)=delete
Openxr_local_space & operator=(Openxr_local_space &&)=delete
XrSession get()
Openxr_session(XrInstance instance, XrSystemId systemId, const void *xrGraphicsBindingOpenGL)
Definition openxr_gl.cpp:87
XrSession session
Openxr_swapchain(XrSession session, const XrSwapchainCreateInfo *info)
Openxr_swapchain & operator=(Openxr_swapchain &&o)
XrSession session
XrSwapchain swapchain
uint32_t get_length()
Openxr_swapchain(XrSession session, int64_t swapchain_format, uint32_t w, uint32_t h)
XrSwapchain get()
Openxr_swapchain(const Openxr_swapchain &)=delete
Openxr_swapchain & operator=(const Openxr_swapchain &)=delete
void swap(Openxr_swapchain &o) noexcept
Openxr_swapchain()=default
Openxr_swapchain(Openxr_swapchain &&o) noexcept
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_NOTICE
information that may be interesting
Definition debug.h:61
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
static void * worker_thread(void *arg)
#define PITCH_DEFAULT
default pitch, i. e. respective linesize
#define PUTF_DISCARD
#define PUTF_BLOCKING
#define VIDEO_DISPLAY_ABI_VERSION
#define DISPLAY_NO_GENERIC_FPS_INDICATOR
@ DISPLAY_PROPERTY_BUF_PITCH
requested framebuffer pitch - int (bytes), may be PITCH_DEFAULT
@ DISPLAY_PROPERTY_RGB_SHIFT
red,green,blue shift - int[3] (bits)
@ DISPLAY_PROPERTY_SUPPORTED_IL_MODES
display supported interlacing modes - enum interlacing_t[]
@ DISPLAY_PROPERTY_CODECS
list of natively supported codecs - codec_t[]
int count
Definition hd-rum.c:44
void exit_uv(int status)
Definition host.cpp:625
@ LIBRARY_CLASS_VIDEO_DISPLAY
Definition lib_common.h:79
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
Definition lib_common.h:144
static void display_xrgl_done(void *state)
static const struct video_display_info openxr_gl_info
static struct video_frame * display_xrgl_getf(void *state)
static void display_xrgl_run(void *state)
static bool display_xrgl_get_property(void *state, int property, void *val, size_t *len)
static bool display_xrgl_reconfigure(void *state, struct video_desc desc)
static int64_t select_swapchain_fmt(const std::vector< int64_t > &swapchain_formats)
static void worker(state_xrgl *s)
static void recycle_frame(video_frame *f)
static glm::mat4 get_proj_mat(const XrFovf &fov, float zNear, float zFar)
static void map_new_buffer(struct video_frame *f)
#define MAX_BUFFER_SIZE
Definition openxr_gl.cpp:83
static video_frame * allocate_frame(state_xrgl *s)
static void delete_frame(video_frame *f)
static void * display_xrgl_init(struct module *parent, const char *fmt, unsigned int flags)
static void display_xrgl_probe(struct device_info **available_cards, int *count, void(**deleter)(void *))
static std::vector< XrViewConfigurationView > get_views(Openxr_state &xr_state)
static std::vector< int64_t > get_swapchain_formats(XrSession session)
static bool display_xrgl_putf(void *state, struct video_frame *frame, long long nonblock)
#define PROFILE_FUNC
#define PROFILE_DETAIL(name)
const char * parent
Definition settings.cpp:198
const char * name
Definition settings.cpp:193
XrSystemId system_id
Openxr_instance instance
void render(int width, int height)
void put_frame(video_frame *f, bool pbo_frame=false)
void make_render_context_current()
void make_worker_context_current()
SDL_Window * sdl_window
char dev[1024]
Definition types.h:383
std::mutex lock
Openxr_state xr_state
std::condition_variable frame_consumed_cv
std::vector< video_frame * > free_frame_pool
std::condition_variable free_frame_ready_cv
int buffered_frames_count
std::vector< video_frame * > dispose_frame_pool
PanoramaScene scene
std::condition_variable new_frame_ready_cv
std::chrono::steady_clock::time_point last_frame
Sdl_window window
std::queue< video_frame * > frame_queue
video_desc current_desc
char * data
Raw tile data. Pointer must be at least 4B aligned.
Definition types.h:287
unsigned int data_len
length of the data
Definition types.h:288
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
struct tile tiles[FLEXIBLE_ARRAY_MEMBER]
Definition types.h:345
struct video_frame_callbacks callbacks
Definition types.h:324
interlacing_t
Definition types.h:163
@ PROGRESSIVE
progressive frame
Definition types.h:164
codec_t
Definition types.h:65
This is an umbrella header for video functions.
static struct @76 codecs[]
bool video_desc_eq(struct video_desc a, struct video_desc b)
Compares two video descriptions.
static class worker_pool instance
Definition worker.cpp:250