Blender V4.5
GHOST_XrGraphicsBinding.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10#include <list>
11#include <sstream>
12
13#if defined(WITH_GHOST_X11)
14# include "GHOST_ContextEGL.hh"
15# include "GHOST_ContextGLX.hh"
16# include "GHOST_SystemX11.hh"
17#endif
18#if defined(WITH_GHOST_WAYLAND)
19# include "GHOST_ContextEGL.hh"
20# include "GHOST_SystemWayland.hh"
21#endif
22#if defined(WIN32)
23# include "GHOST_ContextD3D.hh"
24# include "GHOST_ContextWGL.hh"
25# include "GHOST_SystemWin32.hh"
27#endif
28#ifdef WITH_VULKAN_BACKEND
30#endif
31
32#include "GHOST_C-api.h"
33#include "GHOST_XrException.hh"
34#include "GHOST_Xr_intern.hh"
35
37
38static std::optional<int64_t> choose_swapchain_format_from_candidates(
39 const std::vector<int64_t> &gpu_binding_formats, const std::vector<int64_t> &runtime_formats)
40{
41 if (gpu_binding_formats.empty()) {
42 return std::nullopt;
43 }
44
45 auto res = std::find_first_of(gpu_binding_formats.begin(),
46 gpu_binding_formats.end(),
47 runtime_formats.begin(),
48 runtime_formats.end());
49 if (res == gpu_binding_formats.end()) {
50 return std::nullopt;
51 }
52
53 return *res;
54}
55
57 public:
59 {
60 if (m_fbo != 0) {
61 glDeleteFramebuffers(1, &m_fbo);
62 }
63 }
64
66 XrInstance instance,
67 XrSystemId system_id,
68 std::string *r_requirement_info) const override
69 {
70 int gl_major_version, gl_minor_version;
71#if defined(WIN32)
72 GHOST_ContextWGL &ctx_gl = static_cast<GHOST_ContextWGL &>(ghost_ctx);
73 gl_major_version = ctx_gl.m_contextMajorVersion;
74 gl_minor_version = ctx_gl.m_contextMinorVersion;
75#elif defined(WITH_GHOST_X11) || defined(WITH_GHOST_WAYLAND)
76 if (dynamic_cast<GHOST_ContextEGL *>(&ghost_ctx)) {
77 GHOST_ContextEGL &ctx_gl = static_cast<GHOST_ContextEGL &>(ghost_ctx);
78 gl_major_version = ctx_gl.m_contextMajorVersion;
79 gl_minor_version = ctx_gl.m_contextMinorVersion;
80 }
81# if defined(WITH_GHOST_X11)
82 else {
83 GHOST_ContextGLX &ctx_gl = static_cast<GHOST_ContextGLX &>(ghost_ctx);
84 gl_major_version = ctx_gl.m_contextMajorVersion;
85 gl_minor_version = ctx_gl.m_contextMinorVersion;
86 }
87# endif
88#endif
89 static PFN_xrGetOpenGLGraphicsRequirementsKHR s_xrGetOpenGLGraphicsRequirementsKHR_fn =
90 nullptr;
91 // static XrInstance s_instance = XR_NULL_HANDLE;
92 XrGraphicsRequirementsOpenGLKHR gpu_requirements = {XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR};
93 const XrVersion gl_version = XR_MAKE_VERSION(gl_major_version, gl_minor_version, 0);
94
95 /* Although it would seem reasonable that the PROC address would not change if the instance was
96 * the same, in testing, repeated calls to #xrGetInstanceProcAddress() with the same instance
97 * can still result in changes so the workaround is to simply set the function pointer every
98 * time (trivializing its 'static' designation). */
99 // if (instance != s_instance) {
100 // s_instance = instance;
101 s_xrGetOpenGLGraphicsRequirementsKHR_fn = nullptr;
102 //}
103 if (!s_xrGetOpenGLGraphicsRequirementsKHR_fn &&
104 XR_FAILED(
105 xrGetInstanceProcAddr(instance,
106 "xrGetOpenGLGraphicsRequirementsKHR",
107 (PFN_xrVoidFunction *)&s_xrGetOpenGLGraphicsRequirementsKHR_fn)))
108 {
109 s_xrGetOpenGLGraphicsRequirementsKHR_fn = nullptr;
110 return false;
111 }
112
113 s_xrGetOpenGLGraphicsRequirementsKHR_fn(instance, system_id, &gpu_requirements);
114
115 if (r_requirement_info) {
116 std::ostringstream strstream;
117 strstream << "Min OpenGL version "
118 << XR_VERSION_MAJOR(gpu_requirements.minApiVersionSupported) << "."
119 << XR_VERSION_MINOR(gpu_requirements.minApiVersionSupported) << std::endl;
120 strstream << "Max OpenGL version "
121 << XR_VERSION_MAJOR(gpu_requirements.maxApiVersionSupported) << "."
122 << XR_VERSION_MINOR(gpu_requirements.maxApiVersionSupported) << std::endl;
123
124 *r_requirement_info = strstream.str();
125 }
126
127 return (gl_version >= gpu_requirements.minApiVersionSupported) &&
128 (gl_version <= gpu_requirements.maxApiVersionSupported);
129 }
130
132 XrInstance /*instance*/,
133 XrSystemId /*system_id*/) override
134 {
135#if defined(WITH_GHOST_X11) || defined(WITH_GHOST_WAYLAND)
136 /* WAYLAND/X11 may be dynamically selected at load time but both may also be
137 * supported at compile time individually.
138 * Without `is_ctx_egl` & `is_wayland` preprocessor checks become an unmanageable soup. */
139 const bool is_ctx_egl = dynamic_cast<GHOST_ContextEGL *>(&ghost_ctx) != nullptr;
140 if (is_ctx_egl) {
141 GHOST_ContextEGL &ctx_egl = static_cast<GHOST_ContextEGL &>(ghost_ctx);
142 const bool is_wayland = (
143# if defined(WITH_GHOST_WAYLAND)
144 dynamic_cast<const GHOST_SystemWayland *const>(ctx_egl.m_system) != nullptr
145# else
146 false
147# endif
148 );
149
150 if (is_wayland) {
151# if defined(WITH_GHOST_WAYLAND)
152 /* #GHOST_SystemWayland */
153 oxr_binding.wl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WAYLAND_KHR;
154 oxr_binding.wl.display = (wl_display *)ctx_egl.m_nativeDisplay;
155# else
156 GHOST_ASSERT(false, "Unexpected State: logical error, unreachable!");
157# endif /* !WITH_GHOST_WAYLAND */
158 }
159 else { /* `!is_wayland` */
160# if defined(WITH_GHOST_X11)
161 /* #GHOST_SystemX11. */
162 oxr_binding.egl.type = XR_TYPE_GRAPHICS_BINDING_EGL_MNDX;
163# if XR_CURRENT_API_VERSION >= XR_MAKE_VERSION(1, 0, 29)
164 oxr_binding.egl.getProcAddress = reinterpret_cast<PFN_xrEglGetProcAddressMNDX>(
165 eglGetProcAddress);
166# else
167 oxr_binding.egl.getProcAddress = reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(
168 eglGetProcAddress);
169# endif
170 oxr_binding.egl.display = ctx_egl.getDisplay();
171 oxr_binding.egl.config = ctx_egl.getConfig();
172 oxr_binding.egl.context = ctx_egl.getContext();
173# else
174 GHOST_ASSERT(false, "Unexpected State: built with only WAYLAND and no System found!");
175# endif /* !WITH_GHOST_X11 */
176 }
177 }
178 else { /* `!is_ctx_egl` */
179# if defined(WITH_GHOST_X11)
180 GHOST_ContextGLX &ctx_glx = static_cast<GHOST_ContextGLX &>(ghost_ctx);
181 XVisualInfo *visual_info = glXGetVisualFromFBConfig(ctx_glx.m_display, ctx_glx.m_fbconfig);
182
183 oxr_binding.glx.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR;
184 oxr_binding.glx.xDisplay = ctx_glx.m_display;
185 oxr_binding.glx.glxFBConfig = ctx_glx.m_fbconfig;
186 oxr_binding.glx.glxDrawable = ctx_glx.m_window;
187 oxr_binding.glx.glxContext = ctx_glx.m_context;
188 oxr_binding.glx.visualid = visual_info->visualid;
189
190 XFree(visual_info);
191# else
192 GHOST_ASSERT(false, "Unexpected State: built without X11 and no EGL context is available!");
193# endif /* !WITH_GHOST_X11 */
194 }
195#elif defined(WIN32)
196 GHOST_ContextWGL &ctx_wgl = static_cast<GHOST_ContextWGL &>(ghost_ctx);
197
198 oxr_binding.wgl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR;
199 oxr_binding.wgl.hDC = ctx_wgl.m_hDC;
200 oxr_binding.wgl.hGLRC = ctx_wgl.m_hGLRC;
201#endif /* WIN32 */
202
203 /* Generate a frame-buffer to use for blitting into the texture. */
204 glGenFramebuffers(1, &m_fbo);
205 }
206
207 std::optional<int64_t> chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
208 GHOST_TXrSwapchainFormat &r_format,
209 bool &r_is_srgb_format) const override
210 {
211 std::vector<int64_t> gpu_binding_formats = {
212#if 0 /* RGB10A2, RGBA16 don't seem to work with Oculus head-sets, \
213 * so move them after RGBA16F for the time being. */
214 GL_RGB10_A2,
215 GL_RGBA16,
216#endif
217 GL_RGBA16F,
218#if 1
219 GL_RGB10_A2,
220 GL_RGBA16,
221#endif
222 GL_RGBA8,
223 GL_SRGB8_ALPHA8,
224 };
225
226 std::optional result = choose_swapchain_format_from_candidates(gpu_binding_formats,
227 runtime_formats);
228 if (result) {
229 switch (*result) {
230 case GL_RGB10_A2:
231 r_format = GHOST_kXrSwapchainFormatRGB10_A2;
232 break;
233 case GL_RGBA16:
234 r_format = GHOST_kXrSwapchainFormatRGBA16;
235 break;
236 case GL_RGBA16F:
237 r_format = GHOST_kXrSwapchainFormatRGBA16F;
238 break;
239 case GL_RGBA8:
240 case GL_SRGB8_ALPHA8:
241 r_format = GHOST_kXrSwapchainFormatRGBA8;
242 break;
243 }
244 r_is_srgb_format = (*result == GL_SRGB8_ALPHA8);
245 }
246 else {
247 r_format = GHOST_kXrSwapchainFormatRGBA8;
248 r_is_srgb_format = false;
249 }
250
251 return result;
252 }
253
254 std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(uint32_t image_count) override
255 {
256 std::vector<XrSwapchainImageOpenGLKHR> ogl_images(image_count);
257 std::vector<XrSwapchainImageBaseHeader *> base_images;
258
259 /* Need to return vector of base header pointers, so of a different type. Need to build a new
260 * list with this type, and keep the initial one alive. */
261 for (XrSwapchainImageOpenGLKHR &image : ogl_images) {
262 image.type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR;
263 base_images.push_back(reinterpret_cast<XrSwapchainImageBaseHeader *>(&image));
264 }
265
266 /* Keep alive. */
267 m_image_cache.push_back(std::move(ogl_images));
268
269 return base_images;
270 }
271
272 void submitToSwapchainBegin() override {}
273 void submitToSwapchainImage(XrSwapchainImageBaseHeader &swapchain_image,
274 const GHOST_XrDrawViewInfo &draw_info) override
275 {
276 XrSwapchainImageOpenGLKHR &ogl_swapchain_image = reinterpret_cast<XrSwapchainImageOpenGLKHR &>(
277 swapchain_image);
278
279 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo);
280
281 glFramebufferTexture2D(
282 GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ogl_swapchain_image.image, 0);
283
284 glBlitFramebuffer(draw_info.ofsx,
285 draw_info.ofsy,
286 draw_info.ofsx + draw_info.width,
287 draw_info.ofsy + draw_info.height,
288 draw_info.ofsx,
289 draw_info.ofsy,
290 draw_info.ofsx + draw_info.width,
291 draw_info.ofsy + draw_info.height,
292 GL_COLOR_BUFFER_BIT,
293 GL_LINEAR);
294
295 glBindFramebuffer(GL_FRAMEBUFFER, 0);
296 }
297 void submitToSwapchainEnd() override {}
298
299 bool needsUpsideDownDrawing(GHOST_Context &ghost_ctx) const override
300 {
301 return ghost_ctx.isUpsideDown();
302 }
303
304 private:
305 std::list<std::vector<XrSwapchainImageOpenGLKHR>> m_image_cache;
306 GLuint m_fbo = 0;
307};
308
309std::unique_ptr<GHOST_IXrGraphicsBinding> GHOST_XrGraphicsBindingCreateFromType(
310 GHOST_TXrGraphicsBinding type, GHOST_Context &context)
311{
312 switch (type) {
313#ifdef WITH_OPENGL_BACKEND
314 case GHOST_kXrGraphicsOpenGL:
315 return std::make_unique<GHOST_XrGraphicsBindingOpenGL>();
316#endif
317#ifdef WITH_VULKAN_BACKEND
318 case GHOST_kXrGraphicsVulkan:
319 return std::make_unique<GHOST_XrGraphicsBindingVulkan>(context);
320#endif
321#ifdef WIN32
322# ifdef WITH_OPENGL_BACKEND
323 case GHOST_kXrGraphicsOpenGLD3D11:
324 return std::make_unique<GHOST_XrGraphicsBindingOpenGLD3D>(context);
325# endif
326# ifdef WITH_VULKAN_BACKEND
327 case GHOST_kXrGraphicsVulkanD3D11:
328 return std::make_unique<GHOST_XrGraphicsBindingVulkanD3D>(context);
329# endif
330#endif
331 default:
332 return nullptr;
333 }
334
335 (void)context; /* Might be unused. */
336}
GHOST C-API function and type declarations.
#define wl_display
#define GHOST_ASSERT(x, info)
static std::optional< int64_t > choose_swapchain_format_from_candidates(const std::vector< int64_t > &gpu_binding_formats, const std::vector< int64_t > &runtime_formats)
std::unique_ptr< GHOST_IXrGraphicsBinding > GHOST_XrGraphicsBindingCreateFromType(GHOST_TXrGraphicsBinding type, GHOST_Context &context)
EGLConfig getConfig() const
EGLDisplay getDisplay() const
EGLContext getContext() const
virtual bool isUpsideDown() const
GHOST_IXrGraphicsBinding()=default
union GHOST_IXrGraphicsBinding::@011057040026250276164372003005214271243343351130 oxr_binding
void initFromGhostContext(GHOST_Context &ghost_ctx, XrInstance, XrSystemId) override
bool needsUpsideDownDrawing(GHOST_Context &ghost_ctx) const override
std::optional< int64_t > chooseSwapchainFormat(const std::vector< int64_t > &runtime_formats, GHOST_TXrSwapchainFormat &r_format, bool &r_is_srgb_format) const override
bool checkVersionRequirements(GHOST_Context &ghost_ctx, XrInstance instance, XrSystemId system_id, std::string *r_requirement_info) const override
std::vector< XrSwapchainImageBaseHeader * > createSwapchainImages(uint32_t image_count) override
void submitToSwapchainImage(XrSwapchainImageBaseHeader &swapchain_image, const GHOST_XrDrawViewInfo &draw_info) override