Bug 1886451: Add missing ifdef Nightly guards. r=dminor
[gecko.git] / gfx / gl / SharedSurfaceD3D11Interop.cpp
blob4248bdf23dd8976935d6e68e462c5311a84653e9
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"
8 #include <d3d11.h>
9 #include <d3d11_1.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"
22 namespace mozilla {
23 namespace gl {
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.
30 ID3D11Device *device;
31 ID3D11DeviceContext *devCtx;
32 IDXGISwapChain *swapChain;
34 DXGI_SWAP_CHAIN_DESC scd;
36 <set appropriate swap chain parameters in scd>
38 hr = D3D11CreateDeviceAndSwapChain(
39 NULL, // pAdapter
40 D3D_DRIVER_TYPE_HARDWARE, // DriverType
41 NULL, // Software
42 0, // Flags (Do not set
43 // D3D11_CREATE_DEVICE_SINGLETHREADED)
44 NULL, // pFeatureLevels
45 0, // FeatureLevels
46 D3D11_SDK_VERSION, // SDKVersion
47 &scd, // pSwapChainDesc
48 &swapChain, // ppSwapChain
49 &device, // ppDevice
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>
63 // Create Views
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
78 HANDLE gl_handleD3D;
79 gl_handleD3D = wglDXOpenDeviceNV(device);
81 // register the Direct3D color and depth/stencil buffers as
82 // renderbuffers in opengl
83 GLuint gl_names[2];
84 HANDLE gl_handles[2];
86 glGenRenderbuffers(2, gl_names);
88 gl_handles[0] = wglDXRegisterObjectNV(gl_handleD3D, dxColorBuffer,
89 gl_names[0],
90 GL_RENDERBUFFER,
91 WGL_ACCESS_READ_WRITE_NV);
93 gl_handles[1] = wglDXRegisterObjectNV(gl_handleD3D, dxDepthBuffer,
94 gl_names[1],
95 GL_RENDERBUFFER,
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]);
107 while (!done) {
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 ////////////////////////////////////////////////////////////////////////////////
124 // DXInterop2Device
126 class ScopedContextState final {
127 ID3D11DeviceContext1* const mD3DContext;
128 RefPtr<ID3DDeviceContextState> mOldContextState;
130 public:
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> {
148 public:
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;
156 // AMD workaround.
157 const RefPtr<ID3D11DeviceContext1> mD3DContext;
158 const RefPtr<ID3DDeviceContextState> mContextState;
160 static already_AddRefed<DXInterop2Device> Open(WGLLibrary* wgl,
161 GLContext* gl) {
162 MOZ_ASSERT(wgl->HasDXInterop2());
164 const RefPtr<ID3D11Device> d3d =
165 gfx::DeviceManagerDx::Get()->GetContentDevice();
166 if (!d3d) {
167 gfxCriticalNote
168 << "DXInterop2Device::Open: Failed to create D3D11 device.";
169 return nullptr;
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;
179 auto hr =
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.";
196 return nullptr;
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)
206 : mWGL(wgl),
207 mD3D(d3d),
208 mInteropDevice(interopDevice),
209 mGL(gl),
210 mD3DContext(d3dContext),
211 mContextState(contextState) {}
213 ~DXInterop2Device() {
214 const auto isCurrent = mGL->MakeCurrent();
216 if (mWGL->mSymbols.fDXCloseDeviceNV(mInteropDevice)) return;
218 if (isCurrent) {
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);
236 if (ret) return ret;
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();
244 return nullptr;
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))
252 return true;
254 if (!isCurrent) {
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();
263 return false;
266 bool LockObject(HANDLE lockHandle) const {
267 MOZ_ASSERT(mGL->IsCurrent());
269 if (mWGL->mSymbols.fDXLockObjectsNV(mInteropDevice, 1, &lockHandle))
270 return true;
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))
278 return true;
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();
286 return false;
289 bool UnlockObject(HANDLE lockHandle) const {
290 MOZ_ASSERT(mGL->IsCurrent());
292 if (mWGL->mSymbols.fDXUnlockObjectsNV(mInteropDevice, 1, &lockHandle))
293 return true;
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))
301 return true;
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();
309 return false;
313 ////////////////////////////////////////////////////////////////////////////////
314 // Shared Surface
316 /*static*/
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;
331 auto hr =
332 d3d->CreateTexture2D(&texDesc, nullptr, getter_AddRefs(data.texD3D));
333 if (FAILED(hr)) {
334 NS_WARNING("Failed to create texture for CanvasLayer!");
335 return nullptr;
338 RefPtr<IDXGIResource1> texDXGI;
339 hr = data.texD3D->QueryInterface(__uuidof(IDXGIResource1),
340 getter_AddRefs(texDXGI));
341 if (FAILED(hr)) {
342 NS_WARNING("Failed to open texture for sharing!");
343 return nullptr;
346 HANDLE sharedHandle = nullptr;
347 texDXGI->CreateSharedHandle(
348 nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr,
349 &sharedHandle);
351 data.dxgiHandle = new gfx::FileHandleWrapper(UniqueFileHandle(sharedHandle));
353 ////
355 if (!gl->MakeCurrent()) {
356 NS_WARNING("MakeCurrent failed.");
357 return nullptr;
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.");
366 return nullptr;
369 auto fbForDrawing = MozFramebuffer::CreateForBacking(
370 gl, size, 0, false, LOCAL_GL_RENDERBUFFER, data.interopRb->name);
371 if (!fbForDrawing) return nullptr;
373 // -
376 GLint samples = 0;
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);
394 // -
396 return AsUnique(new SharedSurface_D3D11Interop(desc, std::move(fbForDrawing),
397 std::move(data)));
400 SharedSurface_D3D11Interop::SharedSurface_D3D11Interop(
401 const SharedSurfaceDesc& desc, UniquePtr<MozFramebuffer>&& fbForDrawing,
402 Data&& data)
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));
424 mLockedForGL = true;
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,
434 mDesc.size);
437 if (mNeedsFinish) {
438 gl->fFinish();
439 } else {
440 // We probably don't even need this.
441 gl->fFlush();
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 //////////////////////////////////////////////////////////////////////////////////////////
459 // Factory
461 /*static*/
462 UniquePtr<SurfaceFactory_D3D11Interop> SurfaceFactory_D3D11Interop::Create(
463 GLContext& gl) {
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);
472 if (!interop) {
473 NS_WARNING("Failed to open D3D device for use by WGL.");
474 return nullptr;
477 return AsUnique(new SurfaceFactory_D3D11Interop(
478 {&gl, SharedSurfaceType::DXGLInterop2, layers::TextureType::D3D11, true},
479 interop));
482 SurfaceFactory_D3D11Interop::SurfaceFactory_D3D11Interop(
483 const PartialSharedSurfaceDesc& desc, DXInterop2Device* interop)
484 : SurfaceFactory(desc), mInterop(interop) {}
486 SurfaceFactory_D3D11Interop::~SurfaceFactory_D3D11Interop() = default;
488 } // namespace gl
489 } // namespace mozilla