Bug 1135935 - Part 6: Wait for the compositor to stop using textures before recycling them. r=jrmuizel
authorMatt Woodrow <mwoodrow@mozilla.com>
Thu, 13 Aug 2015 15:18:53 -0400
changeset 257930 fc15644207d8bc16931ed6a2ba49e1e537b65258
parent 257929 9e3380c345331eaa076e1a4b20375288942d0d09
child 257931 657e3bb3d40c0a6f5ce670d611f8cf38169dc8cf
push id29238
push userryanvm@gmail.com
push dateMon, 17 Aug 2015 13:06:57 +0000
treeherdermozilla-central@a6eeb28458fd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1135935
milestone43.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 1135935 - Part 6: Wait for the compositor to stop using textures before recycling them. r=jrmuizel
gfx/layers/client/ImageClient.cpp
gfx/layers/client/TextureClient.cpp
gfx/layers/client/TextureClient.h
gfx/layers/client/TextureClientRecycleAllocator.cpp
gfx/layers/client/TextureClientRecycleAllocator.h
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -76,28 +76,33 @@ ImageClient::RemoveTexture(TextureClient
 {
   RemoveTextureWithWaiter(aTexture);
 }
 
 void
 ImageClient::RemoveTextureWithWaiter(TextureClient* aTexture,
                                      AsyncTransactionWaiter* aAsyncTransactionWaiter)
 {
-#ifdef MOZ_WIDGET_GONK
-  if (aAsyncTransactionWaiter ||
-      GetForwarder()->IsImageBridgeChild()) {
+  if ((aAsyncTransactionWaiter ||
+      GetForwarder()->IsImageBridgeChild())
+#ifndef MOZ_WIDGET_GONK
+      // If the texture client is taking part in recycling then we should make sure
+      // the host has finished with it before dropping the ref and triggering
+      // the recycle callback.
+      && aTexture->GetRecycleAllocator()
+#endif
+     ) {
     RefPtr<AsyncTransactionTracker> request =
       new RemoveTextureFromCompositableTracker(aAsyncTransactionWaiter);
     // Hold TextureClient until the transaction complete to postpone
     // the TextureClient recycle/delete.
     request->SetTextureClient(aTexture);
     GetForwarder()->RemoveTextureFromCompositableAsync(request, this, aTexture);
     return;
   }
-#endif
 
   GetForwarder()->RemoveTextureFromCompositable(this, aTexture);
 }
 
 ImageClientSingle::ImageClientSingle(CompositableForwarder* aFwd,
                                      TextureFlags aFlags,
                                      CompositableType aType)
   : ImageClient(aFwd, aFlags, aType)
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -244,16 +244,34 @@ TextureClient::WaitForCompositorRecycle(
 void
 TextureClient::SetAddedToCompositableClient()
 {
   if (!mAddedToCompositableClient) {
     mAddedToCompositableClient = true;
   }
 }
 
+/* static */ void
+TextureClient::TextureClientRecycleCallback(TextureClient* aClient, void* aClosure)
+{
+  MOZ_ASSERT(aClient->GetRecycleAllocator());
+  aClient->GetRecycleAllocator()->RecycleTextureClient(aClient);
+}
+
+void
+TextureClient::SetRecycleAllocator(TextureClientRecycleAllocator* aAllocator)
+{
+  mRecycleAllocator = aAllocator;
+  if (aAllocator) {
+    SetRecycleCallback(TextureClientRecycleCallback, nullptr);
+  } else {
+    ClearRecycleCallback();
+  }
+}
+
 bool
 TextureClient::InitIPDLActor(CompositableForwarder* aForwarder)
 {
   MOZ_ASSERT(aForwarder && aForwarder->GetMessageLoop() == mAllocator->GetMessageLoop());
   if (mActor && mActor->GetForwarder() == aForwarder) {
     return true;
   }
   MOZ_ASSERT(!mActor, "Cannot use a texture on several IPC channels.");
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -45,16 +45,17 @@ class CompositableForwarder;
 class ISurfaceAllocator;
 class CompositableClient;
 struct PlanarYCbCrData;
 class Image;
 class PTextureChild;
 class TextureChild;
 class BufferTextureClient;
 class TextureClient;
+class TextureClientRecycleAllocator;
 #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
 class TextureClientPool;
 #endif
 class KeepAlive;
 
 /**
  * TextureClient is the abstraction that allows us to share data between the
  * content and the compositor side.
@@ -491,17 +492,22 @@ public:
      mShared = true;
    }
 
   ISurfaceAllocator* GetAllocator()
   {
     return mAllocator;
   }
 
+   TextureClientRecycleAllocator* GetRecycleAllocator() { return mRecycleAllocator; }
+   void SetRecycleAllocator(TextureClientRecycleAllocator* aAllocator);
+
 private:
+  static void TextureClientRecycleCallback(TextureClient* aClient, void* aClosure);
+
   /**
    * Called once, just before the destructor.
    *
    * Here goes the shut-down code that uses virtual methods.
    * Must only be called by Release().
    */
   B2G_ACL_EXPORT void Finalize();
 
@@ -521,16 +527,17 @@ protected:
    * Calling ToSurfaceDescriptor again after it has already returned true,
    * or never constructing a TextureHost with aDescriptor may result in a memory
    * leak (see TextureClientD3D9 for example).
    */
   virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aDescriptor) = 0;
 
   RefPtr<TextureChild> mActor;
   RefPtr<ISurfaceAllocator> mAllocator;
+  RefPtr<TextureClientRecycleAllocator> mRecycleAllocator;
   TextureFlags mFlags;
   FenceHandle mReleaseFenceHandle;
   FenceHandle mAcquireFenceHandle;
   gl::GfxTextureWasteTracker mWasteTracker;
   bool mShared;
   bool mValid;
   bool mAddedToCompositableClient;
 
--- a/gfx/layers/client/TextureClientRecycleAllocator.cpp
+++ b/gfx/layers/client/TextureClientRecycleAllocator.cpp
@@ -98,51 +98,50 @@ TextureClientRecycleAllocator::CreateOrR
   }
 
   {
     MutexAutoLock lock(mLock);
     MOZ_ASSERT(mInUseClients.find(textureHolder->GetTextureClient()) == mInUseClients.end());
     // Register TextureClient
     mInUseClients[textureHolder->GetTextureClient()] = textureHolder;
   }
-  textureHolder->GetTextureClient()->SetRecycleCallback(TextureClientRecycleAllocator::RecycleCallback, this);
   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::RecycleCallbackImp(TextureClient* aClient)
+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;
-  aClient->ClearRecycleCallback();
   {
     MutexAutoLock lock(mLock);
     if (mInUseClients.find(aClient) != mInUseClients.end()) {
       textureHolder = mInUseClients[aClient]; // Keep reference count of TextureClientHolder within lock.
       if (mPooledClients.size() < mMaxPooledSize) {
         mPooledClients.push(textureHolder);
       }
       mInUseClients.erase(aClient);
     }
   }
 }
 
-/* static */ void
-TextureClientRecycleAllocator::RecycleCallback(TextureClient* aClient, void* aClosure)
-{
-  MOZ_ASSERT(aClient && !aClient->IsDead());
-  TextureClientRecycleAllocator* recycleAllocator = static_cast<TextureClientRecycleAllocator*>(aClosure);
-  recycleAllocator->RecycleCallbackImp(aClient);
-}
-
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/TextureClientRecycleAllocator.h
+++ b/gfx/layers/client/TextureClientRecycleAllocator.h
@@ -55,19 +55,18 @@ protected:
            gfx::IntSize aSize,
            BackendSelector aSelector,
            TextureFlags aTextureFlags,
            TextureAllocationFlags aAllocFlags);
 
   RefPtr<ISurfaceAllocator> mSurfaceAllocator;
 
 private:
-  void RecycleCallbackImp(TextureClient* aClient);
-
-  static void RecycleCallback(TextureClient* aClient, void* aClosure);
+  friend class TextureClient;
+  void RecycleTextureClient(TextureClient* aClient);
 
   static const uint32_t kMaxPooledSized = 2;
   uint32_t mMaxPooledSize;
 
   std::map<TextureClient*, RefPtr<TextureClientHolder> > mInUseClients;
 
   // On b2g gonk, std::queue might be a better choice.
   // On ICS, fence wait happens implicitly before drawing.