tor-browser

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

PersistentBufferProvider.cpp (23447B)


      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 "PersistentBufferProvider.h"
      8 
      9 #include "mozilla/layers/KnowsCompositor.h"
     10 #include "mozilla/layers/RemoteTextureMap.h"
     11 #include "mozilla/layers/TextureClient.h"
     12 #include "mozilla/layers/TextureForwarder.h"
     13 #include "mozilla/layers/TextureRecorded.h"
     14 #include "mozilla/gfx/gfxVars.h"
     15 #include "mozilla/gfx/CanvasManagerChild.h"
     16 #include "mozilla/gfx/DrawTargetWebgl.h"
     17 #include "mozilla/gfx/Logging.h"
     18 #include "mozilla/Maybe.h"
     19 #include "mozilla/StaticPrefs_layers.h"
     20 #include "pratom.h"
     21 #include "gfxPlatform.h"
     22 
     23 namespace mozilla {
     24 
     25 using namespace gfx;
     26 
     27 namespace layers {
     28 
     29 PersistentBufferProviderBasic::PersistentBufferProviderBasic(DrawTarget* aDt)
     30    : mDrawTarget(aDt) {
     31  MOZ_COUNT_CTOR(PersistentBufferProviderBasic);
     32 }
     33 
     34 PersistentBufferProviderBasic::~PersistentBufferProviderBasic() {
     35  MOZ_COUNT_DTOR(PersistentBufferProviderBasic);
     36  Destroy();
     37 }
     38 
     39 already_AddRefed<gfx::DrawTarget>
     40 PersistentBufferProviderBasic::BorrowDrawTarget(
     41    const gfx::IntRect& aPersistedRect) {
     42  MOZ_ASSERT(!mSnapshot);
     43  RefPtr<gfx::DrawTarget> dt(mDrawTarget);
     44  return dt.forget();
     45 }
     46 
     47 bool PersistentBufferProviderBasic::ReturnDrawTarget(
     48    already_AddRefed<gfx::DrawTarget> aDT) {
     49  RefPtr<gfx::DrawTarget> dt(aDT);
     50  MOZ_ASSERT(mDrawTarget == dt);
     51  if (dt) {
     52    // Since SkiaGL default to storing drawing command until flush
     53    // we have to flush it before present.
     54    dt->Flush();
     55  }
     56  return true;
     57 }
     58 
     59 already_AddRefed<gfx::SourceSurface>
     60 PersistentBufferProviderBasic::BorrowSnapshot(gfx::DrawTarget* aTarget) {
     61  mSnapshot = mDrawTarget->Snapshot();
     62  RefPtr<SourceSurface> snapshot = mSnapshot;
     63  return snapshot.forget();
     64 }
     65 
     66 void PersistentBufferProviderBasic::ReturnSnapshot(
     67    already_AddRefed<gfx::SourceSurface> aSnapshot) {
     68  RefPtr<SourceSurface> snapshot = aSnapshot;
     69  MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
     70  mSnapshot = nullptr;
     71 }
     72 
     73 void PersistentBufferProviderBasic::Destroy() {
     74  mSnapshot = nullptr;
     75  mDrawTarget = nullptr;
     76 }
     77 
     78 // static
     79 already_AddRefed<PersistentBufferProviderBasic>
     80 PersistentBufferProviderBasic::Create(gfx::IntSize aSize,
     81                                      gfx::SurfaceFormat aFormat,
     82                                      gfx::BackendType aBackend) {
     83  RefPtr<DrawTarget> dt =
     84      gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(aBackend, aSize,
     85                                                             aFormat);
     86 
     87  if (dt) {
     88    // This is simply to ensure the DrawTarget gets initialized, and will detect
     89    // a device reset, even if we're on the main thread.
     90    dt->ClearRect(Rect(0, 0, 0, 0));
     91  }
     92 
     93  if (!dt || !dt->IsValid()) {
     94    return nullptr;
     95  }
     96 
     97  RefPtr<PersistentBufferProviderBasic> provider =
     98      new PersistentBufferProviderBasic(dt);
     99 
    100  return provider.forget();
    101 }
    102 
    103 static already_AddRefed<TextureClient> CreateTexture(
    104    KnowsCompositor* aKnowsCompositor, gfx::SurfaceFormat aFormat,
    105    gfx::IntSize aSize, bool aWillReadFrequently = false,
    106    bool aUseRemoteTexture = false) {
    107  TextureAllocationFlags flags = ALLOC_DEFAULT;
    108  if (aWillReadFrequently) {
    109    flags = TextureAllocationFlags(flags | ALLOC_DO_NOT_ACCELERATE);
    110  }
    111  if (aUseRemoteTexture) {
    112    flags = TextureAllocationFlags(flags | ALLOC_FORCE_REMOTE);
    113  }
    114  RefPtr<TextureClient> tc = TextureClient::CreateForDrawing(
    115      aKnowsCompositor, aFormat, aSize, BackendSelector::Canvas,
    116      TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK, flags);
    117  return tc.forget();
    118 }
    119 
    120 // static
    121 already_AddRefed<PersistentBufferProviderAccelerated>
    122 PersistentBufferProviderAccelerated::Create(gfx::IntSize aSize,
    123                                            gfx::SurfaceFormat aFormat,
    124                                            KnowsCompositor* aKnowsCompositor) {
    125  if (!aKnowsCompositor || !aKnowsCompositor->GetTextureForwarder() ||
    126      !aKnowsCompositor->GetTextureForwarder()->IPCOpen()) {
    127    return nullptr;
    128  }
    129 
    130  if (!DrawTargetWebgl::CanCreate(aSize, aFormat)) {
    131 #ifdef XP_WIN
    132    // Direct2D acceleration does not require DrawTargetWebgl, but still
    133    // requires PersistentBufferProviderAccelerated.
    134    if (!TextureData::IsRemote(aKnowsCompositor, BackendSelector::Canvas,
    135                               aFormat, aSize)) {
    136      return nullptr;
    137    }
    138 #else
    139    return nullptr;
    140 #endif
    141  }
    142 
    143  RefPtr<TextureClient> texture = CreateTexture(
    144      aKnowsCompositor, aFormat, aSize, false, /* aUseRemoteTexture */ true);
    145  if (!texture) {
    146    return nullptr;
    147  }
    148 
    149  auto* recordedTextureData =
    150      texture->GetInternalData()->AsRecordedTextureData();
    151  if (!recordedTextureData) {
    152    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
    153    gfxCriticalNoteOnce << "Expected RecordedTextureData";
    154    return nullptr;
    155  }
    156 
    157  RefPtr<PersistentBufferProviderAccelerated> provider =
    158      new PersistentBufferProviderAccelerated(
    159          recordedTextureData->mRemoteTextureOwnerId, texture);
    160  return provider.forget();
    161 }
    162 
    163 PersistentBufferProviderAccelerated::PersistentBufferProviderAccelerated(
    164    RemoteTextureOwnerId aRemoteTextureOwnerId,
    165    const RefPtr<TextureClient>& aTexture)
    166    : mRemoteTextureOwnerId(aRemoteTextureOwnerId), mTexture(aTexture) {
    167  MOZ_COUNT_CTOR(PersistentBufferProviderAccelerated);
    168 }
    169 
    170 PersistentBufferProviderAccelerated::~PersistentBufferProviderAccelerated() {
    171  MOZ_COUNT_DTOR(PersistentBufferProviderAccelerated);
    172  Destroy();
    173 }
    174 
    175 void PersistentBufferProviderAccelerated::Destroy() {
    176  mSnapshot = nullptr;
    177  mDrawTarget = nullptr;
    178 
    179  if (mTexture) {
    180    if (mTexture->IsLocked()) {
    181      MOZ_ASSERT(false);
    182      mTexture->Unlock();
    183    }
    184    mTexture = nullptr;
    185  }
    186 }
    187 
    188 already_AddRefed<gfx::DrawTarget>
    189 PersistentBufferProviderAccelerated::BorrowDrawTarget(
    190    const gfx::IntRect& aPersistedRect) {
    191  if (!mDrawTarget) {
    192    if (aPersistedRect.IsEmpty()) {
    193      mTexture->GetInternalData()->InvalidateContents();
    194    }
    195    if (!mTexture->Lock(OpenMode::OPEN_READ_WRITE)) {
    196      return nullptr;
    197    }
    198    mDrawTarget = mTexture->BorrowDrawTarget();
    199    if (!mDrawTarget || !mDrawTarget->IsValid()) {
    200      mDrawTarget = nullptr;
    201      mTexture->Unlock();
    202      return nullptr;
    203    }
    204  }
    205  return do_AddRef(mDrawTarget);
    206 }
    207 
    208 bool PersistentBufferProviderAccelerated::ReturnDrawTarget(
    209    already_AddRefed<gfx::DrawTarget> aDT) {
    210  {
    211    RefPtr<gfx::DrawTarget> dt(aDT);
    212    MOZ_ASSERT(mDrawTarget == dt);
    213    if (!mDrawTarget) {
    214      return false;
    215    }
    216    mDrawTarget = nullptr;
    217  }
    218  mTexture->Unlock();
    219  return true;
    220 }
    221 
    222 already_AddRefed<gfx::SourceSurface>
    223 PersistentBufferProviderAccelerated::BorrowSnapshot(gfx::DrawTarget* aTarget) {
    224  if (mDrawTarget) {
    225    MOZ_ASSERT(mTexture->IsLocked());
    226  } else {
    227    if (mTexture->IsLocked()) {
    228      MOZ_ASSERT(false);
    229      return nullptr;
    230    }
    231    if (!mTexture->Lock(OpenMode::OPEN_READ)) {
    232      return nullptr;
    233    }
    234  }
    235  mSnapshot = mTexture->BorrowSnapshot();
    236  return do_AddRef(mSnapshot);
    237 }
    238 
    239 void PersistentBufferProviderAccelerated::ReturnSnapshot(
    240    already_AddRefed<gfx::SourceSurface> aSnapshot) {
    241  RefPtr<SourceSurface> snapshot = aSnapshot;
    242  MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
    243  snapshot = nullptr;
    244  mTexture->ReturnSnapshot(mSnapshot.forget());
    245  if (!mDrawTarget) {
    246    mTexture->Unlock();
    247  }
    248 }
    249 
    250 Maybe<SurfaceDescriptor> PersistentBufferProviderAccelerated::GetFrontBuffer() {
    251  SurfaceDescriptor desc;
    252  if (mTexture->GetInternalData()->Serialize(desc)) {
    253    return Some(desc);
    254  }
    255  return Nothing();
    256 }
    257 
    258 bool PersistentBufferProviderAccelerated::RequiresRefresh() const {
    259  return mTexture->GetInternalData()->RequiresRefresh();
    260 }
    261 
    262 already_AddRefed<FwdTransactionTracker>
    263 PersistentBufferProviderAccelerated::UseCompositableForwarder(
    264    CompositableForwarder* aForwarder) {
    265  return mTexture->GetInternalData()->UseCompositableForwarder(aForwarder);
    266 }
    267 
    268 // static
    269 already_AddRefed<PersistentBufferProviderShared>
    270 PersistentBufferProviderShared::Create(gfx::IntSize aSize,
    271                                       gfx::SurfaceFormat aFormat,
    272                                       KnowsCompositor* aKnowsCompositor,
    273                                       bool aWillReadFrequently,
    274                                       const Maybe<uint64_t>& aWindowID) {
    275  if (!aKnowsCompositor || !aKnowsCompositor->GetTextureForwarder() ||
    276      !aKnowsCompositor->GetTextureForwarder()->IPCOpen()) {
    277    return nullptr;
    278  }
    279 
    280  if (!StaticPrefs::layers_shared_buffer_provider_enabled()) {
    281    return nullptr;
    282  }
    283 
    284 #ifdef XP_WIN
    285  // Bug 1285271 - Disable shared buffer provider on Windows with D2D due to
    286  // instability.
    287  aWillReadFrequently = true;
    288 #endif
    289 
    290  RefPtr<TextureClient> texture =
    291      CreateTexture(aKnowsCompositor, aFormat, aSize, aWillReadFrequently);
    292  if (!texture) {
    293    return nullptr;
    294  }
    295 
    296  RefPtr<PersistentBufferProviderShared> provider =
    297      new PersistentBufferProviderShared(aSize, aFormat, aKnowsCompositor,
    298                                         texture, aWillReadFrequently,
    299                                         aWindowID);
    300  return provider.forget();
    301 }
    302 
    303 PersistentBufferProviderShared::PersistentBufferProviderShared(
    304    gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
    305    KnowsCompositor* aKnowsCompositor, RefPtr<TextureClient>& aTexture,
    306    bool aWillReadFrequently, const Maybe<uint64_t>& aWindowID)
    307    : mSize(aSize),
    308      mFormat(aFormat),
    309      mKnowsCompositor(aKnowsCompositor),
    310      mFront(Nothing()),
    311      mWillReadFrequently(aWillReadFrequently),
    312      mWindowID(aWindowID) {
    313  MOZ_ASSERT(aKnowsCompositor);
    314  if (mTextures.append(aTexture)) {
    315    mBack = Some<uint32_t>(0);
    316  }
    317 
    318  // XXX KnowsCompositor could be used for mMaxAllowedTextures
    319  if (gfxVars::UseWebRenderTripleBufferingWin()) {
    320    ++mMaxAllowedTextures;
    321  }
    322 
    323  MOZ_COUNT_CTOR(PersistentBufferProviderShared);
    324 }
    325 
    326 PersistentBufferProviderShared::~PersistentBufferProviderShared() {
    327  MOZ_COUNT_DTOR(PersistentBufferProviderShared);
    328 
    329  if (IsActivityTracked()) {
    330    if (auto* cm = CanvasManagerChild::Get()) {
    331      cm->GetActiveResourceTracker()->RemoveObject(this);
    332    } else {
    333      MOZ_ASSERT_UNREACHABLE("Tracked but no CanvasManagerChild!");
    334    }
    335  }
    336 
    337  Destroy();
    338 }
    339 
    340 bool PersistentBufferProviderShared::SetKnowsCompositor(
    341    KnowsCompositor* aKnowsCompositor, bool& aOutLostFrontTexture) {
    342  MOZ_ASSERT(aKnowsCompositor);
    343  MOZ_ASSERT(!aOutLostFrontTexture);
    344  if (!aKnowsCompositor) {
    345    return false;
    346  }
    347 
    348  if (mKnowsCompositor == aKnowsCompositor) {
    349    // The forwarder should not change most of the time.
    350    return true;
    351  }
    352 
    353  if (IsActivityTracked()) {
    354    if (auto* cm = CanvasManagerChild::Get()) {
    355      cm->GetActiveResourceTracker()->RemoveObject(this);
    356    } else {
    357      MOZ_ASSERT_UNREACHABLE("Tracked but no CanvasManagerChild!");
    358    }
    359  }
    360 
    361  if (mKnowsCompositor->GetTextureForwarder() !=
    362          aKnowsCompositor->GetTextureForwarder() ||
    363      mKnowsCompositor->GetCompositorBackendType() !=
    364          aKnowsCompositor->GetCompositorBackendType()) {
    365    // We are going to be used with an different and/or incompatible forwarder.
    366    // This should be extremely rare. We have to copy the front buffer into a
    367    // texture that is compatible with the new forwarder.
    368 
    369    // Grab the current front buffer.
    370    RefPtr<TextureClient> prevTexture = GetTexture(mFront);
    371 
    372    // Get rid of everything else
    373    Destroy();
    374 
    375    if (prevTexture && !prevTexture->IsValid()) {
    376      aOutLostFrontTexture = true;
    377    } else if (prevTexture && prevTexture->IsValid()) {
    378      RefPtr<TextureClient> newTexture =
    379          CreateTexture(aKnowsCompositor, mFormat, mSize, mWillReadFrequently);
    380 
    381      MOZ_ASSERT(newTexture);
    382      if (!newTexture) {
    383        return false;
    384      }
    385 
    386      // If we early-return in one of the following branches, we will
    387      // leave the buffer provider in an empty state, since we called
    388      // Destroy. Not ideal but at least we won't try to use it with a
    389      // an incompatible ipc channel.
    390 
    391      if (!newTexture->Lock(OpenMode::OPEN_WRITE)) {
    392        return false;
    393      }
    394 
    395      if (!prevTexture->Lock(OpenMode::OPEN_READ)) {
    396        newTexture->Unlock();
    397        return false;
    398      }
    399 
    400      bool success =
    401          prevTexture->CopyToTextureClient(newTexture, nullptr, nullptr);
    402 
    403      prevTexture->Unlock();
    404      newTexture->Unlock();
    405 
    406      if (!success) {
    407        return false;
    408      }
    409 
    410      if (!mTextures.append(newTexture)) {
    411        return false;
    412      }
    413      mFront = Some<uint32_t>(mTextures.length() - 1);
    414      mBack = mFront;
    415    }
    416  }
    417 
    418  mKnowsCompositor = aKnowsCompositor;
    419 
    420  return true;
    421 }
    422 
    423 TextureClient* PersistentBufferProviderShared::GetTexture(
    424    const Maybe<uint32_t>& aIndex) {
    425  if (aIndex.isNothing() || !CheckIndex(aIndex.value())) {
    426    return nullptr;
    427  }
    428  return mTextures[aIndex.value()];
    429 }
    430 
    431 already_AddRefed<gfx::DrawTarget>
    432 PersistentBufferProviderShared::BorrowDrawTarget(
    433    const gfx::IntRect& aPersistedRect) {
    434  if (!mKnowsCompositor->GetTextureForwarder() ||
    435      !mKnowsCompositor->GetTextureForwarder()->IPCOpen()) {
    436    return nullptr;
    437  }
    438 
    439  auto* cm = CanvasManagerChild::Get();
    440  if (NS_WARN_IF(!cm)) {
    441    return nullptr;
    442  }
    443 
    444  MOZ_ASSERT(!mSnapshot);
    445 
    446  if (IsActivityTracked()) {
    447    cm->GetActiveResourceTracker()->MarkUsed(this);
    448  } else {
    449    cm->GetActiveResourceTracker()->AddObject(this);
    450  }
    451 
    452  if (mDrawTarget) {
    453    RefPtr<gfx::DrawTarget> dt(mDrawTarget);
    454    return dt.forget();
    455  }
    456 
    457  auto previousBackBuffer = mBack;
    458 
    459  TextureClient* tex = GetTexture(mBack);
    460 
    461  // First try to reuse the current back buffer. If we can do that it means
    462  // we can skip copying its content to the new back buffer.
    463  if (tex && tex->IsReadLocked()) {
    464    // The back buffer is currently used by the compositor, we can't draw
    465    // into it.
    466    tex = nullptr;
    467  }
    468 
    469  if (!tex) {
    470    // Try to grab an already allocated texture if any is available.
    471    for (uint32_t i = 0; i < mTextures.length(); ++i) {
    472      if (!mTextures[i]->IsReadLocked()) {
    473        mBack = Some(i);
    474        tex = mTextures[i];
    475        break;
    476      }
    477    }
    478  }
    479 
    480  if (!tex) {
    481    // We have to allocate a new texture.
    482    if (mTextures.length() >= mMaxAllowedTextures) {
    483      // We should never need to buffer that many textures, something's wrong.
    484      // In theory we throttle the main thread when the compositor can't keep
    485      // up, so we shoud never get in a situation where we sent 4 textures to
    486      // the compositor and the latter has not released any of them. In
    487      // practice, though, the throttling mechanism appears to have some issues,
    488      // especially when switching between layer managers (during tab-switch).
    489      // To make sure we don't get too far ahead of the compositor, we send a
    490      // sync ping to the compositor thread...
    491      mKnowsCompositor->SyncWithCompositor(mWindowID);
    492      // ...and try again.
    493      for (uint32_t i = 0; i < mTextures.length(); ++i) {
    494        if (!mTextures[i]->IsReadLocked()) {
    495          gfxCriticalNote << "Managed to allocate after flush.";
    496          mBack = Some(i);
    497          tex = mTextures[i];
    498          break;
    499        }
    500      }
    501 
    502      if (!tex) {
    503        gfxCriticalNote << "Unexpected BufferProvider over-production.";
    504        // It would be pretty bad to keep piling textures up at this point so we
    505        // call NotifyInactive to remove some of our textures.
    506        NotifyInactive();
    507        // Give up now. The caller can fall-back to a non-shared buffer
    508        // provider.
    509        return nullptr;
    510      }
    511    }
    512 
    513    RefPtr<TextureClient> newTexture =
    514        CreateTexture(mKnowsCompositor, mFormat, mSize, mWillReadFrequently);
    515 
    516    MOZ_ASSERT(newTexture);
    517    if (newTexture) {
    518      if (mTextures.append(newTexture)) {
    519        tex = newTexture;
    520        mBack = Some<uint32_t>(mTextures.length() - 1);
    521      }
    522    }
    523  }
    524 
    525  if (!tex) {
    526    return nullptr;
    527  }
    528 
    529  if (mPermanentBackBuffer) {
    530    // If we have a permanent back buffer lock the selected one and switch to
    531    // the permanent one before borrowing the DrawTarget. We will copy back into
    532    // the selected one when ReturnDrawTarget is called, before we make it the
    533    // new front buffer.
    534    if (!tex->Lock(OpenMode::OPEN_WRITE)) {
    535      return nullptr;
    536    }
    537    tex = mPermanentBackBuffer;
    538  } else {
    539    // Copy from the previous back buffer if required.
    540    Maybe<TextureClientAutoLock> autoReadLock;
    541    TextureClient* previous = nullptr;
    542    if (mBack != previousBackBuffer && !aPersistedRect.IsEmpty()) {
    543      if (tex->HasSynchronization()) {
    544        // We are about to read lock a texture that is in use by the compositor
    545        // and has synchronization. To prevent possible future contention we
    546        // switch to using a permanent back buffer.
    547        mPermanentBackBuffer = CreateTexture(mKnowsCompositor, mFormat, mSize,
    548                                             mWillReadFrequently);
    549        if (!mPermanentBackBuffer) {
    550          return nullptr;
    551        }
    552        if (!tex->Lock(OpenMode::OPEN_WRITE)) {
    553          return nullptr;
    554        }
    555        tex = mPermanentBackBuffer;
    556      }
    557 
    558      previous = GetTexture(previousBackBuffer);
    559      if (previous) {
    560        autoReadLock.emplace(previous, OpenMode::OPEN_READ);
    561      }
    562    }
    563 
    564    if (!tex->Lock(OpenMode::OPEN_READ_WRITE)) {
    565      return nullptr;
    566    }
    567 
    568    if (autoReadLock.isSome() && autoReadLock->Succeeded() && previous) {
    569      DebugOnly<bool> success =
    570          previous->CopyToTextureClient(tex, &aPersistedRect, nullptr);
    571      MOZ_ASSERT(success);
    572    }
    573  }
    574 
    575  mDrawTarget = tex->BorrowDrawTarget();
    576  if (mDrawTarget) {
    577    // This is simply to ensure the DrawTarget gets initialized, and will detect
    578    // a device reset, even if we're on the main thread.
    579    mDrawTarget->ClearRect(Rect(0, 0, 0, 0));
    580 
    581    if (!mDrawTarget->IsValid()) {
    582      mDrawTarget = nullptr;
    583    }
    584  }
    585 
    586  RefPtr<gfx::DrawTarget> dt(mDrawTarget);
    587  return dt.forget();
    588 }
    589 
    590 bool PersistentBufferProviderShared::ReturnDrawTarget(
    591    already_AddRefed<gfx::DrawTarget> aDT) {
    592  RefPtr<gfx::DrawTarget> dt(aDT);
    593  MOZ_ASSERT(mDrawTarget == dt);
    594  // Can't change the current front buffer while its snapshot is borrowed!
    595  MOZ_ASSERT(!mSnapshot);
    596 
    597  TextureClient* back = GetTexture(mBack);
    598  MOZ_ASSERT(back);
    599 
    600  mDrawTarget = nullptr;
    601  dt = nullptr;
    602 
    603  // If we have a permanent back buffer we have actually been drawing to that,
    604  // so now we must copy to the shared one.
    605  if (mPermanentBackBuffer && back) {
    606    DebugOnly<bool> success =
    607        mPermanentBackBuffer->CopyToTextureClient(back, nullptr, nullptr);
    608    MOZ_ASSERT(success);
    609 
    610    // Let our permanent back buffer know that we have finished drawing.
    611    mPermanentBackBuffer->EndDraw();
    612  }
    613 
    614  if (back) {
    615    back->Unlock();
    616    mFront = mBack;
    617  }
    618 
    619  return !!back;
    620 }
    621 
    622 TextureClient* PersistentBufferProviderShared::GetTextureClient() {
    623  // Can't access the front buffer while drawing.
    624  MOZ_ASSERT(!mDrawTarget);
    625  TextureClient* texture = GetTexture(mFront);
    626  if (!texture) {
    627    gfxCriticalNote
    628        << "PersistentBufferProviderShared: front buffer unavailable";
    629    return nullptr;
    630  }
    631 
    632  // Sometimes, for example on tab switch, we re-forward our texture. So if we
    633  // are and it is still read locked, then borrow and return our DrawTarget to
    634  // force it to be copied to a texture that we will safely read lock.
    635  if (texture->IsReadLocked()) {
    636    RefPtr<DrawTarget> dt =
    637        BorrowDrawTarget(IntRect(0, 0, mSize.width, mSize.height));
    638 
    639    // If we failed to borrow a DrawTarget then all our textures must be read
    640    // locked or we failed to create one, so we'll just return the current front
    641    // buffer even though that might lead to contention.
    642    if (dt) {
    643      ReturnDrawTarget(dt.forget());
    644      texture = GetTexture(mFront);
    645      if (!texture) {
    646        gfxCriticalNote
    647            << "PersistentBufferProviderShared: front buffer unavailable";
    648        return nullptr;
    649      }
    650    }
    651  } else {
    652    // If it isn't read locked then make sure it is set as updated, so that we
    653    // will always read lock even if we've forwarded the texture before.
    654    texture->SetUpdated();
    655  }
    656 
    657  return texture;
    658 }
    659 
    660 already_AddRefed<gfx::SourceSurface>
    661 PersistentBufferProviderShared::BorrowSnapshot(gfx::DrawTarget* aTarget) {
    662  // If we have a permanent back buffer we can always use that to snapshot.
    663  if (mPermanentBackBuffer) {
    664    mSnapshot = mPermanentBackBuffer->BorrowSnapshot();
    665    return do_AddRef(mSnapshot);
    666  }
    667 
    668  if (mDrawTarget) {
    669    auto back = GetTexture(mBack);
    670    if (NS_WARN_IF(!back) || NS_WARN_IF(!back->IsLocked())) {
    671      return nullptr;
    672    }
    673    mSnapshot = back->BorrowSnapshot();
    674    return do_AddRef(mSnapshot);
    675  }
    676 
    677  auto front = GetTexture(mFront);
    678  if (NS_WARN_IF(!front) || NS_WARN_IF(front->IsLocked())) {
    679    return nullptr;
    680  }
    681 
    682  if (front->IsReadLocked() && front->HasSynchronization()) {
    683    // We are about to read lock a texture that is in use by the compositor and
    684    // has synchronization. To prevent possible future contention we switch to
    685    // using a permanent back buffer.
    686    mPermanentBackBuffer =
    687        CreateTexture(mKnowsCompositor, mFormat, mSize, mWillReadFrequently);
    688    if (!mPermanentBackBuffer ||
    689        !mPermanentBackBuffer->Lock(OpenMode::OPEN_READ_WRITE)) {
    690      return nullptr;
    691    }
    692 
    693    if (!front->Lock(OpenMode::OPEN_READ)) {
    694      return nullptr;
    695    }
    696 
    697    DebugOnly<bool> success =
    698        front->CopyToTextureClient(mPermanentBackBuffer, nullptr, nullptr);
    699    MOZ_ASSERT(success);
    700    front->Unlock();
    701    mSnapshot = mPermanentBackBuffer->BorrowSnapshot();
    702    return do_AddRef(mSnapshot);
    703  }
    704 
    705  if (!front->Lock(OpenMode::OPEN_READ)) {
    706    return nullptr;
    707  }
    708 
    709  mSnapshot = front->BorrowSnapshot();
    710 
    711  return do_AddRef(mSnapshot);
    712 }
    713 
    714 void PersistentBufferProviderShared::ReturnSnapshot(
    715    already_AddRefed<gfx::SourceSurface> aSnapshot) {
    716  RefPtr<SourceSurface> snapshot = aSnapshot;
    717  MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
    718 
    719  mSnapshot = nullptr;
    720  snapshot = nullptr;
    721 
    722  if (mDrawTarget || mPermanentBackBuffer) {
    723    return;
    724  }
    725 
    726  auto front = GetTexture(mFront);
    727  if (front) {
    728    front->Unlock();
    729  }
    730 }
    731 
    732 void PersistentBufferProviderShared::NotifyInactive() {
    733  ClearCachedResources();
    734 }
    735 
    736 void PersistentBufferProviderShared::ClearCachedResources() {
    737  RefPtr<TextureClient> front = GetTexture(mFront);
    738  RefPtr<TextureClient> back = GetTexture(mBack);
    739 
    740  // Clear all textures (except the front and back ones that we just kept).
    741  mTextures.clear();
    742  mPermanentBackBuffer = nullptr;
    743 
    744  if (back) {
    745    if (mTextures.append(back)) {
    746      mBack = Some<uint32_t>(0);
    747    }
    748    if (front == back) {
    749      mFront = mBack;
    750    }
    751  }
    752 
    753  if (front && front != back) {
    754    if (mTextures.append(front)) {
    755      mFront = Some<uint32_t>(mTextures.length() - 1);
    756    }
    757  }
    758 }
    759 
    760 void PersistentBufferProviderShared::Destroy() {
    761  mSnapshot = nullptr;
    762  mDrawTarget = nullptr;
    763 
    764  if (mPermanentBackBuffer) {
    765    mPermanentBackBuffer->Unlock();
    766    mPermanentBackBuffer = nullptr;
    767  }
    768 
    769  for (auto& texture : mTextures) {
    770    if (texture && texture->IsLocked()) {
    771      MOZ_ASSERT(false);
    772      texture->Unlock();
    773    }
    774  }
    775 
    776  mTextures.clear();
    777 }
    778 
    779 }  // namespace layers
    780 }  // namespace mozilla