SharedSurfaceD3D11Interop.cpp (16402B)
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/. */ 5 6 #include "SharedSurfaceD3D11Interop.h" 7 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" 21 22 namespace mozilla { 23 namespace gl { 24 25 /* 26 Sample Code for WGL_NV_DX_interop2: 27 Example: Render to Direct3D 11 backbuffer with openGL: 28 29 // create D3D11 device, context and swap chain. 30 ID3D11Device *device; 31 ID3D11DeviceContext *devCtx; 32 IDXGISwapChain *swapChain; 33 34 DXGI_SWAP_CHAIN_DESC scd; 35 36 <set appropriate swap chain parameters in scd> 37 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 52 53 // Fetch the swapchain backbuffer 54 ID3D11Texture2D *dxColorbuffer; 55 swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID *)&dxColorbuffer); 56 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> 62 63 // Create Views 64 ID3D11RenderTargetView *colorBufferView; 65 D3D11_RENDER_TARGET_VIEW_DESC rtd; 66 <set rtd parameters appropriately> 67 device->CreateRenderTargetView(dxColorbuffer, &rtd, &colorBufferView); 68 69 ID3D11DepthStencilView *depthBufferView; 70 D3D11_DEPTH_STENCIL_VIEW_DESC dsd; 71 <set dsd parameters appropriately> 72 device->CreateDepthStencilView(dxDepthBuffer, &dsd, &depthBufferView); 73 74 // Attach back buffer and depth texture to redertarget for the device. 75 devCtx->OMSetRenderTargets(1, &colorBufferView, depthBufferView); 76 77 // Register D3D11 device with GL 78 HANDLE gl_handleD3D; 79 gl_handleD3D = wglDXOpenDeviceNV(device); 80 81 // register the Direct3D color and depth/stencil buffers as 82 // renderbuffers in opengl 83 GLuint gl_names[2]; 84 HANDLE gl_handles[2]; 85 86 glGenRenderbuffers(2, gl_names); 87 88 gl_handles[0] = wglDXRegisterObjectNV(gl_handleD3D, dxColorBuffer, 89 gl_names[0], 90 GL_RENDERBUFFER, 91 WGL_ACCESS_READ_WRITE_NV); 92 93 gl_handles[1] = wglDXRegisterObjectNV(gl_handleD3D, dxDepthBuffer, 94 gl_names[1], 95 GL_RENDERBUFFER, 96 WGL_ACCESS_READ_WRITE_NV); 97 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]); 106 107 while (!done) { 108 <direct3d renders to the render targets> 109 110 // lock the render targets for GL access 111 wglDXLockObjectsNVX(gl_handleD3D, 2, gl_handles); 112 113 <opengl renders to the render targets> 114 115 // unlock the render targets 116 wglDXUnlockObjectsNVX(gl_handleD3D, 2, gl_handles); 117 118 <direct3d renders to the render targets and presents 119 the results on the screen> 120 } 121 */ 122 123 //////////////////////////////////////////////////////////////////////////////// 124 // DXInterop2Device 125 126 class ScopedContextState final { 127 ID3D11DeviceContext1* const mD3DContext; 128 RefPtr<ID3DDeviceContextState> mOldContextState; 129 130 public: 131 ScopedContextState(ID3D11DeviceContext1* d3dContext, 132 ID3DDeviceContextState* newContextState) 133 : mD3DContext(d3dContext), mOldContextState(nullptr) { 134 if (!mD3DContext) return; 135 136 mD3DContext->SwapDeviceContextState(newContextState, 137 getter_AddRefs(mOldContextState)); 138 } 139 140 ~ScopedContextState() { 141 if (!mD3DContext) return; 142 143 mD3DContext->SwapDeviceContextState(mOldContextState, nullptr); 144 } 145 }; 146 147 class DXInterop2Device : public RefCounted<DXInterop2Device> { 148 public: 149 MOZ_DECLARE_REFCOUNTED_TYPENAME(DXInterop2Device) 150 151 WGLLibrary* const mWGL; 152 const RefPtr<ID3D11Device> mD3D; // Only needed for lifetime guarantee. 153 const HANDLE mInteropDevice; 154 GLContext* const mGL; 155 156 // AMD workaround. 157 const RefPtr<ID3D11DeviceContext1> mD3DContext; 158 const RefPtr<ID3DDeviceContextState> mContextState; 159 160 static already_AddRefed<DXInterop2Device> Open(WGLLibrary* wgl, 161 GLContext* gl) { 162 MOZ_ASSERT(wgl->HasDXInterop2()); 163 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; 170 } 171 172 if (!gl->MakeCurrent()) return nullptr; 173 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; 182 183 d3d11_1->GetImmediateContext1(getter_AddRefs(d3dContext)); 184 MOZ_ASSERT(d3dContext); 185 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; 191 } 192 193 const auto interopDevice = wgl->mSymbols.fDXOpenDeviceNV(d3d); 194 if (!interopDevice) { 195 gfxCriticalNote << "DXInterop2Device::Open: DXOpenDevice failed."; 196 return nullptr; 197 } 198 199 return MakeAndAddRef<DXInterop2Device>(wgl, d3d, interopDevice, gl, 200 d3dContext, contextState); 201 } 202 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) {} 212 213 ~DXInterop2Device() { 214 const auto isCurrent = mGL->MakeCurrent(); 215 216 if (mWGL->mSymbols.fDXCloseDeviceNV(mInteropDevice)) return; 217 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(); 226 } 227 } 228 229 HANDLE RegisterObject(void* d3dObject, GLuint name, GLenum type, 230 GLenum access) const { 231 if (!mGL->MakeCurrent()) return nullptr; 232 233 const ScopedContextState autoCS(mD3DContext, mContextState); 234 const auto ret = mWGL->mSymbols.fDXRegisterObjectNV( 235 mInteropDevice, d3dObject, name, type, access); 236 if (ret) return ret; 237 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; 245 } 246 247 bool UnregisterObject(HANDLE lockHandle) const { 248 const auto isCurrent = mGL->MakeCurrent(); 249 250 const ScopedContextState autoCS(mD3DContext, mContextState); 251 if (mWGL->mSymbols.fDXUnregisterObjectNV(mInteropDevice, lockHandle)) 252 return true; 253 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(); 262 } 263 return false; 264 } 265 266 bool LockObject(HANDLE lockHandle) const { 267 MOZ_ASSERT(mGL->IsCurrent()); 268 269 if (mWGL->mSymbols.fDXLockObjectsNV(mInteropDevice, 1, &lockHandle)) 270 return true; 271 272 if (!mGL->MakeCurrent()) return false; 273 274 gfxCriticalNote << "wglDXLockObjects called without mGL being current." 275 << " Retrying after MakeCurrent."; 276 277 if (mWGL->mSymbols.fDXLockObjectsNV(mInteropDevice, 1, &lockHandle)) 278 return true; 279 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; 287 } 288 289 bool UnlockObject(HANDLE lockHandle) const { 290 MOZ_ASSERT(mGL->IsCurrent()); 291 292 if (mWGL->mSymbols.fDXUnlockObjectsNV(mInteropDevice, 1, &lockHandle)) 293 return true; 294 295 if (!mGL->MakeCurrent()) return false; 296 297 gfxCriticalNote << "wglDXUnlockObjects called without mGL being current." 298 << " Retrying after MakeCurrent."; 299 300 if (mWGL->mSymbols.fDXUnlockObjectsNV(mInteropDevice, 1, &lockHandle)) 301 return true; 302 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; 310 } 311 }; 312 313 //////////////////////////////////////////////////////////////////////////////// 314 // Shared Surface 315 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; 322 323 auto data = Data{interop}; 324 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; 330 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; 336 } 337 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; 344 } 345 346 HANDLE sharedHandle = nullptr; 347 texDXGI->CreateSharedHandle( 348 nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, 349 &sharedHandle); 350 351 data.dxgiHandle = new gfx::FileHandleWrapper(UniqueFileHandle(sharedHandle)); 352 353 //// 354 355 if (!gl->MakeCurrent()) { 356 NS_WARNING("MakeCurrent failed."); 357 return nullptr; 358 } 359 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; 367 } 368 369 auto fbForDrawing = MozFramebuffer::CreateForBacking( 370 gl, size, 0, false, LOCAL_GL_RENDERBUFFER, data.interopRb->name); 371 if (!fbForDrawing) return nullptr; 372 373 // - 374 375 { 376 GLint samples = 0; 377 { 378 const ScopedBindRenderbuffer bindRB(gl, data.interopRb->name); 379 gl->fGetRenderbufferParameteriv(LOCAL_GL_RENDERBUFFER, 380 LOCAL_GL_RENDERBUFFER_SAMPLES, &samples); 381 } 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 387 388 // Our ShSurf tex or rb must be single-sampled. 389 data.interopFbIfNeedsIndirect = std::move(fbForDrawing); 390 fbForDrawing = MozFramebuffer::Create(gl, size, 0, false); 391 } 392 } 393 394 // - 395 396 return AsUnique(new SharedSurface_D3D11Interop(desc, std::move(fbForDrawing), 397 std::move(data))); 398 } 399 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()) {} 406 407 SharedSurface_D3D11Interop::~SharedSurface_D3D11Interop() { 408 MOZ_ASSERT(!IsProducerAcquired()); 409 410 const auto& gl = mDesc.gl; 411 if (!gl || !gl->MakeCurrent()) return; 412 413 if (!mData.interop->UnregisterObject(mData.lockHandle)) { 414 NS_WARNING("Failed to release mLockHandle, possibly leaking it."); 415 } 416 } 417 418 void SharedSurface_D3D11Interop::ProducerAcquireImpl() { 419 MOZ_ASSERT(!mLockedForGL); 420 421 // Now we have the mutex, we can lock for GL. 422 MOZ_ALWAYS_TRUE(mData.interop->LockObject(mData.lockHandle)); 423 424 mLockedForGL = true; 425 } 426 427 void SharedSurface_D3D11Interop::ProducerReleaseImpl() { 428 const auto& gl = mDesc.gl; 429 MOZ_ASSERT(mLockedForGL); 430 431 if (mData.interopFbIfNeedsIndirect) { 432 const ScopedBindFramebuffer bindFB(gl, mData.interopFbIfNeedsIndirect->mFB); 433 gl->BlitHelper()->DrawBlitTextureToFramebuffer(mFb->ColorTex(), mDesc.size, 434 mDesc.size); 435 } 436 437 if (mNeedsFinish) { 438 gl->fFinish(); 439 } else { 440 // We probably don't even need this. 441 gl->fFlush(); 442 } 443 MOZ_ALWAYS_TRUE(mData.interop->UnlockObject(mData.lockHandle)); 444 445 mLockedForGL = false; 446 } 447 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 /* fencesHolderId */ Nothing())); 456 } 457 458 ////////////////////////////////////////////////////////////////////////////////////////// 459 // Factory 460 461 /*static*/ 462 UniquePtr<SurfaceFactory_D3D11Interop> SurfaceFactory_D3D11Interop::Create( 463 GLContext& gl) { 464 WGLLibrary* wgl = &sWGLLib; 465 if (!wgl || !wgl->HasDXInterop2()) return nullptr; 466 467 if (XRE_IsContentProcess()) { 468 gfxPlatform::GetPlatform()->EnsureDevicesInitialized(); 469 } 470 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; 475 } 476 477 return AsUnique(new SurfaceFactory_D3D11Interop( 478 {&gl, SharedSurfaceType::DXGLInterop2, layers::TextureType::D3D11, true}, 479 interop)); 480 } 481 482 SurfaceFactory_D3D11Interop::SurfaceFactory_D3D11Interop( 483 const PartialSharedSurfaceDesc& desc, DXInterop2Device* interop) 484 : SurfaceFactory(desc), mInterop(interop) {} 485 486 SurfaceFactory_D3D11Interop::~SurfaceFactory_D3D11Interop() = default; 487 488 } // namespace gl 489 } // namespace mozilla