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"
10 #include "GLContext.h"
11 #include "GLBlitHelper.h"
12 #include "MozFramebuffer.h"
13 #include "ScopedGLHelpers.h"
14 #include "WGLLibrary.h"
15 #include "nsPrintfCString.h"
16 #include "mozilla/gfx/DeviceManagerDx.h"
17 #include "mozilla/gfx/FileHandleWrapper.h"
18 #include "mozilla/gfx/Logging.h"
19 #include "mozilla/layers/LayersSurfaces.h"
20 #include "mozilla/StaticPrefs_webgl.h"
26 Sample Code for WGL_NV_DX_interop2:
27 Example: Render to Direct3D 11 backbuffer with openGL:
29 // create D3D11 device, context and swap chain.
31 ID3D11DeviceContext *devCtx;
32 IDXGISwapChain *swapChain;
34 DXGI_SWAP_CHAIN_DESC scd;
36 <set appropriate swap chain parameters in scd>
38 hr = D3D11CreateDeviceAndSwapChain(
40 D3D_DRIVER_TYPE_HARDWARE, // DriverType
42 0, // Flags (Do not set
43 // D3D11_CREATE_DEVICE_SINGLETHREADED)
44 NULL, // pFeatureLevels
46 D3D11_SDK_VERSION, // SDKVersion
47 &scd, // pSwapChainDesc
48 &swapChain, // ppSwapChain
50 NULL, // pFeatureLevel
51 &devCtx); // ppImmediateContext
53 // Fetch the swapchain backbuffer
54 ID3D11Texture2D *dxColorbuffer;
55 swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID *)&dxColorbuffer);
57 // Create depth stencil texture
58 ID3D11Texture2D *dxDepthBuffer;
59 D3D11_TEXTURE2D_DESC depthDesc;
60 depthDesc.Usage = D3D11_USAGE_DEFAULT;
61 <set other depthDesc parameters appropriately>
64 ID3D11RenderTargetView *colorBufferView;
65 D3D11_RENDER_TARGET_VIEW_DESC rtd;
66 <set rtd parameters appropriately>
67 device->CreateRenderTargetView(dxColorbuffer, &rtd, &colorBufferView);
69 ID3D11DepthStencilView *depthBufferView;
70 D3D11_DEPTH_STENCIL_VIEW_DESC dsd;
71 <set dsd parameters appropriately>
72 device->CreateDepthStencilView(dxDepthBuffer, &dsd, &depthBufferView);
74 // Attach back buffer and depth texture to redertarget for the device.
75 devCtx->OMSetRenderTargets(1, &colorBufferView, depthBufferView);
77 // Register D3D11 device with GL
79 gl_handleD3D = wglDXOpenDeviceNV(device);
81 // register the Direct3D color and depth/stencil buffers as
82 // renderbuffers in opengl
86 glGenRenderbuffers(2, gl_names);
88 gl_handles[0] = wglDXRegisterObjectNV(gl_handleD3D, dxColorBuffer,
91 WGL_ACCESS_READ_WRITE_NV);
93 gl_handles[1] = wglDXRegisterObjectNV(gl_handleD3D, dxDepthBuffer,
96 WGL_ACCESS_READ_WRITE_NV);
98 // attach the Direct3D buffers to an FBO
99 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
100 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
101 GL_RENDERBUFFER, gl_names[0]);
102 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
103 GL_RENDERBUFFER, gl_names[1]);
104 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
105 GL_RENDERBUFFER, gl_names[1]);
108 <direct3d renders to the render targets>
110 // lock the render targets for GL access
111 wglDXLockObjectsNVX(gl_handleD3D, 2, gl_handles);
113 <opengl renders to the render targets>
115 // unlock the render targets
116 wglDXUnlockObjectsNVX(gl_handleD3D, 2, gl_handles);
118 <direct3d renders to the render targets and presents
119 the results on the screen>
123 ////////////////////////////////////////////////////////////////////////////////
126 class ScopedContextState final
{
127 ID3D11DeviceContext1
* const mD3DContext
;
128 RefPtr
<ID3DDeviceContextState
> mOldContextState
;
131 ScopedContextState(ID3D11DeviceContext1
* d3dContext
,
132 ID3DDeviceContextState
* newContextState
)
133 : mD3DContext(d3dContext
), mOldContextState(nullptr) {
134 if (!mD3DContext
) return;
136 mD3DContext
->SwapDeviceContextState(newContextState
,
137 getter_AddRefs(mOldContextState
));
140 ~ScopedContextState() {
141 if (!mD3DContext
) return;
143 mD3DContext
->SwapDeviceContextState(mOldContextState
, nullptr);
147 class DXInterop2Device
: public RefCounted
<DXInterop2Device
> {
149 MOZ_DECLARE_REFCOUNTED_TYPENAME(DXInterop2Device
)
151 WGLLibrary
* const mWGL
;
152 const RefPtr
<ID3D11Device
> mD3D
; // Only needed for lifetime guarantee.
153 const HANDLE mInteropDevice
;
154 GLContext
* const mGL
;
157 const RefPtr
<ID3D11DeviceContext1
> mD3DContext
;
158 const RefPtr
<ID3DDeviceContextState
> mContextState
;
160 static already_AddRefed
<DXInterop2Device
> Open(WGLLibrary
* wgl
,
162 MOZ_ASSERT(wgl
->HasDXInterop2());
164 const RefPtr
<ID3D11Device
> d3d
=
165 gfx::DeviceManagerDx::Get()->GetContentDevice();
168 << "DXInterop2Device::Open: Failed to create D3D11 device.";
172 if (!gl
->MakeCurrent()) return nullptr;
174 RefPtr
<ID3D11DeviceContext1
> d3dContext
;
175 RefPtr
<ID3DDeviceContextState
> contextState
;
176 if (gl
->WorkAroundDriverBugs() && gl
->Vendor() == GLVendor::ATI
) {
177 // AMD calls ID3D10Device::Flush, so we need to be in ID3D10Device mode.
178 RefPtr
<ID3D11Device1
> d3d11_1
;
180 d3d
->QueryInterface(__uuidof(ID3D11Device1
), getter_AddRefs(d3d11_1
));
181 if (!SUCCEEDED(hr
)) return nullptr;
183 d3d11_1
->GetImmediateContext1(getter_AddRefs(d3dContext
));
184 MOZ_ASSERT(d3dContext
);
186 const D3D_FEATURE_LEVEL featureLevel10_0
= D3D_FEATURE_LEVEL_10_0
;
187 hr
= d3d11_1
->CreateDeviceContextState(
188 0, &featureLevel10_0
, 1, D3D11_SDK_VERSION
, __uuidof(ID3D10Device
),
189 nullptr, getter_AddRefs(contextState
));
190 if (!SUCCEEDED(hr
)) return nullptr;
193 const auto interopDevice
= wgl
->mSymbols
.fDXOpenDeviceNV(d3d
);
194 if (!interopDevice
) {
195 gfxCriticalNote
<< "DXInterop2Device::Open: DXOpenDevice failed.";
199 return MakeAndAddRef
<DXInterop2Device
>(wgl
, d3d
, interopDevice
, gl
,
200 d3dContext
, contextState
);
203 DXInterop2Device(WGLLibrary
* wgl
, ID3D11Device
* d3d
, HANDLE interopDevice
,
204 GLContext
* gl
, ID3D11DeviceContext1
* d3dContext
,
205 ID3DDeviceContextState
* contextState
)
208 mInteropDevice(interopDevice
),
210 mD3DContext(d3dContext
),
211 mContextState(contextState
) {}
213 ~DXInterop2Device() {
214 const auto isCurrent
= mGL
->MakeCurrent();
216 if (mWGL
->mSymbols
.fDXCloseDeviceNV(mInteropDevice
)) return;
219 // That shouldn't have failed.
220 const uint32_t error
= GetLastError();
221 const nsPrintfCString
errorMessage(
222 "wglDXCloseDevice(0x%p) failed:"
223 " GetLastError(): %u\n",
224 mInteropDevice
, error
);
225 gfxCriticalError() << errorMessage
.BeginReading();
229 HANDLE
RegisterObject(void* d3dObject
, GLuint name
, GLenum type
,
230 GLenum access
) const {
231 if (!mGL
->MakeCurrent()) return nullptr;
233 const ScopedContextState
autoCS(mD3DContext
, mContextState
);
234 const auto ret
= mWGL
->mSymbols
.fDXRegisterObjectNV(
235 mInteropDevice
, d3dObject
, name
, type
, access
);
238 const uint32_t error
= GetLastError();
239 const nsPrintfCString
errorMessage(
240 "wglDXRegisterObject(0x%p, 0x%p, %u, 0x%04x,"
241 " 0x%04x) failed: GetLastError(): %u\n",
242 mInteropDevice
, d3dObject
, name
, type
, access
, error
);
243 gfxCriticalNote
<< errorMessage
.BeginReading();
247 bool UnregisterObject(HANDLE lockHandle
) const {
248 const auto isCurrent
= mGL
->MakeCurrent();
250 const ScopedContextState
autoCS(mD3DContext
, mContextState
);
251 if (mWGL
->mSymbols
.fDXUnregisterObjectNV(mInteropDevice
, lockHandle
))
255 // That shouldn't have failed.
256 const uint32_t error
= GetLastError();
257 const nsPrintfCString
errorMessage(
258 "wglDXUnregisterObject(0x%p, 0x%p) failed:"
259 " GetLastError(): %u\n",
260 mInteropDevice
, lockHandle
, error
);
261 gfxCriticalError() << errorMessage
.BeginReading();
266 bool LockObject(HANDLE lockHandle
) const {
267 MOZ_ASSERT(mGL
->IsCurrent());
269 if (mWGL
->mSymbols
.fDXLockObjectsNV(mInteropDevice
, 1, &lockHandle
))
272 if (!mGL
->MakeCurrent()) return false;
274 gfxCriticalNote
<< "wglDXLockObjects called without mGL being current."
275 << " Retrying after MakeCurrent.";
277 if (mWGL
->mSymbols
.fDXLockObjectsNV(mInteropDevice
, 1, &lockHandle
))
280 const uint32_t error
= GetLastError();
281 const nsPrintfCString
errorMessage(
282 "wglDXLockObjects(0x%p, 1, {0x%p}) failed:"
283 " GetLastError(): %u\n",
284 mInteropDevice
, lockHandle
, error
);
285 gfxCriticalError() << errorMessage
.BeginReading();
289 bool UnlockObject(HANDLE lockHandle
) const {
290 MOZ_ASSERT(mGL
->IsCurrent());
292 if (mWGL
->mSymbols
.fDXUnlockObjectsNV(mInteropDevice
, 1, &lockHandle
))
295 if (!mGL
->MakeCurrent()) return false;
297 gfxCriticalNote
<< "wglDXUnlockObjects called without mGL being current."
298 << " Retrying after MakeCurrent.";
300 if (mWGL
->mSymbols
.fDXUnlockObjectsNV(mInteropDevice
, 1, &lockHandle
))
303 const uint32_t error
= GetLastError();
304 const nsPrintfCString
errorMessage(
305 "wglDXUnlockObjects(0x%p, 1, {0x%p}) failed:"
306 " GetLastError(): %u\n",
307 mInteropDevice
, lockHandle
, error
);
308 gfxCriticalError() << errorMessage
.BeginReading();
313 ////////////////////////////////////////////////////////////////////////////////
317 UniquePtr
<SharedSurface_D3D11Interop
> SharedSurface_D3D11Interop::Create(
318 const SharedSurfaceDesc
& desc
, DXInterop2Device
* interop
) {
319 const auto& gl
= desc
.gl
;
320 const auto& size
= desc
.size
;
321 const auto& d3d
= interop
->mD3D
;
323 auto data
= Data
{interop
};
325 // Create a texture in case we need to readback.
326 const DXGI_FORMAT format
= DXGI_FORMAT_B8G8R8A8_UNORM
;
327 CD3D11_TEXTURE2D_DESC
texDesc(format
, size
.width
, size
.height
, 1, 1);
328 texDesc
.MiscFlags
= D3D11_RESOURCE_MISC_SHARED_NTHANDLE
|
329 D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX
;
332 d3d
->CreateTexture2D(&texDesc
, nullptr, getter_AddRefs(data
.texD3D
));
334 NS_WARNING("Failed to create texture for CanvasLayer!");
338 RefPtr
<IDXGIResource1
> texDXGI
;
339 hr
= data
.texD3D
->QueryInterface(__uuidof(IDXGIResource1
),
340 getter_AddRefs(texDXGI
));
342 NS_WARNING("Failed to open texture for sharing!");
346 HANDLE sharedHandle
= nullptr;
347 texDXGI
->CreateSharedHandle(
348 nullptr, DXGI_SHARED_RESOURCE_READ
| DXGI_SHARED_RESOURCE_WRITE
, nullptr,
351 data
.dxgiHandle
= new gfx::FileHandleWrapper(UniqueFileHandle(sharedHandle
));
355 if (!gl
->MakeCurrent()) {
356 NS_WARNING("MakeCurrent failed.");
360 data
.interopRb
= MakeUnique
<Renderbuffer
>(*gl
);
361 data
.lockHandle
= interop
->RegisterObject(data
.texD3D
, data
.interopRb
->name
,
362 LOCAL_GL_RENDERBUFFER
,
363 LOCAL_WGL_ACCESS_WRITE_DISCARD_NV
);
364 if (!data
.lockHandle
) {
365 NS_WARNING("Failed to register D3D object with WGL.");
369 auto fbForDrawing
= MozFramebuffer::CreateForBacking(
370 gl
, size
, 0, false, LOCAL_GL_RENDERBUFFER
, data
.interopRb
->name
);
371 if (!fbForDrawing
) return nullptr;
378 const ScopedBindRenderbuffer
bindRB(gl
, data
.interopRb
->name
);
379 gl
->fGetRenderbufferParameteriv(LOCAL_GL_RENDERBUFFER
,
380 LOCAL_GL_RENDERBUFFER_SAMPLES
, &samples
);
382 if (samples
> 0) { // Intel
383 // Intel's dx_interop GL-side textures have SAMPLES=1, likely because
384 // that's what the D3DTextures technically have. However, SAMPLES=1 is
385 // very different from SAMPLES=0 in GL. For more, see
386 // https://bugzilla.mozilla.org/show_bug.cgi?id=1325835
388 // Our ShSurf tex or rb must be single-sampled.
389 data
.interopFbIfNeedsIndirect
= std::move(fbForDrawing
);
390 fbForDrawing
= MozFramebuffer::Create(gl
, size
, 0, false);
396 return AsUnique(new SharedSurface_D3D11Interop(desc
, std::move(fbForDrawing
),
400 SharedSurface_D3D11Interop::SharedSurface_D3D11Interop(
401 const SharedSurfaceDesc
& desc
, UniquePtr
<MozFramebuffer
>&& fbForDrawing
,
403 : SharedSurface(desc
, std::move(fbForDrawing
)),
404 mData(std::move(data
)),
405 mNeedsFinish(StaticPrefs::webgl_dxgl_needs_finish()) {}
407 SharedSurface_D3D11Interop::~SharedSurface_D3D11Interop() {
408 MOZ_ASSERT(!IsProducerAcquired());
410 const auto& gl
= mDesc
.gl
;
411 if (!gl
|| !gl
->MakeCurrent()) return;
413 if (!mData
.interop
->UnregisterObject(mData
.lockHandle
)) {
414 NS_WARNING("Failed to release mLockHandle, possibly leaking it.");
418 void SharedSurface_D3D11Interop::ProducerAcquireImpl() {
419 MOZ_ASSERT(!mLockedForGL
);
421 // Now we have the mutex, we can lock for GL.
422 MOZ_ALWAYS_TRUE(mData
.interop
->LockObject(mData
.lockHandle
));
427 void SharedSurface_D3D11Interop::ProducerReleaseImpl() {
428 const auto& gl
= mDesc
.gl
;
429 MOZ_ASSERT(mLockedForGL
);
431 if (mData
.interopFbIfNeedsIndirect
) {
432 const ScopedBindFramebuffer
bindFB(gl
, mData
.interopFbIfNeedsIndirect
->mFB
);
433 gl
->BlitHelper()->DrawBlitTextureToFramebuffer(mFb
->ColorTex(), mDesc
.size
,
440 // We probably don't even need this.
443 MOZ_ALWAYS_TRUE(mData
.interop
->UnlockObject(mData
.lockHandle
));
445 mLockedForGL
= false;
448 Maybe
<layers::SurfaceDescriptor
>
449 SharedSurface_D3D11Interop::ToSurfaceDescriptor() {
450 const auto format
= gfx::SurfaceFormat::B8G8R8A8
;
451 return Some(layers::SurfaceDescriptorD3D10(
452 mData
.dxgiHandle
, /* gpuProcessTextureId */ Nothing(),
453 /* arrayIndex */ 0, format
, mDesc
.size
, mDesc
.colorSpace
,
454 gfx::ColorRange::FULL
, /* hasKeyedMutex */ true,
455 /* fenceInfo */ Nothing(), /* gpuProcessQueryId */ Nothing()));
458 //////////////////////////////////////////////////////////////////////////////////////////
462 UniquePtr
<SurfaceFactory_D3D11Interop
> SurfaceFactory_D3D11Interop::Create(
464 WGLLibrary
* wgl
= &sWGLLib
;
465 if (!wgl
|| !wgl
->HasDXInterop2()) return nullptr;
467 if (XRE_IsContentProcess()) {
468 gfxPlatform::GetPlatform()->EnsureDevicesInitialized();
471 const RefPtr
<DXInterop2Device
> interop
= DXInterop2Device::Open(wgl
, &gl
);
473 NS_WARNING("Failed to open D3D device for use by WGL.");
477 return AsUnique(new SurfaceFactory_D3D11Interop(
478 {&gl
, SharedSurfaceType::DXGLInterop2
, layers::TextureType::D3D11
, true},
482 SurfaceFactory_D3D11Interop::SurfaceFactory_D3D11Interop(
483 const PartialSharedSurfaceDesc
& desc
, DXInterop2Device
* interop
)
484 : SurfaceFactory(desc
), mInterop(interop
) {}
486 SurfaceFactory_D3D11Interop::~SurfaceFactory_D3D11Interop() = default;
489 } // namespace mozilla