Remove FenceInfo. Instead use GpuProcessFencesHolderId and GpuProcessD3D11FencesHolderMap. GpuProcessD3D11FencesHolderMap is changed to instantiate also in Parent process when GPU process does not exist. Then it seems better to change its name. The name change is going to be handled in Bug 1958752. Differential Revision: https://phabricator.services.mozilla.com/D244539
490 lines
16 KiB
C++
490 lines
16 KiB
C++
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 4; -*- */
|
|
/* 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 <d3d11_1.h>
|
|
#include "GLContext.h"
|
|
#include "GLBlitHelper.h"
|
|
#include "MozFramebuffer.h"
|
|
#include "ScopedGLHelpers.h"
|
|
#include "WGLLibrary.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "mozilla/gfx/DeviceManagerDx.h"
|
|
#include "mozilla/gfx/FileHandleWrapper.h"
|
|
#include "mozilla/gfx/Logging.h"
|
|
#include "mozilla/layers/LayersSurfaces.h"
|
|
#include "mozilla/StaticPrefs_webgl.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 ScopedContextState final {
|
|
ID3D11DeviceContext1* const mD3DContext;
|
|
RefPtr<ID3DDeviceContextState> mOldContextState;
|
|
|
|
public:
|
|
ScopedContextState(ID3D11DeviceContext1* d3dContext,
|
|
ID3DDeviceContextState* newContextState)
|
|
: mD3DContext(d3dContext), mOldContextState(nullptr) {
|
|
if (!mD3DContext) return;
|
|
|
|
mD3DContext->SwapDeviceContextState(newContextState,
|
|
getter_AddRefs(mOldContextState));
|
|
}
|
|
|
|
~ScopedContextState() {
|
|
if (!mD3DContext) return;
|
|
|
|
mD3DContext->SwapDeviceContextState(mOldContextState, nullptr);
|
|
}
|
|
};
|
|
|
|
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;
|
|
|
|
// AMD workaround.
|
|
const RefPtr<ID3D11DeviceContext1> mD3DContext;
|
|
const RefPtr<ID3DDeviceContextState> mContextState;
|
|
|
|
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;
|
|
|
|
RefPtr<ID3D11DeviceContext1> d3dContext;
|
|
RefPtr<ID3DDeviceContextState> contextState;
|
|
if (gl->WorkAroundDriverBugs() && gl->Vendor() == GLVendor::ATI) {
|
|
// AMD calls ID3D10Device::Flush, so we need to be in ID3D10Device mode.
|
|
RefPtr<ID3D11Device1> d3d11_1;
|
|
auto hr =
|
|
d3d->QueryInterface(__uuidof(ID3D11Device1), getter_AddRefs(d3d11_1));
|
|
if (!SUCCEEDED(hr)) return nullptr;
|
|
|
|
d3d11_1->GetImmediateContext1(getter_AddRefs(d3dContext));
|
|
MOZ_ASSERT(d3dContext);
|
|
|
|
const D3D_FEATURE_LEVEL featureLevel10_0 = D3D_FEATURE_LEVEL_10_0;
|
|
hr = d3d11_1->CreateDeviceContextState(
|
|
0, &featureLevel10_0, 1, D3D11_SDK_VERSION, __uuidof(ID3D10Device),
|
|
nullptr, getter_AddRefs(contextState));
|
|
if (!SUCCEEDED(hr)) return nullptr;
|
|
}
|
|
|
|
const auto interopDevice = wgl->mSymbols.fDXOpenDeviceNV(d3d);
|
|
if (!interopDevice) {
|
|
gfxCriticalNote << "DXInterop2Device::Open: DXOpenDevice failed.";
|
|
return nullptr;
|
|
}
|
|
|
|
return MakeAndAddRef<DXInterop2Device>(wgl, d3d, interopDevice, gl,
|
|
d3dContext, contextState);
|
|
}
|
|
|
|
DXInterop2Device(WGLLibrary* wgl, ID3D11Device* d3d, HANDLE interopDevice,
|
|
GLContext* gl, ID3D11DeviceContext1* d3dContext,
|
|
ID3DDeviceContextState* contextState)
|
|
: mWGL(wgl),
|
|
mD3D(d3d),
|
|
mInteropDevice(interopDevice),
|
|
mGL(gl),
|
|
mD3DContext(d3dContext),
|
|
mContextState(contextState) {}
|
|
|
|
~DXInterop2Device() {
|
|
const auto isCurrent = mGL->MakeCurrent();
|
|
|
|
if (mWGL->mSymbols.fDXCloseDeviceNV(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 ScopedContextState autoCS(mD3DContext, mContextState);
|
|
const auto ret = mWGL->mSymbols.fDXRegisterObjectNV(
|
|
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();
|
|
|
|
const ScopedContextState autoCS(mD3DContext, mContextState);
|
|
if (mWGL->mSymbols.fDXUnregisterObjectNV(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->mSymbols.fDXLockObjectsNV(mInteropDevice, 1, &lockHandle))
|
|
return true;
|
|
|
|
if (!mGL->MakeCurrent()) return false;
|
|
|
|
gfxCriticalNote << "wglDXLockObjects called without mGL being current."
|
|
<< " Retrying after MakeCurrent.";
|
|
|
|
if (mWGL->mSymbols.fDXLockObjectsNV(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->mSymbols.fDXUnlockObjectsNV(mInteropDevice, 1, &lockHandle))
|
|
return true;
|
|
|
|
if (!mGL->MakeCurrent()) return false;
|
|
|
|
gfxCriticalNote << "wglDXUnlockObjects called without mGL being current."
|
|
<< " Retrying after MakeCurrent.";
|
|
|
|
if (mWGL->mSymbols.fDXUnlockObjectsNV(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(
|
|
const SharedSurfaceDesc& desc, DXInterop2Device* interop) {
|
|
const auto& gl = desc.gl;
|
|
const auto& size = desc.size;
|
|
const auto& d3d = interop->mD3D;
|
|
|
|
auto data = Data{interop};
|
|
|
|
// Create a texture in case we need to readback.
|
|
const DXGI_FORMAT format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
|
CD3D11_TEXTURE2D_DESC texDesc(format, size.width, size.height, 1, 1);
|
|
texDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE |
|
|
D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
|
|
|
|
auto hr =
|
|
d3d->CreateTexture2D(&texDesc, nullptr, getter_AddRefs(data.texD3D));
|
|
if (FAILED(hr)) {
|
|
NS_WARNING("Failed to create texture for CanvasLayer!");
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<IDXGIResource1> texDXGI;
|
|
hr = data.texD3D->QueryInterface(__uuidof(IDXGIResource1),
|
|
getter_AddRefs(texDXGI));
|
|
if (FAILED(hr)) {
|
|
NS_WARNING("Failed to open texture for sharing!");
|
|
return nullptr;
|
|
}
|
|
|
|
HANDLE sharedHandle = nullptr;
|
|
texDXGI->CreateSharedHandle(
|
|
nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr,
|
|
&sharedHandle);
|
|
|
|
data.dxgiHandle = new gfx::FileHandleWrapper(UniqueFileHandle(sharedHandle));
|
|
|
|
////
|
|
|
|
if (!gl->MakeCurrent()) {
|
|
NS_WARNING("MakeCurrent failed.");
|
|
return nullptr;
|
|
}
|
|
|
|
data.interopRb = MakeUnique<Renderbuffer>(*gl);
|
|
data.lockHandle = interop->RegisterObject(data.texD3D, data.interopRb->name,
|
|
LOCAL_GL_RENDERBUFFER,
|
|
LOCAL_WGL_ACCESS_WRITE_DISCARD_NV);
|
|
if (!data.lockHandle) {
|
|
NS_WARNING("Failed to register D3D object with WGL.");
|
|
return nullptr;
|
|
}
|
|
|
|
auto fbForDrawing = MozFramebuffer::CreateForBacking(
|
|
gl, size, 0, false, LOCAL_GL_RENDERBUFFER, data.interopRb->name);
|
|
if (!fbForDrawing) return nullptr;
|
|
|
|
// -
|
|
|
|
{
|
|
GLint samples = 0;
|
|
{
|
|
const ScopedBindRenderbuffer bindRB(gl, data.interopRb->name);
|
|
gl->fGetRenderbufferParameteriv(LOCAL_GL_RENDERBUFFER,
|
|
LOCAL_GL_RENDERBUFFER_SAMPLES, &samples);
|
|
}
|
|
if (samples > 0) { // Intel
|
|
// Intel's dx_interop GL-side textures have SAMPLES=1, likely because
|
|
// that's what the D3DTextures technically have. However, SAMPLES=1 is
|
|
// very different from SAMPLES=0 in GL. For more, see
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1325835
|
|
|
|
// Our ShSurf tex or rb must be single-sampled.
|
|
data.interopFbIfNeedsIndirect = std::move(fbForDrawing);
|
|
fbForDrawing = MozFramebuffer::Create(gl, size, 0, false);
|
|
}
|
|
}
|
|
|
|
// -
|
|
|
|
return AsUnique(new SharedSurface_D3D11Interop(desc, std::move(fbForDrawing),
|
|
std::move(data)));
|
|
}
|
|
|
|
SharedSurface_D3D11Interop::SharedSurface_D3D11Interop(
|
|
const SharedSurfaceDesc& desc, UniquePtr<MozFramebuffer>&& fbForDrawing,
|
|
Data&& data)
|
|
: SharedSurface(desc, std::move(fbForDrawing)),
|
|
mData(std::move(data)),
|
|
mNeedsFinish(StaticPrefs::webgl_dxgl_needs_finish()) {}
|
|
|
|
SharedSurface_D3D11Interop::~SharedSurface_D3D11Interop() {
|
|
MOZ_ASSERT(!IsProducerAcquired());
|
|
|
|
const auto& gl = mDesc.gl;
|
|
if (!gl || !gl->MakeCurrent()) return;
|
|
|
|
if (!mData.interop->UnregisterObject(mData.lockHandle)) {
|
|
NS_WARNING("Failed to release mLockHandle, possibly leaking it.");
|
|
}
|
|
}
|
|
|
|
void SharedSurface_D3D11Interop::ProducerAcquireImpl() {
|
|
MOZ_ASSERT(!mLockedForGL);
|
|
|
|
// Now we have the mutex, we can lock for GL.
|
|
MOZ_ALWAYS_TRUE(mData.interop->LockObject(mData.lockHandle));
|
|
|
|
mLockedForGL = true;
|
|
}
|
|
|
|
void SharedSurface_D3D11Interop::ProducerReleaseImpl() {
|
|
const auto& gl = mDesc.gl;
|
|
MOZ_ASSERT(mLockedForGL);
|
|
|
|
if (mData.interopFbIfNeedsIndirect) {
|
|
const ScopedBindFramebuffer bindFB(gl, mData.interopFbIfNeedsIndirect->mFB);
|
|
gl->BlitHelper()->DrawBlitTextureToFramebuffer(mFb->ColorTex(), mDesc.size,
|
|
mDesc.size);
|
|
}
|
|
|
|
if (mNeedsFinish) {
|
|
gl->fFinish();
|
|
} else {
|
|
// We probably don't even need this.
|
|
gl->fFlush();
|
|
}
|
|
MOZ_ALWAYS_TRUE(mData.interop->UnlockObject(mData.lockHandle));
|
|
|
|
mLockedForGL = false;
|
|
}
|
|
|
|
Maybe<layers::SurfaceDescriptor>
|
|
SharedSurface_D3D11Interop::ToSurfaceDescriptor() {
|
|
const auto format = gfx::SurfaceFormat::B8G8R8A8;
|
|
return Some(layers::SurfaceDescriptorD3D10(
|
|
mData.dxgiHandle, /* gpuProcessTextureId */ Nothing(),
|
|
/* arrayIndex */ 0, format, mDesc.size, mDesc.colorSpace,
|
|
gfx::ColorRange::FULL, /* hasKeyedMutex */ true,
|
|
/* fencesHolderId */ Nothing()));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// Factory
|
|
|
|
/*static*/
|
|
UniquePtr<SurfaceFactory_D3D11Interop> SurfaceFactory_D3D11Interop::Create(
|
|
GLContext& gl) {
|
|
WGLLibrary* wgl = &sWGLLib;
|
|
if (!wgl || !wgl->HasDXInterop2()) return nullptr;
|
|
|
|
if (XRE_IsContentProcess()) {
|
|
gfxPlatform::GetPlatform()->EnsureDevicesInitialized();
|
|
}
|
|
|
|
const RefPtr<DXInterop2Device> interop = DXInterop2Device::Open(wgl, &gl);
|
|
if (!interop) {
|
|
NS_WARNING("Failed to open D3D device for use by WGL.");
|
|
return nullptr;
|
|
}
|
|
|
|
return AsUnique(new SurfaceFactory_D3D11Interop(
|
|
{&gl, SharedSurfaceType::DXGLInterop2, layers::TextureType::D3D11, true},
|
|
interop));
|
|
}
|
|
|
|
SurfaceFactory_D3D11Interop::SurfaceFactory_D3D11Interop(
|
|
const PartialSharedSurfaceDesc& desc, DXInterop2Device* interop)
|
|
: SurfaceFactory(desc), mInterop(interop) {}
|
|
|
|
SurfaceFactory_D3D11Interop::~SurfaceFactory_D3D11Interop() = default;
|
|
|
|
} // namespace gl
|
|
} // namespace mozilla
|