Blender  V3.3
GHOST_ContextD3D.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
10 #include <iostream>
11 #include <string>
12 
13 #include <GL/glew.h>
14 #include <GL/wglew.h>
15 
16 #include "GHOST_ContextD3D.h"
17 #include "GHOST_ContextWGL.h" /* For shared drawing */
18 
19 HMODULE GHOST_ContextD3D::s_d3d_lib = NULL;
20 PFN_D3D11_CREATE_DEVICE GHOST_ContextD3D::s_D3D11CreateDeviceFn = NULL;
21 
22 GHOST_ContextD3D::GHOST_ContextD3D(bool stereoVisual, HWND hWnd)
23  : GHOST_Context(stereoVisual), m_hWnd(hWnd)
24 {
25 }
26 
28 {
29  m_device->Release();
30  m_device_ctx->ClearState();
31  m_device_ctx->Release();
32 }
33 
35 {
36  return GHOST_kSuccess;
37 }
38 
40 {
41  return GHOST_kFailure;
42 }
43 
45 {
46  return GHOST_kFailure;
47 }
48 
49 GHOST_TSuccess GHOST_ContextD3D::setupD3DLib()
50 {
51  if (s_d3d_lib == NULL) {
52  s_d3d_lib = LoadLibraryA("d3d11.dll");
53 
54  WIN32_CHK(s_d3d_lib != NULL);
55 
56  if (s_d3d_lib == NULL) {
57  fprintf(stderr, "LoadLibrary(\"d3d11.dll\") failed!\n");
58  return GHOST_kFailure;
59  }
60  }
61 
62  if (s_D3D11CreateDeviceFn == NULL) {
63  s_D3D11CreateDeviceFn = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(s_d3d_lib,
64  "D3D11CreateDevice");
65 
66  WIN32_CHK(s_D3D11CreateDeviceFn != NULL);
67 
68  if (s_D3D11CreateDeviceFn == NULL) {
69  fprintf(stderr, "GetProcAddress(s_d3d_lib, \"D3D11CreateDevice\") failed!\n");
70  return GHOST_kFailure;
71  }
72  }
73 
74  return GHOST_kSuccess;
75 }
76 
78 {
79  if (setupD3DLib() == GHOST_kFailure) {
80  return GHOST_kFailure;
81  }
82 
83  HRESULT hres = s_D3D11CreateDeviceFn(
84  NULL,
85  D3D_DRIVER_TYPE_HARDWARE,
86  NULL,
87  /* For debugging you may want to pass D3D11_CREATE_DEVICE_DEBUG here, but that requires
88  * additional setup, see
89  * https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-devices-layers#debug-layer.
90  */
91  0,
92  NULL,
93  0,
94  D3D11_SDK_VERSION,
95  &m_device,
96  NULL,
97  &m_device_ctx);
98 
99  WIN32_CHK(hres == S_OK);
100 
101  return GHOST_kSuccess;
102 }
103 
105 {
106  return GHOST_kFailure;
107 }
108 
110  struct SharedData {
111  HANDLE device;
112  GLuint fbo;
113  HANDLE render_target{nullptr};
114  } m_shared;
115 
116  enum RenderTarget { TARGET_RENDERBUF, TARGET_TEX2D };
117 
118  public:
119  GHOST_SharedOpenGLResource(ID3D11Device *device,
120  ID3D11DeviceContext *device_ctx,
121  unsigned int width,
122  unsigned int height,
124  ID3D11RenderTargetView *render_target = nullptr)
125  : m_device(device), m_device_ctx(device_ctx), m_cur_width(width), m_cur_height(height)
126  {
127  ID3D11Resource *backbuffer_res;
128 
129  if (!render_target) {
130  D3D11_TEXTURE2D_DESC texDesc{};
131  D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc{};
132  ID3D11Texture2D *tex;
133 
134  texDesc.Width = width;
135  texDesc.Height = height;
136  texDesc.Format = format;
137  texDesc.SampleDesc.Count = 1;
138  texDesc.ArraySize = 1;
139  texDesc.MipLevels = 1;
140  texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
141 
142  device->CreateTexture2D(&texDesc, NULL, &tex);
143  if (!tex) {
144  /* If texture creation fails, we just return and leave the render target unset. So it needs
145  * to be NULL-checked before use. */
146  fprintf(stderr, "Error creating texture for shared DirectX-OpenGL resource\n");
147  return;
148  }
149 
150  renderTargetViewDesc.Format = texDesc.Format;
151  renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
152  renderTargetViewDesc.Texture2D.MipSlice = 0;
153 
154  device->CreateRenderTargetView(tex, &renderTargetViewDesc, &render_target);
155 
156  tex->Release();
157  }
158 
159  m_render_target = render_target;
160  if (m_render_target) {
161  m_render_target->GetResource(&backbuffer_res);
162  }
163  if (backbuffer_res) {
164  backbuffer_res->QueryInterface<ID3D11Texture2D>(&m_render_target_tex);
165  backbuffer_res->Release();
166  }
167 
169  fprintf(stderr, "Error creating render target for shared DirectX-OpenGL resource\n");
170  return;
171  }
172  }
173 
175  {
176  if (m_render_target_tex) {
177  m_render_target_tex->Release();
178  }
179  if (m_render_target) {
180  m_render_target->Release();
181  }
182 
183  if (m_is_initialized) {
184  if (m_shared.render_target
185 #if 1
186  /* TODO: #wglDXUnregisterObjectNV() causes an access violation on AMD when the shared
187  * resource is a GL texture. Since there is currently no good alternative, just skip
188  * unregistering the shared resource. */
189  && !m_use_gl_texture2d
190 #endif
191  ) {
192  wglDXUnregisterObjectNV(m_shared.device, m_shared.render_target);
193  }
194  if (m_shared.device) {
195  wglDXCloseDeviceNV(m_shared.device);
196  }
197  glDeleteFramebuffers(1, &m_shared.fbo);
198  if (m_use_gl_texture2d) {
199  glDeleteTextures(1, &m_gl_render_target);
200  }
201  else {
202  glDeleteRenderbuffers(1, &m_gl_render_target);
203  }
204  }
205  }
206 
207  /* Returns true if the shared object was successfully registered, false otherwise. */
208  bool reregisterSharedObject(RenderTarget target)
209  {
210  if (m_shared.render_target) {
211  wglDXUnregisterObjectNV(m_shared.device, m_shared.render_target);
212  }
213 
214  if (!m_render_target_tex) {
215  return false;
216  }
217 
218  if (target == TARGET_TEX2D) {
219  glTexImage2D(GL_TEXTURE_2D,
220  0,
221  GL_RGBA8,
222  m_cur_width,
223  m_cur_height,
224  0,
225  GL_RGBA,
226  GL_UNSIGNED_BYTE,
227  nullptr);
228  }
229 
230  m_shared.render_target = wglDXRegisterObjectNV(m_shared.device,
232  m_gl_render_target,
233  (target == TARGET_TEX2D) ? GL_TEXTURE_2D :
234  GL_RENDERBUFFER,
235  WGL_ACCESS_READ_WRITE_NV);
236  if (!m_shared.render_target) {
237  fprintf(stderr, "Error registering shared object using wglDXRegisterObjectNV()\n");
238  return false;
239  }
240 
241  return true;
242  }
243 
245  {
246  m_shared.device = wglDXOpenDeviceNV(m_device);
247  if (m_shared.device == NULL) {
248  fprintf(stderr, "Error opening shared device using wglDXOpenDeviceNV()\n");
249  return GHOST_kFailure;
250  }
251 
252  /* Build the renderbuffer. */
253  glGenRenderbuffers(1, &m_gl_render_target);
254  glBindRenderbuffer(GL_RENDERBUFFER, m_gl_render_target);
255 
256  if (!reregisterSharedObject(TARGET_RENDERBUF)) {
257  glBindRenderbuffer(GL_RENDERBUFFER, 0);
258  if (m_gl_render_target) {
259  glDeleteRenderbuffers(1, &m_gl_render_target);
260  }
261  /* Fall back to texture 2d. */
262  m_use_gl_texture2d = true;
263  glGenTextures(1, &m_gl_render_target);
264  glBindTexture(GL_TEXTURE_2D, m_gl_render_target);
265 
266  reregisterSharedObject(TARGET_TEX2D);
267  }
268 
269  /* Build the framebuffer */
270  glGenFramebuffers(1, &m_shared.fbo);
271  glBindFramebuffer(GL_FRAMEBUFFER, m_shared.fbo);
272  if (m_use_gl_texture2d) {
273  glFramebufferTexture2D(
274  GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_gl_render_target, 0);
275  }
276  else {
277  glFramebufferRenderbuffer(
278  GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_gl_render_target);
279  }
280  m_is_initialized = true;
281 
282  return GHOST_kSuccess;
283  }
284 
285  void ensureUpdated(unsigned int width, unsigned int height)
286  {
287  if (m_is_initialized == false) {
288  initialize();
289  }
290 
291  if ((m_cur_width != width) || (m_cur_height != height)) {
292  m_cur_width = width;
293  m_cur_height = height;
294  reregisterSharedObject(m_use_gl_texture2d ? TARGET_TEX2D : TARGET_RENDERBUF);
295  }
296  }
297 
298  GHOST_TSuccess blit(unsigned int width, unsigned int height)
299  {
300  GLint fbo;
301  glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &fbo);
302 
304  return GHOST_kFailure;
305  }
306 
308 
309 #ifdef NDEBUG
310  const float clear_col[] = {0.8f, 0.5f, 1.0f, 1.0f};
311  m_device_ctx->ClearRenderTargetView(m_render_target, clear_col);
312 #endif
313  m_device_ctx->OMSetRenderTargets(1, &m_render_target, nullptr);
314 
315  beginGLOnly();
316 
317  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_shared.fbo);
318  GLenum err = glCheckFramebufferStatus(GL_FRAMEBUFFER);
319  if (err != GL_FRAMEBUFFER_COMPLETE) {
320  fprintf(
321  stderr, "Error: Framebuffer for shared DirectX-OpenGL resource incomplete %u\n", err);
322  return GHOST_kFailure;
323  }
324 
325  /* No glBlitNamedFramebuffer, gotta be 3.3 compatible. */
326  glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
327  glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
328 
329  glBindFramebuffer(GL_FRAMEBUFFER, fbo);
330 
331  endGLOnly();
332 
333  return GHOST_kSuccess;
334  }
335 
336  ID3D11RenderTargetView *m_render_target{nullptr};
337  ID3D11Texture2D *m_render_target_tex{nullptr};
338 
339  private:
340  void beginGLOnly()
341  {
342  wglDXLockObjectsNV(m_shared.device, 1, &m_shared.render_target);
343  }
344  void endGLOnly()
345  {
346  wglDXUnlockObjectsNV(m_shared.device, 1, &m_shared.render_target);
347  }
348 
349  ID3D11Device *m_device;
350  ID3D11DeviceContext *m_device_ctx;
351  GLuint m_gl_render_target;
352  unsigned int m_cur_width, m_cur_height;
353  bool m_is_initialized{false};
354  bool m_use_gl_texture2d{false};
355 };
356 
358  unsigned int width,
359  unsigned int height,
361  ID3D11RenderTargetView *render_target)
362 {
363  if (!(WGL_NV_DX_interop && WGL_NV_DX_interop2)) {
364  fprintf(stderr,
365  "Error: Can't render OpenGL framebuffer using Direct3D. NV_DX_interop extension not "
366  "available.");
367  return nullptr;
368  }
370  m_device, m_device_ctx, width, height, format, render_target);
371 
372  return shared_res;
373 }
375  unsigned int height,
377 {
378  return createSharedOpenGLResource(width, height, format, nullptr);
379 }
380 
382 {
383  delete shared_res;
384 }
385 
387  unsigned int width,
388  unsigned int height)
389 {
390  return shared_res->blit(width, height);
391 }
392 
394 {
395  return shared_res->m_render_target_tex;
396 }
GHOST_TSuccess
Definition: GHOST_Types.h:74
@ GHOST_kFailure
Definition: GHOST_Types.h:74
@ GHOST_kSuccess
Definition: GHOST_Types.h:74
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei height
_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 const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei width
GHOST_TSuccess activateDrawingContext()
GHOST_TSuccess releaseNativeHandles()
GHOST_TSuccess swapBuffers()
GHOST_ContextD3D(bool stereoVisual, HWND hWnd)
GHOST_TSuccess blitFromOpenGLContext(class GHOST_SharedOpenGLResource *shared_res, unsigned int width, unsigned int height)
ID3D11Texture2D * getSharedTexture2D(class GHOST_SharedOpenGLResource *shared_res)
class GHOST_SharedOpenGLResource * createSharedOpenGLResource(unsigned int width, unsigned int height, DXGI_FORMAT format, ID3D11RenderTargetView *render_target)
GHOST_TSuccess initializeDrawingContext()
void disposeSharedOpenGLResource(class GHOST_SharedOpenGLResource *shared_res)
GHOST_TSuccess releaseDrawingContext()
void ensureUpdated(unsigned int width, unsigned int height)
GHOST_TSuccess blit(unsigned int width, unsigned int height)
GHOST_SharedOpenGLResource(ID3D11Device *device, ID3D11DeviceContext *device_ctx, unsigned int width, unsigned int height, DXGI_FORMAT format, ID3D11RenderTargetView *render_target=nullptr)
bool reregisterSharedObject(RenderTarget target)
ID3D11RenderTargetView * m_render_target
ID3D11Texture2D * m_render_target_tex
format
Definition: logImageCore.h:38
static FT_Error err