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