gecko/gfx/gl/SharedSurfaceD3D11Interop.cpp

419 lines
14 KiB
C++

/* -*- 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 "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;
<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>
}
*/
////////////////////////////////////////////////////////////////////////////////
// DXGL Device
class DXGLDevice : public RefCounted<DXGLDevice>
{
public:
MOZ_DECLARE_REFCOUNTED_TYPENAME(DXGLDevice)
WGLLibrary* const mWGL;
const RefPtr<ID3D11Device> mD3D; // Only needed for lifetime guarantee.
const HANDLE mDXGLDeviceHandle;
static already_AddRefed<DXGLDevice> Open(WGLLibrary* wgl)
{
MOZ_ASSERT(wgl->HasDXInterop2());
gfxWindowsPlatform* plat = gfxWindowsPlatform::GetPlatform();
RefPtr<ID3D11Device> 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<DXGLDevice>(wgl, d3d, dxglDeviceHandle);
}
DXGLDevice(WGLLibrary* wgl, const RefPtr<ID3D11Device>& 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>
SharedSurface_D3D11Interop::Create(const RefPtr<DXGLDevice>& 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<ID3D11Texture2D> textureD3D;
HRESULT hr = d3d.CreateTexture2D(&desc, nullptr, getter_AddRefs(textureD3D));
if (FAILED(hr)) {
NS_WARNING("Failed to create texture for CanvasLayer!");
return nullptr;
}
RefPtr<IDXGIResource> textureDXGI;
hr = textureD3D->QueryInterface(__uuidof(IDXGIResource), getter_AddRefs(textureDXGI));
if (FAILED(hr)) {
NS_WARNING("Failed to open texture for sharing!");
return nullptr;
}
RefPtr<IDXGIKeyedMutex> 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<ptrT> 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<DXGLDevice>& dxgl,
HANDLE objectWGL,
const RefPtr<ID3D11Texture2D>& textureD3D,
HANDLE sharedHandle,
const RefPtr<IDXGIKeyedMutex>& 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 (mFence) {
mGL->MakeCurrent();
mGL->fDeleteFences(1, &mFence);
}
// 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>
SurfaceFactory_D3D11Interop::Create(GLContext* gl, const SurfaceCaps& caps,
const RefPtr<layers::ISurfaceAllocator>& allocator,
const layers::TextureFlags& flags)
{
WGLLibrary* wgl = &sWGLLib;
if (!wgl || !wgl->HasDXInterop2())
return nullptr;
RefPtr<DXGLDevice> dxgl = DXGLDevice::Open(wgl);
if (!dxgl) {
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, dxgl));
return Move(ret);
}
SurfaceFactory_D3D11Interop::SurfaceFactory_D3D11Interop(GLContext* gl,
const SurfaceCaps& caps,
const RefPtr<layers::ISurfaceAllocator>& allocator,
const layers::TextureFlags& flags,
const RefPtr<DXGLDevice>& dxgl)
: SurfaceFactory(SharedSurfaceType::DXGLInterop2, gl, caps, allocator, flags)
, mDXGL(dxgl)
{ }
SurfaceFactory_D3D11Interop::~SurfaceFactory_D3D11Interop()
{ }
} // namespace gl
} // namespace mozilla