tor-browser

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

RenderCompositorD3D11SWGL.cpp (17651B)


      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 *
      5 * License, v. 2.0. If a copy of the MPL was not distributed with this
      6 * file,
      7 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      8 
      9 #include "RenderCompositorD3D11SWGL.h"
     10 
     11 #include "gfxConfig.h"
     12 #include "mozilla/gfx/2D.h"
     13 #include "mozilla/widget/CompositorWidget.h"
     14 #include "mozilla/layers/TextureD3D11.h"
     15 #include "mozilla/layers/Effects.h"
     16 #include "mozilla/webrender/RenderD3D11TextureHost.h"
     17 #include "RenderCompositorRecordedFrame.h"
     18 #include "RenderThread.h"
     19 
     20 namespace mozilla {
     21 using namespace layers;
     22 
     23 namespace wr {
     24 
     25 extern LazyLogModule gRenderThreadLog;
     26 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
     27 
     28 RenderCompositorD3D11SWGL::UploadMode
     29 RenderCompositorD3D11SWGL::GetUploadMode() {
     30  int mode = StaticPrefs::gfx_webrender_software_d3d11_upload_mode();
     31  switch (mode) {
     32    case 1:
     33      return Upload_Immediate;
     34    case 2:
     35      return Upload_Staging;
     36    case 3:
     37      return Upload_StagingNoBlock;
     38    case 4:
     39      return Upload_StagingPooled;
     40    default:
     41      return Upload_Staging;
     42  }
     43 }
     44 
     45 UniquePtr<RenderCompositor> RenderCompositorD3D11SWGL::Create(
     46    const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) {
     47  if (!aWidget->GetCompositorOptions().AllowSoftwareWebRenderD3D11() ||
     48      !gfx::gfxConfig::IsEnabled(gfx::Feature::D3D11_COMPOSITING)) {
     49    return nullptr;
     50  }
     51 
     52  void* ctx = wr_swgl_create_context();
     53  if (!ctx) {
     54    gfxCriticalNote << "Failed SWGL context creation for WebRender";
     55    return nullptr;
     56  }
     57 
     58  RefPtr<CompositorD3D11> compositor = MakeAndAddRef<CompositorD3D11>(aWidget);
     59  nsCString log;
     60  if (!compositor->Initialize(&log)) {
     61    gfxCriticalNote << "Failed to initialize CompositorD3D11 for SWGL: "
     62                    << log.get();
     63    return nullptr;
     64  }
     65  return MakeUnique<RenderCompositorD3D11SWGL>(compositor, aWidget, ctx);
     66 }
     67 
     68 RenderCompositorD3D11SWGL::RenderCompositorD3D11SWGL(
     69    CompositorD3D11* aCompositor,
     70    const RefPtr<widget::CompositorWidget>& aWidget, void* aContext)
     71    : RenderCompositorLayersSWGL(aCompositor, aWidget, aContext) {
     72  LOG("RenderCompositorD3D11SWGL::RenderCompositorD3D11SWGL()");
     73 
     74  mSyncObject = GetCompositorD3D11()->GetSyncObject();
     75 }
     76 
     77 RenderCompositorD3D11SWGL::~RenderCompositorD3D11SWGL() {
     78  LOG("RenderCompositorD3D11SWGL::~RenderCompositorD3D11SWGL()");
     79 }
     80 
     81 bool RenderCompositorD3D11SWGL::BeginFrame() {
     82  if (!RenderCompositorLayersSWGL::BeginFrame()) {
     83    return false;
     84  }
     85 
     86  mUploadMode = GetUploadMode();
     87  return true;
     88 }
     89 
     90 void RenderCompositorD3D11SWGL::HandleExternalImage(
     91    RenderTextureHost* aExternalImage, FrameSurface& aFrameSurface) {
     92  // We need to hold the texture source separately from the effect,
     93  // since the effect doesn't hold a strong reference.
     94  RefPtr<DataTextureSourceD3D11> layer;
     95  RefPtr<TexturedEffect> texturedEffect;
     96  gfx::IntSize size;
     97  if (auto* host = aExternalImage->AsRenderDXGITextureHost()) {
     98    if (!host->EnsureD3D11Texture2D(GetDevice())) {
     99      return;
    100    }
    101 
    102    layer = new DataTextureSourceD3D11(GetDevice(), host->GetFormat(),
    103                                       host->GetD3D11Texture2D());
    104    if (host->GetFormat() == gfx::SurfaceFormat::NV12 ||
    105        host->GetFormat() == gfx::SurfaceFormat::P010 ||
    106        host->GetFormat() == gfx::SurfaceFormat::P016) {
    107      const auto yuv = FromYUVRangedColorSpace(host->GetYUVColorSpace());
    108      texturedEffect =
    109          new EffectNV12(layer, yuv.space, yuv.range, host->GetColorDepth(),
    110                         aFrameSurface.mFilter);
    111    } else {
    112      MOZ_ASSERT(host->GetFormat() == gfx::SurfaceFormat::B8G8R8X8 ||
    113                 host->GetFormat() == gfx::SurfaceFormat::B8G8R8A8);
    114      texturedEffect = CreateTexturedEffect(host->GetFormat(), layer,
    115                                            aFrameSurface.mFilter, true);
    116    }
    117    size = host->GetSize(0);
    118    host->LockInternal();
    119  } else if (auto* host = aExternalImage->AsRenderDXGIYCbCrTextureHost()) {
    120    if (!host->EnsureD3D11Texture2D(GetDevice())) {
    121      return;
    122    }
    123 
    124    layer = new DataTextureSourceD3D11(GetDevice(), gfx::SurfaceFormat::A8,
    125                                       host->GetD3D11Texture2D(0));
    126    RefPtr<DataTextureSourceD3D11> u = new DataTextureSourceD3D11(
    127        GetDevice(), gfx::SurfaceFormat::A8, host->GetD3D11Texture2D(1));
    128    layer->SetNextSibling(u);
    129    RefPtr<DataTextureSourceD3D11> v = new DataTextureSourceD3D11(
    130        GetDevice(), gfx::SurfaceFormat::A8, host->GetD3D11Texture2D(2));
    131    u->SetNextSibling(v);
    132 
    133    const auto yuv = FromYUVRangedColorSpace(host->GetYUVColorSpace());
    134    texturedEffect =
    135        new EffectYCbCr(layer, yuv.space, yuv.range, host->GetColorDepth(),
    136                        aFrameSurface.mFilter);
    137    size = host->GetSize(0);
    138    host->LockInternal();
    139  }
    140 
    141  gfx::Rect drawRect(0, 0, size.width, size.height);
    142 
    143  EffectChain effect;
    144  effect.mPrimaryEffect = texturedEffect;
    145  mCompositor->DrawQuad(drawRect, aFrameSurface.mClipRect, effect, 1.0,
    146                        aFrameSurface.mTransform, drawRect);
    147 
    148  if (auto* host = aExternalImage->AsRenderDXGITextureHost()) {
    149    host->Unlock();
    150  } else if (auto* host = aExternalImage->AsRenderDXGIYCbCrTextureHost()) {
    151    host->Unlock();
    152  }
    153 }
    154 
    155 void RenderCompositorD3D11SWGL::Pause() {}
    156 
    157 bool RenderCompositorD3D11SWGL::Resume() { return true; }
    158 
    159 gfx::DeviceResetReason RenderCompositorD3D11SWGL::IsContextLost(bool aForce) {
    160  // CompositorD3D11 uses ID3D11Device for composite. The device status needs to
    161  // be checked.
    162  auto reason = GetDevice()->GetDeviceRemovedReason();
    163  return layers::DXGIErrorToDeviceResetReason(reason);
    164 }
    165 
    166 UniquePtr<RenderCompositorLayersSWGL::Surface>
    167 RenderCompositorD3D11SWGL::DoCreateSurface(wr::DeviceIntSize aTileSize,
    168                                           bool aIsOpaque) {
    169  return MakeUnique<SurfaceD3D11SWGL>(aTileSize, aIsOpaque);
    170 }
    171 
    172 SurfaceD3D11SWGL::SurfaceD3D11SWGL(wr::DeviceIntSize aTileSize, bool aIsOpaque)
    173    : Surface(aTileSize, aIsOpaque) {}
    174 
    175 RenderCompositorD3D11SWGL::TileD3D11::TileD3D11(
    176    layers::DataTextureSourceD3D11* aTexture, ID3D11Texture2D* aStagingTexture,
    177    gfx::DataSourceSurface* aDataSourceSurface, Surface* aOwner,
    178    RenderCompositorD3D11SWGL* aRenderCompositor)
    179    : Tile(),
    180      mTexture(aTexture),
    181      mStagingTexture(aStagingTexture),
    182      mSurface(aDataSourceSurface),
    183      mOwner(aOwner->AsSurfaceD3D11SWGL()),
    184      mRenderCompositor(aRenderCompositor) {}
    185 
    186 bool RenderCompositorD3D11SWGL::TileD3D11::Map(wr::DeviceIntRect aDirtyRect,
    187                                               wr::DeviceIntRect aValidRect,
    188                                               void** aData, int32_t* aStride) {
    189  const UploadMode uploadMode = mRenderCompositor->GetUploadMode();
    190  const gfx::IntSize tileSize = mOwner->TileSize();
    191 
    192  if (!IsValid()) {
    193    return false;
    194  }
    195 
    196  // Check if this tile's upload method matches what we're using for this frame,
    197  // and if not then reallocate to fix it. Do this before we copy the struct
    198  // into mCurrentTile.
    199  if (uploadMode == Upload_Immediate) {
    200    if (mStagingTexture) {
    201      MOZ_ASSERT(!mSurface);
    202      mStagingTexture = nullptr;
    203      mSurface = mRenderCompositor->CreateStagingSurface(tileSize);
    204    }
    205  } else {
    206    if (mSurface) {
    207      MOZ_ASSERT(!mStagingTexture);
    208      mSurface = nullptr;
    209      mStagingTexture = mRenderCompositor->CreateStagingTexture(tileSize);
    210    }
    211  }
    212 
    213  mRenderCompositor->mCurrentStagingTexture = mStagingTexture;
    214  mRenderCompositor->mCurrentStagingTextureIsTemp = false;
    215 
    216  if (uploadMode == Upload_Immediate) {
    217    gfx::DataSourceSurface::MappedSurface map;
    218    if (!mSurface->Map(gfx::DataSourceSurface::READ_WRITE, &map)) {
    219      return false;
    220    }
    221 
    222    *aData = map.mData + aValidRect.min.y * map.mStride + aValidRect.min.x * 4;
    223    *aStride = map.mStride;
    224    // Ensure our mapped data is accessible by writing to the beginning and end
    225    // of the dirty region. See bug 1717519
    226    if (aDirtyRect.width() > 0 && aDirtyRect.height() > 0) {
    227      uint32_t* probeData = (uint32_t*)map.mData +
    228                            aDirtyRect.min.y * (map.mStride / 4) +
    229                            aDirtyRect.min.x;
    230      *probeData = 0;
    231      uint32_t* probeDataEnd = (uint32_t*)map.mData +
    232                               (aDirtyRect.max.y - 1) * (map.mStride / 4) +
    233                               (aDirtyRect.max.x - 1);
    234      *probeDataEnd = 0;
    235    }
    236 
    237    mValidRect = gfx::Rect(aValidRect.min.x, aValidRect.min.y,
    238                           aValidRect.width(), aValidRect.height());
    239    return true;
    240  }
    241 
    242  if (!mRenderCompositor->mCurrentStagingTexture) {
    243    return false;
    244  }
    245 
    246  RefPtr<ID3D11DeviceContext> context;
    247  mRenderCompositor->GetDevice()->GetImmediateContext(getter_AddRefs(context));
    248 
    249  D3D11_MAPPED_SUBRESOURCE mappedSubresource;
    250 
    251  bool shouldBlock = uploadMode == Upload_Staging;
    252 
    253  HRESULT hr = context->Map(
    254      mRenderCompositor->mCurrentStagingTexture, 0, D3D11_MAP_READ_WRITE,
    255      shouldBlock ? 0 : D3D11_MAP_FLAG_DO_NOT_WAIT, &mappedSubresource);
    256  if (hr == DXGI_ERROR_WAS_STILL_DRAWING) {
    257    // mCurrentTile is a copy of the real tile data, so we can just replace the
    258    // staging one with a temporary for this draw. The staging texture for the
    259    // real tile remains untouched, so we'll go back to using that for future
    260    // frames and discard the new one. In the future we could improve this by
    261    // having a pool of shared staging textures for all the tiles.
    262 
    263    // Mark the tile as having a temporary staging texture.
    264    mRenderCompositor->mCurrentStagingTextureIsTemp = true;
    265 
    266    // Try grabbing a texture from the staging pool and see if we can use that.
    267    if (uploadMode == Upload_StagingPooled && mOwner->mStagingPool.Length()) {
    268      mRenderCompositor->mCurrentStagingTexture =
    269          mOwner->mStagingPool.ElementAt(0);
    270      mOwner->mStagingPool.RemoveElementAt(0);
    271      hr = context->Map(mRenderCompositor->mCurrentStagingTexture, 0,
    272                        D3D11_MAP_READ_WRITE, D3D11_MAP_FLAG_DO_NOT_WAIT,
    273                        &mappedSubresource);
    274 
    275      // If that failed, put it back into the pool (but at the end).
    276      if (hr == DXGI_ERROR_WAS_STILL_DRAWING) {
    277        mOwner->mStagingPool.AppendElement(
    278            mRenderCompositor->mCurrentStagingTexture);
    279      }
    280    }
    281 
    282    // No staging textures, or we tried one and it was busy. Allocate a brand
    283    // new one instead.
    284    if (hr == DXGI_ERROR_WAS_STILL_DRAWING) {
    285      mRenderCompositor->mCurrentStagingTexture =
    286          mRenderCompositor->CreateStagingTexture(tileSize);
    287      if (!mRenderCompositor->mCurrentStagingTexture) {
    288        return false;
    289      }
    290      hr = context->Map(mRenderCompositor->mCurrentStagingTexture, 0,
    291                        D3D11_MAP_READ_WRITE, 0, &mappedSubresource);
    292    }
    293  }
    294  if (!SUCCEEDED(hr)) {
    295    gfxCriticalError() << "Failed to map tile: " << gfx::hexa(hr);
    296    // This is only expected to fail if we hit a device reset.
    297    MOZ_RELEASE_ASSERT(
    298        mRenderCompositor->GetDevice()->GetDeviceRemovedReason() != S_OK);
    299    return false;
    300  }
    301 
    302  // aData is expected to contain a pointer to the first pixel within the valid
    303  // rect, so take the mapped resource's data (which covers the full tile size)
    304  // and offset it by the top/left of the valid rect.
    305  *aData = (uint8_t*)mappedSubresource.pData +
    306           aValidRect.min.y * mappedSubresource.RowPitch + aValidRect.min.x * 4;
    307  *aStride = mappedSubresource.RowPitch;
    308 
    309  // Ensure our mapped data is accessible by writing to the beginning and end
    310  // of the dirty region. See bug 1717519
    311  if (aDirtyRect.width() > 0 && aDirtyRect.height() > 0) {
    312    uint32_t* probeData = (uint32_t*)mappedSubresource.pData +
    313                          aDirtyRect.min.y * (mappedSubresource.RowPitch / 4) +
    314                          aDirtyRect.min.x;
    315    *probeData = 0;
    316    uint32_t* probeDataEnd =
    317        (uint32_t*)mappedSubresource.pData +
    318        (aDirtyRect.max.y - 1) * (mappedSubresource.RowPitch / 4) +
    319        (aDirtyRect.max.x - 1);
    320    *probeDataEnd = 0;
    321  }
    322 
    323  // Store the new valid rect, so that we can composite only those pixels
    324  mValidRect = gfx::Rect(aValidRect.min.x, aValidRect.min.y, aValidRect.width(),
    325                         aValidRect.height());
    326 
    327  return true;
    328 }
    329 
    330 void RenderCompositorD3D11SWGL::TileD3D11::Unmap(
    331    const gfx::IntRect& aDirtyRect) {
    332  const UploadMode uploadMode = mRenderCompositor->GetUploadMode();
    333 
    334  if (!IsValid()) {
    335    return;
    336  }
    337 
    338  if (mSurface) {
    339    MOZ_ASSERT(uploadMode == Upload_Immediate);
    340    mSurface->Unmap();
    341    nsIntRegion dirty(aDirtyRect);
    342    // This uses UpdateSubresource, which blocks, so is likely implemented as a
    343    // memcpy into driver owned memory, followed by an async upload. The staging
    344    // method should avoid this extra copy, and is likely to be faster usually.
    345    // We could possible do this call on a background thread so that sw-wr can
    346    // start drawing the next tile while the memcpy is in progress.
    347    mTexture->Update(mSurface, &dirty);
    348    return;
    349  }
    350 
    351  if (!mRenderCompositor->mCurrentStagingTexture) {
    352    return;
    353  }
    354 
    355  RefPtr<ID3D11DeviceContext> context;
    356  mRenderCompositor->GetDevice()->GetImmediateContext(getter_AddRefs(context));
    357 
    358  context->Unmap(mRenderCompositor->mCurrentStagingTexture, 0);
    359 
    360  D3D11_BOX box;
    361  box.front = 0;
    362  box.back = 1;
    363  box.left = aDirtyRect.X();
    364  box.top = aDirtyRect.Y();
    365  box.right = aDirtyRect.XMost();
    366  box.bottom = aDirtyRect.YMost();
    367 
    368  context->CopySubresourceRegion(
    369      mTexture->GetD3D11Texture(), 0, aDirtyRect.x, aDirtyRect.y, 0,
    370      mRenderCompositor->mCurrentStagingTexture, 0, &box);
    371 
    372  // If we allocated a temp staging texture for this tile, and we're running
    373  // in pooled mode, then consider adding it to the pool for later.
    374  if (mRenderCompositor->mCurrentStagingTextureIsTemp &&
    375      uploadMode == Upload_StagingPooled) {
    376    static const uint32_t kMaxPoolSize = 5;
    377    if (mOwner->mStagingPool.Length() < kMaxPoolSize) {
    378      mOwner->mStagingPool.AppendElement(
    379          mRenderCompositor->mCurrentStagingTexture);
    380    }
    381  }
    382 
    383  mRenderCompositor->mCurrentStagingTexture = nullptr;
    384  mRenderCompositor->mCurrentStagingTextureIsTemp = false;
    385 }
    386 
    387 bool RenderCompositorD3D11SWGL::TileD3D11::IsValid() { return !!mTexture; }
    388 
    389 already_AddRefed<ID3D11Texture2D>
    390 RenderCompositorD3D11SWGL::CreateStagingTexture(const gfx::IntSize aSize) {
    391  CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, aSize.width,
    392                             aSize.height, 1, 1);
    393 
    394  desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ;
    395  desc.Usage = D3D11_USAGE_STAGING;
    396  desc.BindFlags = 0;
    397 
    398  RefPtr<ID3D11Texture2D> cpuTexture;
    399  DebugOnly<HRESULT> hr =
    400      GetDevice()->CreateTexture2D(&desc, nullptr, getter_AddRefs(cpuTexture));
    401  MOZ_ASSERT(SUCCEEDED(hr));
    402  if (!cpuTexture) {
    403    gfxCriticalNote << "Failed to create StagingTexture: " << aSize;
    404    RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
    405  }
    406  return cpuTexture.forget();
    407 }
    408 
    409 already_AddRefed<gfx::DataSourceSurface>
    410 RenderCompositorD3D11SWGL::CreateStagingSurface(const gfx::IntSize aSize) {
    411  return gfx::Factory::CreateDataSourceSurface(aSize,
    412                                               gfx::SurfaceFormat::B8G8R8A8);
    413 }
    414 
    415 UniquePtr<RenderCompositorLayersSWGL::Tile>
    416 RenderCompositorD3D11SWGL::DoCreateTile(Surface* aSurface) {
    417  MOZ_RELEASE_ASSERT(aSurface);
    418 
    419  const auto tileSize = aSurface->TileSize();
    420 
    421  if (mUploadMode == Upload_Immediate) {
    422    RefPtr<DataTextureSourceD3D11> source =
    423        new DataTextureSourceD3D11(gfx::SurfaceFormat::B8G8R8A8, mCompositor,
    424                                   layers::TextureFlags::NO_FLAGS);
    425    RefPtr<gfx::DataSourceSurface> surf = CreateStagingSurface(tileSize);
    426    return MakeUnique<TileD3D11>(source, nullptr, surf, aSurface, this);
    427  }
    428 
    429  MOZ_ASSERT(mUploadMode == Upload_Staging ||
    430             mUploadMode == Upload_StagingNoBlock ||
    431             mUploadMode == Upload_StagingPooled);
    432 
    433  CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, tileSize.width,
    434                             tileSize.height, 1, 1);
    435 
    436  RefPtr<ID3D11Texture2D> texture;
    437  DebugOnly<HRESULT> hr =
    438      GetDevice()->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
    439  MOZ_ASSERT(SUCCEEDED(hr));
    440  if (!texture) {
    441    gfxCriticalNote << "Failed to allocate Texture2D: " << aSurface->TileSize();
    442    RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
    443    return MakeUnique<TileD3D11>(nullptr, nullptr, nullptr, aSurface, this);
    444  }
    445 
    446  RefPtr<DataTextureSourceD3D11> source = new DataTextureSourceD3D11(
    447      GetDevice(), gfx::SurfaceFormat::B8G8R8A8, texture);
    448 
    449  RefPtr<ID3D11Texture2D> cpuTexture = CreateStagingTexture(tileSize);
    450  return MakeUnique<TileD3D11>(source, cpuTexture, nullptr, aSurface, this);
    451 }
    452 
    453 bool RenderCompositorD3D11SWGL::MaybeReadback(
    454    const gfx::IntSize& aReadbackSize, const wr::ImageFormat& aReadbackFormat,
    455    const Range<uint8_t>& aReadbackBuffer, bool* aNeedsYFlip) {
    456  MOZ_ASSERT(aReadbackFormat == wr::ImageFormat::BGRA8);
    457 
    458  auto stride =
    459      aReadbackSize.width * gfx::BytesPerPixel(gfx::SurfaceFormat::B8G8R8A8);
    460  RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateDrawTargetForData(
    461      gfx::BackendType::SKIA, &aReadbackBuffer[0], aReadbackSize, stride,
    462      gfx::SurfaceFormat::B8G8R8A8, false);
    463  if (!dt) {
    464    return false;
    465  }
    466 
    467  GetCompositorD3D11()->Readback(dt);
    468  return true;
    469 }
    470 
    471 }  // namespace wr
    472 }  // namespace mozilla