tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

GLBlitHelperD3D.cpp (15304B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "GLBlitHelper.h"
      8 
      9 #include <d3d11.h>
     10 #include <d3d11_1.h>
     11 
     12 #include "GLContextEGL.h"
     13 #include "GLLibraryEGL.h"
     14 #include "GPUVideoImage.h"
     15 #include "ScopedGLHelpers.h"
     16 
     17 #include "mozilla/layers/CompositeProcessD3D11FencesHolderMap.h"
     18 #include "mozilla/layers/D3D11ShareHandleImage.h"
     19 #include "mozilla/layers/D3D11ZeroCopyTextureImage.h"
     20 #include "mozilla/layers/D3D11YCbCrImage.h"
     21 #include "mozilla/layers/GpuProcessD3D11TextureMap.h"
     22 #include "mozilla/layers/TextureD3D11.h"
     23 #include "mozilla/StaticPrefs_gl.h"
     24 
     25 namespace mozilla {
     26 namespace gl {
     27 
     28 #define NOTE_IF_FALSE(expr)                          \
     29  do {                                               \
     30    if (!(expr)) {                                   \
     31      gfxCriticalNote << "NOTE_IF_FALSE: " << #expr; \
     32    }                                                \
     33  } while (false)
     34 
     35 static EGLStreamKHR StreamFromD3DTexture(EglDisplay* const egl,
     36                                         ID3D11Texture2D* const texD3D,
     37                                         const EGLAttrib* const postAttribs) {
     38  if (!egl->IsExtensionSupported(
     39          EGLExtension::NV_stream_consumer_gltexture_yuv) ||
     40      !egl->IsExtensionSupported(
     41          EGLExtension::ANGLE_stream_producer_d3d_texture)) {
     42    return 0;
     43  }
     44 
     45  const auto stream = egl->fCreateStreamKHR(nullptr);
     46  MOZ_ASSERT(stream);
     47  if (!stream) return 0;
     48  bool ok = true;
     49  NOTE_IF_FALSE(ok &= bool(egl->fStreamConsumerGLTextureExternalAttribsNV(
     50                    stream, nullptr)));
     51  NOTE_IF_FALSE(
     52      ok &= bool(egl->fCreateStreamProducerD3DTextureANGLE(stream, nullptr)));
     53  NOTE_IF_FALSE(
     54      ok &= bool(egl->fStreamPostD3DTextureANGLE(stream, texD3D, postAttribs)));
     55  if (ok) return stream;
     56 
     57  (void)egl->fDestroyStreamKHR(stream);
     58  return 0;
     59 }
     60 
     61 static RefPtr<ID3D11Texture2D> OpenSharedTexture(ID3D11Device* const d3d,
     62                                                 const WindowsHandle handle) {
     63  RefPtr<ID3D11Device1> device1;
     64  d3d->QueryInterface((ID3D11Device1**)getter_AddRefs(device1));
     65  if (!device1) {
     66    gfxCriticalNoteOnce << "Failed to get ID3D11Device1";
     67    return nullptr;
     68  }
     69 
     70  RefPtr<ID3D11Texture2D> tex;
     71  auto hr = device1->OpenSharedResource1(
     72      (HANDLE)handle, __uuidof(ID3D11Texture2D),
     73      (void**)(ID3D11Texture2D**)getter_AddRefs(tex));
     74  if (FAILED(hr)) {
     75    gfxCriticalError() << "Error code from OpenSharedResource1: "
     76                       << gfx::hexa(hr);
     77    return nullptr;
     78  }
     79  return tex;
     80 }
     81 
     82 // -------------------------------------
     83 
     84 class BindAnglePlanes final {
     85  const GLBlitHelper& mParent;
     86  const uint8_t mNumPlanes;
     87  const ScopedSaveMultiTex mMultiTex;
     88  GLuint mTempTexs[3];
     89  EGLStreamKHR mStreams[3];
     90  RefPtr<IDXGIKeyedMutex> mMutexList[3];
     91  bool mSuccess;
     92 
     93 public:
     94  BindAnglePlanes(const GLBlitHelper* const parent, const uint8_t numPlanes,
     95                  const RefPtr<ID3D11Texture2D>* const texD3DList,
     96                  const EGLAttrib* const* postAttribsList = nullptr)
     97      : mParent(*parent),
     98        mNumPlanes(numPlanes),
     99        mMultiTex(mParent.mGL, mNumPlanes, LOCAL_GL_TEXTURE_EXTERNAL),
    100        mTempTexs{0},
    101        mStreams{0},
    102        mSuccess(true) {
    103    MOZ_RELEASE_ASSERT(numPlanes >= 1 && numPlanes <= 3);
    104 
    105    const auto& gl = mParent.mGL;
    106    const auto& gle = GLContextEGL::Cast(gl);
    107    const auto& egl = gle->mEgl;
    108 
    109    gl->fGenTextures(numPlanes, mTempTexs);
    110 
    111    for (uint8_t i = 0; i < mNumPlanes; i++) {
    112      gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
    113      gl->fBindTexture(LOCAL_GL_TEXTURE_EXTERNAL, mTempTexs[i]);
    114      const EGLAttrib* postAttribs = nullptr;
    115      if (postAttribsList) {
    116        postAttribs = postAttribsList[i];
    117      }
    118      mStreams[i] = StreamFromD3DTexture(egl.get(), texD3DList[i], postAttribs);
    119      mSuccess &= bool(mStreams[i]);
    120    }
    121 
    122    if (mSuccess) {
    123      for (uint8_t i = 0; i < mNumPlanes; i++) {
    124        NOTE_IF_FALSE(egl->fStreamConsumerAcquireKHR(mStreams[i]));
    125 
    126        auto& mutex = mMutexList[i];
    127        texD3DList[i]->QueryInterface(IID_IDXGIKeyedMutex,
    128                                      (void**)getter_AddRefs(mutex));
    129        if (mutex) {
    130          const auto hr = mutex->AcquireSync(0, 100);
    131          if (FAILED(hr)) {
    132            NS_WARNING("BindAnglePlanes failed to acquire KeyedMutex.");
    133            mSuccess = false;
    134          }
    135        }
    136      }
    137    }
    138  }
    139 
    140  ~BindAnglePlanes() {
    141    const auto& gl = mParent.mGL;
    142    const auto& gle = GLContextEGL::Cast(gl);
    143    const auto& egl = gle->mEgl;
    144 
    145    if (mSuccess) {
    146      for (uint8_t i = 0; i < mNumPlanes; i++) {
    147        NOTE_IF_FALSE(egl->fStreamConsumerReleaseKHR(mStreams[i]));
    148        if (mMutexList[i]) {
    149          mMutexList[i]->ReleaseSync(0);
    150        }
    151      }
    152    }
    153 
    154    for (uint8_t i = 0; i < mNumPlanes; i++) {
    155      (void)egl->fDestroyStreamKHR(mStreams[i]);
    156    }
    157 
    158    gl->fDeleteTextures(mNumPlanes, mTempTexs);
    159  }
    160 
    161  const bool& Success() const { return mSuccess; }
    162 };
    163 
    164 // -------------------------------------
    165 
    166 ID3D11Device* GLBlitHelper::GetD3D11() const {
    167  if (mD3D11) return mD3D11;
    168 
    169  if (!mGL->IsANGLE()) return nullptr;
    170 
    171  const auto& gle = GLContextEGL::Cast(mGL);
    172  const auto& egl = gle->mEgl;
    173  EGLDeviceEXT deviceEGL = 0;
    174  NOTE_IF_FALSE(egl->fQueryDisplayAttribEXT(LOCAL_EGL_DEVICE_EXT,
    175                                            (EGLAttrib*)&deviceEGL));
    176  ID3D11Device* device = nullptr;
    177  // ANGLE does not `AddRef` its returned pointer for `QueryDeviceAttrib`, so no
    178  // `getter_AddRefs`.
    179  if (!egl->mLib->fQueryDeviceAttribEXT(deviceEGL, LOCAL_EGL_D3D11_DEVICE_ANGLE,
    180                                        (EGLAttrib*)&device)) {
    181    MOZ_ASSERT(false, "d3d9?");
    182    return nullptr;
    183  }
    184  mD3D11 = device;
    185  return mD3D11;
    186 }
    187 
    188 // -------------------------------------
    189 
    190 bool GLBlitHelper::BlitImage(layers::D3D11ShareHandleImage* const srcImage,
    191                             const gfx::IntRect& destRect,
    192                             const OriginPos destOrigin,
    193                             const gfx::IntSize& fbSize) const {
    194  const auto& data = srcImage->GetData();
    195  if (!data) return false;
    196 
    197  layers::SurfaceDescriptorD3D10 desc;
    198  if (!data->SerializeSpecific(&desc)) return false;
    199 
    200  return BlitDescriptor(desc, destRect, destOrigin, fbSize);
    201 }
    202 
    203 // -------------------------------------
    204 
    205 bool GLBlitHelper::BlitImage(layers::D3D11ZeroCopyTextureImage* const srcImage,
    206                             const gfx::IntRect& destRect,
    207                             const OriginPos destOrigin,
    208                             const gfx::IntSize& fbSize) const {
    209  const auto& data = srcImage->GetData();
    210  if (!data) return false;
    211 
    212  layers::SurfaceDescriptorD3D10 desc;
    213  if (!data->SerializeSpecific(&desc)) return false;
    214 
    215  return BlitDescriptor(desc, destRect, destOrigin, fbSize);
    216 }
    217 
    218 // -------------------------------------
    219 
    220 bool GLBlitHelper::BlitDescriptor(const layers::SurfaceDescriptorD3D10& desc,
    221                                  const gfx::IntRect& destRect,
    222                                  const OriginPos destOrigin,
    223                                  const gfx::IntSize& fbSize,
    224                                  Maybe<gfxAlphaType> convertAlpha) const {
    225  const auto& d3d = GetD3D11();
    226  if (!d3d) return false;
    227 
    228  const auto& gpuProcessTextureId = desc.gpuProcessTextureId();
    229  auto arrayIndex = desc.arrayIndex();
    230  const auto& format = desc.format();
    231  const auto& clipSize = desc.size();
    232 
    233  const auto srcOrigin = OriginPos::BottomLeft;
    234  const bool yFlip = destOrigin != srcOrigin;
    235  const gfx::IntRect clipRect(0, 0, clipSize.width, clipSize.height);
    236  const auto colorSpace = desc.colorSpace();
    237  const auto fencesHolderId = desc.fencesHolderId();
    238 
    239  bool yuv = true;
    240  switch (format) {
    241    case gfx::SurfaceFormat::B8G8R8A8:
    242    case gfx::SurfaceFormat::B8G8R8X8:
    243    case gfx::SurfaceFormat::R8G8B8A8:
    244    case gfx::SurfaceFormat::R8G8B8X8:
    245    case gfx::SurfaceFormat::R10G10B10A2_UINT32:
    246    case gfx::SurfaceFormat::R10G10B10X2_UINT32:
    247    case gfx::SurfaceFormat::R16G16B16A16F:
    248      yuv = false;
    249      break;
    250    case gfx::SurfaceFormat::NV12:
    251    case gfx::SurfaceFormat::P010:
    252    case gfx::SurfaceFormat::P016:
    253      break;
    254    default:
    255      gfxCriticalError() << "Non-RGBA/NV12 format for SurfaceDescriptorD3D10: "
    256                         << uint32_t(format);
    257      return false;
    258  }
    259 
    260  RefPtr<ID3D11Texture2D> tex;
    261  if (gpuProcessTextureId.isSome()) {
    262    auto* textureMap = layers::GpuProcessD3D11TextureMap::Get();
    263    if (textureMap) {
    264      Maybe<HANDLE> handle =
    265          textureMap->GetSharedHandle(gpuProcessTextureId.ref());
    266      if (handle.isSome()) {
    267        tex = OpenSharedTexture(d3d, (WindowsHandle)handle.ref());
    268        arrayIndex = 0;
    269      }
    270    }
    271  } else if (desc.handle()) {
    272    tex = OpenSharedTexture(d3d, (WindowsHandle)desc.handle()->GetHandle());
    273  }
    274  if (!tex) {
    275    MOZ_GL_ASSERT(mGL, false);  // Get a nullptr from OpenSharedResource1.
    276    return false;
    277  }
    278 
    279  auto* fencesHolderMap = layers::CompositeProcessD3D11FencesHolderMap::Get();
    280  MOZ_ASSERT(fencesHolderMap);
    281  if (fencesHolderMap && fencesHolderId.isSome()) {
    282    fencesHolderMap->WaitWriteFence(fencesHolderId.ref(), d3d);
    283  }
    284 
    285  if (!yuv) {
    286    const RefPtr<ID3D11Texture2D> texList[1] = {tex};
    287    const EGLAttrib postAttribs0[] = {
    288        LOCAL_EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE,
    289        static_cast<EGLAttrib>(arrayIndex), LOCAL_EGL_NONE};
    290    const EGLAttrib* const postAttribsList[1] = {postAttribs0};
    291    const BindAnglePlanes bindPlanes(this, 1, texList,
    292                                     arrayIndex ? postAttribsList : nullptr);
    293    if (!bindPlanes.Success()) {
    294      MOZ_GL_ASSERT(mGL, false);  // BindAnglePlanes failed.
    295      return false;
    296    }
    297 
    298    D3D11_TEXTURE2D_DESC texDesc = {0};
    299    tex->GetDesc(&texDesc);
    300    gfx::IntSize texSize(texDesc.Width, texDesc.Height);
    301 
    302    const DrawBlitProg::BaseArgs baseArgs = {SubRectMat3(clipRect, texSize),
    303                                             yFlip, fbSize, destRect, clipSize};
    304    const auto& prog =
    305        GetDrawBlitProg({kFragHeader_TexExt,
    306                         {kFragSample_OnePlane, kFragConvert_None,
    307                          GetAlphaMixin(convertAlpha)}});
    308    prog.Draw(baseArgs);
    309    return true;
    310  }
    311 
    312  const RefPtr<ID3D11Texture2D> texList[2] = {tex, tex};
    313  const EGLAttrib postAttribs0[] = {LOCAL_EGL_NATIVE_BUFFER_PLANE_OFFSET_IMG, 0,
    314                                    LOCAL_EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE,
    315                                    static_cast<EGLAttrib>(arrayIndex),
    316                                    LOCAL_EGL_NONE};
    317  const EGLAttrib postAttribs1[] = {LOCAL_EGL_NATIVE_BUFFER_PLANE_OFFSET_IMG, 1,
    318                                    LOCAL_EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE,
    319                                    static_cast<EGLAttrib>(arrayIndex),
    320                                    LOCAL_EGL_NONE};
    321  const EGLAttrib* const postAttribsList[2] = {postAttribs0, postAttribs1};
    322  // /layers/d3d11/CompositorD3D11.cpp uses bt601 for EffectTypes::NV12.
    323  // return BlitAngleNv12(tex, YUVColorSpace::BT601, destSize, destOrigin);
    324 
    325  const BindAnglePlanes bindPlanes(this, 2, texList, postAttribsList);
    326  if (!bindPlanes.Success()) {
    327    MOZ_GL_ASSERT(mGL, false);  // BindAnglePlanes failed.
    328    return false;
    329  }
    330 
    331  D3D11_TEXTURE2D_DESC texDesc = {0};
    332  tex->GetDesc(&texDesc);
    333 
    334  const gfx::IntSize ySize(texDesc.Width, texDesc.Height);
    335  const gfx::IntSize divisors(2, 2);
    336  MOZ_ASSERT(ySize.width % divisors.width == 0);
    337  MOZ_ASSERT(ySize.height % divisors.height == 0);
    338  const gfx::IntSize uvSize(ySize.width / divisors.width,
    339                            ySize.height / divisors.height);
    340 
    341  const auto yuvColorSpace = [&]() {
    342    switch (colorSpace) {
    343      case gfx::ColorSpace2::UNKNOWN:
    344      case gfx::ColorSpace2::SRGB:
    345      case gfx::ColorSpace2::DISPLAY_P3:
    346        MOZ_CRASH("Expected BT* colorspace");
    347      case gfx::ColorSpace2::BT601_525:
    348        return gfx::YUVColorSpace::BT601;
    349      case gfx::ColorSpace2::BT709:
    350        return gfx::YUVColorSpace::BT709;
    351      case gfx::ColorSpace2::BT2020:
    352        return gfx::YUVColorSpace::BT2020;
    353    }
    354    MOZ_ASSERT_UNREACHABLE();
    355  }();
    356 
    357  const DrawBlitProg::BaseArgs baseArgs = {SubRectMat3(clipRect, ySize), yFlip,
    358                                           fbSize, destRect, clipSize};
    359  const DrawBlitProg::YUVArgs yuvArgs = {
    360      SubRectMat3(clipRect, uvSize, divisors), Some(yuvColorSpace)};
    361 
    362  const auto& prog =
    363      GetDrawBlitProg({kFragHeader_TexExt,
    364                       {kFragSample_TwoPlane, kFragConvert_ColorMatrix,
    365                        GetAlphaMixin(convertAlpha)}});
    366  prog.Draw(baseArgs, &yuvArgs);
    367  return true;
    368 }
    369 
    370 bool GLBlitHelper::BlitDescriptor(
    371    const layers::SurfaceDescriptorDXGIYCbCr& desc,
    372    const gfx::IntRect& destRect, const OriginPos destOrigin,
    373    const gfx::IntSize& fbSize, Maybe<gfxAlphaType> convertAlpha) const {
    374  const auto& clipSize = desc.size();
    375  const auto& ySize = desc.sizeY();
    376  const auto& uvSize = desc.sizeCbCr();
    377  const auto& colorSpace = desc.yUVColorSpace();
    378 
    379  const gfx::IntRect clipRect(0, 0, clipSize.width, clipSize.height);
    380 
    381  auto handleY = desc.handleY() ? desc.handleY()->GetHandle() : nullptr;
    382  auto handleCb = desc.handleCb() ? desc.handleCb()->GetHandle() : nullptr;
    383  auto handleCr = desc.handleCr() ? desc.handleCr()->GetHandle() : nullptr;
    384 
    385  const WindowsHandle handles[3] = {
    386      (WindowsHandle)handleY, (WindowsHandle)handleCb, (WindowsHandle)handleCr};
    387  return BlitAngleYCbCr(handles, clipRect, ySize, uvSize, colorSpace, destRect,
    388                        destOrigin, fbSize, convertAlpha);
    389 }
    390 
    391 // --
    392 
    393 bool GLBlitHelper::BlitAngleYCbCr(
    394    const WindowsHandle (&handleList)[3], const gfx::IntRect& clipRect,
    395    const gfx::IntSize& ySize, const gfx::IntSize& uvSize,
    396    const gfx::YUVColorSpace colorSpace, const gfx::IntRect& destRect,
    397    const OriginPos destOrigin, const gfx::IntSize& fbSize,
    398    Maybe<gfxAlphaType> convertAlpha) const {
    399  const auto& d3d = GetD3D11();
    400  if (!d3d) return false;
    401 
    402  const auto srcOrigin = OriginPos::BottomLeft;
    403 
    404  gfx::IntSize divisors;
    405  if (!GuessDivisors(ySize, uvSize, &divisors)) return false;
    406 
    407  const RefPtr<ID3D11Texture2D> texList[3] = {
    408      OpenSharedTexture(d3d, handleList[0]),
    409      OpenSharedTexture(d3d, handleList[1]),
    410      OpenSharedTexture(d3d, handleList[2])};
    411  const BindAnglePlanes bindPlanes(this, 3, texList);
    412 
    413  const bool yFlip = destOrigin != srcOrigin;
    414  const DrawBlitProg::BaseArgs baseArgs = {SubRectMat3(clipRect, ySize), yFlip,
    415                                           fbSize, destRect, clipRect.Size()};
    416  const DrawBlitProg::YUVArgs yuvArgs = {
    417      SubRectMat3(clipRect, uvSize, divisors), Some(colorSpace)};
    418 
    419  const auto& prog =
    420      GetDrawBlitProg({kFragHeader_TexExt,
    421                       {kFragSample_ThreePlane, kFragConvert_ColorMatrix,
    422                        GetAlphaMixin(convertAlpha)}});
    423  prog.Draw(baseArgs, &yuvArgs);
    424  return true;
    425 }
    426 
    427 }  // namespace gl
    428 }  // namespace mozilla