Bug 1091777 - Add TextureClient recycling to CairoImag r=nical
authorSotaro Ikeda <sikeda@mozilla.com>
Thu, 13 Nov 2014 07:53:49 -0800
changeset 239835 42f47a2b3eb523c9381479462e90ac66c43ef0b1
parent 239834 c39b72dd4d10b356c4005983c00497b61deeb785
child 239836 683c4915bceef65dc0f260381b966fa54510f29e
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnical
bugs1091777
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1091777 - Add TextureClient recycling to CairoImag r=nical
gfx/layers/ImageContainer.cpp
gfx/layers/ImageContainer.h
gfx/layers/client/CompositableClient.cpp
gfx/layers/client/CompositableClient.h
gfx/layers/client/TextureClient.cpp
gfx/layers/client/TextureClient.h
gfx/layers/client/TextureClientRecycleAllocator.cpp
gfx/layers/client/TextureClientRecycleAllocator.h
gfx/layers/client/TiledContentClient.cpp
gfx/layers/composite/TextureHost.cpp
gfx/layers/composite/TextureHost.h
gfx/layers/ipc/PTexture.ipdl
gfx/layers/moz.build
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -491,21 +491,43 @@ CairoImage::GetTextureClient(Compositabl
   }
 
   RefPtr<SourceSurface> surface = GetAsSourceSurface();
   MOZ_ASSERT(surface);
   if (!surface) {
     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<TextureClientRecycleAllocator> 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;
   }
   MOZ_ASSERT(textureClient->CanExposeDrawTarget());
   if (!textureClient->Lock(OpenMode::OPEN_WRITE_ONLY)) {
     return nullptr;
   }
 
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -806,17 +806,17 @@ public:
 
   virtual TemporaryRef<gfx::SourceSurface> GetAsSourceSurface()
   {
     return mSourceSurface.get();
   }
 
   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; }
 
   CairoImage();
   ~CairoImage();
 
   gfx::IntSize mSize;
 
--- a/gfx/layers/client/CompositableClient.cpp
+++ b/gfx/layers/client/CompositableClient.cpp
@@ -214,24 +214,49 @@ CompositableClient::CreateTextureClientF
 }
 
 bool
 CompositableClient::AddTextureClient(TextureClient* aClient)
 {
   if(!aClient || !aClient->IsAllocated()) {
     return false;
   }
+  aClient->SetAddedToCompositableClient();
   return aClient->InitIPDLActor(mForwarder);
 }
 
 void
 CompositableClient::OnTransaction()
 {
 }
 
 void
+CompositableClient::ClearCachedResources()
+{
+  if (mTextureClientRecycler) {
+    mTextureClientRecycler = nullptr;
+  }
+}
+
+void
 CompositableClient::RemoveTexture(TextureClient* aTexture)
 {
   mForwarder->RemoveTextureFromCompositable(this, aTexture);
 }
 
