tor-browser

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

SharedSurfaceD3D11Interop.cpp (16402B)


      1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 4; -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "SharedSurfaceD3D11Interop.h"
      7 
      8 #include <d3d11.h>
      9 #include <d3d11_1.h>
     10 #include "GLContext.h"
     11 #include "GLBlitHelper.h"
     12 #include "MozFramebuffer.h"
     13 #include "ScopedGLHelpers.h"
     14 #include "WGLLibrary.h"
     15 #include "nsPrintfCString.h"
     16 #include "mozilla/gfx/DeviceManagerDx.h"
     17 #include "mozilla/gfx/FileHandleWrapper.h"
     18 #include "mozilla/gfx/Logging.h"
     19 #include "mozilla/layers/LayersSurfaces.h"
     20 #include "mozilla/StaticPrefs_webgl.h"
     21 
     22 namespace mozilla {
     23 namespace gl {
     24 
     25 /*
     26 Sample Code for WGL_NV_DX_interop2:
     27 Example: Render to Direct3D 11 backbuffer with openGL:
     28 
     29 // create D3D11 device, context and swap chain.
     30 ID3D11Device *device;
     31 ID3D11DeviceContext *devCtx;
     32 IDXGISwapChain *swapChain;
     33 
     34 DXGI_SWAP_CHAIN_DESC scd;
     35 
     36 <set appropriate swap chain parameters in scd>
     37 
     38 hr = D3D11CreateDeviceAndSwapChain(
     39         NULL,                        // pAdapter
     40         D3D_DRIVER_TYPE_HARDWARE,    // DriverType
     41         NULL,                        // Software
     42         0,                           // Flags (Do not set
     43                                      // D3D11_CREATE_DEVICE_SINGLETHREADED)
     44         NULL,                        // pFeatureLevels
     45         0,                           // FeatureLevels
     46         D3D11_SDK_VERSION,           // SDKVersion
     47         &scd,                        // pSwapChainDesc
     48         &swapChain,                  // ppSwapChain
     49         &device,                     // ppDevice
     50         NULL,                        // pFeatureLevel
     51         &devCtx);                    // ppImmediateContext
     52 
     53 // Fetch the swapchain backbuffer
     54 ID3D11Texture2D *dxColorbuffer;
     55 swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID *)&dxColorbuffer);
     56 
     57 // Create depth stencil texture
     58 ID3D11Texture2D *dxDepthBuffer;
     59 D3D11_TEXTURE2D_DESC depthDesc;
     60 depthDesc.Usage = D3D11_USAGE_DEFAULT;
     61 <set other depthDesc parameters appropriately>
     62 
     63 // Create Views
     64 ID3D11RenderTargetView *colorBufferView;
     65 D3D11_RENDER_TARGET_VIEW_DESC rtd;
     66 <set rtd parameters appropriately>
     67 device->CreateRenderTargetView(dxColorbuffer, &rtd, &colorBufferView);
     68 
     69 ID3D11DepthStencilView *depthBufferView;
     70 D3D11_DEPTH_STENCIL_VIEW_DESC dsd;
     71 <set dsd parameters appropriately>
     72 device->CreateDepthStencilView(dxDepthBuffer, &dsd, &depthBufferView);
     73 
     74 // Attach back buffer and depth texture to redertarget for the device.
     75 devCtx->OMSetRenderTargets(1, &colorBufferView, depthBufferView);
     76 
     77 // Register D3D11 device with GL
     78 HANDLE gl_handleD3D;
     79 gl_handleD3D = wglDXOpenDeviceNV(device);
     80 
     81 // register the Direct3D color and depth/stencil buffers as
     82 // renderbuffers in opengl
     83 GLuint gl_names[2];
     84 HANDLE gl_handles[2];
     85 
     86 glGenRenderbuffers(2, gl_names);
     87 
     88 gl_handles[0] = wglDXRegisterObjectNV(gl_handleD3D, dxColorBuffer,
     89                                      gl_names[0],
     90                                      GL_RENDERBUFFER,
     91                                      WGL_ACCESS_READ_WRITE_NV);
     92 
     93 gl_handles[1] = wglDXRegisterObjectNV(gl_handleD3D, dxDepthBuffer,
     94                                      gl_names[1],
     95                                      GL_RENDERBUFFER,
     96                                      WGL_ACCESS_READ_WRITE_NV);
     97 
     98 // attach the Direct3D buffers to an FBO
     99 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    100 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
    101                          GL_RENDERBUFFER, gl_names[0]);
    102 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
    103                          GL_RENDERBUFFER, gl_names[1]);
    104 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
    105                          GL_RENDERBUFFER, gl_names[1]);
    106 
    107 while (!done) {
    108      <direct3d renders to the render targets>
    109 
    110      // lock the render targets for GL access
    111      wglDXLockObjectsNVX(gl_handleD3D, 2, gl_handles);
    112 
    113      <opengl renders to the render targets>
    114 
    115      // unlock the render targets
    116      wglDXUnlockObjectsNVX(gl_handleD3D, 2, gl_handles);
    117 
    118      <direct3d renders to the render targets and presents
    119       the results on the screen>
    120 }
    121 */
    122 
    123 ////////////////////////////////////////////////////////////////////////////////
    124 // DXInterop2Device
    125 
    126 class ScopedContextState final {
    127  ID3D11DeviceContext1* const mD3DContext;
    128  RefPtr<ID3DDeviceContextState> mOldContextState;
    129 
    130 public:
    131  ScopedContextState(ID3D11DeviceContext1* d3dContext,
    132                     ID3DDeviceContextState* newContextState)
    133      : mD3DContext(d3dContext), mOldContextState(nullptr) {
    134    if (!mD3DContext) return;
    135 
    136    mD3DContext->SwapDeviceContextState(newContextState,
    137                                        getter_AddRefs(mOldContextState));
    138  }
    139 
    140  ~ScopedContextState() {
    141    if (!mD3DContext) return;
    142 
    143    mD3DContext->SwapDeviceContextState(mOldContextState, nullptr);
    144  }
    145 };
    146 
    147 class DXInterop2Device : public RefCounted<DXInterop2Device> {
    148 public:
    149  MOZ_DECLARE_REFCOUNTED_TYPENAME(DXInterop2Device)
    150 
    151  WGLLibrary* const mWGL;
    152  const RefPtr<ID3D11Device> mD3D;  // Only needed for lifetime guarantee.
    153  const HANDLE mInteropDevice;
    154  GLContext* const mGL;
    155 
    156  // AMD workaround.
    157  const RefPtr<ID3D11DeviceContext1> mD3DContext;
    158  const RefPtr<ID3DDeviceContextState> mContextState;
    159 
    160  static already_AddRefed<DXInterop2Device> Open(WGLLibrary* wgl,
    161                                                 GLContext* gl) {
    162    MOZ_ASSERT(wgl->HasDXInterop2());
    163 
    164    const RefPtr<ID3D11Device> d3d =
    165        gfx::DeviceManagerDx::Get()->GetContentDevice();
    166    if (!d3d) {
    167      gfxCriticalNote
    168          << "DXInterop2Device::Open: Failed to create D3D11 device.";
    169      return nullptr;
    170    }
    171 
    172    if (!gl->MakeCurrent()) return nullptr;
    173 
    174    RefPtr<ID3D11DeviceContext1> d3dContext;
    175    RefPtr<ID3DDeviceContextState> contextState;
    176    if (gl->WorkAroundDriverBugs() && gl->Vendor() == GLVendor::ATI) {
    177      // AMD calls ID3D10Device::Flush, so we need to be in ID3D10Device mode.
    178      RefPtr<ID3D11Device1> d3d11_1;
    179      auto hr =
    180          d3d->QueryInterface(__uuidof(ID3D11Device1), getter_AddRefs(d3d11_1));
    181      if (!SUCCEEDED(hr)) return nullptr;
    182 
    183      d3d11_1->GetImmediateContext1(getter_AddRefs(d3dContext));
    184      MOZ_ASSERT(d3dContext);
    185 
    186      const D3D_FEATURE_LEVEL featureLevel10_0 = D3D_FEATURE_LEVEL_10_0;
    187      hr = d3d11_1->CreateDeviceContextState(
    188          0, &featureLevel10_0, 1, D3D11_SDK_VERSION, __uuidof(ID3D10Device),
    189          nullptr, getter_AddRefs(contextState));
    190      if (!SUCCEEDED(hr)) return nullptr;
    191    }
    192 
    193    const auto interopDevice = wgl->mSymbols.fDXOpenDeviceNV(d3d);
    194    if (!interopDevice) {
    195      gfxCriticalNote << "DXInterop2Device::Open: DXOpenDevice failed.";
    196      return nullptr;
    197    }
    198 
    199    return MakeAndAddRef<DXInterop2Device>(wgl, d3d, interopDevice, gl,
    200                                           d3dContext, contextState);
    201  }
    202 
    203  DXInterop2Device(WGLLibrary* wgl, ID3D11Device* d3d, HANDLE interopDevice,
    204                   GLContext* gl, ID3D11DeviceContext1* d3dContext,
    205                   ID3DDeviceContextState* contextState)
    206      : mWGL(wgl),
    207        mD3D(d3d),
    208        mInteropDevice(interopDevice),
    209        mGL(gl),
    210        mD3DContext(d3dContext),
    211        mContextState(contextState) {}
    212 
    213  ~DXInterop2Device() {
    214    const auto isCurrent = mGL->MakeCurrent();
    215 
    216    if (mWGL->mSymbols.fDXCloseDeviceNV(mInteropDevice)) return;
    217 
    218    if (isCurrent) {
    219      // That shouldn't have failed.
    220      const uint32_t error = GetLastError();
    221      const nsPrintfCString errorMessage(
    222          "wglDXCloseDevice(0x%p) failed:"
    223          " GetLastError(): %u\n",
    224          mInteropDevice, error);
    225      gfxCriticalError() << errorMessage.BeginReading();
    226    }
    227  }
    228 
    229  HANDLE RegisterObject(void* d3dObject, GLuint name, GLenum type,
    230                        GLenum access) const {
    231    if (!mGL->MakeCurrent()) return nullptr;
    232 
    233    const ScopedContextState autoCS(mD3DContext, mContextState);
    234    const auto ret = mWGL->mSymbols.fDXRegisterObjectNV(
    235        mInteropDevice, d3dObject, name, type, access);
    236    if (ret) return ret;
    237 
    238    const uint32_t error = GetLastError();
    239    const nsPrintfCString errorMessage(
    240        "wglDXRegisterObject(0x%p, 0x%p, %u, 0x%04x,"
    241        " 0x%04x) failed: GetLastError(): %u\n",
    242        mInteropDevice, d3dObject, name, type, access, error);
    243    gfxCriticalNote << errorMessage.BeginReading();
    244    return nullptr;
    245  }
    246 
    247  bool UnregisterObject(HANDLE lockHandle) const {
    248    const auto isCurrent = mGL->MakeCurrent();
    249 
    250    const ScopedContextState autoCS(mD3DContext, mContextState);
    251    if (mWGL->mSymbols.fDXUnregisterObjectNV(mInteropDevice, lockHandle))
    252      return true;
    253 
    254    if (!isCurrent) {
    255      // That shouldn't have failed.
    256      const uint32_t error = GetLastError();
    257      const nsPrintfCString errorMessage(
    258          "wglDXUnregisterObject(0x%p, 0x%p) failed:"
    259          " GetLastError(): %u\n",
    260          mInteropDevice, lockHandle, error);
    261      gfxCriticalError() << errorMessage.BeginReading();
    262    }
    263    return false;
    264  }
    265 
    266  bool LockObject(HANDLE lockHandle) const {
    267    MOZ_ASSERT(mGL->IsCurrent());
    268 
    269    if (mWGL->mSymbols.fDXLockObjectsNV(mInteropDevice, 1, &lockHandle))
    270      return true;
    271 
    272    if (!mGL->MakeCurrent()) return false;
    273 
    274    gfxCriticalNote << "wglDXLockObjects called without mGL being current."
    275                    << " Retrying after MakeCurrent.";
    276 
    277    if (mWGL->mSymbols.fDXLockObjectsNV(mInteropDevice, 1, &lockHandle))
    278      return true;
    279 
    280    const uint32_t error = GetLastError();
    281    const nsPrintfCString errorMessage(
    282        "wglDXLockObjects(0x%p, 1, {0x%p}) failed:"
    283        " GetLastError(): %u\n",
    284        mInteropDevice, lockHandle, error);
    285    gfxCriticalError() << errorMessage.BeginReading();
    286    return false;
    287  }
    288 
    289  bool UnlockObject(HANDLE lockHandle) const {
    290    MOZ_ASSERT(mGL->IsCurrent());
    291 
    292    if (mWGL->mSymbols.fDXUnlockObjectsNV(mInteropDevice, 1, &lockHandle))
    293      return true;
    294 
    295    if (!mGL->MakeCurrent()) return false;
    296 
    297    gfxCriticalNote << "wglDXUnlockObjects called without mGL being current."
    298                    << " Retrying after MakeCurrent.";
    299 
    300    if (mWGL->mSymbols.fDXUnlockObjectsNV(mInteropDevice, 1, &lockHandle))
    301      return true;
    302 
    303    const uint32_t error = GetLastError();
    304    const nsPrintfCString errorMessage(
    305        "wglDXUnlockObjects(0x%p, 1, {0x%p}) failed:"
    306        " GetLastError(): %u\n",
    307        mInteropDevice, lockHandle, error);
    308    gfxCriticalError() << errorMessage.BeginReading();
    309    return false;
    310  }
    311 };
    312 
    313 ////////////////////////////////////////////////////////////////////////////////
    314 // Shared Surface
    315 
    316 /*static*/
    317 UniquePtr<SharedSurface_D3D11Interop> SharedSurface_D3D11Interop::Create(
    318    const SharedSurfaceDesc& desc, DXInterop2Device* interop) {
    319  const auto& gl = desc.gl;
    320  const auto& size = desc.size;
    321  const auto& d3d = interop->mD3D;
    322 
    323  auto data = Data{interop};
    324 
    325  // Create a texture in case we need to readback.
    326  const DXGI_FORMAT format = DXGI_FORMAT_B8G8R8A8_UNORM;
    327  CD3D11_TEXTURE2D_DESC texDesc(format, size.width, size.height, 1, 1);
    328  texDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE |
    329                      D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
    330 
    331  auto hr =
    332      d3d->CreateTexture2D(&texDesc, nullptr, getter_AddRefs(data.texD3D));
    333  if (FAILED(hr)) {
    334    NS_WARNING("Failed to create texture for CanvasLayer!");
    335    return nullptr;
    336  }
    337 
    338  RefPtr<IDXGIResource1> texDXGI;
    339  hr = data.texD3D->QueryInterface(__uuidof(IDXGIResource1),
    340                                   getter_AddRefs(texDXGI));
    341  if (FAILED(hr)) {
    342    NS_WARNING("Failed to open texture for sharing!");
    343    return nullptr;
    344  }
    345 
    346  HANDLE sharedHandle = nullptr;
    347  texDXGI->CreateSharedHandle(
    348      nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr,
    349      &sharedHandle);
    350 
    351  data.dxgiHandle = new gfx::FileHandleWrapper(UniqueFileHandle(sharedHandle));
    352 
    353  ////
    354 
    355  if (!gl->MakeCurrent()) {
    356    NS_WARNING("MakeCurrent failed.");
    357    return nullptr;
    358  }
    359 
    360  data.interopRb = MakeUnique<Renderbuffer>(*gl);
    361  data.lockHandle = interop->RegisterObject(data.texD3D, data.interopRb->name,
    362                                            LOCAL_GL_RENDERBUFFER,
    363                                            LOCAL_WGL_ACCESS_WRITE_DISCARD_NV);
    364  if (!data.lockHandle) {
    365    NS_WARNING("Failed to register D3D object with WGL.");
    366    return nullptr;
    367  }
    368 
    369  auto fbForDrawing = MozFramebuffer::CreateForBacking(
    370      gl, size, 0, false, LOCAL_GL_RENDERBUFFER, data.interopRb->name);
    371  if (!fbForDrawing) return nullptr;
    372 
    373  // -
    374 
    375  {
    376    GLint samples = 0;
    377    {
    378      const ScopedBindRenderbuffer bindRB(gl, data.interopRb->name);
    379      gl->fGetRenderbufferParameteriv(LOCAL_GL_RENDERBUFFER,
    380                                      LOCAL_GL_RENDERBUFFER_SAMPLES, &samples);
    381    }
    382    if (samples > 0) {  // Intel
    383      // Intel's dx_interop GL-side textures have SAMPLES=1, likely because
    384      // that's what the D3DTextures technically have. However, SAMPLES=1 is
    385      // very different from SAMPLES=0 in GL. For more, see
    386      // https://bugzilla.mozilla.org/show_bug.cgi?id=1325835
    387 
    388      // Our ShSurf tex or rb must be single-sampled.
    389      data.interopFbIfNeedsIndirect = std::move(fbForDrawing);
    390      fbForDrawing = MozFramebuffer::Create(gl, size, 0, false);
    391    }
    392  }
    393 
    394  // -
    395 
    396  return AsUnique(new SharedSurface_D3D11Interop(desc, std::move(fbForDrawing),
    397                                                 std::move(data)));
    398 }
    399 
    400 SharedSurface_D3D11Interop::SharedSurface_D3D11Interop(
    401    const SharedSurfaceDesc& desc, UniquePtr<MozFramebuffer>&& fbForDrawing,
    402    Data&& data)
    403    : SharedSurface(desc, std::move(fbForDrawing)),
    404      mData(std::move(data)),
    405      mNeedsFinish(StaticPrefs::webgl_dxgl_needs_finish()) {}
    406 
    407 SharedSurface_D3D11Interop::~SharedSurface_D3D11Interop() {
    408  MOZ_ASSERT(!IsProducerAcquired());
    409 
    410  const auto& gl = mDesc.gl;
    411  if (!gl || !gl->MakeCurrent()) return;
    412 
    413  if (!mData.interop->UnregisterObject(mData.lockHandle)) {
    414    NS_WARNING("Failed to release mLockHandle, possibly leaking it.");
    415  }
    416 }
    417 
    418 void SharedSurface_D3D11Interop::ProducerAcquireImpl() {
    419  MOZ_ASSERT(!mLockedForGL);
    420 
    421  // Now we have the mutex, we can lock for GL.
    422  MOZ_ALWAYS_TRUE(mData.interop->LockObject(mData.lockHandle));
    423 
    424  mLockedForGL = true;
    425 }
    426 
    427 void SharedSurface_D3D11Interop::ProducerReleaseImpl() {
    428  const auto& gl = mDesc.gl;
    429  MOZ_ASSERT(mLockedForGL);
    430 
    431  if (mData.interopFbIfNeedsIndirect) {
    432    const ScopedBindFramebuffer bindFB(gl, mData.interopFbIfNeedsIndirect->mFB);
    433    gl->BlitHelper()->DrawBlitTextureToFramebuffer(mFb->ColorTex(), mDesc.size,
    434                                                   mDesc.size);
    435  }
    436 
    437  if (mNeedsFinish) {
    438    gl->fFinish();
    439  } else {
    440    // We probably don't even need this.
    441    gl->fFlush();
    442  }
    443  MOZ_ALWAYS_TRUE(mData.interop->UnlockObject(mData.lockHandle));
    444 
    445  mLockedForGL = false;
    446 }
    447 
    448 Maybe<layers::SurfaceDescriptor>
    449 SharedSurface_D3D11Interop::ToSurfaceDescriptor() {
    450  const auto format = gfx::SurfaceFormat::B8G8R8A8;
    451  return Some(layers::SurfaceDescriptorD3D10(
    452      mData.dxgiHandle, /* gpuProcessTextureId */ Nothing(),
    453      /* arrayIndex */ 0, format, mDesc.size, mDesc.colorSpace,
    454      gfx::ColorRange::FULL, /* hasKeyedMutex */ true,
    455      /* fencesHolderId */ Nothing()));
    456 }
    457 
    458 //////////////////////////////////////////////////////////////////////////////////////////
    459 // Factory
    460 
    461 /*static*/
    462 UniquePtr<SurfaceFactory_D3D11Interop> SurfaceFactory_D3D11Interop::Create(
    463    GLContext& gl) {
    464  WGLLibrary* wgl = &sWGLLib;
    465  if (!wgl || !wgl->HasDXInterop2()) return nullptr;
    466 
    467  if (XRE_IsContentProcess()) {
    468    gfxPlatform::GetPlatform()->EnsureDevicesInitialized();
    469  }
    470 
    471  const RefPtr<DXInterop2Device> interop = DXInterop2Device::Open(wgl, &gl);
    472  if (!interop) {
    473    NS_WARNING("Failed to open D3D device for use by WGL.");
    474    return nullptr;
    475  }
    476 
    477  return AsUnique(new SurfaceFactory_D3D11Interop(
    478      {&gl, SharedSurfaceType::DXGLInterop2, layers::TextureType::D3D11, true},
    479      interop));
    480 }
    481 
    482 SurfaceFactory_D3D11Interop::SurfaceFactory_D3D11Interop(
    483    const PartialSharedSurfaceDesc& desc, DXInterop2Device* interop)
    484    : SurfaceFactory(desc), mInterop(interop) {}
    485 
    486 SurfaceFactory_D3D11Interop::~SurfaceFactory_D3D11Interop() = default;
    487 
    488 }  // namespace gl
    489 }  // namespace mozilla