mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
7d06300567
gecko uses IDXGIResource::GetSharedHandle(). But it is recommend not to use anymore to retrieve the handle to a shared resource. IDXGIResource1::CreateSharedHandle() with D3D11_RESOURCE_MISC_SHARED_NTHANDLE flag should be used instead of the GetSharedHandle(). The CreateSharedHandle() could be called only once for a shared resource. Later calls fail. HANDLEs of ID3D11Texture2D are replaced by gfx::FileHandleWrappers. Differential Revision: https://phabricator.services.mozilla.com/D192173
410 lines
14 KiB
C++
410 lines
14 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* 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 "GLBlitHelper.h"
|
|
|
|
#include <d3d11.h>
|
|
#include <d3d11_1.h>
|
|
|
|
#include "GLContextEGL.h"
|
|
#include "GLLibraryEGL.h"
|
|
#include "GPUVideoImage.h"
|
|
#include "ScopedGLHelpers.h"
|
|
|
|
#include "mozilla/gfx/D3D11Checks.h"
|
|
#include "mozilla/layers/D3D11ShareHandleImage.h"
|
|
#include "mozilla/layers/D3D11TextureIMFSampleImage.h"
|
|
#include "mozilla/layers/D3D11YCbCrImage.h"
|
|
#include "mozilla/layers/GpuProcessD3D11TextureMap.h"
|
|
#include "mozilla/layers/TextureD3D11.h"
|
|
#include "mozilla/StaticPrefs_gl.h"
|
|
|
|
namespace mozilla {
|
|
namespace gl {
|
|
|
|
#define NOTE_IF_FALSE(expr) \
|
|
do { \
|
|
if (!(expr)) { \
|
|
gfxCriticalNote << "NOTE_IF_FALSE: " << #expr; \
|
|
} \
|
|
} while (false)
|
|
|
|
static EGLStreamKHR StreamFromD3DTexture(EglDisplay* const egl,
|
|
ID3D11Texture2D* const texD3D,
|
|
const EGLAttrib* const postAttribs) {
|
|
if (!egl->IsExtensionSupported(
|
|
EGLExtension::NV_stream_consumer_gltexture_yuv) ||
|
|
!egl->IsExtensionSupported(
|
|
EGLExtension::ANGLE_stream_producer_d3d_texture)) {
|
|
return 0;
|
|
}
|
|
|
|
const auto stream = egl->fCreateStreamKHR(nullptr);
|
|
MOZ_ASSERT(stream);
|
|
if (!stream) return 0;
|
|
bool ok = true;
|
|
NOTE_IF_FALSE(ok &= bool(egl->fStreamConsumerGLTextureExternalAttribsNV(
|
|
stream, nullptr)));
|
|
NOTE_IF_FALSE(
|
|
ok &= bool(egl->fCreateStreamProducerD3DTextureANGLE(stream, nullptr)));
|
|
NOTE_IF_FALSE(
|
|
ok &= bool(egl->fStreamPostD3DTextureANGLE(stream, texD3D, postAttribs)));
|
|
if (ok) return stream;
|
|
|
|
(void)egl->fDestroyStreamKHR(stream);
|
|
return 0;
|
|
}
|
|
|
|
static RefPtr<ID3D11Texture2D> OpenSharedTexture(ID3D11Device* const d3d,
|
|
const WindowsHandle handle) {
|
|
RefPtr<ID3D11Device1> device1;
|
|
d3d->QueryInterface((ID3D11Device1**)getter_AddRefs(device1));
|
|
if (!device1) {
|
|
gfxCriticalNoteOnce << "Failed to get ID3D11Device1";
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<ID3D11Texture2D> tex;
|
|
auto hr = device1->OpenSharedResource1(
|
|
(HANDLE)handle, __uuidof(ID3D11Texture2D),
|
|
(void**)(ID3D11Texture2D**)getter_AddRefs(tex));
|
|
if (FAILED(hr)) {
|
|
gfxCriticalError() << "Error code from OpenSharedResource1: "
|
|
<< gfx::hexa(hr);
|
|
return nullptr;
|
|
}
|
|
return tex;
|
|
}
|
|
|
|
// -------------------------------------
|
|
|
|
class BindAnglePlanes final {
|
|
const GLBlitHelper& mParent;
|
|
const uint8_t mNumPlanes;
|
|
const ScopedSaveMultiTex mMultiTex;
|
|
GLuint mTempTexs[3];
|
|
EGLStreamKHR mStreams[3];
|
|
RefPtr<IDXGIKeyedMutex> mMutexList[3];
|
|
bool mSuccess;
|
|
|
|
public:
|
|
BindAnglePlanes(const GLBlitHelper* const parent, const uint8_t numPlanes,
|
|
const RefPtr<ID3D11Texture2D>* const texD3DList,
|
|
const EGLAttrib* const* postAttribsList = nullptr)
|
|
: mParent(*parent),
|
|
mNumPlanes(numPlanes),
|
|
mMultiTex(
|
|
mParent.mGL,
|
|
[&]() {
|
|
std::vector<uint8_t> ret;
|
|
for (int i = 0; i < numPlanes; i++) {
|
|
ret.push_back(i);
|
|
}
|
|
return ret;
|
|
}(),
|
|
LOCAL_GL_TEXTURE_EXTERNAL),
|
|
mTempTexs{0},
|
|
mStreams{0},
|
|
mSuccess(true) {
|
|
MOZ_RELEASE_ASSERT(numPlanes >= 1 && numPlanes <= 3);
|
|
|
|
const auto& gl = mParent.mGL;
|
|
const auto& gle = GLContextEGL::Cast(gl);
|
|
const auto& egl = gle->mEgl;
|
|
|
|
gl->fGenTextures(numPlanes, mTempTexs);
|
|
|
|
for (uint8_t i = 0; i < mNumPlanes; i++) {
|
|
gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
|
|
gl->fBindTexture(LOCAL_GL_TEXTURE_EXTERNAL, mTempTexs[i]);
|
|
const EGLAttrib* postAttribs = nullptr;
|
|
if (postAttribsList) {
|
|
postAttribs = postAttribsList[i];
|
|
}
|
|
mStreams[i] = StreamFromD3DTexture(egl.get(), texD3DList[i], postAttribs);
|
|
mSuccess &= bool(mStreams[i]);
|
|
}
|
|
|
|
if (mSuccess) {
|
|
for (uint8_t i = 0; i < mNumPlanes; i++) {
|
|
NOTE_IF_FALSE(egl->fStreamConsumerAcquireKHR(mStreams[i]));
|
|
|
|
auto& mutex = mMutexList[i];
|
|
texD3DList[i]->QueryInterface(IID_IDXGIKeyedMutex,
|
|
(void**)getter_AddRefs(mutex));
|
|
if (mutex) {
|
|
const auto hr = mutex->AcquireSync(0, 100);
|
|
if (!gfx::D3D11Checks::DidAcquireSyncSucceed(__func__, hr)) {
|
|
NS_WARNING("BindAnglePlanes failed to acquire KeyedMutex.");
|
|
NOTE_IF_FALSE(egl->fStreamConsumerReleaseKHR(mStreams[i]));
|
|
while (i > 0) {
|
|
--i;
|
|
NOTE_IF_FALSE(egl->fStreamConsumerReleaseKHR(mStreams[i]));
|
|
if (mMutexList[i]) {
|
|
mMutexList[i]->ReleaseSync(0);
|
|
}
|
|
}
|
|
mSuccess = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
~BindAnglePlanes() {
|
|
const auto& gl = mParent.mGL;
|
|
const auto& gle = GLContextEGL::Cast(gl);
|
|
const auto& egl = gle->mEgl;
|
|
|
|
if (mSuccess) {
|
|
for (uint8_t i = 0; i < mNumPlanes; i++) {
|
|
NOTE_IF_FALSE(egl->fStreamConsumerReleaseKHR(mStreams[i]));
|
|
if (mMutexList[i]) {
|
|
mMutexList[i]->ReleaseSync(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (uint8_t i = 0; i < mNumPlanes; i++) {
|
|
(void)egl->fDestroyStreamKHR(mStreams[i]);
|
|
}
|
|
|
|
gl->fDeleteTextures(mNumPlanes, mTempTexs);
|
|
}
|
|
|
|
const bool& Success() const { return mSuccess; }
|
|
};
|
|
|
|
// -------------------------------------
|
|
|
|
ID3D11Device* GLBlitHelper::GetD3D11() const {
|
|
if (mD3D11) return mD3D11;
|
|
|
|
if (!mGL->IsANGLE()) return nullptr;
|
|
|
|
const auto& gle = GLContextEGL::Cast(mGL);
|
|
const auto& egl = gle->mEgl;
|
|
EGLDeviceEXT deviceEGL = 0;
|
|
NOTE_IF_FALSE(egl->fQueryDisplayAttribEXT(LOCAL_EGL_DEVICE_EXT,
|
|
(EGLAttrib*)&deviceEGL));
|
|
ID3D11Device* device = nullptr;
|
|
// ANGLE does not `AddRef` its returned pointer for `QueryDeviceAttrib`, so no
|
|
// `getter_AddRefs`.
|
|
if (!egl->mLib->fQueryDeviceAttribEXT(deviceEGL, LOCAL_EGL_D3D11_DEVICE_ANGLE,
|
|
(EGLAttrib*)&device)) {
|
|
MOZ_ASSERT(false, "d3d9?");
|
|
return nullptr;
|
|
}
|
|
mD3D11 = device;
|
|
return mD3D11;
|
|
}
|
|
|
|
// -------------------------------------
|
|
|
|
bool GLBlitHelper::BlitImage(layers::D3D11ShareHandleImage* const srcImage,
|
|
const gfx::IntSize& destSize,
|
|
const OriginPos destOrigin) const {
|
|
const auto& data = srcImage->GetData();
|
|
if (!data) return false;
|
|
|
|
layers::SurfaceDescriptorD3D10 desc;
|
|
if (!data->SerializeSpecific(&desc)) return false;
|
|
|
|
return BlitDescriptor(desc, destSize, destOrigin);
|
|
}
|
|
|
|
// -------------------------------------
|
|
|
|
bool GLBlitHelper::BlitImage(layers::D3D11TextureIMFSampleImage* const srcImage,
|
|
const gfx::IntSize& destSize,
|
|
const OriginPos destOrigin) const {
|
|
const auto& data = srcImage->GetData();
|
|
if (!data) return false;
|
|
|
|
layers::SurfaceDescriptorD3D10 desc;
|
|
if (!data->SerializeSpecific(&desc)) return false;
|
|
|
|
return BlitDescriptor(desc, destSize, destOrigin);
|
|
}
|
|
|
|
// -------------------------------------
|
|
|
|
bool GLBlitHelper::BlitImage(layers::D3D11YCbCrImage* const srcImage,
|
|
const gfx::IntSize& destSize,
|
|
const OriginPos destOrigin) const {
|
|
const auto& data = srcImage->GetData();
|
|
if (!data) return false;
|
|
|
|
const WindowsHandle handles[3] = {
|
|
(WindowsHandle)(data->mHandles[0] ? data->mHandles[0]->GetHandle()
|
|
: nullptr),
|
|
(WindowsHandle)(data->mHandles[1] ? data->mHandles[1]->GetHandle()
|
|
: nullptr),
|
|
(WindowsHandle)(data->mHandles[2] ? data->mHandles[2]->GetHandle()
|
|
: nullptr)};
|
|
return BlitAngleYCbCr(handles, srcImage->mPictureRect, srcImage->GetYSize(),
|
|
srcImage->GetCbCrSize(), srcImage->mColorSpace,
|
|
destSize, destOrigin);
|
|
}
|
|
|
|
// -------------------------------------
|
|
|
|
bool GLBlitHelper::BlitDescriptor(const layers::SurfaceDescriptorD3D10& desc,
|
|
const gfx::IntSize& destSize,
|
|
const OriginPos destOrigin) const {
|
|
const auto& d3d = GetD3D11();
|
|
if (!d3d) return false;
|
|
|
|
const auto& gpuProcessTextureId = desc.gpuProcessTextureId();
|
|
const auto& arrayIndex = desc.arrayIndex();
|
|
const auto& format = desc.format();
|
|
const auto& clipSize = desc.size();
|
|
|
|
const auto srcOrigin = OriginPos::BottomLeft;
|
|
const gfx::IntRect clipRect(0, 0, clipSize.width, clipSize.height);
|
|
const auto colorSpace = desc.colorSpace();
|
|
|
|
if (format != gfx::SurfaceFormat::NV12 &&
|
|
format != gfx::SurfaceFormat::P010 &&
|
|
format != gfx::SurfaceFormat::P016) {
|
|
gfxCriticalError() << "Non-NV12 format for SurfaceDescriptorD3D10: "
|
|
<< uint32_t(format);
|
|
return false;
|
|
}
|
|
|
|
RefPtr<ID3D11Texture2D> tex;
|
|
if (gpuProcessTextureId.isSome()) {
|
|
auto* textureMap = layers::GpuProcessD3D11TextureMap::Get();
|
|
if (textureMap) {
|
|
Maybe<HANDLE> handle =
|
|
textureMap->GetSharedHandleOfCopiedTexture(gpuProcessTextureId.ref());
|
|
if (handle.isSome()) {
|
|
tex = OpenSharedTexture(d3d, (WindowsHandle)handle.ref());
|
|
}
|
|
}
|
|
} else if (desc.handle()) {
|
|
tex = OpenSharedTexture(d3d, (WindowsHandle)desc.handle()->GetHandle());
|
|
}
|
|
if (!tex) {
|
|
MOZ_GL_ASSERT(mGL, false); // Get a nullptr from OpenSharedResource1.
|
|
return false;
|
|
}
|
|
const RefPtr<ID3D11Texture2D> texList[2] = {tex, tex};
|
|
const EGLAttrib postAttribs0[] = {LOCAL_EGL_NATIVE_BUFFER_PLANE_OFFSET_IMG, 0,
|
|
LOCAL_EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE,
|
|
static_cast<EGLAttrib>(arrayIndex),
|
|
LOCAL_EGL_NONE};
|
|
const EGLAttrib postAttribs1[] = {LOCAL_EGL_NATIVE_BUFFER_PLANE_OFFSET_IMG, 1,
|
|
LOCAL_EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE,
|
|
static_cast<EGLAttrib>(arrayIndex),
|
|
LOCAL_EGL_NONE};
|
|
const EGLAttrib* const postAttribsList[2] = {postAttribs0, postAttribs1};
|
|
// /layers/d3d11/CompositorD3D11.cpp uses bt601 for EffectTypes::NV12.
|
|
// return BlitAngleNv12(tex, YUVColorSpace::BT601, destSize, destOrigin);
|
|
|
|
const BindAnglePlanes bindPlanes(this, 2, texList, postAttribsList);
|
|
if (!bindPlanes.Success()) {
|
|
MOZ_GL_ASSERT(mGL, false); // BindAnglePlanes failed.
|
|
return false;
|
|
}
|
|
|
|
D3D11_TEXTURE2D_DESC texDesc = {0};
|
|
tex->GetDesc(&texDesc);
|
|
|
|
const gfx::IntSize ySize(texDesc.Width, texDesc.Height);
|
|
const gfx::IntSize divisors(2, 2);
|
|
MOZ_ASSERT(ySize.width % divisors.width == 0);
|
|
MOZ_ASSERT(ySize.height % divisors.height == 0);
|
|
const gfx::IntSize uvSize(ySize.width / divisors.width,
|
|
ySize.height / divisors.height);
|
|
|
|
const auto yuvColorSpace = [&]() {
|
|
switch (colorSpace) {
|
|
case gfx::ColorSpace2::UNKNOWN:
|
|
case gfx::ColorSpace2::SRGB:
|
|
case gfx::ColorSpace2::DISPLAY_P3:
|
|
MOZ_CRASH("Expected BT* colorspace");
|
|
case gfx::ColorSpace2::BT601_525:
|
|
return gfx::YUVColorSpace::BT601;
|
|
case gfx::ColorSpace2::BT709:
|
|
return gfx::YUVColorSpace::BT709;
|
|
case gfx::ColorSpace2::BT2020:
|
|
return gfx::YUVColorSpace::BT2020;
|
|
}
|
|
MOZ_ASSERT_UNREACHABLE();
|
|
}();
|
|
|
|
const bool yFlip = destOrigin != srcOrigin;
|
|
const DrawBlitProg::BaseArgs baseArgs = {SubRectMat3(clipRect, ySize), yFlip,
|
|
destSize, Nothing()};
|
|
const DrawBlitProg::YUVArgs yuvArgs = {
|
|
SubRectMat3(clipRect, uvSize, divisors), Some(yuvColorSpace)};
|
|
|
|
const auto& prog = GetDrawBlitProg(
|
|
{kFragHeader_TexExt, {kFragSample_TwoPlane, kFragConvert_ColorMatrix}});
|
|
prog->Draw(baseArgs, &yuvArgs);
|
|
return true;
|
|
}
|
|
|
|
bool GLBlitHelper::BlitDescriptor(
|
|
const layers::SurfaceDescriptorDXGIYCbCr& desc,
|
|
const gfx::IntSize& destSize, const OriginPos destOrigin) const {
|
|
const auto& clipSize = desc.size();
|
|
const auto& ySize = desc.sizeY();
|
|
const auto& uvSize = desc.sizeCbCr();
|
|
const auto& colorSpace = desc.yUVColorSpace();
|
|
|
|
const gfx::IntRect clipRect(0, 0, clipSize.width, clipSize.height);
|
|
|
|
auto handleY = desc.handleY() ? desc.handleY()->GetHandle() : nullptr;
|
|
auto handleCb = desc.handleCb() ? desc.handleCb()->GetHandle() : nullptr;
|
|
auto handleCr = desc.handleCr() ? desc.handleCr()->GetHandle() : nullptr;
|
|
|
|
const WindowsHandle handles[3] = {
|
|
(WindowsHandle)handleY, (WindowsHandle)handleCb, (WindowsHandle)handleCr};
|
|
return BlitAngleYCbCr(handles, clipRect, ySize, uvSize, colorSpace, destSize,
|
|
destOrigin);
|
|
}
|
|
|
|
// --
|
|
|
|
bool GLBlitHelper::BlitAngleYCbCr(const WindowsHandle (&handleList)[3],
|
|
const gfx::IntRect& clipRect,
|
|
const gfx::IntSize& ySize,
|
|
const gfx::IntSize& uvSize,
|
|
const gfx::YUVColorSpace colorSpace,
|
|
const gfx::IntSize& destSize,
|
|
const OriginPos destOrigin) const {
|
|
const auto& d3d = GetD3D11();
|
|
if (!d3d) return false;
|
|
|
|
const auto srcOrigin = OriginPos::BottomLeft;
|
|
|
|
gfx::IntSize divisors;
|
|
if (!GuessDivisors(ySize, uvSize, &divisors)) return false;
|
|
|
|
const RefPtr<ID3D11Texture2D> texList[3] = {
|
|
OpenSharedTexture(d3d, handleList[0]),
|
|
OpenSharedTexture(d3d, handleList[1]),
|
|
OpenSharedTexture(d3d, handleList[2])};
|
|
const BindAnglePlanes bindPlanes(this, 3, texList);
|
|
|
|
const bool yFlip = destOrigin != srcOrigin;
|
|
const DrawBlitProg::BaseArgs baseArgs = {SubRectMat3(clipRect, ySize), yFlip,
|
|
destSize, Nothing()};
|
|
const DrawBlitProg::YUVArgs yuvArgs = {
|
|
SubRectMat3(clipRect, uvSize, divisors), Some(colorSpace)};
|
|
|
|
const auto& prog = GetDrawBlitProg(
|
|
{kFragHeader_TexExt, {kFragSample_ThreePlane, kFragConvert_ColorMatrix}});
|
|
prog->Draw(baseArgs, &yuvArgs);
|
|
return true;
|
|
}
|
|
|
|
} // namespace gl
|
|
} // namespace mozilla
|