+TextureClientRecycleAllocator*
+CompositableClient::GetTextureClientRecycler()
+{
+  if (mTextureClientRecycler) {
+    return mTextureClientRecycler;
+  }
+
+  if (!mForwarder) {
+    return nullptr;
+  }
+
+  mTextureClientRecycler =
+    new layers::TextureClientRecycleAllocator(mForwarder);
+  return mTextureClientRecycler;
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/CompositableClient.h
+++ b/gfx/layers/client/CompositableClient.h
@@ -11,16 +11,17 @@
 #include <map>                          // for map
 #include "mozilla/Assertions.h"         // for MOZ_CRASH
 #include "mozilla/RefPtr.h"             // for TemporaryRef, RefCounted
 #include "mozilla/gfx/Types.h"          // for SurfaceFormat
 #include "mozilla/layers/AsyncTransactionTracker.h" // for AsyncTransactionTracker
 #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 {
 namespace layers {
 
 class CompositableClient;
 class BufferTextureClient;
 class ImageBridgeChild;
@@ -189,17 +190,17 @@ public:
    * A hook for the when the Compositable is detached from it's layer.
    */
   virtual void OnDetach() {}
 
   /**
    * 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
    * some platforms need to do some extra book keeping when this happens (for
    * example to properly keep track of fences on Gonk).
    *
    * See AutoRemoveTexture to automatically invoke this at the end of a scope.
    */
@@ -224,22 +225,25 @@ public:
   static void TransactionCompleteted(PCompositableChild* aActor, uint64_t aTransactionId);
 
   static void HoldUntilComplete(PCompositableChild* aActor, AsyncTransactionTracker* aTracker);
 
   static uint64_t GetTrackersHolderId(PCompositableChild* aActor);
 
   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<TextureClientRecycleAllocator> mTextureClientRecycler;
 
   friend class CompositableChild;
 };
 
 /**
  * Helper to call RemoveTexture at the end of a scope.
  */
 struct AutoRemoveTexture
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -172,21 +172,66 @@ TextureClient::DestroyIPDLActor(PTexture
 // static
 TextureClient*
 TextureClient::AsTextureClient(PTextureChild* actor)
 {
   return actor ? static_cast<TextureChild*>(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)
 {
   MOZ_ASSERT(aForwarder);
   if (mActor && mActor->GetForwarder() == aForwarder) {
     return true;
   }
   MOZ_ASSERT(!mActor, "Cannot use a texture on several IPC channels.");
@@ -424,16 +469,17 @@ TextureClient::CreateWithBufferSize(ISur
 
   return texture;
 }
 
 TextureClient::TextureClient(TextureFlags aFlags)
   : mFlags(aFlags)
   , mShared(false)
   , mValid(true)
+  , mAddedToCompositableClient(false)
 {}
 
 TextureClient::~TextureClient()
 {
   // All the destruction code that may lead to virtual method calls must
   // be in Finalize() which is called just before the destructor.
 }
 
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -296,27 +296,21 @@ public:
    */
   TextureFlags GetFlags() const { return mFlags; }
 
   bool HasFlags(TextureFlags aFlags) const
   {
     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.
    * When called this texture client will grab a strong reference and release
    * it once the compositor notifies that it is done with the texture.
    * NOTE: In this stage the texture client can no longer be used by the
    * client in a transaction.
    */
@@ -337,16 +331,27 @@ public:
 
   /**
    * If this method returns false users of TextureClient are not allowed
    * to access the shared data.
    */
   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.
    * It's a temporary hack to ensure that DXGI textures don't get destroyed
    * between serialization and deserialization.
    */
   void KeepUntilFullDeallocation(KeepAlive* aKeep);
 
   /**
@@ -456,16 +461,17 @@ protected:
   RefPtr<TextureChild> mActor;
   RefPtr<ISurfaceAllocator> mAllocator;
   TextureFlags mFlags;
   FenceHandle mReleaseFenceHandle;
   FenceHandle mAcquireFenceHandle;
   gl::GfxTextureWasteTracker mWasteTracker;
   bool mShared;
   bool mValid;
+  bool mAddedToCompositableClient;
 
   RefPtr<TextureReadbackSink> mReadbackSink;
 
   friend class TextureChild;
   friend class RemoveTextureFromCompositableTracker;
   friend void TestTextureClientSurface(TextureClient*, gfxImageSurface*);
   friend void TestTextureClientYCbCr(TextureClient*, PlanarYCbCrData&);
 };
new file mode 100644
--- /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 <map>
+#include <stack>
+
+#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<TextureClient>
+  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<TextureClient> mTextureClient;
+  };
+
+  bool mDestroyed;
+  uint32_t mMaxPooledSize;
+  RefPtr<ISurfaceAllocator> mSurfaceAllocator;
+  std::map<TextureClient*, RefPtr<TextureClientHolder> > 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<RefPtr<TextureClientHolder> > 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<TextureClient>
+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<TextureClientHolder> 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<TextureClient> 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<TextureClientHolder> 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<TextureClientRecycleAllocatorImp*>(aClosure);
+  recycleAllocator->RecycleCallbackImp(aClient);
+}
+
+TextureClientRecycleAllocator::TextureClientRecycleAllocator(ISurfaceAllocator *aAllocator)
+{
+  mAllocator = new TextureClientRecycleAllocatorImp(aAllocator);
+}
+
+TextureClientRecycleAllocator::~TextureClientRecycleAllocator()
+{
+  mAllocator->Destroy();
+  mAllocator = nullptr;
+}
+
+
+TemporaryRef<TextureClient>
+TextureClientRecycleAllocator::CreateOrRecycleForDrawing(
+                                            gfx::SurfaceFormat aFormat,
+                                            gfx::IntSize aSize,
+                                            gfx::BackendType aMoz2DBackend,
+                                            TextureFlags aTextureFlags,
+                                            TextureAllocationFlags aAllocFlags)
+{
+  return mAllocator->CreateOrRecycleForDrawing(aFormat,
+                                               aSize,
+                                               aMoz2DBackend,
+                                               aTextureFlags,
+                                               aAllocFlags);
+}
+
+}
+}
new file mode 100644
--- /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<TextureClient>
+  CreateOrRecycleForDrawing(gfx::SurfaceFormat aFormat,
+                            gfx::IntSize aSize,
+                            gfx::BackendType aMoz2dBackend,
+                            TextureFlags aTextureFlags,
+                            TextureAllocationFlags flags = ALLOC_DEFAULT);
+
+private:
+  RefPtr<TextureClientRecycleAllocatorImp> mAllocator;
+};
+
+}
+}
+#endif /* MOZILLA_GFX_TEXTURECLIENT_RECYCLE_ALLOCATOR_H */
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -101,16 +101,17 @@ TiledContentClient::TiledContentClient(C
                                                     &mSharedFrameMetricsHelper);
 
   mLowPrecisionTiledBuffer.SetResolution(gfxPrefs::LowPrecisionResolution());
 }
 
 void
 TiledContentClient::ClearCachedResources()
 {
+  CompositableClient::ClearCachedResources();
   mTiledBuffer.DiscardBuffers();
   mLowPrecisionTiledBuffer.DiscardBuffers();
 }
 
 void
 TiledContentClient::UseTiledLayerBuffer(TiledBufferType aType)
 {
   ClientTiledLayerBuffer* buffer = aType == LOW_PRECISION_TILED_BUFFER
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -80,16 +80,18 @@ public:
   void CompositorRecycle();
 
   virtual bool RecvClientRecycle() MOZ_OVERRIDE;
 
   virtual bool RecvClearTextureHostSync() MOZ_OVERRIDE;
 
   virtual bool RecvRemoveTexture() MOZ_OVERRIDE;
 
+  virtual bool RecvRecycleTexture(const TextureFlags& aTextureFlags) MOZ_OVERRIDE;
+
   TextureHost* GetTextureHost() { return mTextureHost; }
 
   void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
   void ClearTextureHost();
 
   CompositableParentManager* mCompositableManager;
   RefPtr<TextureHost> mWaitForClientRecycle;
@@ -297,16 +299,25 @@ void TextureHost::Finalize()
 {
   if (!(GetFlags() & TextureFlags::DEALLOCATE_CLIENT)) {
     DeallocateSharedData();
     DeallocateDeviceData();
   }
 }
 
 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)
 {
   aStream << aPrefix;
   aStream << nsPrintfCString("%s (0x%p)", Name(), this).get();
   // Note: the TextureHost needs to be locked before it is safe to call
   //       GetSize() and GetFormat() on it.
   if (Lock()) {
     AppendToString(aStream, GetSize(), " [size=", "]");
@@ -788,16 +799,26 @@ TextureParent::ClearTextureHost()
   // Clear recycle callback.
   mTextureHost->ClearRecycleCallback();
   mWaitForClientRecycle = nullptr;
 
   mTextureHost->mActor = nullptr;
   mTextureHost = nullptr;
 }
 
+bool
+TextureParent::RecvRecycleTexture(const TextureFlags& aTextureFlags)
+{
+  if (!mTextureHost) {
+    return true;
+  }
+  mTextureHost->RecycleTexture(aTextureFlags);
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 static RefPtr<TextureSource>
 SharedSurfaceToTexSource(gl::SharedSurface* abstractSurf, Compositor* compositor)
 {
   MOZ_ASSERT(abstractSurf);
   MOZ_ASSERT(abstractSurf->mType != gl::SharedSurfaceType::Basic);
   MOZ_ASSERT(abstractSurf->mType != gl::SharedSurfaceType::Gralloc);
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -501,16 +501,18 @@ public:
   virtual bool HasInternalBuffer() const { return false; }
 
   /**
    * Cast to a TextureHost for each backend.
    */
   virtual TextureHostOGL* AsHostOGL() { return nullptr; }
 
 protected:
+  void RecycleTexture(TextureFlags aFlags);
+
   PTextureParent* mActor;
   TextureFlags mFlags;
 
   friend class TextureParent;
 };
 
 /**
  * TextureHost that wraps a random access buffer such as a Shmem or some raw
--- a/gfx/layers/ipc/PTexture.ipdl
+++ b/gfx/layers/ipc/PTexture.ipdl
@@ -6,16 +6,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include LayersSurfaces;
 include protocol PLayerTransaction;
 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 {
 
 /**
  * PTexture is the IPDL glue between a TextureClient and a TextureHost.
  */
 sync protocol PTexture {
@@ -34,12 +35,14 @@ parent:
      * Synchronously tell the TextureChild to clear TextureHost.
      */
     sync ClearTextureHostSync();
 
     /**
      * Asynchronously tell the Compositor side to remove the texture.
      */
     async RemoveTexture();
+
+    async RecycleTexture(TextureFlags aTextureFlags);
 };
 
 } // layers
 } // mozilla
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -120,16 +120,17 @@ EXPORTS.mozilla.layers += [
     'basic/MacIOSurfaceTextureHostBasic.h',
     'basic/TextureHostBasic.h',
     'client/CanvasClient.h',
     'client/CompositableClient.h',
     'client/ContentClient.h',
     'client/ImageClient.h',
     'client/TextureClient.h',
     'client/TextureClientPool.h',
+    'client/TextureClientRecycleAllocator.h',
     'client/TiledContentClient.h',
     'composite/AsyncCompositionManager.h',
     'composite/CanvasLayerComposite.h',
     'composite/ColorLayerComposite.h',
     'composite/ContainerLayerComposite.h',
     'composite/ContentHost.h',
     'composite/ImageHost.h',
     'composite/ImageLayerComposite.h',
@@ -263,16 +264,17 @@ UNIFIED_SOURCES += [
     'client/ClientLayerManager.cpp',
     'client/ClientPaintedLayer.cpp',
     'client/ClientTiledPaintedLayer.cpp',
     'client/CompositableClient.cpp',
     'client/ContentClient.cpp',
     'client/ImageClient.cpp',
     'client/TextureClient.cpp',
     'client/TextureClientPool.cpp',
+    'client/TextureClientRecycleAllocator.cpp',
     'client/TiledContentClient.cpp',
     'composite/AsyncCompositionManager.cpp',
     'composite/CanvasLayerComposite.cpp',
     'composite/ColorLayerComposite.cpp',
     'composite/CompositableHost.cpp',
     'composite/ContainerLayerComposite.cpp',
     'composite/ContentHost.cpp',
     'composite/FPSCounter.cpp',