Mypal/gfx/layers/client/TextureClientRecycleAllocator.cpp
2019-03-11 13:26:37 +03:00

280 lines
9.0 KiB
C++

/* -*- 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