diff --git a/gfx/layers/ImageContainer.cpp b/gfx/layers/ImageContainer.cpp index 75d42fd1257..f0ca6b13bc0 100644 --- a/gfx/layers/ImageContainer.cpp +++ b/gfx/layers/ImageContainer.cpp @@ -496,11 +496,33 @@ CairoImage::GetTextureClient(CompositableClient *aClient) return nullptr; } - // gfx::BackendType::NONE means default to content backend - textureClient = aClient->CreateTextureClientForDrawing(surface->GetFormat(), - surface->GetSize(), - gfx::BackendType::NONE, - TextureFlags::DEFAULT); + +// XXX windows' TextureClients do not hold ISurfaceAllocator, +// recycler does not work on windows. +#ifndef XP_WIN + +// XXX only gonk ensure when TextureClient is recycled, +// TextureHost is not used by CompositableHost. +#ifdef MOZ_WIDGET_GONK + RefPtr recycler = + aClient->GetTextureClientRecycler(); + if (recycler) { + textureClient = + recycler->CreateOrRecycleForDrawing(surface->GetFormat(), + surface->GetSize(), + gfx::BackendType::NONE, + aClient->GetTextureFlags()); + } +#endif + +#endif + if (!textureClient) { + // gfx::BackendType::NONE means default to content backend + textureClient = aClient->CreateTextureClientForDrawing(surface->GetFormat(), + surface->GetSize(), + gfx::BackendType::NONE, + TextureFlags::DEFAULT); + } if (!textureClient) { return nullptr; } diff --git a/gfx/layers/ImageContainer.h b/gfx/layers/ImageContainer.h index 9d7b4166830..8ca6df4941f 100644 --- a/gfx/layers/ImageContainer.h +++ b/gfx/layers/ImageContainer.h @@ -811,7 +811,7 @@ public: virtual ISharedImage* AsSharedImage() { return this; } virtual uint8_t* GetBuffer() { return nullptr; } - virtual TextureClient* GetTextureClient(CompositableClient* aClient); + virtual TextureClient* GetTextureClient(CompositableClient* aClient) MOZ_OVERRIDE; gfx::IntSize GetSize() { return mSize; } diff --git a/gfx/layers/client/CompositableClient.cpp b/gfx/layers/client/CompositableClient.cpp index c51329e0016..2b11c597251 100644 --- a/gfx/layers/client/CompositableClient.cpp +++ b/gfx/layers/client/CompositableClient.cpp @@ -219,6 +219,7 @@ CompositableClient::AddTextureClient(TextureClient* aClient) if(!aClient || !aClient->IsAllocated()) { return false; } + aClient->SetAddedToCompositableClient(); return aClient->InitIPDLActor(mForwarder); } @@ -233,5 +234,28 @@ CompositableClient::RemoveTexture(TextureClient* aTexture) mForwarder->RemoveTextureFromCompositable(this, aTexture); } +CompositableClient::ClearCachedResources() +{ + if (mTextureClientRecycler) { + mTextureClientRecycler = nullptr; + } +} + +TextureClientRecycleAllocator* +CompositableClient::GetTextureClientRecycler() +{ + if (mTextureClientRecycler) { + return mTextureClientRecycler; + } + + if (!mForwarder) { + return nullptr; + } + + mTextureClientRecycler = + new layers::TextureClientRecycleAllocator(mForwarder); + return mTextureClientRecycler; +} + } // namespace layers } // namespace mozilla diff --git a/gfx/layers/client/CompositableClient.h b/gfx/layers/client/CompositableClient.h index 0e0c5cca652..0c51e3f726b 100644 --- a/gfx/layers/client/CompositableClient.h +++ b/gfx/layers/client/CompositableClient.h @@ -16,6 +16,7 @@ #include "mozilla/layers/CompositorTypes.h" #include "mozilla/layers/LayersTypes.h" // for LayersBackend #include "mozilla/layers/TextureClient.h" // for TextureClient +#include "mozilla/layers/TextureClientRecycleAllocator.h" // for TextureClientRecycleAllocator #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc namespace mozilla { @@ -194,7 +195,7 @@ public: * Clear any resources that are not immediately necessary. This may be called * in low-memory conditions. */ - virtual void ClearCachedResources() {} + virtual void ClearCachedResources(); /** * Should be called when deataching a TextureClient from a Compositable, because @@ -229,12 +230,15 @@ public: TextureFlags GetTextureFlags() const { return mTextureFlags; } + TextureClientRecycleAllocator* GetTextureClientRecycler(); + protected: CompositableChild* mCompositableChild; CompositableForwarder* mForwarder; // Some layers may want to enforce some flags to all their textures // (like disallowing tiling) TextureFlags mTextureFlags; + RefPtr mTextureClientRecycler; friend class CompositableChild; }; diff --git a/gfx/layers/client/TextureClient.cpp b/gfx/layers/client/TextureClient.cpp index cb04fc3dc97..ac96d986736 100644 --- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -176,12 +176,57 @@ TextureClient::AsTextureClient(PTextureChild* actor) return actor ? static_cast(actor)->mTextureClient : nullptr; } +void +TextureClient::AddFlags(TextureFlags aFlags) +{ + MOZ_ASSERT(!IsSharedWithCompositor() || + ((GetFlags() & TextureFlags::RECYCLE) && !IsAddedToCompositableClient())); + mFlags |= aFlags; + if (mValid && mActor && mActor->IPCOpen()) { + mActor->SendRecycleTexture(mFlags); + } +} + +void +TextureClient::RemoveFlags(TextureFlags aFlags) +{ + MOZ_ASSERT(!IsSharedWithCompositor() || + ((GetFlags() & TextureFlags::RECYCLE) && !IsAddedToCompositableClient())); + mFlags &= ~aFlags; + if (mValid && mActor && mActor->IPCOpen()) { + mActor->SendRecycleTexture(mFlags); + } +} + +void +TextureClient::RecycleTexture(TextureFlags aFlags) +{ + MOZ_ASSERT(GetFlags() & TextureFlags::RECYCLE); + MOZ_ASSERT(!HasRecycleCallback()); + + mAddedToCompositableClient = false; + if (mFlags != aFlags) { + mFlags = aFlags; + if (mValid && mActor && mActor->IPCOpen()) { + mActor->SendRecycleTexture(mFlags); + } + } +} + void TextureClient::WaitForCompositorRecycle() { mActor->WaitForCompositorRecycle(); } +void +TextureClient::SetAddedToCompositableClient() +{ + if (!mAddedToCompositableClient) { + mAddedToCompositableClient = true; + } +} + bool TextureClient::InitIPDLActor(CompositableForwarder* aForwarder) { @@ -429,6 +474,7 @@ TextureClient::TextureClient(TextureFlags aFlags) : mFlags(aFlags) , mShared(false) , mValid(true) + , mAddedToCompositableClient(false) {} TextureClient::~TextureClient() diff --git a/gfx/layers/client/TextureClient.h b/gfx/layers/client/TextureClient.h index 290fe456897..9932e460039 100644 --- a/gfx/layers/client/TextureClient.h +++ b/gfx/layers/client/TextureClient.h @@ -301,17 +301,11 @@ public: return (mFlags & aFlags) == aFlags; } - void AddFlags(TextureFlags aFlags) - { - MOZ_ASSERT(!IsSharedWithCompositor()); - mFlags |= aFlags; - } + void AddFlags(TextureFlags aFlags); - void RemoveFlags(TextureFlags aFlags) - { - MOZ_ASSERT(!IsSharedWithCompositor()); - mFlags &= ~aFlags; - } + void RemoveFlags(TextureFlags aFlags); + + void RecycleTexture(TextureFlags aFlags); /** * valid only for TextureFlags::RECYCLE TextureClient. @@ -341,6 +335,17 @@ public: */ bool IsValid() const { return mValid; } + /** + * Called when TextureClient is added to CompositableClient. + */ + void SetAddedToCompositableClient(); + + /** + * If this method retuns false, TextureClient is already added to CompositableClient, + * since its creation or recycling. + */ + bool IsAddedToCompositableClient() const { return mAddedToCompositableClient; } + /** * kee the passed object alive until the IPDL actor is destroyed. This can * help avoid race conditions in some cases. @@ -461,6 +466,7 @@ protected: gl::GfxTextureWasteTracker mWasteTracker; bool mShared; bool mValid; + bool mAddedToCompositableClient; RefPtr mReadbackSink; diff --git a/gfx/layers/client/TextureClientRecycleAllocator.cpp b/gfx/layers/client/TextureClientRecycleAllocator.cpp new file mode 100644 index 00000000000..6c9f72e3d9b --- /dev/null +++ b/gfx/layers/client/TextureClientRecycleAllocator.cpp @@ -0,0 +1,257 @@ +/* -*- 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 +#include + +#include "gfxPlatform.h" +#include "mozilla/layers/GrallocTextureClient.h" +#include "mozilla/layers/ISurfaceAllocator.h" +#include "mozilla/Mutex.h" + +#include "TextureClientRecycleAllocator.h" + +namespace mozilla { +namespace layers { + +class TextureClientRecycleAllocatorImp : public ISurfaceAllocator +{ + ~TextureClientRecycleAllocatorImp(); + +public: + explicit TextureClientRecycleAllocatorImp(ISurfaceAllocator* aAllocator); + + // Creates and allocates a TextureClient. + TemporaryRef + CreateOrRecycleForDrawing(gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + gfx::BackendType aMoz2dBackend, + TextureFlags aTextureFlags, + TextureAllocationFlags flags); + + void Destroy(); + + void RecycleCallbackImp(TextureClient* aClient); + + static void RecycleCallback(TextureClient* aClient, void* aClosure); + + // ISurfaceAllocator + virtual LayersBackend GetCompositorBackendType() const MOZ_OVERRIDE + { + return mSurfaceAllocator->GetCompositorBackendType(); + } + + virtual bool AllocShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aType, + mozilla::ipc::Shmem* aShmem) MOZ_OVERRIDE + { + return mSurfaceAllocator->AllocShmem(aSize, aType, aShmem); + } + + virtual bool AllocUnsafeShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aType, + mozilla::ipc::Shmem* aShmem) MOZ_OVERRIDE + { + return mSurfaceAllocator->AllocUnsafeShmem(aSize, aType, aShmem); + } + + virtual void DeallocShmem(mozilla::ipc::Shmem& aShmem) MOZ_OVERRIDE + { + mSurfaceAllocator->DeallocShmem(aShmem); + } + + virtual bool IsSameProcess() const MOZ_OVERRIDE + { + return mSurfaceAllocator->IsSameProcess(); + } + +protected: + // ISurfaceAllocator + virtual bool IsOnCompositorSide() const MOZ_OVERRIDE + { + return false; + } + +private: + static const uint32_t kMaxPooledSized = 2; + + // 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) + {} + + TextureClient* GetTextureClient() + { + return mTextureClient; + } + void ClearTextureClient() { mTextureClient = nullptr; } + protected: + RefPtr mTextureClient; + }; + + bool mDestroyed; + uint32_t mMaxPooledSize; + RefPtr mSurfaceAllocator; + std::map > mInUseClients; + + // On b2g gonk, std::queue might be a better choice. + // On ICS, fence wait happens implicitly before drawing. + // Since JB, fence wait happens explicitly when fetching a client from the pool. + // stack is good from Graphics cache usage point of view. + std::stack > mPooledClients; + Mutex mLock; +}; + +TextureClientRecycleAllocatorImp::TextureClientRecycleAllocatorImp(ISurfaceAllocator *aAllocator) + : mDestroyed(false) + , mMaxPooledSize(kMaxPooledSized) + , mSurfaceAllocator(aAllocator) + , mLock("TextureClientRecycleAllocatorImp.mLock") +{ +} + +TextureClientRecycleAllocatorImp::~TextureClientRecycleAllocatorImp() +{ + MOZ_ASSERT(mDestroyed); + MOZ_ASSERT(mPooledClients.empty()); + MOZ_ASSERT(mInUseClients.empty()); +} + + +TemporaryRef +TextureClientRecycleAllocatorImp::CreateOrRecycleForDrawing( + gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + gfx::BackendType aMoz2DBackend, + TextureFlags aTextureFlags, + TextureAllocationFlags aAllocFlags) +{ + // TextureAllocationFlags is actually used only by ContentClient. + // This class does not handle ConteClient's TextureClient allocation. + MOZ_ASSERT(aAllocFlags == TextureAllocationFlags::ALLOC_DEFAULT); + MOZ_ASSERT(!(aTextureFlags & TextureFlags::RECYCLE)); + aTextureFlags = aTextureFlags | TextureFlags::RECYCLE; // Set recycle flag + + RefPtr textureHolder; + + if (aMoz2DBackend == gfx::BackendType::NONE) { + aMoz2DBackend = gfxPlatform::GetPlatform()->GetContentBackend(); + } + + { + MutexAutoLock lock(mLock); + if (mDestroyed) { + return nullptr; + } else if (!mPooledClients.empty()) { + textureHolder = mPooledClients.top(); + mPooledClients.pop(); + // If a pooled TextureClient is not compatible, release it. + if (textureHolder->GetTextureClient()->GetFormat() != aFormat || + textureHolder->GetTextureClient()->GetSize() != aSize) + { + TextureClientReleaseTask* task = new TextureClientReleaseTask(textureHolder->GetTextureClient()); + textureHolder->ClearTextureClient(); + textureHolder = nullptr; + // Release TextureClient. + mSurfaceAllocator->GetMessageLoop()->PostTask(FROM_HERE, task); + } else { + textureHolder->GetTextureClient()->RecycleTexture(aTextureFlags); + } + } + } + + if (!textureHolder) { + // Allocate new TextureClient + RefPtr texture; + texture = TextureClient::CreateForDrawing(this, aFormat, aSize, aMoz2DBackend, + aTextureFlags, aAllocFlags); + 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; + } + textureHolder->GetTextureClient()->SetRecycleCallback(TextureClientRecycleAllocatorImp::RecycleCallback, this); + return textureHolder->GetTextureClient(); +} + +void +TextureClientRecycleAllocatorImp::Destroy() +{ + MutexAutoLock lock(mLock); + if (mDestroyed) { + return; + } + mDestroyed = true; + while (!mPooledClients.empty()) { + mPooledClients.pop(); + } +} + +void +TextureClientRecycleAllocatorImp::RecycleCallbackImp(TextureClient* aClient) +{ + RefPtr textureHolder; + aClient->ClearRecycleCallback(); + { + MutexAutoLock lock(mLock); + if (mInUseClients.find(aClient) != mInUseClients.end()) { + textureHolder = mInUseClients[aClient]; // Keep reference count of TextureClientHolder within lock. + if (!mDestroyed && mPooledClients.size() < mMaxPooledSize) { + mPooledClients.push(textureHolder); + } + mInUseClients.erase(aClient); + } + } +} + +/* static */ void +TextureClientRecycleAllocatorImp::RecycleCallback(TextureClient* aClient, void* aClosure) +{ + TextureClientRecycleAllocatorImp* recycleAllocator = static_cast(aClosure); + recycleAllocator->RecycleCallbackImp(aClient); +} + +TextureClientRecycleAllocator::TextureClientRecycleAllocator(ISurfaceAllocator *aAllocator) +{ + mAllocator = new TextureClientRecycleAllocatorImp(aAllocator); +} + +TextureClientRecycleAllocator::~TextureClientRecycleAllocator() +{ + mAllocator->Destroy(); + mAllocator = nullptr; +} + + +TemporaryRef +TextureClientRecycleAllocator::CreateOrRecycleForDrawing( + gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + gfx::BackendType aMoz2DBackend, + TextureFlags aTextureFlags, + TextureAllocationFlags aAllocFlags) +{ + return mAllocator->CreateOrRecycleForDrawing(aFormat, + aSize, + aMoz2DBackend, + aTextureFlags, + aAllocFlags); +} + +} +} diff --git a/gfx/layers/client/TextureClientRecycleAllocator.h b/gfx/layers/client/TextureClientRecycleAllocator.h new file mode 100644 index 00000000000..9436c5885d9 --- /dev/null +++ b/gfx/layers/client/TextureClientRecycleAllocator.h @@ -0,0 +1,49 @@ +/* -*- 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/. */ + +#ifndef MOZILLA_GFX_TEXTURECLIENT_RECYCLE_ALLOCATOR_H +#define MOZILLA_GFX_TEXTURECLIENT_RECYCLE_ALLOCATOR_H + +#include "mozilla/gfx/Types.h" +#include "mozilla/RefPtr.h" +#include "TextureClient.h" + +namespace mozilla { +namespace layers { + +class ISurfaceAllocator; +class TextureClientRecycleAllocatorImp; + + +/** + * TextureClientRecycleAllocator provides TextureClients allocation and + * recycling capabilities. It expects allocations of same sizes and + * attributres. If a recycled TextureClient is different from + * requested one, the recycled one is dropped and new TextureClient is allocated. + */ +class TextureClientRecycleAllocator +{ + ~TextureClientRecycleAllocator(); + +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureClientRecycleAllocator) + + explicit TextureClientRecycleAllocator(ISurfaceAllocator* aAllocator); + + // Creates and allocates a TextureClient. + TemporaryRef + CreateOrRecycleForDrawing(gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + gfx::BackendType aMoz2dBackend, + TextureFlags aTextureFlags, + TextureAllocationFlags flags = ALLOC_DEFAULT); + +private: + RefPtr mAllocator; +}; + +} +} +#endif /* MOZILLA_GFX_TEXTURECLIENT_RECYCLE_ALLOCATOR_H */ diff --git a/gfx/layers/client/TiledContentClient.cpp b/gfx/layers/client/TiledContentClient.cpp index 90fce04e66e..6cbdfab36ef 100644 --- a/gfx/layers/client/TiledContentClient.cpp +++ b/gfx/layers/client/TiledContentClient.cpp @@ -106,6 +106,7 @@ TiledContentClient::TiledContentClient(ClientTiledPaintedLayer* aPaintedLayer, void TiledContentClient::ClearCachedResources() { + CompositableClient::ClearCachedResources(); mTiledBuffer.DiscardBuffers(); mLowPrecisionTiledBuffer.DiscardBuffers(); } diff --git a/gfx/layers/composite/TextureHost.cpp b/gfx/layers/composite/TextureHost.cpp index c5fcf2618c5..d91cf47e0b2 100644 --- a/gfx/layers/composite/TextureHost.cpp +++ b/gfx/layers/composite/TextureHost.cpp @@ -85,6 +85,8 @@ public: virtual bool RecvRemoveTexture() MOZ_OVERRIDE; + virtual bool RecvRecycleTexture(const TextureFlags& aTextureFlags) MOZ_OVERRIDE; + TextureHost* GetTextureHost() { return mTextureHost; } void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE; @@ -301,6 +303,15 @@ void TextureHost::Finalize() } } +void +TextureHost::RecycleTexture(TextureFlags aFlags) +{ + MOZ_ASSERT(GetFlags() & TextureFlags::RECYCLE); + MOZ_ASSERT(aFlags & TextureFlags::RECYCLE); + MOZ_ASSERT(!HasRecycleCallback()); + mFlags = aFlags; +} + void TextureHost::PrintInfo(std::stringstream& aStream, const char* aPrefix) { @@ -793,6 +804,16 @@ TextureParent::ClearTextureHost() mTextureHost = nullptr; } +bool +TextureParent::RecvRecycleTexture(const TextureFlags& aTextureFlags) +{ + if (!mTextureHost) { + return true; + } + mTextureHost->RecycleTexture(aTextureFlags); + return true; +} + //////////////////////////////////////////////////////////////////////////////// static RefPtr diff --git a/gfx/layers/composite/TextureHost.h b/gfx/layers/composite/TextureHost.h index f0c2ec38eb9..dfe8b2bcb6c 100644 --- a/gfx/layers/composite/TextureHost.h +++ b/gfx/layers/composite/TextureHost.h @@ -506,6 +506,8 @@ public: virtual TextureHostOGL* AsHostOGL() { return nullptr; } protected: + void RecycleTexture(TextureFlags aFlags); + PTextureParent* mActor; TextureFlags mFlags; diff --git a/gfx/layers/ipc/PTexture.ipdl b/gfx/layers/ipc/PTexture.ipdl index 5e5f53cdd45..c654c8d159b 100644 --- a/gfx/layers/ipc/PTexture.ipdl +++ b/gfx/layers/ipc/PTexture.ipdl @@ -11,6 +11,7 @@ include protocol PImageBridge; include "mozilla/GfxMessageUtils.h"; using struct mozilla::layers::FrameMetrics from "FrameMetrics.h"; +using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h"; namespace mozilla { namespace layers { @@ -39,6 +40,8 @@ parent: * Asynchronously tell the Compositor side to remove the texture. */ async RemoveTexture(); + + async RecycleTexture(TextureFlags aTextureFlags); }; } // layers diff --git a/gfx/layers/moz.build b/gfx/layers/moz.build index c582bca44ca..2be45a169cb 100644 --- a/gfx/layers/moz.build +++ b/gfx/layers/moz.build @@ -125,6 +125,7 @@ EXPORTS.mozilla.layers += [ 'client/ImageClient.h', 'client/TextureClient.h', 'client/TextureClientPool.h', + 'client/TextureClientRecycleAllocator.h', 'client/TiledContentClient.h', 'composite/AsyncCompositionManager.h', 'composite/CanvasLayerComposite.h', @@ -268,6 +269,7 @@ UNIFIED_SOURCES += [ 'client/ImageClient.cpp', 'client/TextureClient.cpp', 'client/TextureClientPool.cpp', + 'client/TextureClientRecycleAllocator.cpp', 'client/TiledContentClient.cpp', 'composite/AsyncCompositionManager.cpp', 'composite/CanvasLayerComposite.cpp',