/* 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 "RenderD3D11TextureHost.h"
#include <dcomp.h>
#include "GLContextEGL.h"
#include "GLLibraryEGL.h"
#include "RenderThread.h"
#include "RenderCompositor.h"
#include "RenderCompositorD3D11SWGL.h"
#include "ScopedGLHelpers.h"
#include "mozilla/gfx/CanvasManagerParent.h"
#include "mozilla/gfx/DeviceManagerDx.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/layers/FenceD3D11.h"
#include "mozilla/layers/GpuProcessD3D11TextureMap.h"
#include "mozilla/layers/CompositeProcessD3D11FencesHolderMap.h"
#include "mozilla/layers/TextureD3D11.h"
namespace mozilla {
namespace wr {
RenderDXGITextureHost::RenderDXGITextureHost(
const RefPtr<gfx::FileHandleWrapper> aHandle,
const Maybe<layers::GpuProcessTextureId>& aGpuProcessTextureId,
const uint32_t aArrayIndex,
const gfx::SurfaceFormat aFormat,
const gfx::ColorSpace2 aColorSpace,
const gfx::ColorRange aColorRange,
const gfx::TransferFunction aTransferFunction,
const Maybe<gfx::HDRMetadata>& aHDRMetadata,
const gfx::IntSize aSize,
bool aHasKeyedMutex,
const Maybe<layers::CompositeProcessFencesHolderId>& aFencesHolderId)
: mHandle(aHandle),
mGpuProcessTextureId(aGpuProcessTextureId),
mArrayIndex(aArrayIndex),
mSurface(nullptr),
mStream(nullptr),
mTextureHandle{
0},
mFormat(aFormat),
mColorSpace(aColorSpace),
mColorRange(aColorRange),
mTransferFunction(aTransferFunction),
mHDRMetadata(aHDRMetadata),
mSize(aSize),
mHasKeyedMutex(aHasKeyedMutex),
mFencesHolderId(aFencesHolderId),
mLocked(
false) {
MOZ_COUNT_CTOR_INHERITED(RenderDXGITextureHost, RenderTextureHost);
MOZ_ASSERT((mFormat != gfx::SurfaceFormat::NV12 &&
mFormat != gfx::SurfaceFormat::P010 &&
mFormat != gfx::SurfaceFormat::P016) ||
(mSize.width %
2 ==
0 && mSize.height %
2 ==
0));
MOZ_ASSERT(!(!aHandle && aGpuProcessTextureId.isNothing()));
}
RenderDXGITextureHost::~RenderDXGITextureHost() {
MOZ_COUNT_DTOR_INHERITED(RenderDXGITextureHost, RenderTextureHost);
DeleteTextureHandle();
}
static bool IsCompatibleDXGIFormat(gfx::SurfaceFormat aFormat,
DXGI_FORMAT aDXGIFormat) {
switch (aFormat) {
case gfx::SurfaceFormat::B8G8R8A8:
case gfx::SurfaceFormat::B8G8R8X8:
case gfx::SurfaceFormat::R8G8B8A8:
case gfx::SurfaceFormat::R8G8B8X8:
return aDXGIFormat == DXGI_FORMAT_B8G8R8A8_UNORM ||
aDXGIFormat == DXGI_FORMAT_B8G8R8X8_UNORM ||
aDXGIFormat == DXGI_FORMAT_R8G8B8A8_UNORM;
case gfx::SurfaceFormat::R10G10B10A2_UINT32:
case gfx::SurfaceFormat::R10G10B10X2_UINT32:
return aDXGIFormat == DXGI_FORMAT_R10G10B10A2_UNORM;
case gfx::SurfaceFormat::R16G16B16A16F:
return aDXGIFormat == DXGI_FORMAT_R16G16B16A16_FLOAT;
case gfx::SurfaceFormat::NV12:
return aDXGIFormat == DXGI_FORMAT_NV12;
case gfx::SurfaceFormat::P010:
return aDXGIFormat == DXGI_FORMAT_P010;
case gfx::SurfaceFormat::P016:
return aDXGIFormat == DXGI_FORMAT_P016;
case gfx::SurfaceFormat::A8:
return aDXGIFormat == DXGI_FORMAT_R8_UNORM ||
aDXGIFormat == DXGI_FORMAT_A8_UNORM;
case gfx::SurfaceFormat::A16:
return aDXGIFormat == DXGI_FORMAT_R16_UNORM;
default:
return false;
}
}
/* static */
bool RenderDXGITextureHost::UseDCompositionTextureOverlay(
gfx::SurfaceFormat aFormat) {
if (!gfx::gfxVars::UseWebRenderDCompositionTextureOverlayWin()) {
return false;
}
if (!gfx::DeviceManagerDx::Get()->CanUseDCompositionTexture()) {
return false;
}
if (aFormat != gfx::SurfaceFormat::B8G8R8A8 &&
aFormat != gfx::SurfaceFormat::B8G8R8X8) {
return false;
}
return true;
}
IDCompositionTexture* RenderDXGITextureHost::GetDCompositionTexture() {
if (mDCompositionTexture) {
return mDCompositionTexture;
}
if (!UseDCompositionTextureOverlay(mFormat)) {
return nullptr;
}
if (mFencesHolderId.isNothing()) {
MOZ_ASSERT_UNREACHABLE(
"Unexpected to be called!");
return nullptr;
}
HRESULT hr;
RefPtr<IDCompositionDevice2> dcomp =
gfx::DeviceManagerDx::Get()->GetDirectCompositionDevice();
if (!dcomp) {
MOZ_ASSERT_UNREACHABLE(
"Unexpected to be called!");
gfxCriticalNoteOnce <<
"Failed to get DirectCompositionDevice";
return nullptr;
}
RefPtr<IDCompositionDevice4> dcomp4;
hr = dcomp->QueryInterface((IDCompositionDevice4**)getter_AddRefs(dcomp4));
if (FAILED(hr)) {
MOZ_ASSERT_UNREACHABLE(
"Unexpected to be called!");
gfxCriticalNoteOnce <<
"Failed to get DCompositionDevice4";
return nullptr;
}
RefPtr<ID3D11Texture2D> texture2D = GetD3D11Texture2DWithGL();
if (!texture2D) {
gfxCriticalNoteOnce <<
"Failed to get D3D11Texture";
return nullptr;
}
RefPtr<IDCompositionTexture> dcompTexture;
hr =
dcomp4->CreateCompositionTexture(texture2D, getter_AddRefs(dcompTexture));
if (FAILED(hr)) {
gfxCriticalNoteOnce <<
"CreateCompositionTexture failed: "
<< gfx::hexa(hr);
return nullptr;
}
mDCompositionTexture = dcompTexture;
return mDCompositionTexture;
}
ID3D11Texture2D* RenderDXGITextureHost::GetD3D11Texture2DWithGL() {
if (mTexture) {
return mTexture;
}
if (!mGL) {
// SingletonGL is always used on Windows with ANGLE.
mGL = RenderThread::Get()->SingletonGL();
}
if (!EnsureD3D11Texture2DWithGL()) {
return nullptr;
}
return mTexture;
}
size_t RenderDXGITextureHost::GetPlaneCount()
const {
switch (mFormat) {
case gfx::SurfaceFormat::NV12:
case gfx::SurfaceFormat::P010:
case gfx::SurfaceFormat::P016: {
return 2;
}
case gfx::SurfaceFormat::B8G8R8A8:
case gfx::SurfaceFormat::B8G8R8X8:
case gfx::SurfaceFormat::R8G8B8A8:
case gfx::SurfaceFormat::R8G8B8X8:
case gfx::SurfaceFormat::A8R8G8B8:
case gfx::SurfaceFormat::X8R8G8B8:
case gfx::SurfaceFormat::R8G8B8:
case gfx::SurfaceFormat::B8G8R8:
case gfx::SurfaceFormat::R5G6B5_UINT16:
case gfx::SurfaceFormat::R10G10B10A2_UINT32:
case gfx::SurfaceFormat::R10G10B10X2_UINT32:
case gfx::SurfaceFormat::R16G16B16A16F:
case gfx::SurfaceFormat::A8:
case gfx::SurfaceFormat::A16:
case gfx::SurfaceFormat::R8G8:
case gfx::SurfaceFormat::R16G16:
case gfx::SurfaceFormat::YUV420:
case gfx::SurfaceFormat::YUV420P10:
case gfx::SurfaceFormat::YUV422P10:
case gfx::SurfaceFormat::NV16:
case gfx::SurfaceFormat::YUY2:
case gfx::SurfaceFormat::HSV:
case gfx::SurfaceFormat::Lab:
case gfx::SurfaceFormat::Depth:
case gfx::SurfaceFormat::UNKNOWN:
return 1;
}
MOZ_ASSERT_UNREACHABLE(
"unhandled enum value for gfx::SurfaceFormat");
return 1;
}
template <typename T>
static bool MapTexture(T* aHost, RenderCompositor* aCompositor,
RefPtr<ID3D11Texture2D>& aTexture,
RefPtr<ID3D11DeviceContext>& aDeviceContext,
RefPtr<ID3D11Texture2D>& aCpuTexture,
D3D11_MAPPED_SUBRESOURCE& aMappedSubresource) {
if (!aCompositor) {
return false;
}
RenderCompositorD3D11SWGL* compositor =
aCompositor->AsRenderCompositorD3D11SWGL();
if (!compositor) {
return false;
}
if (!aHost->EnsureD3D11Texture2D(compositor->GetDevice())) {
return false;
}
if (!aHost->LockInternal()) {
return false;
}
D3D11_TEXTURE2D_DESC textureDesc = {
0};
aTexture->GetDesc(&textureDesc);
compositor->GetDevice()->GetImmediateContext(getter_AddRefs(aDeviceContext));
textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
textureDesc.Usage = D3D11_USAGE_STAGING;
textureDesc.BindFlags =
0;
textureDesc.MiscFlags =
0;
textureDesc.MipLevels =
1;
HRESULT hr = compositor->GetDevice()->CreateTexture2D(
&textureDesc, nullptr, getter_AddRefs(aCpuTexture));
if (FAILED(hr)) {
return false;
}
aDeviceContext->CopyResource(aCpuTexture, aTexture);
aHost->Unlock();
hr = aDeviceContext->Map(aCpuTexture,
0, D3D11_MAP_READ,
0,
&aMappedSubresource);
return SUCCEEDED(hr);
}
bool RenderDXGITextureHost::MapPlane(RenderCompositor* aCompositor,
uint8_t aChannelIndex,
PlaneInfo& aPlaneInfo) {
// TODO: We currently readback from the GPU texture into a new
// staging texture every time this is mapped. We might be better
// off retaining the mapped memory to trade performance for memory
// usage.
if (!mCpuTexture && !MapTexture(this, aCompositor, mTexture, mDeviceContext,
mCpuTexture, mMappedSubresource)) {
return false;
}
aPlaneInfo.mSize = GetSize(aChannelIndex);
aPlaneInfo.mStride = mMappedSubresource.RowPitch;
aPlaneInfo.mData = mMappedSubresource.pData;
// If this is the second plane, then offset the data pointer by the
// size of the first plane.
if (aChannelIndex ==
1) {
aPlaneInfo.mData =
(uint8_t*)aPlaneInfo.mData + aPlaneInfo.mStride * GetSize(
0).height;
}
return true;
}
void RenderDXGITextureHost::UnmapPlanes() {
mMappedSubresource.pData = nullptr;
if (mCpuTexture) {
mDeviceContext->Unmap(mCpuTexture,
0);
mCpuTexture = nullptr;
}
mDeviceContext = nullptr;
}
bool RenderDXGITextureHost::EnsureD3D11Texture2DWithGL() {
if (mTexture) {
return true;
}
const auto& gle = gl::GLContextEGL::Cast(mGL);
const auto& egl = gle->mEgl;
// Fetch the D3D11 device.
EGLDeviceEXT eglDevice = nullptr;
egl->fQueryDisplayAttribEXT(LOCAL_EGL_DEVICE_EXT, (EGLAttrib*)&eglDevice);
MOZ_ASSERT(eglDevice);
ID3D11Device* device = nullptr;
egl->mLib->fQueryDeviceAttribEXT(eglDevice, LOCAL_EGL_D3D11_DEVICE_ANGLE,
(EGLAttrib*)&device);
// There's a chance this might fail if we end up on d3d9 angle for some
// reason.
if (!device) {
gfxCriticalNote <<
"RenderDXGITextureHost device is not available";
return false;
}
return EnsureD3D11Texture2D(device);
}
bool RenderDXGITextureHost::EnsureD3D11Texture2D(ID3D11Device* aDevice) {
if (mTexture) {
RefPtr<ID3D11Device> device;
mTexture->GetDevice(getter_AddRefs(device));
if (aDevice != device) {
gfxCriticalNote <<
"RenderDXGITextureHost uses obsoleted device";
return false;
}
return true;
}
if (mGpuProcessTextureId.isSome()) {
auto* textureMap = layers::GpuProcessD3D11TextureMap::Get();
if (textureMap) {
RefPtr<ID3D11Texture2D> texture;
textureMap->WaitTextureReady(mGpuProcessTextureId.ref());
mTexture = textureMap->GetTexture(mGpuProcessTextureId.ref());
}
if (!mTexture) {
gfxCriticalNote <<
"GpuProcessTextureId is not valid";
return false;
}
}
else {
RefPtr<ID3D11Device1> device1;
aDevice->QueryInterface((ID3D11Device1**)getter_AddRefs(device1));
if (!device1) {
gfxCriticalNoteOnce <<
"Failed to get ID3D11Device1";
return false;
}
// Get the D3D11 texture from shared handle.
HRESULT hr = device1->OpenSharedResource1(
(HANDLE)mHandle->GetHandle(), __uuidof(ID3D11Texture2D),
(
void**)(ID3D11Texture2D**)getter_AddRefs(mTexture));
if (FAILED(hr)) {
MOZ_ASSERT(
false,
"RenderDXGITextureHost::EnsureLockable(): Failed to open "
"shared texture");
gfxCriticalNote
<<
"RenderDXGITextureHost Failed to open shared texture, hr="
<< gfx::hexa(hr);
return false;
}
MOZ_ASSERT(mTexture.get());
mTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mKeyedMutex));
MOZ_ASSERT(mHasKeyedMutex == !!mKeyedMutex);
if (mHasKeyedMutex != !!mKeyedMutex) {
gfxCriticalNoteOnce <<
"KeyedMutex mismatch";
}
}
D3D11_TEXTURE2D_DESC desc{};
mTexture->GetDesc(&desc);
if (uint32_t(mSize.width) > desc.Width ||
uint32_t(mSize.height) > desc.Height ||
!IsCompatibleDXGIFormat(mFormat, desc.Format)) {
gfxCriticalNote <<
"RenderDXGITextureHost descriptor (" << mSize.width
<<
"x" << mSize.height <<
" fmt " <<
int(mFormat)
<<
") does not match resource (" << desc.Width <<
"x"
<< desc.Height <<
" DXGI " <<
int(desc.Format) <<
")";
mTexture = nullptr;
mKeyedMutex = nullptr;
return false;
}
return true;
}
bool RenderDXGITextureHost::EnsureLockable() {
if (mTextureHandle[
0]) {
return true;
}
const auto& gle = gl::GLContextEGL::Cast(mGL);
const auto& egl = gle->mEgl;
// We use EGLStream to get the converted gl handle from d3d texture. The
// NV_stream_consumer_gltexture_yuv and ANGLE_stream_producer_d3d_texture
// could support nv12 and rgb d3d texture format.
if (!egl->IsExtensionSupported(
gl::EGLExtension::NV_stream_consumer_gltexture_yuv) ||
!egl->IsExtensionSupported(
gl::EGLExtension::ANGLE_stream_producer_d3d_texture)) {
gfxCriticalNote <<
"RenderDXGITextureHost egl extensions are not suppored";
return false;
}
// Get the D3D11 texture from shared handle.
if (!EnsureD3D11Texture2DWithGL()) {
return false;
}
// Create the EGLStream.
mStream = egl->fCreateStreamKHR(nullptr);
MOZ_ASSERT(mStream);
bool ok = true;
if (mFormat != gfx::SurfaceFormat::NV12 &&
mFormat != gfx::SurfaceFormat::P010 &&
mFormat != gfx::SurfaceFormat::P016) {
// The non-nv12 format.
mGL->fGenTextures(
1, mTextureHandle);
ActivateBindAndTexParameteri(mGL, LOCAL_GL_TEXTURE0,
LOCAL_GL_TEXTURE_EXTERNAL_OES,
mTextureHandle[
0]);
ok &=
bool(egl->fStreamConsumerGLTextureExternalAttribsNV(mStream, nullptr));
ok &=
bool(egl->fCreateStreamProducerD3DTextureANGLE(mStream, nullptr));
}
else {
// The nv12/p016 format.
// Setup the NV12 stream consumer/producer.
EGLAttrib consumerAttributes[] = {
LOCAL_EGL_COLOR_BUFFER_TYPE,
LOCAL_EGL_YUV_BUFFER_EXT,
LOCAL_EGL_YUV_NUMBER_OF_PLANES_EXT,
2,
LOCAL_EGL_YUV_PLANE0_TEXTURE_UNIT_NV,
0,
LOCAL_EGL_YUV_PLANE1_TEXTURE_UNIT_NV,
1,
LOCAL_EGL_NONE,
};
mGL->fGenTextures(
2, mTextureHandle);
ActivateBindAndTexParameteri(mGL, LOCAL_GL_TEXTURE0,
LOCAL_GL_TEXTURE_EXTERNAL_OES,
mTextureHandle[
0]);
ActivateBindAndTexParameteri(mGL, LOCAL_GL_TEXTURE1,
LOCAL_GL_TEXTURE_EXTERNAL_OES,
mTextureHandle[
1]);
ok &=
bool(egl->fStreamConsumerGLTextureExternalAttribsNV(
mStream, consumerAttributes));
ok &=
bool(egl->fCreateStreamProducerD3DTextureANGLE(mStream, nullptr));
}
const EGLAttrib frameAttributes[] = {
LOCAL_EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE,
static_cast<EGLAttrib>(mArrayIndex),
LOCAL_EGL_NONE,
};
// Insert the d3d texture.
ok &=
bool(egl->fStreamPostD3DTextureANGLE(mStream, (
void*)mTexture.get(),
frameAttributes));
if (!ok) {
gfxCriticalNote <<
"RenderDXGITextureHost init stream failed";
DeleteTextureHandle();
return false;
}
// Now, we could get the gl handle from the stream.
MOZ_ALWAYS_TRUE(egl->fStreamConsumerAcquireKHR(mStream));
return true;
}
wr::WrExternalImage RenderDXGITextureHost::Lock(uint8_t aChannelIndex,
gl::GLContext* aGL) {
if (mGL.get() != aGL) {
// Release the texture handle in the previous gl context.
DeleteTextureHandle();
mGL = aGL;
}
if (!mGL) {
// XXX Software WebRender is not handled yet.
// Software WebRender does not provide GLContext
gfxCriticalNoteOnce
<<
"Software WebRender is not suppored by RenderDXGITextureHost.";
return InvalidToWrExternalImage();
}
if (!EnsureLockable()) {
return InvalidToWrExternalImage();
}
if (!LockInternal()) {
return InvalidToWrExternalImage();
}
const gfx::IntSize size = GetSize(aChannelIndex);
return NativeTextureToWrExternalImage(GetGLHandle(aChannelIndex),
0.
0,
0.
0,
static_cast<
float>(size.width),
static_cast<
float>(size.height));
}
bool RenderDXGITextureHost::LockInternal() {
MOZ_ASSERT(mTexture);
if (!mLocked) {
if (mFencesHolderId.isSome()) {
auto* fencesHolderMap =
layers::CompositeProcessD3D11FencesHolderMap::Get();
if (!fencesHolderMap) {
MOZ_ASSERT_UNREACHABLE(
"unexpected to be called");
return false;
}
RefPtr<ID3D11Device> device;
mTexture->GetDevice(getter_AddRefs(device));
if (!fencesHolderMap->WaitWriteFence(mFencesHolderId.ref(), device)) {
return false;
}
}
if (mKeyedMutex) {
HRESULT hr = mKeyedMutex->AcquireSync(
0,
10000);
if (hr != S_OK) {
gfxCriticalError() <<
"RenderDXGITextureHost AcquireSync timeout, hr="
<< gfx::hexa(hr);
return false;
}
}
mLocked = true;
}
return true;
}
void RenderDXGITextureHost::Unlock() {
if (mLocked) {
if (mKeyedMutex) {
mKeyedMutex->ReleaseSync(
0);
}
mLocked =
false;
}
}
void RenderDXGITextureHost::ClearCachedResources() {
DeleteTextureHandle();
mGL = nullptr;
}
void RenderDXGITextureHost::DeleteTextureHandle() {
if (mTextureHandle[
0] ==
0) {
return;
}
MOZ_ASSERT(mGL.get());
if (!mGL) {
return;
}
if (mGL->MakeCurrent()) {
mGL->fDeleteTextures(
2, mTextureHandle);
const auto& gle = gl::GLContextEGL::Cast(mGL);
const auto& egl = gle->mEgl;
if (mSurface) {
egl->fDestroySurface(mSurface);
}
if (mStream) {
egl->fDestroyStreamKHR(mStream);
}
}
for (
int i =
0; i <
2; ++i) {
mTextureHandle[i] =
0;
}
mTexture = nullptr;
mKeyedMutex = nullptr;
mSurface = nullptr;
mStream = nullptr;
}
GLuint RenderDXGITextureHost::GetGLHandle(uint8_t aChannelIndex)
const {
MOZ_ASSERT(((mFormat == gfx::SurfaceFormat::NV12 ||
mFormat == gfx::SurfaceFormat::P010 ||
mFormat == gfx::SurfaceFormat::P016) &&
aChannelIndex <
2) ||
aChannelIndex <
1);
return mTextureHandle[aChannelIndex];
}
gfx::IntSize RenderDXGITextureHost::GetSize(uint8_t aChannelIndex)
const {
MOZ_ASSERT(((mFormat == gfx::SurfaceFormat::NV12 ||
mFormat == gfx::SurfaceFormat::P010 ||
mFormat == gfx::SurfaceFormat::P016) &&
aChannelIndex <
2) ||
aChannelIndex <
1);
if (aChannelIndex ==
0) {
return mSize;
}
else {
// The CbCr channel size is a half of Y channel size in NV12 format.
return mSize /
2;
}
}
bool RenderDXGITextureHost::SyncObjectNeeded() {
return mGpuProcessTextureId.isNothing() && !mHasKeyedMutex &&
mFencesHolderId.isNothing();
}
RenderDXGIYCbCrTextureHost::RenderDXGIYCbCrTextureHost(
const RefPtr<gfx::FileHandleWrapper> (&aHandles)[
3],
const gfx::YUVColorSpace aYUVColorSpace,
const gfx::ColorDepth aColorDepth,
const gfx::ColorRange aColorRange,
const gfx::TransferFunction aTransferFunction,
const gfx::IntSize aSizeY,
const gfx::IntSize aSizeCbCr,
const layers::CompositeProcessFencesHolderId aFencesHolderId)
: mHandles{aHandles[
0], aHandles[
1], aHandles[
2]},
mSurfaces{nullptr},
mStreams{nullptr},
mTextureHandles{
0},
mYUVColorSpace(aYUVColorSpace),
mColorDepth(aColorDepth),
mColorRange(aColorRange),
mTransferFunction(aTransferFunction),
mSizeY(aSizeY),
mSizeCbCr(aSizeCbCr),
mFencesHolderId(aFencesHolderId) {
MOZ_COUNT_CTOR_INHERITED(RenderDXGIYCbCrTextureHost, RenderTextureHost);
// Assume the chroma planes are rounded up if the luma plane is odd sized.
MOZ_ASSERT((mSizeCbCr.width == mSizeY.width ||
mSizeCbCr.width == (mSizeY.width +
1) >>
1) &&
(mSizeCbCr.height == mSizeY.height ||
mSizeCbCr.height == (mSizeY.height +
1) >>
1));
MOZ_ASSERT(aHandles[
0] && aHandles[
1] && aHandles[
2]);
}
bool RenderDXGIYCbCrTextureHost::MapPlane(RenderCompositor* aCompositor,
uint8_t aChannelIndex,
PlaneInfo& aPlaneInfo) {
D3D11_MAPPED_SUBRESOURCE mappedSubresource;
if (!MapTexture(this, aCompositor, mTextures[aChannelIndex], mDeviceContext,
mCpuTexture[aChannelIndex], mappedSubresource)) {
return false;
}
aPlaneInfo.mSize = GetSize(aChannelIndex);
aPlaneInfo.mStride = mappedSubresource.RowPitch;
aPlaneInfo.mData = mappedSubresource.pData;
return true;
}
void RenderDXGIYCbCrTextureHost::UnmapPlanes() {
for (uint32_t i =
0; i <
3; i++) {
if (mCpuTexture[i]) {
mDeviceContext->Unmap(mCpuTexture[i],
0);
mCpuTexture[i] = nullptr;
}
}
mDeviceContext = nullptr;
}
RenderDXGIYCbCrTextureHost::~RenderDXGIYCbCrTextureHost() {
MOZ_COUNT_DTOR_INHERITED(RenderDXGIYCbCrTextureHost, RenderTextureHost);
DeleteTextureHandle();
}
bool RenderDXGIYCbCrTextureHost::EnsureLockable() {
if (mTextureHandles[
0]) {
return true;
}
const auto& gle = gl::GLContextEGL::Cast(mGL);
const auto& egl = gle->mEgl;
// The eglCreatePbufferFromClientBuffer doesn't support R8 format, so we
// use EGLStream to get the converted gl handle from d3d R8 texture.
if (!egl->IsExtensionSupported(
gl::EGLExtension::NV_stream_consumer_gltexture_yuv) ||
!egl->IsExtensionSupported(
gl::EGLExtension::ANGLE_stream_producer_d3d_texture)) {
gfxCriticalNote
<<
"RenderDXGIYCbCrTextureHost egl extensions are not suppored";
return false;
}
// Fetch the D3D11 device.
EGLDeviceEXT eglDevice = nullptr;
egl->fQueryDisplayAttribEXT(LOCAL_EGL_DEVICE_EXT, (EGLAttrib*)&eglDevice);
MOZ_ASSERT(eglDevice);
ID3D11Device* device = nullptr;
egl->mLib->fQueryDeviceAttribEXT(eglDevice, LOCAL_EGL_D3D11_DEVICE_ANGLE,
(EGLAttrib*)&device);
// There's a chance this might fail if we end up on d3d9 angle for some
// reason.
if (!device) {
gfxCriticalNote <<
"RenderDXGIYCbCrTextureHost device is not available";
return false;
}
if (!EnsureD3D11Texture2D(device)) {
return false;
}
mGL->fGenTextures(
3, mTextureHandles);
bool ok = true;
for (
int i =
0; i <
3; ++i) {
ActivateBindAndTexParameteri(mGL, LOCAL_GL_TEXTURE0 + i,
LOCAL_GL_TEXTURE_EXTERNAL_OES,
mTextureHandles[i]);
// Create the EGLStream.
mStreams[i] = egl->fCreateStreamKHR(nullptr);
MOZ_ASSERT(mStreams[i]);
ok &=
bool(
egl->fStreamConsumerGLTextureExternalAttribsNV(mStreams[i], nullptr));
ok &=
bool(egl->fCreateStreamProducerD3DTextureANGLE(mStreams[i], nullptr));
// Insert the R8 texture.
ok &=
bool(egl->fStreamPostD3DTextureANGLE(
mStreams[i], (
void*)mTextures[i].get(), nullptr));
// Now, we could get the R8 gl handle from the stream.
MOZ_ALWAYS_TRUE(egl->fStreamConsumerAcquireKHR(mStreams[i]));
}
if (!ok) {
gfxCriticalNote <<
"RenderDXGIYCbCrTextureHost init stream failed";
DeleteTextureHandle();
return false;
}
return true;
}
bool RenderDXGIYCbCrTextureHost::EnsureD3D11Texture2D(ID3D11Device* aDevice) {
RefPtr<ID3D11Device1> device1;
aDevice->QueryInterface((ID3D11Device1**)getter_AddRefs(device1));
if (!device1) {
gfxCriticalNoteOnce <<
"Failed to get ID3D11Device1";
return false;
}
if (mTextures[
0]) {
RefPtr<ID3D11Device> device;
mTextures[
0]->GetDevice(getter_AddRefs(device));
if (aDevice != device) {
gfxCriticalNote <<
"RenderDXGIYCbCrTextureHost uses obsoleted device";
return false;
}
}
if (mTextureHandles[
0]) {
return true;
}
const DXGI_FORMAT expectedFormat = mColorDepth == gfx::ColorDepth::COLOR_8
? DXGI_FORMAT_R8_UNORM
: DXGI_FORMAT_R16_UNORM;
for (
int i =
0; i <
3; ++i) {
// Get the R8 D3D11 texture from shared handle.
HRESULT hr = device1->OpenSharedResource1(
(HANDLE)mHandles[i]->GetHandle(), __uuidof(ID3D11Texture2D),
(
void**)(ID3D11Texture2D**)getter_AddRefs(mTextures[i]));
if (FAILED(hr)) {
NS_WARNING(
"RenderDXGIYCbCrTextureHost::EnsureLockable(): Failed to open "
"shared "
"texture");
gfxCriticalNote
<<
"RenderDXGIYCbCrTextureHost Failed to open shared texture, hr="
<< gfx::hexa(hr);
return false;
}
D3D11_TEXTURE2D_DESC desc{};
mTextures[i]->GetDesc(&desc);
const gfx::IntSize& expected = (i ==
0) ? mSizeY : mSizeCbCr;
if (uint32_t(expected.width) > desc.Width ||
uint32_t(expected.height) > desc.Height ||
desc.Format != expectedFormat) {
gfxCriticalNote <<
"RenderDXGIYCbCrTextureHost descriptor ("
<< expected.width <<
"x" << expected.height <<
" depth "
<<
int(mColorDepth) <<
") does not match resource ("
<< desc.Width <<
"x" << desc.Height <<
" DXGI "
<<
int(desc.Format) <<
")";
for (
auto& tex : mTextures) {
tex = nullptr;
}
return false;
}
}
mDevice = aDevice;
return true;
}
bool RenderDXGIYCbCrTextureHost::LockInternal() {
if (!mLocked) {
auto* fencesHolderMap = layers::CompositeProcessD3D11FencesHolderMap::Get();
if (!fencesHolderMap) {
MOZ_ASSERT_UNREACHABLE(
"unexpected to be called");
return false;
}
if (!fencesHolderMap->WaitWriteFence(mFencesHolderId, mDevice)) {
return false;
}
mLocked = true;
}
return true;
}
wr::WrExternalImage RenderDXGIYCbCrTextureHost::Lock(uint8_t aChannelIndex,
gl::GLContext* aGL) {
if (mGL.get() != aGL) {
// Release the texture handle in the previous gl context.
DeleteTextureHandle();
mGL = aGL;
}
if (!mGL) {
// XXX Software WebRender is not handled yet.
// Software WebRender does not provide GLContext
gfxCriticalNoteOnce <<
"Software WebRender is not suppored by "
"RenderDXGIYCbCrTextureHost.";
return InvalidToWrExternalImage();
}
if (!EnsureLockable()) {
return InvalidToWrExternalImage();
}
if (!LockInternal()) {
return InvalidToWrExternalImage();
}
const gfx::IntSize size = GetSize(aChannelIndex);
return NativeTextureToWrExternalImage(GetGLHandle(aChannelIndex),
0.
0,
0.
0,
static_cast<
float>(size.width),
static_cast<
float>(size.height));
}
void RenderDXGIYCbCrTextureHost::Unlock() {
if (mLocked) {
mLocked =
false;
}
}
void RenderDXGIYCbCrTextureHost::ClearCachedResources() {
DeleteTextureHandle();
mGL = nullptr;
}
GLuint RenderDXGIYCbCrTextureHost::GetGLHandle(uint8_t aChannelIndex)
const {
MOZ_ASSERT(aChannelIndex <
3);
return mTextureHandles[aChannelIndex];
}
gfx::IntSize RenderDXGIYCbCrTextureHost::GetSize(uint8_t aChannelIndex)
const {
MOZ_ASSERT(aChannelIndex <
3);
if (aChannelIndex ==
0) {
return mSizeY;
}
else {
return mSizeCbCr;
}
}
void RenderDXGIYCbCrTextureHost::DeleteTextureHandle() {
if (mTextureHandles[
0] ==
0) {
return;
}
MOZ_ASSERT(mGL.get());
if (!mGL) {
return;
}
if (mGL->MakeCurrent()) {
mGL->fDeleteTextures(
3, mTextureHandles);
const auto& gle = gl::GLContextEGL::Cast(mGL);
const auto& egl = gle->mEgl;
for (
int i =
0; i <
3; ++i) {
mTextureHandles[i] =
0;
mTextures[i] = nullptr;
if (mSurfaces[i]) {
egl->fDestroySurface(mSurfaces[i]);
mSurfaces[i] = nullptr;
}
if (mStreams[i]) {
egl->fDestroyStreamKHR(mStreams[i]);
mStreams[i] = nullptr;
}
}
}
mDevice = nullptr;
}
}
// namespace wr
}
// namespace mozilla