1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 4; -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "SharedSurfaceD3D11Interop.h"
11 #include "GLContext.h"
12 #include "WGLLibrary.h"
13 #include "nsPrintfCString.h"
14 #include "mozilla/gfx/DeviceManagerDx.h"
20 Sample Code for WGL_NV_DX_interop2:
21 Example: Render to Direct3D 11 backbuffer with openGL:
23 // create D3D11 device, context and swap chain.
25 ID3D11DeviceContext *devCtx;
26 IDXGISwapChain *swapChain;
28 DXGI_SWAP_CHAIN_DESC scd;
30 <set appropriate swap chain parameters in scd>
32 hr = D3D11CreateDeviceAndSwapChain(
34 D3D_DRIVER_TYPE_HARDWARE, // DriverType
36 0, // Flags (Do not set
37 // D3D11_CREATE_DEVICE_SINGLETHREADED)
38 NULL, // pFeatureLevels
40 D3D11_SDK_VERSION, // SDKVersion
41 &scd, // pSwapChainDesc
42 &swapChain, // ppSwapChain
44 NULL, // pFeatureLevel
45 &devCtx); // ppImmediateContext
47 // Fetch the swapchain backbuffer
48 ID3D11Texture2D *dxColorbuffer;
49 swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID *)&dxColorbuffer);
51 // Create depth stencil texture
52 ID3D11Texture2D *dxDepthBuffer;
53 D3D11_TEXTURE2D_DESC depthDesc;
54 depthDesc.Usage = D3D11_USAGE_DEFAULT;
55 <set other depthDesc parameters appropriately>
58 ID3D11RenderTargetView *colorBufferView;
59 D3D11_RENDER_TARGET_VIEW_DESC rtd;
60 <set rtd parameters appropriately>
61 device->CreateRenderTargetView(dxColorbuffer, &rtd, &colorBufferView);
63 ID3D11DepthStencilView *depthBufferView;
64 D3D11_DEPTH_STENCIL_VIEW_DESC dsd;
65 <set dsd parameters appropriately>
66 device->CreateDepthStencilView(dxDepthBuffer, &dsd, &depthBufferView);
68 // Attach back buffer and depth texture to redertarget for the device.
69 devCtx->OMSetRenderTargets(1, &colorBufferView, depthBufferView);
71 // Register D3D11 device with GL
73 gl_handleD3D = wglDXOpenDeviceNV(device);
75 // register the Direct3D color and depth/stencil buffers as
76 // renderbuffers in opengl
80 glGenRenderbuffers(2, gl_names);
82 gl_handles[0] = wglDXRegisterObjectNV(gl_handleD3D, dxColorBuffer,
85 WGL_ACCESS_READ_WRITE_NV);
87 gl_handles[1] = wglDXRegisterObjectNV(gl_handleD3D, dxDepthBuffer,
90 WGL_ACCESS_READ_WRITE_NV);
92 // attach the Direct3D buffers to an FBO
93 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
94 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
95 GL_RENDERBUFFER, gl_names[0]);
96 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
97 GL_RENDERBUFFER, gl_names[1]);
98 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
99 GL_RENDERBUFFER, gl_names[1]);
102 <direct3d renders to the render targets>
104 // lock the render targets for GL access
105 wglDXLockObjectsNVX(gl_handleD3D, 2, gl_handles);
107 <opengl renders to the render targets>
109 // unlock the render targets
110 wglDXUnlockObjectsNVX(gl_handleD3D, 2, gl_handles);
112 <direct3d renders to the render targets and presents
113 the results on the screen>
117 ////////////////////////////////////////////////////////////////////////////////
120 class ScopedContextState final
{
121 ID3D11DeviceContext1
* const mD3DContext
;
122 RefPtr
<ID3DDeviceContextState
> mOldContextState
;
125 ScopedContextState(ID3D11DeviceContext1
* d3dContext
,
126 ID3DDeviceContextState
* newContextState
)
127 : mD3DContext(d3dContext
), mOldContextState(nullptr) {
128 if (!mD3DContext
) return;
130 mD3DContext
->SwapDeviceContextState(newContextState
,
131 getter_AddRefs(mOldContextState
));
134 ~ScopedContextState() {
135 if (!mD3DContext
) return;
137 mD3DContext
->SwapDeviceContextState(mOldContextState
, nullptr);
141 class DXInterop2Device
: public RefCounted
<DXInterop2Device
> {
143 MOZ_DECLARE_REFCOUNTED_TYPENAME(DXInterop2Device
)
145 WGLLibrary
* const mWGL
;
146 const RefPtr
<ID3D11Device
> mD3D
; // Only needed for lifetime guarantee.
147 const HANDLE mInteropDevice
;
148 GLContext
* const mGL
;
151 const RefPtr
<ID3D11DeviceContext1
> mD3DContext
;
152 const RefPtr
<ID3DDeviceContextState
> mContextState
;
154 static already_AddRefed
<DXInterop2Device
> Open(WGLLibrary
* wgl
,
156 MOZ_ASSERT(wgl
->HasDXInterop2());
158 const RefPtr
<ID3D11Device
> d3d
=
159 gfx::DeviceManagerDx::Get()->GetContentDevice();
162 << "DXInterop2Device::Open: Failed to create D3D11 device.";
166 if (!gl
->MakeCurrent()) return nullptr;
168 RefPtr
<ID3D11DeviceContext1
> d3dContext
;
169 RefPtr
<ID3DDeviceContextState
> contextState
;
170 if (gl
->WorkAroundDriverBugs() && gl
->Vendor() == GLVendor::ATI
) {
171 // AMD calls ID3D10Device::Flush, so we need to be in ID3D10Device mode.
172 RefPtr
<ID3D11Device1
> d3d11_1
;
174 d3d
->QueryInterface(__uuidof(ID3D11Device1
), getter_AddRefs(d3d11_1
));
175 if (!SUCCEEDED(hr
)) return nullptr;
177 d3d11_1
->GetImmediateContext1(getter_AddRefs(d3dContext
));
178 MOZ_ASSERT(d3dContext
);
180 const D3D_FEATURE_LEVEL featureLevel10_0
= D3D_FEATURE_LEVEL_10_0
;
181 hr
= d3d11_1
->CreateDeviceContextState(
182 0, &featureLevel10_0
, 1, D3D11_SDK_VERSION
, __uuidof(ID3D10Device
),
183 nullptr, getter_AddRefs(contextState
));
184 if (!SUCCEEDED(hr
)) return nullptr;
187 const auto interopDevice
= wgl
->mSymbols
.fDXOpenDeviceNV(d3d
);
188 if (!interopDevice
) {
189 gfxCriticalNote
<< "DXInterop2Device::Open: DXOpenDevice failed.";
193 return MakeAndAddRef
<DXInterop2Device
>(wgl
, d3d
, interopDevice
, gl
,
194 d3dContext
, contextState
);
197 DXInterop2Device(WGLLibrary
* wgl
, ID3D11Device
* d3d
, HANDLE interopDevice
,
198 GLContext
* gl
, ID3D11DeviceContext1
* d3dContext
,
199 ID3DDeviceContextState
* contextState
)
202 mInteropDevice(interopDevice
),
204 mD3DContext(d3dContext
),
205 mContextState(contextState
) {}
207 ~DXInterop2Device() {
208 const auto isCurrent
= mGL
->MakeCurrent();
210 if (mWGL
->mSymbols
.fDXCloseDeviceNV(mInteropDevice
)) return;
213 // That shouldn't have failed.
214 const uint32_t error
= GetLastError();
215 const nsPrintfCString
errorMessage(
216 "wglDXCloseDevice(0x%p) failed:"
217 " GetLastError(): %u\n",
218 mInteropDevice
, error
);
219 gfxCriticalError() << errorMessage
.BeginReading();
223 HANDLE
RegisterObject(void* d3dObject
, GLuint name
, GLenum type
,
224 GLenum access
) const {
225 if (!mGL
->MakeCurrent()) return nullptr;
227 const ScopedContextState
autoCS(mD3DContext
, mContextState
);
228 const auto ret
= mWGL
->mSymbols
.fDXRegisterObjectNV(
229 mInteropDevice
, d3dObject
, name
, type
, access
);
232 const uint32_t error
= GetLastError();
233 const nsPrintfCString
errorMessage(
234 "wglDXRegisterObject(0x%p, 0x%p, %u, 0x%04x,"
235 " 0x%04x) failed: GetLastError(): %u\n",
236 mInteropDevice
, d3dObject
, name
, type
, access
, error
);
237 gfxCriticalNote
<< errorMessage
.BeginReading();
241 bool UnregisterObject(HANDLE lockHandle
) const {
242 const auto isCurrent
= mGL
->MakeCurrent();
244 const ScopedContextState
autoCS(mD3DContext
, mContextState
);
245 if (mWGL
->mSymbols
.fDXUnregisterObjectNV(mInteropDevice
, lockHandle
))
249 // That shouldn't have failed.
250 const uint32_t error
= GetLastError();
251 const nsPrintfCString
errorMessage(
252 "wglDXUnregisterObject(0x%p, 0x%p) failed:"
253 " GetLastError(): %u\n",
254 mInteropDevice
, lockHandle
, error
);
255 gfxCriticalError() << errorMessage
.BeginReading();
260 bool LockObject(HANDLE lockHandle
) const {
261 MOZ_ASSERT(mGL
->IsCurrent());
263 if (mWGL
->mSymbols
.fDXLockObjectsNV(mInteropDevice
, 1, &lockHandle
))
266 if (!mGL
->MakeCurrent()) return false;
268 gfxCriticalNote
<< "wglDXLockObjects called without mGL being current."
269 << " Retrying after MakeCurrent.";
271 if (mWGL
->mSymbols
.fDXLockObjectsNV(mInteropDevice
, 1, &lockHandle
))
274 const uint32_t error
= GetLastError();
275 const nsPrintfCString
errorMessage(
276 "wglDXLockObjects(0x%p, 1, {0x%p}) failed:"
277 " GetLastError(): %u\n",
278 mInteropDevice
, lockHandle
, error
);
279 gfxCriticalError() << errorMessage
.BeginReading();
283 bool UnlockObject(HANDLE lockHandle
) const {
284 MOZ_ASSERT(mGL
->IsCurrent());
286 if (mWGL
->mSymbols
.fDXUnlockObjectsNV(mInteropDevice
, 1, &lockHandle
))
289 if (!mGL
->MakeCurrent()) return false;
291 gfxCriticalNote
<< "wglDXUnlockObjects called without mGL being current."
292 << " Retrying after MakeCurrent.";
294 if (mWGL
->mSymbols
.fDXUnlockObjectsNV(mInteropDevice
, 1, &lockHandle
))
297 const uint32_t error
= GetLastError();
298 const nsPrintfCString
errorMessage(
299 "wglDXUnlockObjects(0x%p, 1, {0x%p}) failed:"
300 " GetLastError(): %u\n",
301 mInteropDevice
, lockHandle
, error
);
302 gfxCriticalError() << errorMessage
.BeginReading();
307 ////////////////////////////////////////////////////////////////////////////////
311 UniquePtr
<SharedSurface_D3D11Interop
> SharedSurface_D3D11Interop::Create(
312 DXInterop2Device
* interop
, GLContext
* gl
, const gfx::IntSize
& size
,
314 const auto& d3d
= interop
->mD3D
;
316 // Create a texture in case we need to readback.
318 hasAlpha
? DXGI_FORMAT_B8G8R8A8_UNORM
: DXGI_FORMAT_B8G8R8X8_UNORM
;
319 CD3D11_TEXTURE2D_DESC
desc(format
, size
.width
, size
.height
, 1, 1);
320 desc
.MiscFlags
= D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX
;
322 RefPtr
<ID3D11Texture2D
> texD3D
;
323 auto hr
= d3d
->CreateTexture2D(&desc
, nullptr, getter_AddRefs(texD3D
));
325 NS_WARNING("Failed to create texture for CanvasLayer!");
329 RefPtr
<IDXGIResource
> texDXGI
;
330 hr
= texD3D
->QueryInterface(__uuidof(IDXGIResource
), getter_AddRefs(texDXGI
));
332 NS_WARNING("Failed to open texture for sharing!");
337 texDXGI
->GetSharedHandle(&dxgiHandle
);
341 if (!gl
->MakeCurrent()) {
342 NS_WARNING("MakeCurrent failed.");
346 GLuint interopRB
= 0;
347 gl
->fGenRenderbuffers(1, &interopRB
);
348 const auto lockHandle
=
349 interop
->RegisterObject(texD3D
, interopRB
, LOCAL_GL_RENDERBUFFER
,
350 LOCAL_WGL_ACCESS_WRITE_DISCARD_NV
);
352 NS_WARNING("Failed to register D3D object with WGL.");
353 gl
->fDeleteRenderbuffers(1, &interopRB
);
360 GLuint interopFB
= 0;
364 const ScopedBindRenderbuffer
bindRB(gl
, interopRB
);
365 gl
->fGetRenderbufferParameteriv(LOCAL_GL_RENDERBUFFER
,
366 LOCAL_GL_RENDERBUFFER_SAMPLES
, &samples
);
368 if (samples
> 0) { // Intel
369 // Intel's dx_interop GL-side textures have SAMPLES=1, likely because
370 // that's what the D3DTextures technically have. However, SAMPLES=1 is
371 // very different from SAMPLES=0 in GL. For more, see
372 // https://bugzilla.mozilla.org/show_bug.cgi?id=1325835
374 // Our ShSurf tex or rb must be single-sampled.
375 gl
->fGenTextures(1, &prodTex
);
376 const ScopedBindTexture
bindTex(gl
, prodTex
);
377 gl
->TexParams_SetClampNoMips();
379 const GLenum format
= (hasAlpha
? LOCAL_GL_RGBA
: LOCAL_GL_RGB
);
380 const ScopedBindPBO
nullPBO(gl
, LOCAL_GL_PIXEL_UNPACK_BUFFER
);
381 gl
->fTexImage2D(LOCAL_GL_TEXTURE_2D
, 0, format
, size
.width
, size
.height
,
382 0, format
, LOCAL_GL_UNSIGNED_BYTE
, nullptr);
384 gl
->fGenFramebuffers(1, &interopFB
);
385 ScopedBindFramebuffer
bindFB(gl
, interopFB
);
386 gl
->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER
,
387 LOCAL_GL_COLOR_ATTACHMENT0
,
388 LOCAL_GL_RENDERBUFFER
, interopRB
);
389 MOZ_ASSERT(gl
->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER
) ==
390 LOCAL_GL_FRAMEBUFFER_COMPLETE
);
396 typedef SharedSurface_D3D11Interop ptrT
;
397 UniquePtr
<ptrT
> ret(new ptrT(gl
, size
, hasAlpha
, prodTex
, interopFB
,
398 interopRB
, interop
, lockHandle
, texD3D
,
403 SharedSurface_D3D11Interop::SharedSurface_D3D11Interop(
404 GLContext
* gl
, const gfx::IntSize
& size
, bool hasAlpha
, GLuint prodTex
,
405 GLuint interopFB
, GLuint interopRB
, DXInterop2Device
* interop
,
406 HANDLE lockHandle
, ID3D11Texture2D
* texD3D
, HANDLE dxgiHandle
)
408 SharedSurfaceType::DXGLInterop2
,
409 prodTex
? AttachmentType::GLTexture
: AttachmentType::GLRenderbuffer
,
410 gl
, size
, hasAlpha
, true),
412 mInteropFB(interopFB
),
413 mInteropRB(interopRB
),
415 mLockHandle(lockHandle
),
417 mDXGIHandle(dxgiHandle
),
418 mNeedsFinish(gfxPrefs::WebGLDXGLNeedsFinish()),
419 mLockedForGL(false) {
420 MOZ_ASSERT(bool(mProdTex
) == bool(mInteropFB
));
423 SharedSurface_D3D11Interop::~SharedSurface_D3D11Interop() {
424 MOZ_ASSERT(!IsProducerAcquired());
426 if (!mGL
|| !mGL
->MakeCurrent()) return;
428 if (!mInterop
->UnregisterObject(mLockHandle
)) {
429 NS_WARNING("Failed to release mLockHandle, possibly leaking it.");
432 mGL
->fDeleteTextures(1, &mProdTex
);
433 mGL
->fDeleteFramebuffers(1, &mInteropFB
);
434 mGL
->fDeleteRenderbuffers(1, &mInteropRB
);
437 void SharedSurface_D3D11Interop::ProducerAcquireImpl() {
438 MOZ_ASSERT(!mLockedForGL
);
440 // Now we have the mutex, we can lock for GL.
441 MOZ_ALWAYS_TRUE(mInterop
->LockObject(mLockHandle
));
446 void SharedSurface_D3D11Interop::ProducerReleaseImpl() {
447 MOZ_ASSERT(mLockedForGL
);
450 const ScopedBindFramebuffer
bindFB(mGL
, mInteropFB
);
451 mGL
->BlitHelper()->DrawBlitTextureToFramebuffer(mProdTex
, mSize
, mSize
);
457 // We probably don't even need this.
460 MOZ_ALWAYS_TRUE(mInterop
->UnlockObject(mLockHandle
));
462 mLockedForGL
= false;
465 bool SharedSurface_D3D11Interop::ToSurfaceDescriptor(
466 layers::SurfaceDescriptor
* const out_descriptor
) {
468 (mHasAlpha
? gfx::SurfaceFormat::B8G8R8A8
: gfx::SurfaceFormat::B8G8R8X8
);
470 layers::SurfaceDescriptorD3D10(WindowsHandle(mDXGIHandle
), format
, mSize
);
474 //////////////////////////////////////////////////////////////////////////////////////////
478 UniquePtr
<SurfaceFactory_D3D11Interop
> SurfaceFactory_D3D11Interop::Create(
479 GLContext
* gl
, const SurfaceCaps
& caps
, layers::LayersIPCChannel
* allocator
,
480 const layers::TextureFlags
& flags
) {
481 WGLLibrary
* wgl
= &sWGLLib
;
482 if (!wgl
|| !wgl
->HasDXInterop2()) return nullptr;
484 const RefPtr
<DXInterop2Device
> interop
= DXInterop2Device::Open(wgl
, gl
);
486 NS_WARNING("Failed to open D3D device for use by WGL.");
490 typedef SurfaceFactory_D3D11Interop ptrT
;
491 UniquePtr
<ptrT
> ret(new ptrT(gl
, caps
, allocator
, flags
, interop
));
495 SurfaceFactory_D3D11Interop::SurfaceFactory_D3D11Interop(
496 GLContext
* gl
, const SurfaceCaps
& caps
, layers::LayersIPCChannel
* allocator
,
497 const layers::TextureFlags
& flags
, DXInterop2Device
* interop
)
498 : SurfaceFactory(SharedSurfaceType::DXGLInterop2
, gl
, caps
, allocator
,
502 SurfaceFactory_D3D11Interop::~SurfaceFactory_D3D11Interop() {}
505 } // namespace mozilla