tor-browser

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

SurfacePoolWayland.cpp (10360B)


      1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nullptr; c-basic-offset: 2
      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 "mozilla/layers/SurfacePoolWayland.h"
      7 
      8 #include "GLBlitHelper.h"
      9 #include "mozilla/gfx/DataSurfaceHelpers.h"
     10 
     11 #ifdef MOZ_LOGGING
     12 #  undef LOG
     13 #  undef LOGVERBOSE
     14 #  include "mozilla/Logging.h"
     15 #  include "nsTArray.h"
     16 #  include "Units.h"
     17 extern mozilla::LazyLogModule gWidgetCompositorLog;
     18 #  define LOG(str, ...)                                     \
     19    MOZ_LOG(gWidgetCompositorLog, mozilla::LogLevel::Debug, \
     20            (str, ##__VA_ARGS__))
     21 #  define LOGVERBOSE(str, ...)                                \
     22    MOZ_LOG(gWidgetCompositorLog, mozilla::LogLevel::Verbose, \
     23            (str, ##__VA_ARGS__))
     24 #else
     25 #  define LOG(args)
     26 #  define LOGVERBOSE(args)
     27 #endif /* MOZ_LOGGING */
     28 
     29 namespace mozilla::layers {
     30 
     31 using gfx::IntSize;
     32 using gl::DepthAndStencilBuffer;
     33 using gl::GLContext;
     34 using gl::MozFramebuffer;
     35 using widget::WaylandBuffer;
     36 
     37 /* static */ RefPtr<SurfacePool> SurfacePool::Create(size_t aPoolSizeLimit) {
     38  return new SurfacePoolWayland(aPoolSizeLimit);
     39 }
     40 
     41 SurfacePoolWayland::SurfacePoolWayland(size_t aPoolSizeLimit)
     42    : mMutex("SurfacePoolWayland"), mPoolSizeLimit(aPoolSizeLimit) {}
     43 
     44 RefPtr<SurfacePoolHandle> SurfacePoolWayland::GetHandleForGL(GLContext* aGL) {
     45  return new SurfacePoolHandleWayland(this, aGL);
     46 }
     47 
     48 template <typename F>
     49 void SurfacePoolWayland::ForEachEntry(F aFn) {
     50  for (auto& iter : mInUseEntries) {
     51    aFn(iter.second);
     52  }
     53  for (auto& entry : mPendingEntries) {
     54    aFn(entry);
     55  }
     56  for (auto& entry : mAvailableEntries) {
     57    aFn(entry);
     58  }
     59 }
     60 
     61 void SurfacePoolWayland::DestroyGLResourcesForContext(GLContext* aGL) {
     62  MutexAutoLock lock(mMutex);
     63 
     64  ForEachEntry([&](SurfacePoolEntry& entry) {
     65    if (entry.mGLResources && entry.mGLResources->mGL == aGL) {
     66      entry.mGLResources = Nothing();
     67      entry.mWaylandBuffer->DestroyGLResources();
     68    }
     69  });
     70  mDepthBuffers.RemoveElementsBy(
     71      [&](const DepthBufferEntry& entry) { return entry.mGL == aGL; });
     72 }
     73 
     74 bool SurfacePoolWayland::CanRecycleSurfaceForRequest(
     75    const MutexAutoLock& aProofOfLock, const SurfacePoolEntry& aEntry,
     76    const IntSize& aSize, GLContext* aGL) {
     77  MOZ_DIAGNOSTIC_ASSERT(!aEntry.mWaylandBuffer->IsAttached());
     78  if (aEntry.mSize != aSize) {
     79    LOGVERBOSE(
     80        "SurfacePoolWayland::CanRecycleSurfaceForRequest(): can't recycle due "
     81        "to different sizes.");
     82    return false;
     83  }
     84  if (aEntry.mGLResources) {
     85    LOGVERBOSE(
     86        "SurfacePoolWayland::CanRecycleSurfaceForRequest(): mGLResources "
     87        "recycle %d",
     88        aEntry.mGLResources->mGL == aGL);
     89    return aEntry.mGLResources->mGL == aGL;
     90  }
     91  LOGVERBOSE(
     92      "SurfacePoolWayland::CanRecycleSurfaceForRequest(): aGL recycle %d",
     93      aGL == nullptr);
     94  return aGL == nullptr;
     95 }
     96 
     97 RefPtr<WaylandBuffer> SurfacePoolWayland::ObtainBufferFromPool(
     98    const IntSize& aSize, GLContext* aGL, RefPtr<widget::DRMFormat> aFormat) {
     99  MutexAutoLock lock(mMutex);
    100 
    101  auto iterToRecycle = std::find_if(
    102      mAvailableEntries.begin(), mAvailableEntries.end(),
    103      [&](const SurfacePoolEntry& aEntry) {
    104        return CanRecycleSurfaceForRequest(lock, aEntry, aSize, aGL);
    105      });
    106  if (iterToRecycle != mAvailableEntries.end()) {
    107    RefPtr<WaylandBuffer> buffer = iterToRecycle->mWaylandBuffer;
    108    mInUseEntries.insert({buffer.get(), std::move(*iterToRecycle)});
    109    mAvailableEntries.RemoveElementAt(iterToRecycle);
    110    LOGVERBOSE(
    111        "SurfacePoolWayland::ObtainBufferFromPool() recycled [%p] U[%zu] "
    112        "P[%zu] "
    113        "A[%zu]",
    114        buffer.get(), mInUseEntries.size(), mPendingEntries.Length(),
    115        mAvailableEntries.Length());
    116    return buffer;
    117  }
    118 
    119  RefPtr<WaylandBuffer> buffer;
    120  if (aGL) {
    121    buffer = widget::WaylandBufferDMABUF::CreateRGBA(
    122        LayoutDeviceIntSize::FromUnknownSize(aSize), aGL, aFormat);
    123  } else {
    124    buffer = widget::WaylandBufferSHM::Create(
    125        LayoutDeviceIntSize::FromUnknownSize(aSize));
    126  }
    127  if (buffer) {
    128    mInUseEntries.insert({buffer.get(), SurfacePoolEntry{aSize, buffer, {}}});
    129  }
    130  LOGVERBOSE(
    131      "SurfacePoolWayland::ObtainBufferFromPool() created [%p] U[%d] P[%d] "
    132      "A[%d]",
    133      buffer.get(), (int)mInUseEntries.size(), (int)mPendingEntries.Length(),
    134      (int)mAvailableEntries.Length());
    135  return buffer;
    136 }
    137 
    138 void SurfacePoolWayland::ReturnBufferToPool(
    139    const RefPtr<WaylandBuffer>& aBuffer) {
    140  MutexAutoLock lock(mMutex);
    141 
    142  auto inUseEntryIter = mInUseEntries.find(aBuffer);
    143  MOZ_RELEASE_ASSERT(inUseEntryIter != mInUseEntries.end());
    144 
    145  if (aBuffer->IsAttached()) {
    146    mPendingEntries.AppendElement(std::move(inUseEntryIter->second));
    147  } else {
    148    mAvailableEntries.AppendElement(std::move(inUseEntryIter->second));
    149  }
    150  mInUseEntries.erase(inUseEntryIter);
    151 
    152  LOGVERBOSE(
    153      "SurfacePoolWayland::ReturnBufferToPool() buffer [%p] U[%d] P[%d] A[%d]",
    154      aBuffer.get(), (int)mInUseEntries.size(), (int)mPendingEntries.Length(),
    155      (int)mAvailableEntries.Length());
    156 }
    157 
    158 void SurfacePoolWayland::EnforcePoolSizeLimit() {
    159  MutexAutoLock lock(mMutex);
    160 
    161  // Enforce the pool size limit, removing least-recently-used entries as
    162  // necessary.
    163  while (mAvailableEntries.Length() > mPoolSizeLimit) {
    164    mAvailableEntries.RemoveElementAt(0);
    165  }
    166 
    167  if (mPendingEntries.Length() > mPoolSizeLimit * 2) {
    168    LOG("SurfacePoolWayland() mPendingEntries num %d mPoolSizeLimit %d Are we "
    169        "leaking pending entries?",
    170        (int)mPendingEntries.Length(), (int)mPoolSizeLimit);
    171  }
    172  if (mInUseEntries.size() > mPoolSizeLimit * 2) {
    173    LOG("SurfacePoolWayland() mInUseEntries num %d mPoolSizeLimit %d Are we "
    174        "leaking in-use entries?",
    175        (int)mInUseEntries.size(), (int)mPoolSizeLimit);
    176  }
    177 }
    178 
    179 void SurfacePoolWayland::CollectPendingSurfaces() {
    180  MutexAutoLock lock(mMutex);
    181  mPendingEntries.RemoveElementsBy([&](auto& entry) {
    182    LOGVERBOSE(
    183        "SurfacePoolWayland::CollectPendingSurfaces() [%p] attached [%d]",
    184        entry.mWaylandBuffer.get(), entry.mWaylandBuffer->IsAttached());
    185    if (!entry.mWaylandBuffer->IsAttached()) {
    186      mAvailableEntries.AppendElement(std::move(entry));
    187      return true;
    188    }
    189    return false;
    190  });
    191  LOGVERBOSE("SurfacePoolWayland::CollectPendingSurfaces() U[%d] P[%d] A[%d]",
    192             (int)mInUseEntries.size(), (int)mPendingEntries.Length(),
    193             (int)mAvailableEntries.Length());
    194 }
    195 
    196 Maybe<GLuint> SurfacePoolWayland::GetFramebufferForBuffer(
    197    const RefPtr<WaylandBuffer>& aBuffer, GLContext* aGL,
    198    bool aNeedsDepthBuffer) {
    199  MutexAutoLock lock(mMutex);
    200  MOZ_RELEASE_ASSERT(aGL);
    201 
    202  auto inUseEntryIter = mInUseEntries.find(aBuffer);
    203  MOZ_RELEASE_ASSERT(inUseEntryIter != mInUseEntries.end());
    204 
    205  SurfacePoolEntry& entry = inUseEntryIter->second;
    206  if (entry.mGLResources) {
    207    // We have an existing framebuffer.
    208    MOZ_RELEASE_ASSERT(entry.mGLResources->mGL == aGL,
    209                       "Recycled surface that still had GL resources from a "
    210                       "different GL context. "
    211                       "This shouldn't happen.");
    212    if (!aNeedsDepthBuffer || entry.mGLResources->mFramebuffer->HasDepth()) {
    213      return Some(entry.mGLResources->mFramebuffer->mFB);
    214    }
    215  }
    216 
    217  // No usable existing framebuffer, we need to create one.
    218 
    219  if (!aGL->MakeCurrent()) {
    220    // Context may have been destroyed.
    221    return {};
    222  }
    223 
    224  const GLuint tex = aBuffer->GetTexture();
    225  auto fb = CreateFramebufferForTexture(lock, aGL, entry.mSize, tex,
    226                                        aNeedsDepthBuffer);
    227  if (!fb) {
    228    // Framebuffer completeness check may have failed.
    229    return {};
    230  }
    231 
    232  GLuint fbo = fb->mFB;
    233  entry.mGLResources = Some(GLResourcesForBuffer{aGL, std::move(fb)});
    234  return Some(fbo);
    235 }
    236 
    237 RefPtr<gl::DepthAndStencilBuffer> SurfacePoolWayland::GetDepthBufferForSharing(
    238    const MutexAutoLock& aProofOfLock, GLContext* aGL, const IntSize& aSize) {
    239  // Clean out entries for which the weak pointer has become null.
    240  mDepthBuffers.RemoveElementsBy(
    241      [&](const DepthBufferEntry& entry) { return !entry.mBuffer; });
    242 
    243  for (const auto& entry : mDepthBuffers) {
    244    if (entry.mGL == aGL && entry.mSize == aSize) {
    245      return entry.mBuffer.get();
    246    }
    247  }
    248  return nullptr;
    249 }
    250 
    251 UniquePtr<MozFramebuffer> SurfacePoolWayland::CreateFramebufferForTexture(
    252    const MutexAutoLock& aProofOfLock, GLContext* aGL, const IntSize& aSize,
    253    GLuint aTexture, bool aNeedsDepthBuffer) {
    254  if (aNeedsDepthBuffer) {
    255    // Try to find an existing depth buffer of aSize in aGL and create a
    256    // framebuffer that shares it.
    257    if (auto buffer = GetDepthBufferForSharing(aProofOfLock, aGL, aSize)) {
    258      return MozFramebuffer::CreateForBackingWithSharedDepthAndStencil(
    259          aSize, 0, LOCAL_GL_TEXTURE_2D, aTexture, buffer);
    260    }
    261  }
    262 
    263  // No depth buffer needed or we didn't find one. Create a framebuffer with a
    264  // new depth buffer and store a weak pointer to the new depth buffer in
    265  // mDepthBuffers.
    266  UniquePtr<MozFramebuffer> fb = MozFramebuffer::CreateForBacking(
    267      aGL, aSize, 0, aNeedsDepthBuffer, LOCAL_GL_TEXTURE_2D, aTexture);
    268  if (fb && fb->GetDepthAndStencilBuffer()) {
    269    mDepthBuffers.AppendElement(
    270        DepthBufferEntry{aGL, aSize, fb->GetDepthAndStencilBuffer().get()});
    271  }
    272 
    273  return fb;
    274 }
    275 
    276 SurfacePoolHandleWayland::SurfacePoolHandleWayland(
    277    RefPtr<SurfacePoolWayland> aPool, GLContext* aGL)
    278    : mPool(std::move(aPool)), mGL(aGL) {}
    279 
    280 void SurfacePoolHandleWayland::OnBeginFrame() {
    281  mPool->CollectPendingSurfaces();
    282 }
    283 
    284 void SurfacePoolHandleWayland::OnEndFrame() { mPool->EnforcePoolSizeLimit(); }
    285 
    286 RefPtr<WaylandBuffer> SurfacePoolHandleWayland::ObtainBufferFromPool(
    287    const IntSize& aSize, RefPtr<widget::DRMFormat> aFormat) {
    288  return mPool->ObtainBufferFromPool(aSize, mGL, aFormat);
    289 }
    290 
    291 void SurfacePoolHandleWayland::ReturnBufferToPool(
    292    const RefPtr<WaylandBuffer>& aBuffer) {
    293  mPool->ReturnBufferToPool(aBuffer);
    294 }
    295 
    296 Maybe<GLuint> SurfacePoolHandleWayland::GetFramebufferForBuffer(
    297    const RefPtr<WaylandBuffer>& aBuffer, bool aNeedsDepthBuffer) {
    298  return mPool->GetFramebufferForBuffer(aBuffer, mGL, aNeedsDepthBuffer);
    299 }
    300 
    301 }  // namespace mozilla::layers