summaryrefslogtreecommitdiffstats
path: root/gfx/gl/GLBlitHelperD3D.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/gl/GLBlitHelperD3D.cpp')
-rw-r--r--gfx/gl/GLBlitHelperD3D.cpp383
1 files changed, 383 insertions, 0 deletions
diff --git a/gfx/gl/GLBlitHelperD3D.cpp b/gfx/gl/GLBlitHelperD3D.cpp
new file mode 100644
index 0000000000..8dda3e45fd
--- /dev/null
+++ b/gfx/gl/GLBlitHelperD3D.cpp
@@ -0,0 +1,383 @@
+/* -*- 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 "GLContextEGL.h"
+#include "GLLibraryEGL.h"
+#include "GPUVideoImage.h"
+#include "ScopedGLHelpers.h"
+
+#include "mozilla/layers/D3D11ShareHandleImage.h"
+#include "mozilla/layers/D3D11TextureIMFSampleImage.h"
+#include "mozilla/layers/D3D11YCbCrImage.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<ID3D11Texture2D> tex;
+ auto hr =
+ d3d->OpenSharedResource((HANDLE)handle, __uuidof(ID3D11Texture2D),
+ (void**)(ID3D11Texture2D**)getter_AddRefs(tex));
+ if (FAILED(hr)) {
+ gfxCriticalError() << "Error code from OpenSharedResource: "
+ << 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 (FAILED(hr)) {
+ NS_WARNING("BindAnglePlanes failed to acquire KeyedMutex.");
+ mSuccess = false;
+ }
+ }
+ }
+ }
+ }
+
+ ~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],
+ (WindowsHandle)data->mHandles[1],
+ (WindowsHandle)data->mHandles[2]};
+ 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& handle = desc.handle();
+ 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 {
+ tex = OpenSharedTexture(d3d, handle);
+ }
+ if (!tex) {
+ MOZ_GL_ASSERT(mGL, false); // Get a nullptr from OpenSharedResource.
+ 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);
+
+ const WindowsHandle handles[3] = {desc.handleY(), desc.handleCb(),
+ desc.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