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