Blender  V2.93
GHOST_XrGraphicsBinding.cpp
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
21 #include <algorithm>
22 #include <list>
23 #include <sstream>
24 
25 #if defined(WITH_GHOST_X11)
26 # include "GHOST_ContextGLX.h"
27 #elif defined(WIN32)
28 # include "GHOST_ContextD3D.h"
29 # include "GHOST_ContextWGL.h"
30 # include "GHOST_SystemWin32.h"
31 #endif
32 #include "GHOST_C-api.h"
33 #include "GHOST_Xr_intern.h"
34 
36 
37 static std::optional<int64_t> choose_swapchain_format_from_candidates(
38  const std::vector<int64_t> &gpu_binding_formats, const std::vector<int64_t> &runtime_formats)
39 {
40  if (gpu_binding_formats.empty()) {
41  return std::nullopt;
42  }
43 
44  auto res = std::find_first_of(gpu_binding_formats.begin(),
45  gpu_binding_formats.end(),
46  runtime_formats.begin(),
47  runtime_formats.end());
48  if (res == gpu_binding_formats.end()) {
49  return std::nullopt;
50  }
51 
52  return *res;
53 }
54 
56  public:
58  {
59  if (m_fbo != 0) {
60  glDeleteFramebuffers(1, &m_fbo);
61  }
62  }
63 
65  XrInstance instance,
66  XrSystemId system_id,
67  std::string *r_requirement_info) const override
68  {
69 #if defined(WITH_GHOST_X11)
70  GHOST_ContextGLX &ctx_gl = static_cast<GHOST_ContextGLX &>(ghost_ctx);
71 #else
72  GHOST_ContextWGL &ctx_gl = static_cast<GHOST_ContextWGL &>(ghost_ctx);
73 #endif
74  static PFN_xrGetOpenGLGraphicsRequirementsKHR s_xrGetOpenGLGraphicsRequirementsKHR_fn =
75  nullptr;
76  XrGraphicsRequirementsOpenGLKHR gpu_requirements = {XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR};
77  const XrVersion gl_version = XR_MAKE_VERSION(
78  ctx_gl.m_contextMajorVersion, ctx_gl.m_contextMinorVersion, 0);
79 
80  if (!s_xrGetOpenGLGraphicsRequirementsKHR_fn &&
81  XR_FAILED(xrGetInstanceProcAddr(
82  instance,
83  "xrGetOpenGLGraphicsRequirementsKHR",
84  (PFN_xrVoidFunction *)&s_xrGetOpenGLGraphicsRequirementsKHR_fn))) {
85  s_xrGetOpenGLGraphicsRequirementsKHR_fn = nullptr;
86  }
87 
88  s_xrGetOpenGLGraphicsRequirementsKHR_fn(instance, system_id, &gpu_requirements);
89 
90  if (r_requirement_info) {
91  std::ostringstream strstream;
92  strstream << "Min OpenGL version "
93  << XR_VERSION_MAJOR(gpu_requirements.minApiVersionSupported) << "."
94  << XR_VERSION_MINOR(gpu_requirements.minApiVersionSupported) << std::endl;
95  strstream << "Max OpenGL version "
96  << XR_VERSION_MAJOR(gpu_requirements.maxApiVersionSupported) << "."
97  << XR_VERSION_MINOR(gpu_requirements.maxApiVersionSupported) << std::endl;
98 
99  *r_requirement_info = strstream.str();
100  }
101 
102  return (gl_version >= gpu_requirements.minApiVersionSupported) &&
103  (gl_version <= gpu_requirements.maxApiVersionSupported);
104  }
105 
106  void initFromGhostContext(GHOST_Context &ghost_ctx) override
107  {
108 #if defined(WITH_GHOST_X11)
109  GHOST_ContextGLX &ctx_glx = static_cast<GHOST_ContextGLX &>(ghost_ctx);
110  XVisualInfo *visual_info = glXGetVisualFromFBConfig(ctx_glx.m_display, ctx_glx.m_fbconfig);
111 
112  oxr_binding.glx.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR;
113  oxr_binding.glx.xDisplay = ctx_glx.m_display;
114  oxr_binding.glx.glxFBConfig = ctx_glx.m_fbconfig;
115  oxr_binding.glx.glxDrawable = ctx_glx.m_window;
116  oxr_binding.glx.glxContext = ctx_glx.m_context;
117  oxr_binding.glx.visualid = visual_info->visualid;
118 
119  XFree(visual_info);
120 #elif defined(WIN32)
121  GHOST_ContextWGL &ctx_wgl = static_cast<GHOST_ContextWGL &>(ghost_ctx);
122 
123  oxr_binding.wgl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR;
124  oxr_binding.wgl.hDC = ctx_wgl.m_hDC;
125  oxr_binding.wgl.hGLRC = ctx_wgl.m_hGLRC;
126 #endif
127 
128  /* Generate a frame-buffer to use for blitting into the texture. */
129  glGenFramebuffers(1, &m_fbo);
130  }
131 
132  std::optional<int64_t> chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
133  bool &r_is_srgb_format) const override
134  {
135  std::vector<int64_t> gpu_binding_formats = {
136  GL_RGBA8,
137  GL_SRGB8_ALPHA8,
138  };
139 
140  std::optional result = choose_swapchain_format_from_candidates(gpu_binding_formats,
141  runtime_formats);
142  r_is_srgb_format = result ? (*result == GL_SRGB8_ALPHA8) : false;
143 
144  return result;
145  }
146 
147  std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(uint32_t image_count) override
148  {
149  std::vector<XrSwapchainImageOpenGLKHR> ogl_images(image_count);
150  std::vector<XrSwapchainImageBaseHeader *> base_images;
151 
152  /* Need to return vector of base header pointers, so of a different type. Need to build a new
153  * list with this type, and keep the initial one alive. */
154  for (XrSwapchainImageOpenGLKHR &image : ogl_images) {
155  image.type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR;
156  base_images.push_back(reinterpret_cast<XrSwapchainImageBaseHeader *>(&image));
157  }
158 
159  /* Keep alive. */
160  m_image_cache.push_back(std::move(ogl_images));
161 
162  return base_images;
163  }
164 
165  void submitToSwapchainImage(XrSwapchainImageBaseHeader &swapchain_image,
166  const GHOST_XrDrawViewInfo &draw_info) override
167  {
168  XrSwapchainImageOpenGLKHR &ogl_swapchain_image = reinterpret_cast<XrSwapchainImageOpenGLKHR &>(
169  swapchain_image);
170 
171  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo);
172 
173  glFramebufferTexture2D(
174  GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ogl_swapchain_image.image, 0);
175 
176  glBlitFramebuffer(draw_info.ofsx,
177  draw_info.ofsy,
178  draw_info.ofsx + draw_info.width,
179  draw_info.ofsy + draw_info.height,
180  draw_info.ofsx,
181  draw_info.ofsy,
182  draw_info.ofsx + draw_info.width,
183  draw_info.ofsy + draw_info.height,
184  GL_COLOR_BUFFER_BIT,
185  GL_LINEAR);
186 
187  glBindFramebuffer(GL_FRAMEBUFFER, 0);
188  }
189 
190  bool needsUpsideDownDrawing(GHOST_Context &ghost_ctx) const override
191  {
192  return ghost_ctx.isUpsideDown();
193  }
194 
195  private:
196  std::list<std::vector<XrSwapchainImageOpenGLKHR>> m_image_cache;
197  GLuint m_fbo = 0;
198 };
199 
200 #ifdef WIN32
201 class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding {
202  public:
203  GHOST_XrGraphicsBindingD3D(GHOST_Context &ghost_ctx)
204  : GHOST_IXrGraphicsBinding(), m_ghost_wgl_ctx(static_cast<GHOST_ContextWGL &>(ghost_ctx))
205  {
207  }
208  ~GHOST_XrGraphicsBindingD3D()
209  {
210  if (m_shared_resource) {
211  m_ghost_d3d_ctx->disposeSharedOpenGLResource(m_shared_resource);
212  }
213  if (m_ghost_d3d_ctx) {
214  GHOST_SystemWin32::disposeContextD3D(m_ghost_d3d_ctx);
215  }
216  }
217 
219  GHOST_Context & /*ghost_ctx*/, /* Remember: This is the OpenGL context! */
220  XrInstance instance,
221  XrSystemId system_id,
222  std::string *r_requirement_info) const override
223  {
224  static PFN_xrGetD3D11GraphicsRequirementsKHR s_xrGetD3D11GraphicsRequirementsKHR_fn = nullptr;
225  XrGraphicsRequirementsD3D11KHR gpu_requirements = {XR_TYPE_GRAPHICS_REQUIREMENTS_D3D11_KHR};
226 
227  if (!s_xrGetD3D11GraphicsRequirementsKHR_fn &&
228  XR_FAILED(xrGetInstanceProcAddr(
229  instance,
230  "xrGetD3D11GraphicsRequirementsKHR",
231  (PFN_xrVoidFunction *)&s_xrGetD3D11GraphicsRequirementsKHR_fn))) {
232  s_xrGetD3D11GraphicsRequirementsKHR_fn = nullptr;
233  }
234 
235  s_xrGetD3D11GraphicsRequirementsKHR_fn(instance, system_id, &gpu_requirements);
236 
237  if (r_requirement_info) {
238  std::ostringstream strstream;
239  strstream << "Minimum DirectX 11 Feature Level " << gpu_requirements.minFeatureLevel
240  << std::endl;
241 
242  *r_requirement_info = std::move(strstream.str());
243  }
244 
245  return m_ghost_d3d_ctx->m_device->GetFeatureLevel() >= gpu_requirements.minFeatureLevel;
246  }
247 
249  GHOST_Context & /*ghost_ctx*/ /* Remember: This is the OpenGL context! */
250  ) override
251  {
252  oxr_binding.d3d11.type = XR_TYPE_GRAPHICS_BINDING_D3D11_KHR;
253  oxr_binding.d3d11.device = m_ghost_d3d_ctx->m_device;
254  }
255 
256  std::optional<int64_t> chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
257  bool &r_is_srgb_format) const override
258  {
259  std::vector<int64_t> gpu_binding_formats = {
262  };
263 
264  std::optional result = choose_swapchain_format_from_candidates(gpu_binding_formats,
265  runtime_formats);
266  r_is_srgb_format = result ? (*result == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB) : false;
267  return result;
268  }
269 
270  std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(uint32_t image_count) override
271  {
272  std::vector<XrSwapchainImageD3D11KHR> d3d_images(image_count);
273  std::vector<XrSwapchainImageBaseHeader *> base_images;
274 
275  /* Need to return vector of base header pointers, so of a different type. Need to build a new
276  * list with this type, and keep the initial one alive. */
277  for (XrSwapchainImageD3D11KHR &image : d3d_images) {
278  image.type = XR_TYPE_SWAPCHAIN_IMAGE_D3D11_KHR;
279  base_images.push_back(reinterpret_cast<XrSwapchainImageBaseHeader *>(&image));
280  }
281 
282  /* Keep alive. */
283  m_image_cache.push_back(std::move(d3d_images));
284 
285  return base_images;
286  }
287 
288  void submitToSwapchainImage(XrSwapchainImageBaseHeader &swapchain_image,
289  const GHOST_XrDrawViewInfo &draw_info) override
290  {
291  XrSwapchainImageD3D11KHR &d3d_swapchain_image = reinterpret_cast<XrSwapchainImageD3D11KHR &>(
292  swapchain_image);
293 
294 # if 0
295  /* Ideally we'd just create a render target view for the OpenXR swap-chain image texture and
296  * blit from the OpenGL context into it. The NV_DX_interop extension doesn't want to work with
297  * this though. At least not with Optimus hardware. See:
298  * https://github.com/mpv-player/mpv/issues/2949#issuecomment-197262807.
299  */
300 
301  ID3D11RenderTargetView *rtv;
302  CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D,
304 
305  m_ghost_ctx->m_device->CreateRenderTargetView(d3d_swapchain_image.texture, &rtv_desc, &rtv);
306  if (!m_shared_resource) {
307  m_shared_resource = m_ghost_ctx->createSharedOpenGLResource(
308  draw_info.width, draw_info.height, rtv);
309  }
310  m_ghost_ctx->blitFromOpenGLContext(m_shared_resource, draw_info.width, draw_info.height);
311 # else
312  if (!m_shared_resource) {
313  m_shared_resource = m_ghost_d3d_ctx->createSharedOpenGLResource(draw_info.width,
314  draw_info.height);
315  }
316  m_ghost_d3d_ctx->blitFromOpenGLContext(m_shared_resource, draw_info.width, draw_info.height);
317 
318  m_ghost_d3d_ctx->m_device_ctx->OMSetRenderTargets(0, nullptr, nullptr);
319  m_ghost_d3d_ctx->m_device_ctx->CopyResource(
320  d3d_swapchain_image.texture, m_ghost_d3d_ctx->getSharedTexture2D(m_shared_resource));
321 # endif
322  }
323 
325  {
326  return m_ghost_d3d_ctx->isUpsideDown();
327  }
328 
329  private:
331  GHOST_ContextWGL &m_ghost_wgl_ctx;
333  GHOST_ContextD3D *m_ghost_d3d_ctx = nullptr;
335  GHOST_SharedOpenGLResource *m_shared_resource = nullptr;
336 
337  std::list<std::vector<XrSwapchainImageD3D11KHR>> m_image_cache;
338 };
339 #endif // WIN32
340 
341 std::unique_ptr<GHOST_IXrGraphicsBinding> GHOST_XrGraphicsBindingCreateFromType(
342  GHOST_TXrGraphicsBinding type, GHOST_Context &context)
343 {
344  switch (type) {
345  case GHOST_kXrGraphicsOpenGL:
346  return std::make_unique<GHOST_XrGraphicsBindingOpenGL>();
347 #ifdef WIN32
348  case GHOST_kXrGraphicsD3D11:
349  return std::make_unique<GHOST_XrGraphicsBindingD3D>(context);
350 #endif
351  default:
352  return nullptr;
353  }
354 
355  (void)context; /* Might be unused. */
356 }
@ 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
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 void initFromGhostContext(class GHOST_Context &ghost_ctx)=0
union GHOST_IXrGraphicsBinding::@1230 oxr_binding
virtual std::vector< XrSwapchainImageBaseHeader * > createSwapchainImages(uint32_t image_count)=0
virtual std::optional< int64_t > chooseSwapchainFormat(const std::vector< int64_t > &runtime_formats, bool &r_is_rgb_format) const =0
virtual void submitToSwapchainImage(XrSwapchainImageBaseHeader &swapchain_image, const GHOST_XrDrawViewInfo &draw_info)=0
static GHOST_ContextD3D * createOffscreenContextD3D()
static GHOST_TSuccess disposeContextD3D(GHOST_ContextD3D *context)
bool needsUpsideDownDrawing(GHOST_Context &ghost_ctx) const override
std::vector< XrSwapchainImageBaseHeader * > createSwapchainImages(uint32_t image_count) override
void initFromGhostContext(GHOST_Context &ghost_ctx) override
std::optional< int64_t > chooseSwapchainFormat(const std::vector< int64_t > &runtime_formats, 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
void submitToSwapchainImage(XrSwapchainImageBaseHeader &swapchain_image, const GHOST_XrDrawViewInfo &draw_info) override
struct SELECTID_Context context
Definition: select_engine.c:47
unsigned int uint32_t
Definition: stdint.h:83