summaryrefslogtreecommitdiffstats
path: root/gfx/gl/SharedSurfaceD3D11Interop.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/gl/SharedSurfaceD3D11Interop.cpp')
-rw-r--r--gfx/gl/SharedSurfaceD3D11Interop.cpp425
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