tor-browser

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

TextureClientRecycleAllocator.cpp (9641B)


      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 "gfxPlatform.h"
      8 #include "ImageContainer.h"
      9 #include "mozilla/layers/BufferTexture.h"
     10 #include "mozilla/layers/ISurfaceAllocator.h"
     11 #include "mozilla/layers/TextureForwarder.h"
     12 #include "TextureClientRecycleAllocator.h"
     13 
     14 namespace mozilla {
     15 namespace layers {
     16 
     17 // Used to keep TextureClient's reference count stable as not to disrupt
     18 // recycling.
     19 class TextureClientHolder final {
     20  ~TextureClientHolder() = default;
     21 
     22 public:
     23  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureClientHolder)
     24 
     25  explicit TextureClientHolder(TextureClient* aClient)
     26      : mTextureClient(aClient), mWillRecycle(true) {}
     27 
     28  TextureClient* GetTextureClient() { return mTextureClient; }
     29 
     30  bool WillRecycle() { return mWillRecycle; }
     31 
     32  void ClearWillRecycle() { mWillRecycle = false; }
     33 
     34  void ClearTextureClient() { mTextureClient = nullptr; }
     35 
     36 protected:
     37  RefPtr<TextureClient> mTextureClient;
     38  bool mWillRecycle;
     39 };
     40 
     41 class MOZ_RAII DefaultTextureClientAllocationHelper
     42    : public ITextureClientAllocationHelper {
     43 public:
     44  DefaultTextureClientAllocationHelper(
     45      TextureClientRecycleAllocator* aAllocator, gfx::SurfaceFormat aFormat,
     46      gfx::IntSize aSize, BackendSelector aSelector, TextureFlags aTextureFlags,
     47      TextureAllocationFlags aAllocationFlags)
     48      : ITextureClientAllocationHelper(aFormat, aSize, aSelector, aTextureFlags,
     49                                       aAllocationFlags),
     50        mAllocator(aAllocator) {}
     51 
     52  bool IsCompatible(TextureClient* aTextureClient) override {
     53    if (aTextureClient->GetFormat() != mFormat ||
     54        aTextureClient->GetSize() != mSize) {
     55      return false;
     56    }
     57    return true;
     58  }
     59 
     60  already_AddRefed<TextureClient> Allocate(
     61      KnowsCompositor* aKnowsCompositor) override {
     62    return mAllocator->Allocate(mFormat, mSize, mSelector, mTextureFlags,
     63                                mAllocationFlags);
     64  }
     65 
     66 protected:
     67  TextureClientRecycleAllocator* mAllocator;
     68 };
     69 
     70 YCbCrTextureClientAllocationHelper::YCbCrTextureClientAllocationHelper(
     71    const PlanarYCbCrData& aData, const gfx::IntSize& aYSize,
     72    const gfx::IntSize& aCbCrSize, TextureFlags aTextureFlags)
     73    : ITextureClientAllocationHelper(gfx::SurfaceFormat::YUV420, aYSize,
     74                                     BackendSelector::Content, aTextureFlags,
     75                                     ALLOC_DEFAULT),
     76      mData(aData),
     77      mYSize(aYSize),
     78      mCbCrSize(aCbCrSize) {}
     79 
     80 YCbCrTextureClientAllocationHelper::YCbCrTextureClientAllocationHelper(
     81    const PlanarYCbCrData& aData, TextureFlags aTextureFlags)
     82    : YCbCrTextureClientAllocationHelper(aData, aData.YDataSize(),
     83                                         aData.CbCrDataSize(), aTextureFlags) {}
     84 
     85 bool YCbCrTextureClientAllocationHelper::IsCompatible(
     86    TextureClient* aTextureClient) {
     87  MOZ_ASSERT(aTextureClient->GetFormat() == gfx::SurfaceFormat::YUV420);
     88 
     89  BufferTextureData* bufferData =
     90      aTextureClient->GetInternalData()->AsBufferTextureData();
     91 
     92  if (!bufferData ||
     93      !bufferData->GetPictureRect().IsEqualEdges(mData.mPictureRect) ||
     94      bufferData->GetYSize().isNothing() ||
     95      bufferData->GetYSize().ref() != mYSize ||
     96      bufferData->GetCbCrSize().isNothing() ||
     97      bufferData->GetCbCrSize().ref() != mCbCrSize ||
     98      bufferData->GetYStride().isNothing() ||
     99      bufferData->GetYStride().ref() != mData.mYStride ||
    100      bufferData->GetCbCrStride().isNothing() ||
    101      bufferData->GetCbCrStride().ref() != mData.mCbCrStride ||
    102      bufferData->GetYUVColorSpace().isNothing() ||
    103      bufferData->GetYUVColorSpace().ref() != mData.mYUVColorSpace ||
    104      bufferData->GetColorDepth().isNothing() ||
    105      bufferData->GetColorDepth().ref() != mData.mColorDepth ||
    106      bufferData->GetStereoMode().isNothing() ||
    107      bufferData->GetStereoMode().ref() != mData.mStereoMode ||
    108      bufferData->GetChromaSubsampling().isNothing() ||
    109      bufferData->GetChromaSubsampling().ref() != mData.mChromaSubsampling) {
    110    return false;
    111  }
    112  return true;
    113 }
    114 
    115 already_AddRefed<TextureClient> YCbCrTextureClientAllocationHelper::Allocate(
    116    KnowsCompositor* aKnowsCompositor) {
    117  return TextureClient::CreateForYCbCr(
    118      aKnowsCompositor, mData.mPictureRect, mYSize, mData.mYStride, mCbCrSize,
    119      mData.mCbCrStride, mData.mStereoMode, mData.mColorDepth,
    120      mData.mYUVColorSpace, mData.mColorRange, mData.mChromaSubsampling,
    121      mTextureFlags);
    122 }
    123 
    124 TextureClientRecycleAllocator::TextureClientRecycleAllocator(
    125    KnowsCompositor* aKnowsCompositor)
    126    : mKnowsCompositor(aKnowsCompositor),
    127      mMaxPooledSize(kMaxPooledSized),
    128      mLock("TextureClientRecycleAllocatorImp.mLock"),
    129      mIsDestroyed(false) {}
    130 
    131 TextureClientRecycleAllocator::~TextureClientRecycleAllocator() {
    132  MutexAutoLock lock(mLock);
    133  while (!mPooledClients.empty()) {
    134    mPooledClients.pop();
    135  }
    136  MOZ_ASSERT(mInUseClients.empty());
    137 }
    138 
    139 void TextureClientRecycleAllocator::SetMaxPoolSize(uint32_t aMax) {
    140  mMaxPooledSize = aMax;
    141 }
    142 
    143 already_AddRefed<TextureClient> TextureClientRecycleAllocator::CreateOrRecycle(
    144    gfx::SurfaceFormat aFormat, gfx::IntSize aSize, BackendSelector aSelector,
    145    TextureFlags aTextureFlags, TextureAllocationFlags aAllocFlags) {
    146  MOZ_ASSERT(!(aTextureFlags & TextureFlags::RECYCLE));
    147  DefaultTextureClientAllocationHelper helper(this, aFormat, aSize, aSelector,
    148                                              aTextureFlags, aAllocFlags);
    149  return CreateOrRecycle(helper).unwrapOr(nullptr);
    150 }
    151 
    152 Result<already_AddRefed<TextureClient>, nsresult>
    153 TextureClientRecycleAllocator::CreateOrRecycle(
    154    ITextureClientAllocationHelper& aHelper) {
    155  MOZ_ASSERT(aHelper.mTextureFlags & TextureFlags::RECYCLE);
    156 
    157  RefPtr<TextureClientHolder> textureHolder;
    158 
    159  {
    160    MutexAutoLock lock(mLock);
    161    if (mIsDestroyed || !mKnowsCompositor->GetTextureForwarder()) {
    162      return Err(NS_ERROR_NOT_AVAILABLE);
    163    }
    164    if (!mPooledClients.empty()) {
    165      textureHolder = mPooledClients.top();
    166      mPooledClients.pop();
    167      // If the texture's allocator is not open or a pooled TextureClient is
    168      // not compatible, release it.
    169      if (!textureHolder->GetTextureClient()->GetAllocator()->IPCOpen() ||
    170          !aHelper.IsCompatible(textureHolder->GetTextureClient())) {
    171        // Release TextureClient.
    172        RefPtr<Runnable> task =
    173            new TextureClientReleaseTask(textureHolder->GetTextureClient());
    174        textureHolder->ClearTextureClient();
    175        textureHolder = nullptr;
    176        mKnowsCompositor->GetTextureForwarder()->GetThread()->Dispatch(
    177            task.forget());
    178      } else {
    179        textureHolder->GetTextureClient()->RecycleTexture(
    180            aHelper.mTextureFlags);
    181      }
    182    }
    183  }
    184 
    185  if (!textureHolder) {
    186    // Allocate new TextureClient
    187    RefPtr<TextureClient> texture = aHelper.Allocate(mKnowsCompositor);
    188    if (!texture) {
    189      return Err(NS_ERROR_OUT_OF_MEMORY);
    190    }
    191    textureHolder = new TextureClientHolder(texture);
    192  }
    193 
    194  {
    195    MutexAutoLock lock(mLock);
    196    MOZ_ASSERT(mInUseClients.find(textureHolder->GetTextureClient()) ==
    197               mInUseClients.end());
    198    // Register TextureClient
    199    mInUseClients[textureHolder->GetTextureClient()] = textureHolder;
    200  }
    201  RefPtr<TextureClient> client(textureHolder->GetTextureClient());
    202 
    203  // Make sure the texture holds a reference to us, and ask it to call
    204  // RecycleTextureClient when its ref count drops to 1.
    205  client->SetRecycleAllocator(this);
    206  return client.forget();
    207 }
    208 
    209 already_AddRefed<TextureClient> TextureClientRecycleAllocator::Allocate(
    210    gfx::SurfaceFormat aFormat, gfx::IntSize aSize, BackendSelector aSelector,
    211    TextureFlags aTextureFlags, TextureAllocationFlags aAllocFlags) {
    212  return TextureClient::CreateForDrawing(mKnowsCompositor, aFormat, aSize,
    213                                         aSelector, aTextureFlags, aAllocFlags);
    214 }
    215 
    216 void TextureClientRecycleAllocator::ShrinkToMinimumSize() {
    217  MutexAutoLock lock(mLock);
    218  while (!mPooledClients.empty()) {
    219    mPooledClients.pop();
    220  }
    221  // We can not clear using TextureClients safely.
    222  // Just clear WillRecycle here.
    223  std::map<TextureClient*, RefPtr<TextureClientHolder> >::iterator it;
    224  for (it = mInUseClients.begin(); it != mInUseClients.end(); it++) {
    225    RefPtr<TextureClientHolder> holder = it->second;
    226    holder->ClearWillRecycle();
    227  }
    228 }
    229 
    230 void TextureClientRecycleAllocator::Destroy() {
    231  MutexAutoLock lock(mLock);
    232  while (!mPooledClients.empty()) {
    233    mPooledClients.pop();
    234  }
    235  mIsDestroyed = true;
    236 }
    237 
    238 void TextureClientRecycleAllocator::RecycleTextureClient(
    239    TextureClient* aClient) {
    240  // Clearing the recycle allocator drops a reference, so make sure we stay
    241  // alive for the duration of this function.
    242  RefPtr<TextureClientRecycleAllocator> kungFuDeathGrip(this);
    243  aClient->SetRecycleAllocator(nullptr);
    244 
    245  RefPtr<TextureClientHolder> textureHolder;
    246  {
    247    MutexAutoLock lock(mLock);
    248    if (mInUseClients.find(aClient) != mInUseClients.end()) {
    249      textureHolder =
    250          mInUseClients[aClient];  // Keep reference count of
    251                                   // TextureClientHolder within lock.
    252      if (textureHolder->WillRecycle() && !mIsDestroyed &&
    253          mPooledClients.size() < mMaxPooledSize) {
    254        mPooledClients.push(textureHolder);
    255      }
    256      mInUseClients.erase(aClient);
    257    }
    258  }
    259 }
    260 
    261 }  // namespace layers
    262 }  // namespace mozilla