/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "SharedSurfaceD3D11Interop.h" #include #include "GLContext.h" #include "WGLLibrary.h" namespace mozilla { namespace gl { /* Sample Code for WGL_NV_DX_interop2: Example: Render to Direct3D 11 backbuffer with openGL: // create D3D11 device, context and swap chain. ID3D11Device *device; ID3D11DeviceContext *devCtx; IDXGISwapChain *swapChain; DXGI_SWAP_CHAIN_DESC scd; hr = D3D11CreateDeviceAndSwapChain(NULL, // pAdapter D3D_DRIVER_TYPE_HARDWARE, // DriverType NULL, // Software 0, // Flags (Do not set D3D11_CREATE_DEVICE_SINGLETHREADED) NULL, // pFeatureLevels 0, // FeatureLevels D3D11_SDK_VERSION, // SDKVersion &scd, // pSwapChainDesc &swapChain, // ppSwapChain &device, // ppDevice NULL, // pFeatureLevel &devCtx); // ppImmediateContext // Fetch the swapchain backbuffer ID3D11Texture2D *dxColorbuffer; swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID *)&dxColorbuffer); // Create depth stencil texture ID3D11Texture2D *dxDepthBuffer; D3D11_TEXTURE2D_DESC depthDesc; depthDesc.Usage = D3D11_USAGE_DEFAULT; // Create Views ID3D11RenderTargetView *colorBufferView; D3D11_RENDER_TARGET_VIEW_DESC rtd; device->CreateRenderTargetView(dxColorbuffer, &rtd, &colorBufferView); ID3D11DepthStencilView *depthBufferView; D3D11_DEPTH_STENCIL_VIEW_DESC dsd; device->CreateDepthStencilView(dxDepthBuffer, &dsd, &depthBufferView); // Attach back buffer and depth texture to redertarget for the device. devCtx->OMSetRenderTargets(1, &colorBufferView, depthBufferView); // Register D3D11 device with GL HANDLE gl_handleD3D; gl_handleD3D = wglDXOpenDeviceNV(device); // register the Direct3D color and depth/stencil buffers as // renderbuffers in opengl GLuint gl_names[2]; HANDLE gl_handles[2]; glGenRenderbuffers(2, gl_names); gl_handles[0] = wglDXRegisterObjectNV(gl_handleD3D, dxColorBuffer, gl_names[0], GL_RENDERBUFFER, WGL_ACCESS_READ_WRITE_NV); gl_handles[1] = wglDXRegisterObjectNV(gl_handleD3D, dxDepthBuffer, gl_names[1], GL_RENDERBUFFER, WGL_ACCESS_READ_WRITE_NV); // attach the Direct3D buffers to an FBO glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, gl_names[0]); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, gl_names[1]); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gl_names[1]); while (!done) { // lock the render targets for GL access wglDXLockObjectsNVX(gl_handleD3D, 2, gl_handles); // unlock the render targets wglDXUnlockObjectsNVX(gl_handleD3D, 2, gl_handles); } */ //////////////////////////////////////////////////////////////////////////////// // DXGL Device class DXGLDevice : public RefCounted { public: MOZ_DECLARE_REFCOUNTED_TYPENAME(DXGLDevice) WGLLibrary* const mWGL; const RefPtr mD3D; // Only needed for lifetime guarantee. const HANDLE mDXGLDeviceHandle; static already_AddRefed Open(WGLLibrary* wgl) { MOZ_ASSERT(wgl->HasDXInterop2()); gfxWindowsPlatform* plat = gfxWindowsPlatform::GetPlatform(); RefPtr d3d = plat->GetD3D11ContentDevice(); if (!d3d) { NS_WARNING("Failed to create D3D11 device."); return nullptr; } HANDLE dxglDeviceHandle = wgl->fDXOpenDevice(d3d); if (!dxglDeviceHandle) { NS_WARNING("Failed to open D3D device for use by WGL."); return nullptr; } return MakeAndAddRef(wgl, d3d, dxglDeviceHandle); } DXGLDevice(WGLLibrary* wgl, const RefPtr& d3d, HANDLE dxglDeviceHandle) : mWGL(wgl) , mD3D(d3d) , mDXGLDeviceHandle(dxglDeviceHandle) { } ~DXGLDevice() { if (!mWGL->fDXCloseDevice(mDXGLDeviceHandle)) { #ifdef DEBUG uint32_t error = GetLastError(); printf_stderr("wglDXCloseDevice(0x%x) failed: GetLastError(): 0x%x\n", mDXGLDeviceHandle, error); #endif MOZ_CRASH(); } } HANDLE RegisterObject(void* dxObject, GLuint name, GLenum type, GLenum access) const { HANDLE ret = mWGL->fDXRegisterObject(mDXGLDeviceHandle, dxObject, name, type, access); if (!ret) { #ifdef DEBUG uint32_t error = GetLastError(); printf_stderr("wglDXRegisterObject(0x%x, 0x%x, %u, 0x%x, 0x%x) failed:" " GetLastError(): 0x%x\n", mDXGLDeviceHandle, dxObject, name, type, access, error); #endif MOZ_CRASH(); } return ret; } bool UnregisterObject(HANDLE hObject) const { bool ret = mWGL->fDXUnregisterObject(mDXGLDeviceHandle, hObject); if (!ret) { #ifdef DEBUG uint32_t error = GetLastError(); printf_stderr("wglDXUnregisterObject(0x%x, 0x%x) failed: GetLastError():" " 0x%x\n", mDXGLDeviceHandle, hObject, error); #endif MOZ_CRASH(); } return ret; } bool LockObject(HANDLE hObject) const { bool ret = mWGL->fDXLockObjects(mDXGLDeviceHandle, 1, &hObject); if (!ret) { #ifdef DEBUG uint32_t error = GetLastError(); printf_stderr("wglDXLockObjects(0x%x, 1, {0x%x}) failed: GetLastError():" " 0x%x\n", mDXGLDeviceHandle, hObject, error); #endif MOZ_CRASH(); } return ret; } bool UnlockObject(HANDLE hObject) const { bool ret = mWGL->fDXUnlockObjects(mDXGLDeviceHandle, 1, &hObject); if (!ret) { #ifdef DEBUG uint32_t error = GetLastError(); printf_stderr("wglDXUnlockObjects(0x%x, 1, {0x%x}) failed: GetLastError():" " 0x%x\n", mDXGLDeviceHandle, hObject, error); #endif MOZ_CRASH(); } return ret; } }; //////////////////////////////////////////////////////////////////////////////// // Shared Surface /*static*/ UniquePtr SharedSurface_D3D11Interop::Create(const RefPtr& dxgl, GLContext* gl, const gfx::IntSize& size, bool hasAlpha) { auto& d3d = *dxgl->mD3D; // Create a texture in case we need to readback. DXGI_FORMAT format = hasAlpha ? DXGI_FORMAT_B8G8R8A8_UNORM : DXGI_FORMAT_B8G8R8X8_UNORM; CD3D11_TEXTURE2D_DESC desc(format, size.width, size.height, 1, 1); desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; RefPtr textureD3D; HRESULT hr = d3d.CreateTexture2D(&desc, nullptr, getter_AddRefs(textureD3D)); if (FAILED(hr)) { NS_WARNING("Failed to create texture for CanvasLayer!"); return nullptr; } RefPtr textureDXGI; hr = textureD3D->QueryInterface(__uuidof(IDXGIResource), getter_AddRefs(textureDXGI)); if (FAILED(hr)) { NS_WARNING("Failed to open texture for sharing!"); return nullptr; } RefPtr keyedMutex; hr = textureD3D->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(keyedMutex)); if (FAILED(hr)) { NS_WARNING("Failed to obtained keyed mutex from texture!"); return nullptr; } HANDLE sharedHandle; textureDXGI->GetSharedHandle(&sharedHandle); GLuint renderbufferGL = 0; gl->MakeCurrent(); gl->fGenRenderbuffers(1, &renderbufferGL); HANDLE objectWGL = dxgl->RegisterObject(textureD3D, renderbufferGL, LOCAL_GL_RENDERBUFFER, LOCAL_WGL_ACCESS_WRITE_DISCARD_NV); if (!objectWGL) { NS_WARNING("Failed to register D3D object with WGL."); return nullptr; } GLuint fence = 0; if (gl->IsExtensionSupported(GLContext::NV_fence)) { gl->MakeCurrent(); gl->fGenFences(1, &fence); } typedef SharedSurface_D3D11Interop ptrT; UniquePtr ret ( new ptrT(gl, size, hasAlpha, renderbufferGL, dxgl, objectWGL, textureD3D, sharedHandle, keyedMutex, fence) ); return Move(ret); } SharedSurface_D3D11Interop::SharedSurface_D3D11Interop(GLContext* gl, const gfx::IntSize& size, bool hasAlpha, GLuint renderbufferGL, const RefPtr& dxgl, HANDLE objectWGL, const RefPtr& textureD3D, HANDLE sharedHandle, const RefPtr& keyedMutex, GLuint fence) : SharedSurface(SharedSurfaceType::DXGLInterop2, AttachmentType::GLRenderbuffer, gl, size, hasAlpha, true) , mProdRB(renderbufferGL) , mDXGL(dxgl) , mObjectWGL(objectWGL) , mTextureD3D(textureD3D) , mSharedHandle(sharedHandle) , mKeyedMutex(keyedMutex) , mFence(fence) , mLockedForGL(false) { } SharedSurface_D3D11Interop::~SharedSurface_D3D11Interop() { MOZ_ASSERT(!mLockedForGL); mGL->fDeleteRenderbuffers(1, &mProdRB); if (!mDXGL->UnregisterObject(mObjectWGL)) { NS_WARNING("Failed to release a DXGL object, possibly leaking it."); } if (!mGL->MakeCurrent()) return; if (mFence) { mGL->fDeleteFences(1, &mFence); } mGL->fDeleteRenderbuffers(1, &mProdRB); // mDXGL is closed when it runs out of refs. } void SharedSurface_D3D11Interop::LockProdImpl() { } void SharedSurface_D3D11Interop::UnlockProdImpl() { } void SharedSurface_D3D11Interop::ProducerAcquireImpl() { MOZ_ASSERT(!mLockedForGL); if (mKeyedMutex) { const uint64_t keyValue = 0; const DWORD timeoutMs = 10000; HRESULT hr = mKeyedMutex->AcquireSync(keyValue, timeoutMs); if (hr == WAIT_TIMEOUT) { // Doubt we should do this? Maybe Wait for ever? MOZ_CRASH("d3d11Interop timeout"); } } // Now we have the mutex, we can lock for GL. MOZ_ALWAYS_TRUE(mDXGL->LockObject(mObjectWGL)); mLockedForGL = true; } void SharedSurface_D3D11Interop::ProducerReleaseImpl() { MOZ_ASSERT(mLockedForGL); mGL->fFlush(); MOZ_ALWAYS_TRUE(mDXGL->UnlockObject(mObjectWGL)); mLockedForGL = false; // Now we have unlocked for GL, we can release to consumer. if (mKeyedMutex) { mKeyedMutex->ReleaseSync(0); } // TODO fence properly. This kills performance. mGL->fFinish(); } bool SharedSurface_D3D11Interop::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) { gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::B8G8R8A8 : gfx::SurfaceFormat::B8G8R8X8; *out_descriptor = layers::SurfaceDescriptorD3D10((WindowsHandle)GetSharedHandle(), format, mSize); return true; } ////////////////////////////////////////////////////////////////////////////////////////// // Factory /*static*/ UniquePtr SurfaceFactory_D3D11Interop::Create(GLContext* gl, const SurfaceCaps& caps, const RefPtr& allocator, const layers::TextureFlags& flags) { WGLLibrary* wgl = &sWGLLib; if (!wgl || !wgl->HasDXInterop2()) return nullptr; RefPtr dxgl = DXGLDevice::Open(wgl); if (!dxgl) { NS_WARNING("Failed to open D3D device for use by WGL."); return nullptr; } typedef SurfaceFactory_D3D11Interop ptrT; UniquePtr ret(new ptrT(gl, caps, allocator, flags, dxgl)); return Move(ret); } SurfaceFactory_D3D11Interop::SurfaceFactory_D3D11Interop(GLContext* gl, const SurfaceCaps& caps, const RefPtr& allocator, const layers::TextureFlags& flags, const RefPtr& dxgl) : SurfaceFactory(SharedSurfaceType::DXGLInterop2, gl, caps, allocator, flags) , mDXGL(dxgl) { } SurfaceFactory_D3D11Interop::~SurfaceFactory_D3D11Interop() { } } // namespace gl } // namespace mozilla