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