Blender  V3.3
GHOST_XrGraphicsBinding.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include <algorithm>
8 #include <list>
9 #include <sstream>
10 
11 #if defined(WITH_GL_EGL)
12 # include "GHOST_ContextEGL.h"
13 # if defined(WITH_GHOST_X11)
14 # include "GHOST_SystemX11.h"
15 # endif
16 # if defined(WITH_GHOST_WAYLAND)
17 # include "GHOST_SystemWayland.h"
18 # endif
19 #elif defined(WITH_GHOST_X11)
20 # include "GHOST_ContextGLX.h"
21 #elif defined(WIN32)
22 # include "GHOST_ContextD3D.h"
23 # include "GHOST_ContextWGL.h"
24 # include "GHOST_SystemWin32.h"
25 #endif
26 #include "GHOST_C-api.h"
27 #include "GHOST_XrException.h"
28 #include "GHOST_Xr_intern.h"
29 
31 
32 static std::optional<int64_t> choose_swapchain_format_from_candidates(
33  const std::vector<int64_t> &gpu_binding_formats, const std::vector<int64_t> &runtime_formats)
34 {
35  if (gpu_binding_formats.empty()) {
36  return std::nullopt;
37  }
38 
39  auto res = std::find_first_of(gpu_binding_formats.begin(),
40  gpu_binding_formats.end(),
41  runtime_formats.begin(),
42  runtime_formats.end());
43  if (res == gpu_binding_formats.end()) {
44  return std::nullopt;
45  }
46 
47  return *res;
48 }
49 
51  public:
53  {
54  if (m_fbo != 0) {
55  glDeleteFramebuffers(1, &m_fbo);
56  }
57  }
58 
60  XrInstance instance,
61  XrSystemId system_id,
62  std::string *r_requirement_info) const override
63  {
64 #if defined(WITH_GL_EGL)
65  GHOST_ContextEGL &ctx_gl = static_cast<GHOST_ContextEGL &>(ghost_ctx);
66 #elif defined(WITH_GHOST_X11)
67  GHOST_ContextGLX &ctx_gl = static_cast<GHOST_ContextGLX &>(ghost_ctx);
68 #else
69  GHOST_ContextWGL &ctx_gl = static_cast<GHOST_ContextWGL &>(ghost_ctx);
70 #endif
71  static PFN_xrGetOpenGLGraphicsRequirementsKHR s_xrGetOpenGLGraphicsRequirementsKHR_fn =
72  nullptr;
73  // static XrInstance s_instance = XR_NULL_HANDLE;
74  XrGraphicsRequirementsOpenGLKHR gpu_requirements = {XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR};
75  const XrVersion gl_version = XR_MAKE_VERSION(
76  ctx_gl.m_contextMajorVersion, ctx_gl.m_contextMinorVersion, 0);
77 
78  /* Although it would seem reasonable that the proc address would not change if the instance was
79  * the same, in testing, repeated calls to #xrGetInstanceProcAddress() with the same instance
80  * can still result in changes so the workaround is to simply set the function pointer every
81  * time (trivializing its 'static' designation). */
82  // if (instance != s_instance) {
83  // s_instance = instance;
84  s_xrGetOpenGLGraphicsRequirementsKHR_fn = nullptr;
85  //}
86  if (!s_xrGetOpenGLGraphicsRequirementsKHR_fn &&
87  XR_FAILED(xrGetInstanceProcAddr(
88  instance,
89  "xrGetOpenGLGraphicsRequirementsKHR",
90  (PFN_xrVoidFunction *)&s_xrGetOpenGLGraphicsRequirementsKHR_fn))) {
91  s_xrGetOpenGLGraphicsRequirementsKHR_fn = nullptr;
92  return false;
93  }
94 
95  s_xrGetOpenGLGraphicsRequirementsKHR_fn(instance, system_id, &gpu_requirements);
96 
97  if (r_requirement_info) {
98  std::ostringstream strstream;
99  strstream << "Min OpenGL version "
100  << XR_VERSION_MAJOR(gpu_requirements.minApiVersionSupported) << "."
101  << XR_VERSION_MINOR(gpu_requirements.minApiVersionSupported) << std::endl;
102  strstream << "Max OpenGL version "
103  << XR_VERSION_MAJOR(gpu_requirements.maxApiVersionSupported) << "."
104  << XR_VERSION_MINOR(gpu_requirements.maxApiVersionSupported) << std::endl;
105 
106  *r_requirement_info = strstream.str();
107  }
108 
109  return (gl_version >= gpu_requirements.minApiVersionSupported) &&
110  (gl_version <= gpu_requirements.maxApiVersionSupported);
111  }
112 
113  void initFromGhostContext(GHOST_Context &ghost_ctx) override
114  {
115 #if defined(WITH_GHOST_X11)
116 # if defined(WITH_GL_EGL)
117  GHOST_ContextEGL &ctx_egl = static_cast<GHOST_ContextEGL &>(ghost_ctx);
118 
119  if (dynamic_cast<const GHOST_SystemX11 *const>(ctx_egl.m_system)) {
120  oxr_binding.egl.type = XR_TYPE_GRAPHICS_BINDING_EGL_MNDX;
121  oxr_binding.egl.getProcAddress = eglGetProcAddress;
122  oxr_binding.egl.display = ctx_egl.getDisplay();
123  oxr_binding.egl.config = ctx_egl.getConfig();
124  oxr_binding.egl.context = ctx_egl.getContext();
125  }
126 # else
127  GHOST_ContextGLX &ctx_glx = static_cast<GHOST_ContextGLX &>(ghost_ctx);
128  XVisualInfo *visual_info = glXGetVisualFromFBConfig(ctx_glx.m_display, ctx_glx.m_fbconfig);
129 
130  oxr_binding.glx.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR;
131  oxr_binding.glx.xDisplay = ctx_glx.m_display;
132  oxr_binding.glx.glxFBConfig = ctx_glx.m_fbconfig;
133  oxr_binding.glx.glxDrawable = ctx_glx.m_window;
134  oxr_binding.glx.glxContext = ctx_glx.m_context;
135  oxr_binding.glx.visualid = visual_info->visualid;
136 
137  XFree(visual_info);
138 # endif
139 #elif defined(WIN32)
140  GHOST_ContextWGL &ctx_wgl = static_cast<GHOST_ContextWGL &>(ghost_ctx);
141 
142  oxr_binding.wgl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR;
143  oxr_binding.wgl.hDC = ctx_wgl.m_hDC;
144  oxr_binding.wgl.hGLRC = ctx_wgl.m_hGLRC;
145 #endif
146 
147 #if defined(WITH_GHOST_WAYLAND)
148  GHOST_ContextEGL &ctx_wl_egl = static_cast<GHOST_ContextEGL &>(ghost_ctx);
149  if (dynamic_cast<const GHOST_SystemWayland *const>(ctx_wl_egl.m_system)) {
150  oxr_binding.wl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WAYLAND_KHR;
151  oxr_binding.wl.display = (struct wl_display *)ctx_wl_egl.m_nativeDisplay;
152  }
153 #endif
154 
155  /* Generate a frame-buffer to use for blitting into the texture. */
156  glGenFramebuffers(1, &m_fbo);
157  }
158 
159  std::optional<int64_t> chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
160  GHOST_TXrSwapchainFormat &r_format,
161  bool &r_is_srgb_format) const override
162  {
163  std::vector<int64_t> gpu_binding_formats = {
164 #if 0 /* RGB10A2, RGBA16 don't seem to work with Oculus head-sets, \
165  * so move them after RGBA16F for the time being. */
166  GL_RGB10_A2,
167  GL_RGBA16,
168 #endif
169  GL_RGBA16F,
170 #if 1
171  GL_RGB10_A2,
172  GL_RGBA16,
173 #endif
174  GL_RGBA8,
175  GL_SRGB8_ALPHA8,
176  };
177 
178  std::optional result = choose_swapchain_format_from_candidates(gpu_binding_formats,
179  runtime_formats);
180  if (result) {
181  switch (*result) {
182  case GL_RGB10_A2:
183  r_format = GHOST_kXrSwapchainFormatRGB10_A2;
184  break;
185  case GL_RGBA16:
186  r_format = GHOST_kXrSwapchainFormatRGBA16;
187  break;
188  case GL_RGBA16F:
189  r_format = GHOST_kXrSwapchainFormatRGBA16F;
190  break;
191  case GL_RGBA8:
192  case GL_SRGB8_ALPHA8:
193  r_format = GHOST_kXrSwapchainFormatRGBA8;
194  break;
195  }
196  r_is_srgb_format = (*result == GL_SRGB8_ALPHA8);
197  }
198  else {
199  r_format = GHOST_kXrSwapchainFormatRGBA8;
200  r_is_srgb_format = false;
201  }
202 
203  return result;
204  }
205 
206  std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(uint32_t image_count) override
207  {
208  std::vector<XrSwapchainImageOpenGLKHR> ogl_images(image_count);
209  std::vector<XrSwapchainImageBaseHeader *> base_images;
210 
211  /* Need to return vector of base header pointers, so of a different type. Need to build a new
212  * list with this type, and keep the initial one alive. */
213  for (XrSwapchainImageOpenGLKHR &image : ogl_images) {
214  image.type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR;
215  base_images.push_back(reinterpret_cast<XrSwapchainImageBaseHeader *>(&image));
216  }
217 
218  /* Keep alive. */
219  m_image_cache.push_back(std::move(ogl_images));
220 
221  return base_images;
222  }
223 
224  void submitToSwapchainImage(XrSwapchainImageBaseHeader &swapchain_image,
225  const GHOST_XrDrawViewInfo &draw_info) override
226  {
227  XrSwapchainImageOpenGLKHR &ogl_swapchain_image = reinterpret_cast<XrSwapchainImageOpenGLKHR &>(
228  swapchain_image);
229 
230  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo);
231 
232  glFramebufferTexture2D(
233  GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ogl_swapchain_image.image, 0);
234 
235  glBlitFramebuffer(draw_info.ofsx,
236  draw_info.ofsy,
237  draw_info.ofsx + draw_info.width,
238  draw_info.ofsy + draw_info.height,
239  draw_info.ofsx,
240  draw_info.ofsy,
241  draw_info.ofsx + draw_info.width,
242  draw_info.ofsy + draw_info.height,
243  GL_COLOR_BUFFER_BIT,
244  GL_LINEAR);
245 
246  glBindFramebuffer(GL_FRAMEBUFFER, 0);
247  }
248 
249  bool needsUpsideDownDrawing(GHOST_Context &ghost_ctx) const override
250  {
251  return ghost_ctx.isUpsideDown();
252  }
253 
254  private:
255  std::list<std::vector<XrSwapchainImageOpenGLKHR>> m_image_cache;
256  GLuint m_fbo = 0;
257 };
258 
259 #ifdef WIN32
260 static void ghost_format_to_dx_format(GHOST_TXrSwapchainFormat ghost_format,
261  bool expects_srgb_buffer,
262  DXGI_FORMAT &r_dx_format)
263 {
264  r_dx_format = DXGI_FORMAT_UNKNOWN;
265 
266  switch (ghost_format) {
267  case GHOST_kXrSwapchainFormatRGBA8:
268  r_dx_format = expects_srgb_buffer ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB :
270  break;
271  case GHOST_kXrSwapchainFormatRGBA16:
272  r_dx_format = DXGI_FORMAT_R16G16B16A16_UNORM;
273  break;
274  case GHOST_kXrSwapchainFormatRGBA16F:
275  r_dx_format = DXGI_FORMAT_R16G16B16A16_FLOAT;
276  break;
277  case GHOST_kXrSwapchainFormatRGB10_A2:
278  r_dx_format = DXGI_FORMAT_R10G10B10A2_UNORM;
279  break;
280  }
281 
282  if (r_dx_format == DXGI_FORMAT_UNKNOWN) {
283  throw GHOST_XrException("No supported DirectX swapchain format found.");
284  }
285 }
286 
287 class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding {
288  public:
289  GHOST_XrGraphicsBindingD3D(GHOST_Context &ghost_ctx)
290  : GHOST_IXrGraphicsBinding(), m_ghost_wgl_ctx(static_cast<GHOST_ContextWGL &>(ghost_ctx))
291  {
293  }
294  ~GHOST_XrGraphicsBindingD3D()
295  {
296  if (m_shared_resource) {
297  m_ghost_d3d_ctx->disposeSharedOpenGLResource(m_shared_resource);
298  }
299  if (m_ghost_d3d_ctx) {
300  GHOST_SystemWin32::disposeContextD3D(m_ghost_d3d_ctx);
301  }
302  }
303 
305  GHOST_Context & /*ghost_ctx*/, /* Remember: This is the OpenGL context! */
306  XrInstance instance,
307  XrSystemId system_id,
308  std::string *r_requirement_info) const override
309  {
310  static PFN_xrGetD3D11GraphicsRequirementsKHR s_xrGetD3D11GraphicsRequirementsKHR_fn = nullptr;
311  // static XrInstance s_instance = XR_NULL_HANDLE;
312  XrGraphicsRequirementsD3D11KHR gpu_requirements = {XR_TYPE_GRAPHICS_REQUIREMENTS_D3D11_KHR};
313 
314  /* Although it would seem reasonable that the proc address would not change if the instance was
315  * the same, in testing, repeated calls to #xrGetInstanceProcAddress() with the same instance
316  * can still result in changes so the workaround is to simply set the function pointer every
317  * time (trivializing its 'static' designation). */
318  // if (instance != s_instance) {
319  // s_instance = instance;
320  s_xrGetD3D11GraphicsRequirementsKHR_fn = nullptr;
321  //}
322  if (!s_xrGetD3D11GraphicsRequirementsKHR_fn &&
323  XR_FAILED(xrGetInstanceProcAddr(
324  instance,
325  "xrGetD3D11GraphicsRequirementsKHR",
326  (PFN_xrVoidFunction *)&s_xrGetD3D11GraphicsRequirementsKHR_fn))) {
327  s_xrGetD3D11GraphicsRequirementsKHR_fn = nullptr;
328  return false;
329  }
330 
331  s_xrGetD3D11GraphicsRequirementsKHR_fn(instance, system_id, &gpu_requirements);
332 
333  if (r_requirement_info) {
334  std::ostringstream strstream;
335  strstream << "Minimum DirectX 11 Feature Level " << gpu_requirements.minFeatureLevel
336  << std::endl;
337 
338  *r_requirement_info = std::move(strstream.str());
339  }
340 
341  return m_ghost_d3d_ctx->m_device->GetFeatureLevel() >= gpu_requirements.minFeatureLevel;
342  }
343 
345  GHOST_Context & /*ghost_ctx*/ /* Remember: This is the OpenGL context! */
346  ) override
347  {
348  oxr_binding.d3d11.type = XR_TYPE_GRAPHICS_BINDING_D3D11_KHR;
349  oxr_binding.d3d11.device = m_ghost_d3d_ctx->m_device;
350  }
351 
352  std::optional<int64_t> chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
353  GHOST_TXrSwapchainFormat &r_format,
354  bool &r_is_srgb_format) const override
355  {
356  std::vector<int64_t> gpu_binding_formats = {
357 # if 0 /* RGB10A2, RGBA16 don't seem to work with Oculus head-sets, \
358  * so move them after RGBA16F for the time being. */
361 # endif
363 # if 1
366 # endif
369  };
370 
371  std::optional result = choose_swapchain_format_from_candidates(gpu_binding_formats,
372  runtime_formats);
373  if (result) {
374  switch (*result) {
376  r_format = GHOST_kXrSwapchainFormatRGB10_A2;
377  break;
379  r_format = GHOST_kXrSwapchainFormatRGBA16;
380  break;
382  r_format = GHOST_kXrSwapchainFormatRGBA16F;
383  break;
386  r_format = GHOST_kXrSwapchainFormatRGBA8;
387  break;
388  }
389  r_is_srgb_format = (*result == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB);
390  }
391  else {
392  r_format = GHOST_kXrSwapchainFormatRGBA8;
393  r_is_srgb_format = false;
394  }
395 
396  return result;
397  }
398 
399  std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(uint32_t image_count) override
400  {
401  std::vector<XrSwapchainImageD3D11KHR> d3d_images(image_count);
402  std::vector<XrSwapchainImageBaseHeader *> base_images;
403 
404  /* Need to return vector of base header pointers, so of a different type. Need to build a new
405  * list with this type, and keep the initial one alive. */
406  for (XrSwapchainImageD3D11KHR &image : d3d_images) {
407  image.type = XR_TYPE_SWAPCHAIN_IMAGE_D3D11_KHR;
408  base_images.push_back(reinterpret_cast<XrSwapchainImageBaseHeader *>(&image));
409  }
410 
411  /* Keep alive. */
412  m_image_cache.push_back(std::move(d3d_images));
413 
414  return base_images;
415  }
416 
417  void submitToSwapchainImage(XrSwapchainImageBaseHeader &swapchain_image,
418  const GHOST_XrDrawViewInfo &draw_info) override
419  {
420  XrSwapchainImageD3D11KHR &d3d_swapchain_image = reinterpret_cast<XrSwapchainImageD3D11KHR &>(
421  swapchain_image);
422 
423 # if 0
424  /* Ideally we'd just create a render target view for the OpenXR swap-chain image texture and
425  * blit from the OpenGL context into it. The NV_DX_interop extension doesn't want to work with
426  * this though. At least not with Optimus hardware. See:
427  * https://github.com/mpv-player/mpv/issues/2949#issuecomment-197262807.
428  */
429 
430  ID3D11RenderTargetView *rtv;
431  CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D,
433 
434  m_ghost_ctx->m_device->CreateRenderTargetView(d3d_swapchain_image.texture, &rtv_desc, &rtv);
435  if (!m_shared_resource) {
437  ghost_format_to_dx_format(draw_info.swapchain_format, draw_info.expects_srgb_buffer, format);
438  m_shared_resource = m_ghost_ctx->createSharedOpenGLResource(
439  draw_info.width, draw_info.height, format, rtv);
440  }
441  m_ghost_ctx->blitFromOpenGLContext(m_shared_resource, draw_info.width, draw_info.height);
442 # else
443  if (!m_shared_resource) {
445  ghost_format_to_dx_format(draw_info.swapchain_format, draw_info.expects_srgb_buffer, format);
446  m_shared_resource = m_ghost_d3d_ctx->createSharedOpenGLResource(
447  draw_info.width, draw_info.height, format);
448  }
449  m_ghost_d3d_ctx->blitFromOpenGLContext(m_shared_resource, draw_info.width, draw_info.height);
450 
451  m_ghost_d3d_ctx->m_device_ctx->OMSetRenderTargets(0, nullptr, nullptr);
452  m_ghost_d3d_ctx->m_device_ctx->CopyResource(
453  d3d_swapchain_image.texture, m_ghost_d3d_ctx->getSharedTexture2D(m_shared_resource));
454 # endif
455  }
456 
458  {
459  return m_ghost_d3d_ctx->isUpsideDown();
460  }
461 
462  private:
464  GHOST_ContextWGL &m_ghost_wgl_ctx;
466  GHOST_ContextD3D *m_ghost_d3d_ctx = nullptr;
468  GHOST_SharedOpenGLResource *m_shared_resource = nullptr;
469 
470  std::list<std::vector<XrSwapchainImageD3D11KHR>> m_image_cache;
471 };
472 #endif // WIN32
473 
474 std::unique_ptr<GHOST_IXrGraphicsBinding> GHOST_XrGraphicsBindingCreateFromType(
475  GHOST_TXrGraphicsBinding type, GHOST_Context &context)
476 {
477  switch (type) {
478  case GHOST_kXrGraphicsOpenGL:
479  return std::make_unique<GHOST_XrGraphicsBindingOpenGL>();
480 #ifdef WIN32
481  case GHOST_kXrGraphicsD3D11:
482  return std::make_unique<GHOST_XrGraphicsBindingD3D>(context);
483 #endif
484  default:
485  return nullptr;
486  }
487 
488  (void)context; /* Might be unused. */
489 }
@ DXGI_FORMAT_R16G16B16A16_UNORM
@ DXGI_FORMAT_UNKNOWN
@ DXGI_FORMAT_R10G10B10A2_UNORM
@ DXGI_FORMAT_R16G16B16A16_FLOAT
@ DXGI_FORMAT_R8G8B8A8_UNORM
@ DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
GHOST C-API function and type declarations.
std::unique_ptr< GHOST_IXrGraphicsBinding > GHOST_XrGraphicsBindingCreateFromType(GHOST_TXrGraphicsBinding type, GHOST_Context &context)
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)
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object instance
EGLConfig getConfig() const
EGLDisplay getDisplay() const
EGLContext getContext() const
virtual bool isUpsideDown() const
virtual bool needsUpsideDownDrawing(GHOST_Context &ghost_ctx) const =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 initFromGhostContext(class GHOST_Context &ghost_ctx)=0
union GHOST_IXrGraphicsBinding::@1290 oxr_binding
virtual std::vector< XrSwapchainImageBaseHeader * > createSwapchainImages(uint32_t image_count)=0
virtual void submitToSwapchainImage(XrSwapchainImageBaseHeader &swapchain_image, const GHOST_XrDrawViewInfo &draw_info)=0
static GHOST_ContextD3D * createOffscreenContextD3D()
static GHOST_TSuccess disposeContextD3D(GHOST_ContextD3D *context)
std::optional< int64_t > chooseSwapchainFormat(const std::vector< int64_t > &runtime_formats, GHOST_TXrSwapchainFormat &r_format, bool &r_is_srgb_format) const override
bool needsUpsideDownDrawing(GHOST_Context &ghost_ctx) const override
std::vector< XrSwapchainImageBaseHeader * > createSwapchainImages(uint32_t image_count) override
void initFromGhostContext(GHOST_Context &ghost_ctx) override
bool checkVersionRequirements(GHOST_Context &ghost_ctx, XrInstance instance, XrSystemId system_id, std::string *r_requirement_info) const override
void submitToSwapchainImage(XrSwapchainImageBaseHeader &swapchain_image, const GHOST_XrDrawViewInfo &draw_info) override
SyclQueue void void size_t num_bytes void
depth_tx normal_tx diffuse_light_tx specular_light_tx volume_light_tx environment_tx ambient_occlusion_tx aov_value_tx in_weight_img image(1, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "out_weight_img") .image(3
format
Definition: logImageCore.h:38
unsigned int uint32_t
Definition: stdint.h:80