diff options
Diffstat (limited to 'gfx/gl/SharedSurfaceD3D11Interop.cpp')
-rw-r--r-- | gfx/gl/SharedSurfaceD3D11Interop.cpp | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/gfx/gl/SharedSurfaceD3D11Interop.cpp b/gfx/gl/SharedSurfaceD3D11Interop.cpp new file mode 100644 index 000000000..e667005d8 --- /dev/null +++ b/gfx/gl/SharedSurfaceD3D11Interop.cpp @@ -0,0 +1,425 @@ +/* -*- 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 <d3d11.h> +#include "gfxPrefs.h" +#include "GLContext.h" +#include "WGLLibrary.h" +#include "nsPrintfCString.h" +#include "mozilla/gfx/DeviceManagerDx.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; + +<set appropriate swap chain parameters in 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; +<set other depthDesc parameters appropriately> + +// Create Views +ID3D11RenderTargetView *colorBufferView; +D3D11_RENDER_TARGET_VIEW_DESC rtd; +<set rtd parameters appropriately> +device->CreateRenderTargetView(dxColorbuffer, &rtd, &colorBufferView); + +ID3D11DepthStencilView *depthBufferView; +D3D11_DEPTH_STENCIL_VIEW_DESC dsd; +<set dsd parameters appropriately> +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) { + <direct3d renders to the render targets> + + // lock the render targets for GL access + wglDXLockObjectsNVX(gl_handleD3D, 2, gl_handles); + + <opengl renders to the render targets> + + // unlock the render targets + wglDXUnlockObjectsNVX(gl_handleD3D, 2, gl_handles); + + <direct3d renders to the render targets and presents + the results on the screen> +} +*/ + +//////////////////////////////////////////////////////////////////////////////// +// DXInterop2Device + +class DXInterop2Device : public RefCounted<DXInterop2Device> +{ +public: + MOZ_DECLARE_REFCOUNTED_TYPENAME(DXInterop2Device) + + WGLLibrary* const mWGL; + const RefPtr<ID3D11Device> mD3D; // Only needed for lifetime guarantee. + const HANDLE mInteropDevice; + GLContext* const mGL; + + static already_AddRefed<DXInterop2Device> Open(WGLLibrary* wgl, GLContext* gl) + { + MOZ_ASSERT(wgl->HasDXInterop2()); + + const RefPtr<ID3D11Device> d3d = gfx::DeviceManagerDx::Get()->GetContentDevice(); + if (!d3d) { + gfxCriticalNote << "DXInterop2Device::Open: Failed to create D3D11 device."; + return nullptr; + } + + if (!gl->MakeCurrent()) + return nullptr; + + const auto interopDevice = wgl->fDXOpenDevice(d3d); + if (!interopDevice) { + gfxCriticalNote << "DXInterop2Device::Open: DXOpenDevice failed."; + return nullptr; + } + + return MakeAndAddRef<DXInterop2Device>(wgl, d3d, interopDevice, gl); + } + + DXInterop2Device(WGLLibrary* wgl, ID3D11Device* d3d, HANDLE interopDevice, + GLContext* gl) + : mWGL(wgl) + , mD3D(d3d) + , mInteropDevice(interopDevice) + , mGL(gl) + { } + + ~DXInterop2Device() { + const auto isCurrent = mGL->MakeCurrent(); + + if (mWGL->fDXCloseDevice(mInteropDevice)) + return; + + if (isCurrent) { + // That shouldn't have failed. + const uint32_t error = GetLastError(); + const nsPrintfCString errorMessage("wglDXCloseDevice(0x%p) failed:" + " GetLastError(): %u\n", + mInteropDevice, error); + gfxCriticalError() << errorMessage.BeginReading(); + } + } + + HANDLE RegisterObject(void* d3dObject, GLuint name, GLenum type, + GLenum access) const + { + if (!mGL->MakeCurrent()) + return nullptr; + + const auto ret = mWGL->fDXRegisterObject(mInteropDevice, d3dObject, name, type, + access); + if (ret) + return ret; + + const uint32_t error = GetLastError(); + const nsPrintfCString errorMessage("wglDXRegisterObject(0x%p, 0x%p, %u, 0x%04x," + " 0x%04x) failed: GetLastError(): %u\n", + mInteropDevice, d3dObject, name, type, access, + error); + gfxCriticalNote << errorMessage.BeginReading(); + return nullptr; + } + + bool UnregisterObject(HANDLE lockHandle) const { + const auto isCurrent = mGL->MakeCurrent(); + + if (mWGL->fDXUnregisterObject(mInteropDevice, lockHandle)) + return true; + + if (!isCurrent) { + // That shouldn't have failed. + const uint32_t error = GetLastError(); + const nsPrintfCString errorMessage("wglDXUnregisterObject(0x%p, 0x%p) failed:" + " GetLastError(): %u\n", + mInteropDevice, lockHandle, error); + gfxCriticalError() << errorMessage.BeginReading(); + } + return false; + } + + bool LockObject(HANDLE lockHandle) const { + MOZ_ASSERT(mGL->IsCurrent()); + + if (mWGL->fDXLockObjects(mInteropDevice, 1, &lockHandle)) + return true; + + if (!mGL->MakeCurrent()) + return false; + + gfxCriticalNote << "wglDXLockObjects called without mGL being current." + << " Retrying after MakeCurrent."; + + if (mWGL->fDXLockObjects(mInteropDevice, 1, &lockHandle)) + return true; + + const uint32_t error = GetLastError(); + const nsPrintfCString errorMessage("wglDXLockObjects(0x%p, 1, {0x%p}) failed:" + " GetLastError(): %u\n", + mInteropDevice, lockHandle, error); + gfxCriticalError() << errorMessage.BeginReading(); + return false; + } + + bool UnlockObject(HANDLE lockHandle) const { + MOZ_ASSERT(mGL->IsCurrent()); + + if (mWGL->fDXUnlockObjects(mInteropDevice, 1, &lockHandle)) + return true; + + if (!mGL->MakeCurrent()) + return false; + + gfxCriticalNote << "wglDXUnlockObjects called without mGL being current." + << " Retrying after MakeCurrent."; + + if (mWGL->fDXUnlockObjects(mInteropDevice, 1, &lockHandle)) + return true; + + const uint32_t error = GetLastError(); + const nsPrintfCString errorMessage("wglDXUnlockObjects(0x%p, 1, {0x%p}) failed:" + " GetLastError(): %u\n", + mInteropDevice, lockHandle, error); + gfxCriticalError() << errorMessage.BeginReading(); + return false; + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// Shared Surface + +/*static*/ UniquePtr<SharedSurface_D3D11Interop> +SharedSurface_D3D11Interop::Create(DXInterop2Device* interop, + GLContext* gl, + const gfx::IntSize& size, + bool hasAlpha) +{ + const auto& d3d = interop->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<ID3D11Texture2D> texD3D; + auto hr = d3d->CreateTexture2D(&desc, nullptr, getter_AddRefs(texD3D)); + if (FAILED(hr)) { + NS_WARNING("Failed to create texture for CanvasLayer!"); + return nullptr; + } + + RefPtr<IDXGIResource> texDXGI; + hr = texD3D->QueryInterface(__uuidof(IDXGIResource), getter_AddRefs(texDXGI)); + if (FAILED(hr)) { + NS_WARNING("Failed to open texture for sharing!"); + return nullptr; + } + + HANDLE dxgiHandle; + texDXGI->GetSharedHandle(&dxgiHandle); + + //// + + if (!gl->MakeCurrent()) { + NS_WARNING("MakeCurrent failed."); + return nullptr; + } + + GLuint rbGL = 0; + gl->fGenRenderbuffers(1, &rbGL); + const auto lockHandle = interop->RegisterObject(texD3D, rbGL, LOCAL_GL_RENDERBUFFER, + LOCAL_WGL_ACCESS_WRITE_DISCARD_NV); + if (!lockHandle) { + NS_WARNING("Failed to register D3D object with WGL."); + gl->fDeleteRenderbuffers(1, &rbGL); + return nullptr; + } + + //// + + typedef SharedSurface_D3D11Interop ptrT; + UniquePtr<ptrT> ret ( new ptrT(gl, size, hasAlpha, rbGL, interop, lockHandle, + texD3D, dxgiHandle) ); + return Move(ret); +} + +SharedSurface_D3D11Interop::SharedSurface_D3D11Interop(GLContext* gl, + const gfx::IntSize& size, + bool hasAlpha, GLuint rbGL, + DXInterop2Device* interop, + HANDLE lockHandle, + ID3D11Texture2D* texD3D, + HANDLE dxgiHandle) + : SharedSurface(SharedSurfaceType::DXGLInterop2, + AttachmentType::GLRenderbuffer, + gl, + size, + hasAlpha, + true) + , mProdRB(rbGL) + , mInterop(interop) + , mLockHandle(lockHandle) + , mTexD3D(texD3D) + , mDXGIHandle(dxgiHandle) + , mNeedsFinish(gfxPrefs::WebGLDXGLNeedsFinish()) + , mLockedForGL(false) +{ } + +SharedSurface_D3D11Interop::~SharedSurface_D3D11Interop() +{ + MOZ_ASSERT(!IsProducerAcquired()); + + if (!mGL || !mGL->MakeCurrent()) + return; + + if (!mInterop->UnregisterObject(mLockHandle)) { + NS_WARNING("Failed to release mLockHandle, possibly leaking it."); + } + + mGL->fDeleteRenderbuffers(1, &mProdRB); +} + +void +SharedSurface_D3D11Interop::ProducerAcquireImpl() +{ + MOZ_ASSERT(!mLockedForGL); + + // Now we have the mutex, we can lock for GL. + MOZ_ALWAYS_TRUE( mInterop->LockObject(mLockHandle) ); + + mLockedForGL = true; +} + +void +SharedSurface_D3D11Interop::ProducerReleaseImpl() +{ + MOZ_ASSERT(mLockedForGL); + + if (mNeedsFinish) { + mGL->fFinish(); + } else { + // We probably don't even need this. + mGL->fFlush(); + } + MOZ_ALWAYS_TRUE( mInterop->UnlockObject(mLockHandle) ); + + mLockedForGL = false; +} + +bool +SharedSurface_D3D11Interop::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) +{ + const auto format = (mHasAlpha ? gfx::SurfaceFormat::B8G8R8A8 + : gfx::SurfaceFormat::B8G8R8X8); + *out_descriptor = layers::SurfaceDescriptorD3D10(WindowsHandle(mDXGIHandle), format, + mSize); + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// Factory + +/*static*/ UniquePtr<SurfaceFactory_D3D11Interop> +SurfaceFactory_D3D11Interop::Create(GLContext* gl, const SurfaceCaps& caps, + layers::LayersIPCChannel* allocator, + const layers::TextureFlags& flags) +{ + WGLLibrary* wgl = &sWGLLib; + if (!wgl || !wgl->HasDXInterop2()) + return nullptr; + + const RefPtr<DXInterop2Device> interop = DXInterop2Device::Open(wgl, gl); + if (!interop) { + NS_WARNING("Failed to open D3D device for use by WGL."); + return nullptr; + } + + typedef SurfaceFactory_D3D11Interop ptrT; + UniquePtr<ptrT> ret(new ptrT(gl, caps, allocator, flags, interop)); + return Move(ret); +} + +SurfaceFactory_D3D11Interop::SurfaceFactory_D3D11Interop(GLContext* gl, + const SurfaceCaps& caps, + layers::LayersIPCChannel* allocator, + const layers::TextureFlags& flags, + DXInterop2Device* interop) + : SurfaceFactory(SharedSurfaceType::DXGLInterop2, gl, caps, allocator, flags) + , mInterop(interop) +{ } + +SurfaceFactory_D3D11Interop::~SurfaceFactory_D3D11Interop() +{ } + +} // namespace gl +} // namespace mozilla |