mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
419 lines
14 KiB
C++
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
|