Bug 1508381 - remove now-unnecessary TASKCLUSTER_* variables r=tomprince
[gecko.git] / gfx / gl / SharedSurfaceD3D11Interop.cpp
blobe8f4a76da32433c0f0e30fb4e31cd17568fbf5c7
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 "gfxPrefs.h"
11 #include "GLContext.h"
12 #include "WGLLibrary.h"
13 #include "nsPrintfCString.h"
14 #include "mozilla/gfx/DeviceManagerDx.h"
16 namespace mozilla {
17 namespace gl {
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.
24 ID3D11Device *device;
25 ID3D11DeviceContext *devCtx;
26 IDXGISwapChain *swapChain;
28 DXGI_SWAP_CHAIN_DESC scd;
30 <set appropriate swap chain parameters in scd>
32 hr = D3D11CreateDeviceAndSwapChain(
33 NULL, // pAdapter
34 D3D_DRIVER_TYPE_HARDWARE, // DriverType
35 NULL, // Software
36 0, // Flags (Do not set
37 // D3D11_CREATE_DEVICE_SINGLETHREADED)
38 NULL, // pFeatureLevels
39 0, // FeatureLevels
40 D3D11_SDK_VERSION, // SDKVersion
41 &scd, // pSwapChainDesc
42 &swapChain, // ppSwapChain
43 &device, // ppDevice
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>
57 // Create Views
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
72 HANDLE gl_handleD3D;
73 gl_handleD3D = wglDXOpenDeviceNV(device);
75 // register the Direct3D color and depth/stencil buffers as
76 // renderbuffers in opengl
77 GLuint gl_names[2];
78 HANDLE gl_handles[2];
80 glGenRenderbuffers(2, gl_names);
82 gl_handles[0] = wglDXRegisterObjectNV(gl_handleD3D, dxColorBuffer,
83 gl_names[0],
84 GL_RENDERBUFFER,
85 WGL_ACCESS_READ_WRITE_NV);
87 gl_handles[1] = wglDXRegisterObjectNV(gl_handleD3D, dxDepthBuffer,
88 gl_names[1],
89 GL_RENDERBUFFER,
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]);
101 while (!done) {
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 ////////////////////////////////////////////////////////////////////////////////
118 // DXInterop2Device
120 class ScopedContextState final {
121 ID3D11DeviceContext1* const mD3DContext;
122 RefPtr<ID3DDeviceContextState> mOldContextState;
124 public:
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> {
142 public:
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;
150 // AMD workaround.
151 const RefPtr<ID3D11DeviceContext1> mD3DContext;
152 const RefPtr<ID3DDeviceContextState> mContextState;
154 static already_AddRefed<DXInterop2Device> Open(WGLLibrary* wgl,
155 GLContext* gl) {
156 MOZ_ASSERT(wgl->HasDXInterop2());
158 const RefPtr<ID3D11Device> d3d =
159 gfx::DeviceManagerDx::Get()->GetContentDevice();
160 if (!d3d) {
161 gfxCriticalNote
162 << "DXInterop2Device::Open: Failed to create D3D11 device.";
163 return nullptr;
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;
173 auto hr =
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.";
190 return nullptr;
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)
200 : mWGL(wgl),
201 mD3D(d3d),
202 mInteropDevice(interopDevice),
203 mGL(gl),
204 mD3DContext(d3dContext),
205 mContextState(contextState) {}
207 ~DXInterop2Device() {
208 const auto isCurrent = mGL->MakeCurrent();
210 if (mWGL->mSymbols.fDXCloseDeviceNV(mInteropDevice)) return;
212 if (isCurrent) {
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);
230 if (ret) return ret;
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();
238 return nullptr;
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))
246 return true;
248 if (!isCurrent) {
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();
257 return false;
260 bool LockObject(HANDLE lockHandle) const {
261 MOZ_ASSERT(mGL->IsCurrent());
263 if (mWGL->mSymbols.fDXLockObjectsNV(mInteropDevice, 1, &lockHandle))
264 return true;
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))
272 return true;
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();
280 return false;
283 bool UnlockObject(HANDLE lockHandle) const {
284 MOZ_ASSERT(mGL->IsCurrent());
286 if (mWGL->mSymbols.fDXUnlockObjectsNV(mInteropDevice, 1, &lockHandle))
287 return true;
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))
295 return true;
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();
303 return false;
307 ////////////////////////////////////////////////////////////////////////////////
308 // Shared Surface
310 /*static*/
311 UniquePtr<SharedSurface_D3D11Interop> SharedSurface_D3D11Interop::Create(
312 DXInterop2Device* interop, GLContext* gl, const gfx::IntSize& size,
313 bool hasAlpha) {
314 const auto& d3d = interop->mD3D;
316 // Create a texture in case we need to readback.
317 DXGI_FORMAT format =
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));
324 if (FAILED(hr)) {
325 NS_WARNING("Failed to create texture for CanvasLayer!");
326 return nullptr;
329 RefPtr<IDXGIResource> texDXGI;
330 hr = texD3D->QueryInterface(__uuidof(IDXGIResource), getter_AddRefs(texDXGI));
331 if (FAILED(hr)) {
332 NS_WARNING("Failed to open texture for sharing!");
333 return nullptr;
336 HANDLE dxgiHandle;
337 texDXGI->GetSharedHandle(&dxgiHandle);
339 ////
341 if (!gl->MakeCurrent()) {
342 NS_WARNING("MakeCurrent failed.");
343 return nullptr;
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);
351 if (!lockHandle) {
352 NS_WARNING("Failed to register D3D object with WGL.");
353 gl->fDeleteRenderbuffers(1, &interopRB);
354 return nullptr;
357 ////
359 GLuint prodTex = 0;
360 GLuint interopFB = 0;
362 GLint samples = 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);
394 ////
396 typedef SharedSurface_D3D11Interop ptrT;
397 UniquePtr<ptrT> ret(new ptrT(gl, size, hasAlpha, prodTex, interopFB,
398 interopRB, interop, lockHandle, texD3D,
399 dxgiHandle));
400 return ret;
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)
407 : SharedSurface(
408 SharedSurfaceType::DXGLInterop2,
409 prodTex ? AttachmentType::GLTexture : AttachmentType::GLRenderbuffer,
410 gl, size, hasAlpha, true),
411 mProdTex(prodTex),
412 mInteropFB(interopFB),
413 mInteropRB(interopRB),
414 mInterop(interop),
415 mLockHandle(lockHandle),
416 mTexD3D(texD3D),
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));
443 mLockedForGL = true;
446 void SharedSurface_D3D11Interop::ProducerReleaseImpl() {
447 MOZ_ASSERT(mLockedForGL);
449 if (mProdTex) {
450 const ScopedBindFramebuffer bindFB(mGL, mInteropFB);
451 mGL->BlitHelper()->DrawBlitTextureToFramebuffer(mProdTex, mSize, mSize);
454 if (mNeedsFinish) {
455 mGL->fFinish();
456 } else {
457 // We probably don't even need this.
458 mGL->fFlush();
460 MOZ_ALWAYS_TRUE(mInterop->UnlockObject(mLockHandle));
462 mLockedForGL = false;
465 bool SharedSurface_D3D11Interop::ToSurfaceDescriptor(
466 layers::SurfaceDescriptor* const out_descriptor) {
467 const auto format =
468 (mHasAlpha ? gfx::SurfaceFormat::B8G8R8A8 : gfx::SurfaceFormat::B8G8R8X8);
469 *out_descriptor =
470 layers::SurfaceDescriptorD3D10(WindowsHandle(mDXGIHandle), format, mSize);
471 return true;
474 //////////////////////////////////////////////////////////////////////////////////////////
475 // Factory
477 /*static*/
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);
485 if (!interop) {
486 NS_WARNING("Failed to open D3D device for use by WGL.");
487 return nullptr;
490 typedef SurfaceFactory_D3D11Interop ptrT;
491 UniquePtr<ptrT> ret(new ptrT(gl, caps, allocator, flags, interop));
492 return ret;
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,
499 flags),
500 mInterop(interop) {}
502 SurfaceFactory_D3D11Interop::~SurfaceFactory_D3D11Interop() {}
504 } // namespace gl
505 } // namespace mozilla