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
17 /** \file
18 * \ingroup GHOST
19 *
20 * For testing purposes, it can be useful to do some DirectX only drawing. A patch for this can be
21 * found here: https://developer.blender.org/P1284
22 */
23
24 #include <iostream>
25 #include <string>
26
27 #include <GL/glew.h>
28 #include <GL/wglew.h>
29
30 #include "GHOST_ContextD3D.h"
31 #include "GHOST_ContextWGL.h" /* For shared drawing */
32
33 HMODULE GHOST_ContextD3D::s_d3d_lib = NULL;
34 PFN_D3D11_CREATE_DEVICE GHOST_ContextD3D::s_D3D11CreateDeviceFn = NULL;
35
GHOST_ContextD3D(bool stereoVisual,HWND hWnd)36 GHOST_ContextD3D::GHOST_ContextD3D(bool stereoVisual, HWND hWnd)
37 : GHOST_Context(stereoVisual), m_hWnd(hWnd)
38 {
39 }
40
~GHOST_ContextD3D()41 GHOST_ContextD3D::~GHOST_ContextD3D()
42 {
43 m_device->Release();
44 m_device_ctx->ClearState();
45 m_device_ctx->Release();
46 }
47
swapBuffers()48 GHOST_TSuccess GHOST_ContextD3D::swapBuffers()
49 {
50 return GHOST_kSuccess;
51 }
52
activateDrawingContext()53 GHOST_TSuccess GHOST_ContextD3D::activateDrawingContext()
54 {
55 return GHOST_kFailure;
56 }
57
releaseDrawingContext()58 GHOST_TSuccess GHOST_ContextD3D::releaseDrawingContext()
59 {
60 return GHOST_kFailure;
61 }
62
setupD3DLib()63 GHOST_TSuccess GHOST_ContextD3D::setupD3DLib()
64 {
65 if (s_d3d_lib == NULL) {
66 s_d3d_lib = LoadLibraryA("d3d11.dll");
67
68 WIN32_CHK(s_d3d_lib != NULL);
69
70 if (s_d3d_lib == NULL) {
71 fprintf(stderr, "LoadLibrary(\"d3d11.dll\") failed!\n");
72 return GHOST_kFailure;
73 }
74 }
75
76 if (s_D3D11CreateDeviceFn == NULL) {
77 s_D3D11CreateDeviceFn = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(s_d3d_lib,
78 "D3D11CreateDevice");
79
80 WIN32_CHK(s_D3D11CreateDeviceFn != NULL);
81
82 if (s_D3D11CreateDeviceFn == NULL) {
83 fprintf(stderr, "GetProcAddress(s_d3d_lib, \"D3D11CreateDevice\") failed!\n");
84 return GHOST_kFailure;
85 }
86 }
87
88 return GHOST_kSuccess;
89 }
90
initializeDrawingContext()91 GHOST_TSuccess GHOST_ContextD3D::initializeDrawingContext()
92 {
93 if (setupD3DLib() == GHOST_kFailure) {
94 return GHOST_kFailure;
95 }
96
97 HRESULT hres = s_D3D11CreateDeviceFn(
98 NULL,
99 D3D_DRIVER_TYPE_HARDWARE,
100 NULL,
101 /* For debugging you may want to pass D3D11_CREATE_DEVICE_DEBUG here, but that requires
102 * additional setup, see
103 * https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-devices-layers#debug-layer.
104 */
105 0,
106 NULL,
107 0,
108 D3D11_SDK_VERSION,
109 &m_device,
110 NULL,
111 &m_device_ctx);
112
113 WIN32_CHK(hres == S_OK);
114
115 return GHOST_kSuccess;
116 }
117
releaseNativeHandles()118 GHOST_TSuccess GHOST_ContextD3D::releaseNativeHandles()
119 {
120 return GHOST_kFailure;
121 }
122
123 class GHOST_SharedOpenGLResource {
124 struct SharedData {
125 HANDLE device;
126 GLuint fbo;
127 HANDLE render_buf{nullptr};
128 } m_shared;
129
130 public:
GHOST_SharedOpenGLResource(ID3D11Device * device,ID3D11DeviceContext * device_ctx,unsigned int width,unsigned int height,ID3D11RenderTargetView * render_target=nullptr)131 GHOST_SharedOpenGLResource(ID3D11Device *device,
132 ID3D11DeviceContext *device_ctx,
133 unsigned int width,
134 unsigned int height,
135 ID3D11RenderTargetView *render_target = nullptr)
136 : m_device(device), m_device_ctx(device_ctx), m_cur_width(width), m_cur_height(height)
137 {
138 ID3D11Resource *backbuffer_res;
139
140 if (!render_target) {
141 D3D11_TEXTURE2D_DESC texDesc{};
142 D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc{};
143 ID3D11Texture2D *tex;
144
145 texDesc.Width = width;
146 texDesc.Height = height;
147 texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
148 texDesc.SampleDesc.Count = 1;
149 texDesc.ArraySize = 1;
150 texDesc.MipLevels = 1;
151 texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
152
153 device->CreateTexture2D(&texDesc, NULL, &tex);
154 if (!tex) {
155 /* If texture creation fails, we just return and leave the render target unset. So it needs
156 * to be NULL-checked before use. */
157 fprintf(stderr, "Error creating texture for shared DirectX-OpenGL resource\n");
158 return;
159 }
160
161 renderTargetViewDesc.Format = texDesc.Format;
162 renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
163 renderTargetViewDesc.Texture2D.MipSlice = 0;
164
165 device->CreateRenderTargetView(tex, &renderTargetViewDesc, &render_target);
166
167 tex->Release();
168 }
169
170 m_render_target = render_target;
171 if (m_render_target) {
172 m_render_target->GetResource(&backbuffer_res);
173 }
174 if (backbuffer_res) {
175 backbuffer_res->QueryInterface<ID3D11Texture2D>(&m_render_target_tex);
176 backbuffer_res->Release();
177 }
178
179 if (!m_render_target || !m_render_target_tex) {
180 fprintf(stderr, "Error creating render target for shared DirectX-OpenGL resource\n");
181 return;
182 }
183 }
184
~GHOST_SharedOpenGLResource()185 ~GHOST_SharedOpenGLResource()
186 {
187 if (m_render_target_tex) {
188 m_render_target_tex->Release();
189 }
190 if (m_render_target) {
191 m_render_target->Release();
192 }
193
194 if (m_is_initialized) {
195 if (m_shared.render_buf) {
196 wglDXUnregisterObjectNV(m_shared.device, m_shared.render_buf);
197 }
198 if (m_shared.device) {
199 wglDXCloseDeviceNV(m_shared.device);
200 }
201 glDeleteFramebuffers(1, &m_shared.fbo);
202 glDeleteRenderbuffers(1, &m_gl_render_buf);
203 }
204 }
205
reregisterSharedObject()206 void reregisterSharedObject()
207 {
208 if (m_shared.render_buf) {
209 wglDXUnregisterObjectNV(m_shared.device, m_shared.render_buf);
210 }
211
212 if (!m_render_target_tex) {
213 return;
214 }
215
216 m_shared.render_buf = wglDXRegisterObjectNV(m_shared.device,
217 m_render_target_tex,
218 m_gl_render_buf,
219 GL_RENDERBUFFER,
220 WGL_ACCESS_READ_WRITE_NV);
221
222 if (!m_shared.render_buf) {
223 fprintf(stderr, "Error registering shared object using wglDXRegisterObjectNV()\n");
224 return;
225 }
226 }
227
initialize()228 GHOST_TSuccess initialize()
229 {
230 m_shared.device = wglDXOpenDeviceNV(m_device);
231 if (m_shared.device == NULL) {
232 fprintf(stderr, "Error opening shared device using wglDXOpenDeviceNV()\n");
233 return GHOST_kFailure;
234 }
235
236 /* Build the renderbuffer. */
237 glGenRenderbuffers(1, &m_gl_render_buf);
238 glBindRenderbuffer(GL_RENDERBUFFER, m_gl_render_buf);
239
240 reregisterSharedObject();
241
242 /* Build the framebuffer */
243 glGenFramebuffers(1, &m_shared.fbo);
244 glBindFramebuffer(GL_FRAMEBUFFER, m_shared.fbo);
245 glFramebufferRenderbuffer(
246 GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_gl_render_buf);
247 m_is_initialized = true;
248
249 return GHOST_kSuccess;
250 }
251
ensureUpdated(unsigned int width,unsigned int height)252 void ensureUpdated(unsigned int width, unsigned int height)
253 {
254 if (m_is_initialized == false) {
255 initialize();
256 }
257
258 if ((m_cur_width != width) || (m_cur_height != height)) {
259 m_cur_width = width;
260 m_cur_height = height;
261 reregisterSharedObject();
262 }
263 }
264
blit(unsigned int width,unsigned int height)265 GHOST_TSuccess blit(unsigned int width, unsigned int height)
266 {
267 GLint fbo;
268 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &fbo);
269
270 if (!m_render_target || !m_render_target_tex) {
271 return GHOST_kFailure;
272 }
273
274 ensureUpdated(width, height);
275
276 #ifdef NDEBUG
277 const float clear_col[] = {0.8f, 0.5f, 1.0f, 1.0f};
278 m_device_ctx->ClearRenderTargetView(m_render_target, clear_col);
279 #endif
280 m_device_ctx->OMSetRenderTargets(1, &m_render_target, nullptr);
281
282 beginGLOnly();
283
284 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_shared.fbo);
285 GLenum err = glCheckFramebufferStatus(GL_FRAMEBUFFER);
286 if (err != GL_FRAMEBUFFER_COMPLETE) {
287 fprintf(
288 stderr, "Error: Framebuffer for shared DirectX-OpenGL resource incomplete %u\n", err);
289 return GHOST_kFailure;
290 }
291
292 /* No glBlitNamedFramebuffer, gotta be 3.3 compatible. */
293 glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
294 glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
295
296 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
297
298 endGLOnly();
299
300 return GHOST_kSuccess;
301 }
302
303 ID3D11RenderTargetView *m_render_target{nullptr};
304 ID3D11Texture2D *m_render_target_tex{nullptr};
305
306 private:
beginGLOnly()307 void beginGLOnly()
308 {
309 wglDXLockObjectsNV(m_shared.device, 1, &m_shared.render_buf);
310 }
endGLOnly()311 void endGLOnly()
312 {
313 wglDXUnlockObjectsNV(m_shared.device, 1, &m_shared.render_buf);
314 }
315
316 ID3D11Device *m_device;
317 ID3D11DeviceContext *m_device_ctx;
318 GLuint m_gl_render_buf;
319 unsigned int m_cur_width, m_cur_height;
320 bool m_is_initialized{false};
321 };
322
createSharedOpenGLResource(unsigned int width,unsigned int height,ID3D11RenderTargetView * render_target)323 GHOST_SharedOpenGLResource *GHOST_ContextD3D::createSharedOpenGLResource(
324 unsigned int width, unsigned int height, ID3D11RenderTargetView *render_target)
325 {
326 if (!(WGL_NV_DX_interop && WGL_NV_DX_interop2)) {
327 fprintf(stderr,
328 "Error: Can't render OpenGL framebuffer using Direct3D. NV_DX_interop extension not "
329 "available.");
330 return nullptr;
331 }
332 GHOST_SharedOpenGLResource *shared_res = new GHOST_SharedOpenGLResource(
333 m_device, m_device_ctx, width, height, render_target);
334
335 return shared_res;
336 }
createSharedOpenGLResource(unsigned int width,unsigned int height)337 GHOST_SharedOpenGLResource *GHOST_ContextD3D::createSharedOpenGLResource(unsigned int width,
338 unsigned int height)
339 {
340 return createSharedOpenGLResource(width, height, nullptr);
341 }
342
disposeSharedOpenGLResource(GHOST_SharedOpenGLResource * shared_res)343 void GHOST_ContextD3D::disposeSharedOpenGLResource(GHOST_SharedOpenGLResource *shared_res)
344 {
345 delete shared_res;
346 }
347
blitFromOpenGLContext(GHOST_SharedOpenGLResource * shared_res,unsigned int width,unsigned int height)348 GHOST_TSuccess GHOST_ContextD3D::blitFromOpenGLContext(GHOST_SharedOpenGLResource *shared_res,
349 unsigned int width,
350 unsigned int height)
351 {
352 return shared_res->blit(width, height);
353 }
354
getSharedTexture2D(GHOST_SharedOpenGLResource * shared_res)355 ID3D11Texture2D *GHOST_ContextD3D::getSharedTexture2D(GHOST_SharedOpenGLResource *shared_res)
356 {
357 return shared_res->m_render_target_tex;
358 }
359