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