diff options
Diffstat (limited to 'gfx/layers/client/TextureClientRecycleAllocator.cpp')
-rw-r--r-- | gfx/layers/client/TextureClientRecycleAllocator.cpp | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/gfx/layers/client/TextureClientRecycleAllocator.cpp b/gfx/layers/client/TextureClientRecycleAllocator.cpp new file mode 100644 index 000000000..6a06d3f68 --- /dev/null +++ b/gfx/layers/client/TextureClientRecycleAllocator.cpp @@ -0,0 +1,279 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- +* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gfxPlatform.h" +#include "ImageContainer.h" +#include "mozilla/layers/BufferTexture.h" +#include "mozilla/layers/ISurfaceAllocator.h" +#include "mozilla/layers/TextureForwarder.h" +#include "TextureClientRecycleAllocator.h" + +namespace mozilla { +namespace layers { + +// Used to keep TextureClient's reference count stable as not to disrupt recycling. +class TextureClientHolder +{ + ~TextureClientHolder() {} +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureClientHolder) + + explicit TextureClientHolder(TextureClient* aClient) + : mTextureClient(aClient) + , mWillRecycle(true) + {} + + TextureClient* GetTextureClient() + { + return mTextureClient; + } + + bool WillRecycle() + { + return mWillRecycle; + } + + void ClearWillRecycle() + { + mWillRecycle = false; + } + + void ClearTextureClient() { mTextureClient = nullptr; } +protected: + RefPtr<TextureClient> mTextureClient; + bool mWillRecycle; +}; + +class DefaultTextureClientAllocationHelper : public ITextureClientAllocationHelper +{ +public: + DefaultTextureClientAllocationHelper(TextureClientRecycleAllocator* aAllocator, + gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + BackendSelector aSelector, + TextureFlags aTextureFlags, + TextureAllocationFlags aAllocationFlags) + : ITextureClientAllocationHelper(aFormat, + aSize, + aSelector, + aTextureFlags, + aAllocationFlags) + , mAllocator(aAllocator) + {} + + bool IsCompatible(TextureClient* aTextureClient) override + { + if (aTextureClient->GetFormat() != mFormat || + aTextureClient->GetSize() != mSize) { + return false; + } + return true; + } + + already_AddRefed<TextureClient> Allocate(KnowsCompositor* aAllocator) override + { + return mAllocator->Allocate(mFormat, + mSize, + mSelector, + mTextureFlags, + mAllocationFlags); + } + +protected: + TextureClientRecycleAllocator* mAllocator; +}; + +YCbCrTextureClientAllocationHelper::YCbCrTextureClientAllocationHelper(const PlanarYCbCrData& aData, + TextureFlags aTextureFlags) + : ITextureClientAllocationHelper(gfx::SurfaceFormat::YUV, + aData.mYSize, + BackendSelector::Content, + aTextureFlags, + ALLOC_DEFAULT) + , mData(aData) +{ +} + +bool +YCbCrTextureClientAllocationHelper::IsCompatible(TextureClient* aTextureClient) +{ + MOZ_ASSERT(aTextureClient->GetFormat() == gfx::SurfaceFormat::YUV); + + BufferTextureData* bufferData = aTextureClient->GetInternalData()->AsBufferTextureData(); + if (!bufferData || + aTextureClient->GetSize() != mData.mYSize || + bufferData->GetCbCrSize().isNothing() || + bufferData->GetCbCrSize().ref() != mData.mCbCrSize || + bufferData->GetYUVColorSpace().isNothing() || + bufferData->GetYUVColorSpace().ref() != mData.mYUVColorSpace || + bufferData->GetStereoMode().isNothing() || + bufferData->GetStereoMode().ref() != mData.mStereoMode) { + return false; + } + return true; +} + +already_AddRefed<TextureClient> +YCbCrTextureClientAllocationHelper::Allocate(KnowsCompositor* aAllocator) +{ + return TextureClient::CreateForYCbCr(aAllocator, + mData.mYSize, mData.mCbCrSize, + mData.mStereoMode, + mData.mYUVColorSpace, + mTextureFlags); +} + +TextureClientRecycleAllocator::TextureClientRecycleAllocator(KnowsCompositor* aAllocator) + : mSurfaceAllocator(aAllocator) + , mMaxPooledSize(kMaxPooledSized) + , mLock("TextureClientRecycleAllocatorImp.mLock") + , mIsDestroyed(false) +{ +} + +TextureClientRecycleAllocator::~TextureClientRecycleAllocator() +{ + MutexAutoLock lock(mLock); + while (!mPooledClients.empty()) { + mPooledClients.pop(); + } + MOZ_ASSERT(mInUseClients.empty()); +} + +void +TextureClientRecycleAllocator::SetMaxPoolSize(uint32_t aMax) +{ + mMaxPooledSize = aMax; +} + +already_AddRefed<TextureClient> +TextureClientRecycleAllocator::CreateOrRecycle(gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + BackendSelector aSelector, + TextureFlags aTextureFlags, + TextureAllocationFlags aAllocFlags) +{ + MOZ_ASSERT(!(aTextureFlags & TextureFlags::RECYCLE)); + DefaultTextureClientAllocationHelper helper(this, + aFormat, + aSize, + aSelector, + aTextureFlags, + aAllocFlags); + return CreateOrRecycle(helper); +} + +already_AddRefed<TextureClient> +TextureClientRecycleAllocator::CreateOrRecycle(ITextureClientAllocationHelper& aHelper) +{ + MOZ_ASSERT(aHelper.mTextureFlags & TextureFlags::RECYCLE); + + RefPtr<TextureClientHolder> textureHolder; + + { + MutexAutoLock lock(mLock); + if (mIsDestroyed) { + return nullptr; + } + if (!mPooledClients.empty()) { + textureHolder = mPooledClients.top(); + mPooledClients.pop(); + // If a pooled TextureClient is not compatible, release it. + if (!aHelper.IsCompatible(textureHolder->GetTextureClient())) { + // Release TextureClient. + RefPtr<Runnable> task = new TextureClientReleaseTask(textureHolder->GetTextureClient()); + textureHolder->ClearTextureClient(); + textureHolder = nullptr; + mSurfaceAllocator->GetTextureForwarder()->GetMessageLoop()->PostTask(task.forget()); + } else { + textureHolder->GetTextureClient()->RecycleTexture(aHelper.mTextureFlags); + } + } + } + + if (!textureHolder) { + // Allocate new TextureClient + RefPtr<TextureClient> texture = aHelper.Allocate(mSurfaceAllocator); + if (!texture) { + return nullptr; + } + textureHolder = new TextureClientHolder(texture); + } + + { + MutexAutoLock lock(mLock); + MOZ_ASSERT(mInUseClients.find(textureHolder->GetTextureClient()) == mInUseClients.end()); + // Register TextureClient + mInUseClients[textureHolder->GetTextureClient()] = textureHolder; + } + RefPtr<TextureClient> client(textureHolder->GetTextureClient()); + + // Make sure the texture holds a reference to us, and ask it to call RecycleTextureClient when its + // ref count drops to 1. + client->SetRecycleAllocator(this); + return client.forget(); +} + +already_AddRefed<TextureClient> +TextureClientRecycleAllocator::Allocate(gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + BackendSelector aSelector, + TextureFlags aTextureFlags, + TextureAllocationFlags aAllocFlags) +{ + return TextureClient::CreateForDrawing(mSurfaceAllocator, aFormat, aSize, + aSelector, aTextureFlags, aAllocFlags); +} + +void +TextureClientRecycleAllocator::ShrinkToMinimumSize() +{ + MutexAutoLock lock(mLock); + while (!mPooledClients.empty()) { + mPooledClients.pop(); + } + // We can not clear using TextureClients safely. + // Just clear WillRecycle here. + std::map<TextureClient*, RefPtr<TextureClientHolder> >::iterator it; + for (it = mInUseClients.begin(); it != mInUseClients.end(); it++) { + RefPtr<TextureClientHolder> holder = it->second; + holder->ClearWillRecycle(); + } +} + +void +TextureClientRecycleAllocator::Destroy() +{ + MutexAutoLock lock(mLock); + while (!mPooledClients.empty()) { + mPooledClients.pop(); + } + mIsDestroyed = true; +} + +void +TextureClientRecycleAllocator::RecycleTextureClient(TextureClient* aClient) +{ + // Clearing the recycle allocator drops a reference, so make sure we stay alive + // for the duration of this function. + RefPtr<TextureClientRecycleAllocator> kungFuDeathGrip(this); + aClient->SetRecycleAllocator(nullptr); + + RefPtr<TextureClientHolder> textureHolder; + { + MutexAutoLock lock(mLock); + if (mInUseClients.find(aClient) != mInUseClients.end()) { + textureHolder = mInUseClients[aClient]; // Keep reference count of TextureClientHolder within lock. + if (textureHolder->WillRecycle() && + !mIsDestroyed && mPooledClients.size() < mMaxPooledSize) { + mPooledClients.push(textureHolder); + } + mInUseClients.erase(aClient); + } + } +} + +} // namespace layers +} // namespace mozilla |