Bug 901224 - Postpone deallocation of shared data to the end of the next transaction. r=sotaro
authorNicolas Silva <nical.bugzilla@gmail.com>
Thu, 12 Sep 2013 16:50:28 +0200
changeset 159753 855f1df4acfba65b66621636256fbd1162ac0a3c
parent 159752 c789a0ddda51c028d715bbd651ecd36695fd5b14
child 159754 f49d97642d4c4ad1a6f83bdd159453cc68e8b525
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssotaro
bugs901224
milestone26.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 901224 - Postpone deallocation of shared data to the end of the next transaction. r=sotaro
gfx/layers/GrallocImages.cpp
gfx/layers/ImageContainer.cpp
gfx/layers/ImageContainer.h
gfx/layers/client/ClientLayerManager.cpp
gfx/layers/client/CompositableClient.cpp
gfx/layers/client/CompositableClient.h
gfx/layers/client/ImageClient.cpp
gfx/layers/client/ImageClient.h
gfx/layers/client/TextureClient.cpp
gfx/layers/client/TextureClient.h
gfx/layers/ipc/CompositableForwarder.h
gfx/layers/ipc/ImageBridgeChild.cpp
gfx/layers/ipc/ImageBridgeChild.h
gfx/layers/ipc/ShadowLayers.cpp
gfx/layers/opengl/GrallocTextureClient.cpp
gfx/layers/opengl/GrallocTextureClient.h
gfx/layers/opengl/TextureClientOGL.h
--- a/gfx/layers/GrallocImages.cpp
+++ b/gfx/layers/GrallocImages.cpp
@@ -48,17 +48,19 @@ GrallocImage::GrallocImage()
     mGraphicBuffer(nullptr),
     mTextureClient(nullptr)
 {
   mFormat = GRALLOC_PLANAR_YCBCR;
 }
 
 GrallocImage::~GrallocImage()
 {
-  if (mGraphicBuffer.get()) {
+  // If we have a texture client, the latter takes over the responsibility to
+  // unlock the GraphicBufferLocked.
+  if (mGraphicBuffer.get() && !mTextureClient) {
     mGraphicBuffer->Unlock();
     if (mBufferAllocated) {
       ImageBridgeChild *ibc = ImageBridgeChild::GetSingleton();
       ibc->DeallocSurfaceDescriptorGralloc(mGraphicBuffer->GetSurfaceDescriptor());
       mBufferAllocated = false;
     }
   }
 }
@@ -289,14 +291,15 @@ GrallocImage::GetTextureClient()
                                          : TEXTURE_DEALLOCATE_HOST;
     if (desc.isRBSwapped()) {
       flags |= TEXTURE_RB_SWAPPED;
     }
     GrallocBufferActor* actor = static_cast<GrallocBufferActor*>(desc.bufferChild());
     mTextureClient = new GrallocTextureClientOGL(actor,
                                                  gfx::ToIntSize(mSize),
                                                  flags);
+    mTextureClient->SetGraphicBufferLocked(mGraphicBuffer);
   }
   return mTextureClient;
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -172,31 +172,35 @@ ImageContainer::SetCurrentImageInternal(
   CurrentImageChanged();
 
   if (mRemoteData) {
     mRemoteDataMutex->Unlock();
   }
 }
 
 void
+ImageContainer::ClearCurrentImage()
+{
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  SetCurrentImageInternal(nullptr);
+}
+
+void
 ImageContainer::SetCurrentImage(Image *aImage)
 {
+  if (IsAsync() && !aImage) {
+    // Let ImageClient to release all TextureClients.
+    ImageBridgeChild::FlushImage(mImageClient, this);
+    return;
+  }
+
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   if (IsAsync()) {
-    if (aImage) {
-      ImageBridgeChild::DispatchImageClientUpdate(mImageClient, this);
-    } else {
-      // here we used to have a SetIdle() call on the image bridge to tell
-      // the compositor that the video element is not going to be seen for
-      // moment and that it can release its shared memory. It was causing
-      // crashes so it has been removed.
-      // This may be reimplemented after 858914 lands.
-    }
+    ImageBridgeChild::DispatchImageClientUpdate(mImageClient, this);
   }
-
   SetCurrentImageInternal(aImage);
 }
 
 void
 ImageContainer::SetCurrentImageInTransaction(Image *aImage)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   NS_ASSERTION(!mImageClient, "Should use async image transfer with ImageBridge.");
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -329,16 +329,24 @@ public:
    *
    * If this ImageContainer has an ImageClient for async video:
    * Schelude a task to send the image to the compositor using the 
    * PImageBridge protcol without using the main thread.
    */
   void SetCurrentImage(Image* aImage);
 
   /**
+   * Clear the current image.
+   * This function is expect to be called only from a CompositableClient
+   * that belongs to ImageBridgeChild. Created to prevent dead lock.
+   * See Bug 901224.
+   */
+  void ClearCurrentImage();
+
+  /**
    * Set an Image as the current image to display. The Image must have
    * been created by this ImageContainer.
    * Must be called on the main thread, within a layers transaction.
    * 
    * This method takes mReentrantMonitor
    * when accessing thread-shared state.
    * aImage can be null. While it's null, nothing will be painted.
    * 
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -332,16 +332,20 @@ ClientLayerManager::ForwardTransaction()
           ->SetDescriptorFromReply(ots.textureId(), ots.image());
         break;
       }
       case EditReply::TReplyTextureRemoved: {
         // XXX - to manage reuse of gralloc buffers, we'll need to add some
         // glue code here to find the TextureClient and invoke a callback to
         // let the camera know that the gralloc buffer is not used anymore on
         // the compositor side and that it can reuse it.
+        const ReplyTextureRemoved& rep = reply.get_ReplyTextureRemoved();
+        CompositableClient* compositable
+          = static_cast<CompositableChild*>(rep.compositableChild())->GetCompositableClient();
+        compositable->OnReplyTextureRemoved(rep.textureId());
         break;
       }
 
       default:
         NS_RUNTIMEABORT("not reached");
       }
     }
   } else if (HasShadowManager()) {
--- a/gfx/layers/client/CompositableClient.cpp
+++ b/gfx/layers/client/CompositableClient.cpp
@@ -201,24 +201,44 @@ CompositableClient::AddTextureClient(Tex
   aClient->SetID(mNextTextureID);
   mForwarder->AddTexture(this, aClient);
 }
 
 void
 CompositableClient::RemoveTextureClient(TextureClient* aClient)
 {
   MOZ_ASSERT(aClient);
-  mTexturesToRemove.AppendElement(aClient->GetID());
+  mTexturesToRemove.AppendElement(TextureIDAndFlags(aClient->GetID(),
+                                                    aClient->GetFlags()));
+  if (!(aClient->GetFlags() & TEXTURE_DEALLOCATE_HOST)) {
+    TextureClientData* data = aClient->DropTextureData();
+    if (data) {
+      mTexturesToRemoveCallbacks[aClient->GetID()] = data;
+    }
+  }
   aClient->ClearID();
   aClient->MarkInvalid();
 }
 
 void
+CompositableClient::OnReplyTextureRemoved(uint64_t aTextureID)
+{
+  std::map<uint64_t,TextureClientData*>::iterator it
+    = mTexturesToRemoveCallbacks.find(aTextureID);
+  if (it != mTexturesToRemoveCallbacks.end()) {
+    it->second->DeallocateSharedData(GetForwarder());
+    delete it->second;
+    mTexturesToRemoveCallbacks.erase(it);
+  }
+}
+
+void
 CompositableClient::OnTransaction()
 {
   for (unsigned i = 0; i < mTexturesToRemove.Length(); ++i) {
-    mForwarder->RemoveTexture(this, mTexturesToRemove[i]);
+    const TextureIDAndFlags& texture = mTexturesToRemove[i];
+    mForwarder->RemoveTexture(this, texture.mID, texture.mFlags);
   }
   mTexturesToRemove.Clear();
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/CompositableClient.h
+++ b/gfx/layers/client/CompositableClient.h
@@ -3,16 +3,17 @@
  * 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_BUFFERCLIENT_H
 #define MOZILLA_GFX_BUFFERCLIENT_H
 
 #include <stdint.h>                     // for uint64_t
 #include <vector>                       // for vector
+#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/CompositorTypes.h"
 #include "mozilla/layers/LayersTypes.h"  // for LayersBackend
 #include "mozilla/layers/PCompositableChild.h"  // for PCompositableChild
 #include "nsTraceRefcnt.h"              // for MOZ_COUNT_CTOR, etc
 
@@ -22,16 +23,17 @@ namespace layers {
 class CompositableClient;
 class DeprecatedTextureClient;
 class TextureClient;
 class BufferTextureClient;
 class ImageBridgeChild;
 class CompositableForwarder;
 class CompositableChild;
 class SurfaceDescriptor;
+class TextureClientData;
 
 /**
  * CompositableClient manages the texture-specific logic for composite layers,
  * independently of the layer. It is the content side of a CompositableClient/
  * CompositableHost pair.
  *
  * CompositableClient's purpose is to send texture data to the compositor side
  * along with any extra information about how the texture is to be composited.
@@ -132,19 +134,28 @@ public:
    */
   virtual void OnTransaction();
 
   /**
    * A hook for the when the Compositable is detached from it's layer.
    */
   virtual void OnDetach() {}
 
+  void OnReplyTextureRemoved(uint64_t aTextureID);
+
 protected:
+  struct TextureIDAndFlags {
+    TextureIDAndFlags(uint64_t aID, TextureFlags aFlags)
+    : mID(aID), mFlags(aFlags) {}
+    uint64_t mID;
+    TextureFlags mFlags;
+  };
   // The textures to destroy in the next transaction;
-  nsTArray<uint64_t> mTexturesToRemove;
+  nsTArray<TextureIDAndFlags> mTexturesToRemove;
+  std::map<uint64_t, TextureClientData*> mTexturesToRemoveCallbacks;
   uint64_t mNextTextureID;
   CompositableChild* mCompositableChild;
   CompositableForwarder* mForwarder;
 };
 
 /**
  * IPDL actor used by CompositableClient to match with its corresponding
  * CompositableHost on the compositor side.
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -92,16 +92,38 @@ ImageClientBuffered::ImageClientBuffered
 {
 }
 
 TextureInfo ImageClientSingle::GetTextureInfo() const
 {
   return TextureInfo(COMPOSITABLE_IMAGE);
 }
 
+void
+ImageClientSingle::FlushImage()
+{
+  if (mFrontBuffer) {
+    RemoveTextureClient(mFrontBuffer);
+    mFrontBuffer = nullptr;
+  }
+}
+
+void
+ImageClientBuffered::FlushImage()
+{
+  if (mFrontBuffer) {
+    RemoveTextureClient(mFrontBuffer);
+    mFrontBuffer = nullptr;
+  }
+  if (mBackBuffer) {
+    RemoveTextureClient(mBackBuffer);
+    mBackBuffer = nullptr;
+  }
+}
+
 bool
 ImageClientSingle::UpdateImage(ImageContainer* aContainer,
                                uint32_t aContentFlags)
 {
   AutoLockImage autoLock(aContainer);
 
   Image *image = autoLock.GetImage();
   if (!image) {
--- a/gfx/layers/client/ImageClient.h
+++ b/gfx/layers/client/ImageClient.h
@@ -57,16 +57,21 @@ public:
    * The picture rect is the area of the texture which makes up the image. That
    * is, the area that should be composited. In texture space.
    */
   virtual void UpdatePictureRect(nsIntRect aPictureRect);
 
   virtual already_AddRefed<Image> CreateImage(const uint32_t *aFormats,
                                               uint32_t aNumFormats) = 0;
 
+  /**
+   * Synchronously remove all the textures used by the image client.
+   */
+  virtual void FlushImage() {}
+
 protected:
   ImageClient(CompositableForwarder* aFwd, CompositableType aType);
 
   CompositableType mType;
   int32_t mLastPaintedImageSerial;
   nsIntRect mPictureRect;
 };
 
@@ -91,16 +96,19 @@ public:
 
   virtual TemporaryRef<BufferTextureClient>
   CreateBufferTextureClient(gfx::SurfaceFormat aFormat) MOZ_OVERRIDE;
 
   virtual TextureInfo GetTextureInfo() const MOZ_OVERRIDE;
 
   virtual already_AddRefed<Image> CreateImage(const uint32_t *aFormats,
                                               uint32_t aNumFormats) MOZ_OVERRIDE;
+
+  virtual void FlushImage() MOZ_OVERRIDE;
+
 protected:
   RefPtr<TextureClient> mFrontBuffer;
   // Some layers may want to enforce some flags to all their textures
   // (like disallowing tiling)
   TextureFlags mTextureFlags;
 };
 
 /**
@@ -112,16 +120,18 @@ public:
   ImageClientBuffered(CompositableForwarder* aFwd,
                       TextureFlags aFlags,
                       CompositableType aType);
 
   virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags);
 
   virtual void OnDetach() MOZ_OVERRIDE;
 
+  virtual void FlushImage() MOZ_OVERRIDE;
+
 protected:
   RefPtr<TextureClient> mBackBuffer;
 };
 
 /**
  * An image client which uses a single texture client, may be single or double
  * buffered. (As opposed to using two texture clients for buffering, as in
  * ContentClientDoubleBuffered, or using multiple clients for YCbCr or tiled
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -30,41 +30,109 @@
 #  include "gfxSharedImageSurface.h"
 #endif
 
 using namespace mozilla::gl;
 
 namespace mozilla {
 namespace layers {
 
+class ShmemTextureClientData : public TextureClientData
+{
+public:
+  ShmemTextureClientData(ipc::Shmem& aShmem)
+  : mShmem(aShmem)
+  {
+    MOZ_COUNT_CTOR(ShmemTextureClientData);
+  }
+
+  ~ShmemTextureClientData()
+  {
+    MOZ_COUNT_CTOR(ShmemTextureClientData);
+  }
+
+  virtual void DeallocateSharedData(ISurfaceAllocator* allocator)
+  {
+    allocator->DeallocShmem(mShmem);
+    mShmem = ipc::Shmem();
+  }
+
+private:
+  ipc::Shmem mShmem;
+};
+
+class MemoryTextureClientData : public TextureClientData
+{
+public:
+  MemoryTextureClientData(uint8_t* aBuffer)
+  : mBuffer(aBuffer)
+  {
+    MOZ_COUNT_CTOR(MemoryTextureClientData);
+  }
+
+  ~MemoryTextureClientData()
+  {
+    MOZ_ASSERT(!mBuffer, "Forgot to deallocate the shared texture data?");
+    MOZ_COUNT_CTOR(MemoryTextureClientData);
+  }
+
+  virtual void DeallocateSharedData(ISurfaceAllocator*)
+  {
+    delete[] mBuffer;
+  }
+
+private:
+  uint8_t* mBuffer;
+};
+
+TextureClientData*
+MemoryTextureClient::DropTextureData()
+{
+  if (!mBuffer) {
+    return nullptr;
+  }
+  TextureClientData* result = new MemoryTextureClientData(mBuffer);
+  MarkInvalid();
+  mBuffer = nullptr;
+  return result;
+}
+
+TextureClientData*
+ShmemTextureClient::DropTextureData()
+{
+  if (!mShmem.IsReadable()) {
+    return nullptr;
+  }
+  TextureClientData* result = new ShmemTextureClientData(mShmem);
+  MarkInvalid();
+  mShmem = ipc::Shmem();
+  return result;
+}
+
 TextureClient::TextureClient(TextureFlags aFlags)
   : mID(0)
   , mFlags(aFlags)
   , mShared(false)
   , mValid(true)
 {}
 
 TextureClient::~TextureClient()
 {}
 
 bool
 TextureClient::ShouldDeallocateInDestructor() const
 {
   if (!IsAllocated()) {
     return false;
   }
-  if (GetFlags() & TEXTURE_DEALLOCATE_CLIENT) {
-    return true;
-  }
 
   // If we're meant to be deallocated by the host,
   // but we haven't been shared yet, then we should
   // deallocate on the client instead.
-  return (GetFlags() & TEXTURE_DEALLOCATE_HOST) &&
-         !IsSharedWithCompositor();
+  return !IsSharedWithCompositor();
 }
 
 bool
 ShmemTextureClient::ToSurfaceDescriptor(SurfaceDescriptor& aDescriptor)
 {
   MOZ_ASSERT(IsValid());
   if (!IsAllocated() || GetFormat() == gfx::FORMAT_UNKNOWN) {
     return false;
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -64,16 +64,38 @@ class TextureClientYCbCr
 {
 public:
   virtual bool UpdateYCbCr(const PlanarYCbCrImage::Data& aData) = 0;
   virtual bool AllocateForYCbCr(gfx::IntSize aYSize,
                                 gfx::IntSize aCbCrSize,
                                 StereoMode aStereoMode) = 0;
 };
 
+/**
+ * Holds the shared data of a TextureClient, to be destroyed later.
+ *
+ * TextureClient's destructor initiates the destruction sequence of the
+ * texture client/host pair. If the shared data is to be deallocated on the
+ * host side, there is nothing to do.
+ * On the other hand, if the client data must be deallocated on the client
+ * side, the CompositableClient will ask the TextureClient to drop its shared
+ * data in the form of a TextureClientData object. The compositable will keep
+ * this object until it has received from the host side the confirmation that
+ * the compositor is not using the texture and that it is completely safe to
+ * deallocate the shared data.
+ *
+ * See:
+ *  - CompositableClient::RemoveTextureClient
+ *  - CompositableClient::OnReplyTextureRemoved
+ */
+class TextureClientData {
+public:
+  virtual void DeallocateSharedData(ISurfaceAllocator* allocator) = 0;
+  virtual ~TextureClientData() {}
+};
 
 /**
  * TextureClient is a thin abstraction over texture data that need to be shared
  * between the content process and the compositor process. It is the
  * content-side half of a TextureClient/TextureHost pair. A corresponding
  * TextureHost lives on the compositor-side.
  *
  * TextureClient's primary purpose is to present texture data in a way that is
@@ -139,16 +161,18 @@ public:
   }
 
   virtual bool IsAllocated() const = 0;
 
   virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aDescriptor) = 0;
 
   virtual gfx::IntSize GetSize() const = 0;
 
+  virtual TextureClientData* DropTextureData() = 0;
+
   TextureFlags GetFlags() const { return mFlags; }
 
   /**
    * After being shared with the compositor side, an immutable texture is never
    * modified, it can only be read. It is safe to not Lock/Unlock immutable
    * textures.
    */
   bool IsImmutable() const { return mFlags & TEXTURE_IMMUTABLE; }
@@ -263,16 +287,18 @@ public:
   virtual bool Allocate(uint32_t aSize) MOZ_OVERRIDE;
 
   virtual uint8_t* GetBuffer() const MOZ_OVERRIDE;
 
   virtual size_t GetBufferSize() const MOZ_OVERRIDE;
 
   virtual bool IsAllocated() const MOZ_OVERRIDE { return mAllocated; }
 
+  virtual TextureClientData* DropTextureData() MOZ_OVERRIDE;
+
   ISurfaceAllocator* GetAllocator() const;
 
   ipc::Shmem& GetShmem() { return mShmem; }
 
 protected:
   ipc::Shmem mShmem;
   ISurfaceAllocator* mAllocator;
   bool mAllocated;
@@ -296,16 +322,18 @@ public:
   virtual bool Allocate(uint32_t aSize) MOZ_OVERRIDE;
 
   virtual uint8_t* GetBuffer() const MOZ_OVERRIDE { return mBuffer; }
 
   virtual size_t GetBufferSize() const MOZ_OVERRIDE { return mBufSize; }
 
   virtual bool IsAllocated() const MOZ_OVERRIDE { return mBuffer != nullptr; }
 
+  virtual TextureClientData* DropTextureData() MOZ_OVERRIDE;
+
 protected:
   uint8_t* mBuffer;
   size_t mBufSize;
 };
 
 struct TextureClientAutoUnlock
 {
   TextureClient* mTexture;
--- a/gfx/layers/ipc/CompositableForwarder.h
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -164,17 +164,17 @@ public:
    * this behaviour can be overriden by the TextureFlags passed here.
    * XXX - This is kind of bad, but for now we have to do this, because of some
    * edge cases caused by the lifetime of the TextureHost being limited by the
    * lifetime of the CompositableHost. We should be able to remove this flags
    * parameter when we remove the lifetime constraint.
    */
   virtual void RemoveTexture(CompositableClient* aCompositable,
                              uint64_t aTextureID,
-                             TextureFlags aFlags = TEXTURE_FLAGS_DEFAULT) = 0;
+                             TextureFlags aFlags) = 0;
 
   /**
    * Tell the CompositableHost on the compositor side what texture to use for
    * the next composition.
    */
   virtual void UseTexture(CompositableClient* aCompositable,
                           TextureClient* aClient) = 0;
 
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -117,19 +117,27 @@ ImageBridgeChild::AddTexture(Compositabl
                              aTexture->GetFlags()));
 }
 
 void
 ImageBridgeChild::RemoveTexture(CompositableClient* aCompositable,
                                 uint64_t aTexture,
                                 TextureFlags aFlags)
 {
-  mTxn->AddNoSwapEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(),
-                                      aTexture,
-                                      aFlags));
+  if (aFlags & TEXTURE_DEALLOCATE_HOST) {
+    // if deallocation happens on the host side, we don't need the transaction
+    // to be synchronous.
+    mTxn->AddNoSwapEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(),
+                                        aTexture,
+                                        aFlags));
+  } else {
+    mTxn->AddEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(),
+                                  aTexture,
+                                  aFlags));
+  }
 }
 
 void
 ImageBridgeChild::UseTexture(CompositableClient* aCompositable,
                              TextureClient* aTexture)
 {
   mTxn->AddNoSwapEdit(OpUseTexture(nullptr, aCompositable->GetIPDLActor(),
                                    aTexture->GetID()));
@@ -388,16 +396,61 @@ void ImageBridgeChild::DispatchImageClie
   sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
     FROM_HERE,
     NewRunnableFunction<
       void (*)(ImageClient*, ImageContainer*),
       ImageClient*,
       nsRefPtr<ImageContainer> >(&UpdateImageClientNow, aClient, aContainer));
 }
 
+static void FlushImageSync(ImageClient* aClient, ImageContainer* aContainer, ReentrantMonitor* aBarrier, bool* aDone)
+{
+  ImageBridgeChild::FlushImageNow(aClient, aContainer);
+
+  ReentrantMonitorAutoEnter autoMon(*aBarrier);
+  *aDone = true;
+  aBarrier->NotifyAll();
+}
+
+//static
+void ImageBridgeChild::FlushImage(ImageClient* aClient, ImageContainer* aContainer)
+{
+  if (InImageBridgeChildThread()) {
+    FlushImageNow(aClient, aContainer);
+    return;
+  }
+
+  ReentrantMonitor barrier("CreateImageClient Lock");
+  ReentrantMonitorAutoEnter autoMon(barrier);
+  bool done = false;
+
+  sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
+    FROM_HERE,
+    NewRunnableFunction(&FlushImageSync, aClient, aContainer, &barrier, &done));
+
+  // should stop the thread until the ImageClient has been created on
+  // the other thread
+  while (!done) {
+    barrier.Wait();
+  }
+}
+
+//static
+void ImageBridgeChild::FlushImageNow(ImageClient* aClient, ImageContainer* aContainer)
+{
+  MOZ_ASSERT(aClient);
+  sImageBridgeChildSingleton->BeginTransaction();
+  if (aContainer) {
+    aContainer->ClearCurrentImage();
+  }
+  aClient->FlushImage();
+  aClient->OnTransaction();
+  sImageBridgeChildSingleton->EndTransaction();
+}
+
 void
 ImageBridgeChild::BeginTransaction()
 {
   MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?");
   mTxn->Begin();
 }
 
 void
@@ -449,16 +502,20 @@ ImageBridgeChild::EndTransaction()
       break;
     }
     case EditReply::TReplyTextureRemoved: {
       // We receive this reply when a Texture is removed and when it is not
       // the responsibility of the compositor side to deallocate memory.
       // This would be, for instance, the place to implement a mechanism to
       // notify the B2G camera that the gralloc buffer is not used by the
       // compositor anymore and that it can be recycled.
+      const ReplyTextureRemoved& rep = reply.get_ReplyTextureRemoved();
+      CompositableClient* compositable
+        = static_cast<CompositableChild*>(rep.compositableChild())->GetCompositableClient();
+      compositable->OnReplyTextureRemoved(rep.textureId());
       break;
     }
     default:
       NS_RUNTIMEABORT("not reached");
     }
   }
 }
 
--- a/gfx/layers/ipc/ImageBridgeChild.h
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -234,16 +234,25 @@ public:
   DeallocSurfaceDescriptorGrallocNow(const SurfaceDescriptor& aBuffer);
 
   TemporaryRef<ImageClient> CreateImageClient(CompositableType aType);
   TemporaryRef<ImageClient> CreateImageClientNow(CompositableType aType);
 
   static void DispatchReleaseImageClient(ImageClient* aClient);
   static void DispatchImageClientUpdate(ImageClient* aClient, ImageContainer* aContainer);
 
+  /**
+   * Flush all Images sent to CompositableHost.
+   */
+  static void FlushImage(ImageClient* aClient, ImageContainer* aContainer);
+
+  /**
+   * Must be called on the ImageBridgeChild's thread.
+   */
+  static void FlushImageNow(ImageClient* aClient, ImageContainer* aContainer);
 
   // CompositableForwarder
 
   virtual void Connect(CompositableClient* aCompositable) MOZ_OVERRIDE;
 
   /**
    * See CompositableForwarder::AddTexture
    */
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -74,17 +74,20 @@ public:
       // we should only compose if it is non-empty. See the caller(s) of
       // RotationChanged.
       mRotationChanged = true;
     }
     mTargetRotation = aRotation;
     mClientBounds = aClientBounds;
     mTargetOrientation = aOrientation;
   }
-
+  void MarkSyncTransaction()
+  {
+    mSwapRequired = true;
+  }
   void AddEdit(const Edit& aEdit)
   {
     NS_ABORT_IF_FALSE(!Finished(), "forgot BeginTransaction?");
     mCset.push_back(aEdit);
   }
   void AddEdit(const CompositableOperation& aEdit)
   {
     AddEdit(Edit(aEdit));
@@ -400,20 +403,22 @@ ShadowLayerForwarder::AddTexture(Composi
                              aTexture->GetFlags()));
 }
 
 void
 ShadowLayerForwarder::RemoveTexture(CompositableClient* aCompositable,
                                     uint64_t aTexture,
                                     TextureFlags aFlags)
 {
-  mTxn->AddEdit(OpRemoveTexture(nullptr,
-                aCompositable->GetIPDLActor(),
-                aTexture,
-                aFlags));
+  mTxn->AddEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(),
+                                aTexture,
+                                aFlags));
+  if (!(aFlags & TEXTURE_DEALLOCATE_HOST)) {
+    mTxn->MarkSyncTransaction();
+  }
 }
 
 void
 ShadowLayerForwarder::UpdatedTexture(CompositableClient* aCompositable,
                                      TextureClient* aTexture,
                                      nsIntRegion* aRegion)
 {
   MaybeRegion region = aRegion ? MaybeRegion(*aRegion)
@@ -421,17 +426,16 @@ ShadowLayerForwarder::UpdatedTexture(Com
   if (aTexture->GetFlags() & TEXTURE_IMMEDIATE_UPLOAD) {
     mTxn->AddPaint(OpUpdateTexture(nullptr, aCompositable->GetIPDLActor(),
                                    aTexture->GetID(),
                                    region));
   } else {
     mTxn->AddNoSwapPaint(OpUpdateTexture(nullptr, aCompositable->GetIPDLActor(),
                                          aTexture->GetID(),
                                          region));
-
   }
 }
 
 void
 ShadowLayerForwarder::UseTexture(CompositableClient* aCompositable,
                                  TextureClient* aTexture)
 {
   mTxn->AddEdit(OpUseTexture(nullptr, aCompositable->GetIPDLActor(),
--- a/gfx/layers/opengl/GrallocTextureClient.cpp
+++ b/gfx/layers/opengl/GrallocTextureClient.cpp
@@ -5,23 +5,93 @@
 
 #ifdef MOZ_WIDGET_GONK
 
 #include "mozilla/layers/GrallocTextureClient.h"
 #include "mozilla/layers/CompositableClient.h"
 #include "mozilla/layers/CompositableForwarder.h"
 #include "mozilla/layers/ISurfaceAllocator.h"
 #include "mozilla/layers/ShadowLayerUtilsGralloc.h"
+#include "GrallocImages.h"
 #include "gfx2DGlue.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace android;
 
+class GraphicBufferLockedTextureClientData : public TextureClientData {
+public:
+  GraphicBufferLockedTextureClientData(GraphicBufferLocked* aBufferLocked)
+    : mBufferLocked(aBufferLocked)
+  {
+    MOZ_COUNT_CTOR(GrallocTextureClientData);
+  }
+
+  ~GraphicBufferLockedTextureClientData()
+  {
+    MOZ_COUNT_DTOR(GrallocTextureClientData);
+    MOZ_ASSERT(!mBufferLocked, "Forgot to unlock the GraphicBufferLocked?");
+  }
+
+  virtual void DeallocateSharedData(ISurfaceAllocator*) MOZ_OVERRIDE
+  {
+    mBufferLocked->Unlock();
+    mBufferLocked = nullptr;
+  }
+
+private:
+  RefPtr<GraphicBufferLocked> mBufferLocked;
+};
+
+class GrallocTextureClientData : public TextureClientData {
+public:
+  GrallocTextureClientData(GrallocBufferActor* aActor)
+    : mGrallocActor(aActor)
+  {
+    MOZ_COUNT_CTOR(GrallocTextureClientData);
+  }
+
+  ~GrallocTextureClientData()
+  {
+    MOZ_COUNT_DTOR(GrallocTextureClientData);
+    MOZ_ASSERT(!mGrallocActor, "Forgot to unlock the GraphicBufferLocked?");
+  }
+
+  virtual void DeallocateSharedData(ISurfaceAllocator* allocator) MOZ_OVERRIDE
+  {
+    // We just need to wrap the actor in a SurfaceDescriptor because that's what
+    // ISurfaceAllocator uses as input, we don't care about the other parameters.
+    SurfaceDescriptor sd = SurfaceDescriptorGralloc(nullptr, mGrallocActor,
+                                                    nsIntSize(0,0), false, false);
+    allocator->DestroySharedSurface(&sd);
+    mGrallocActor = nullptr;
+  }
+
+private:
+  GrallocBufferActor* mGrallocActor;
+};
+
+TextureClientData*
+GrallocTextureClientOGL::DropTextureData()
+{
+  if (mBufferLocked) {
+    TextureClientData* result = new GraphicBufferLockedTextureClientData(mBufferLocked);
+    mBufferLocked = nullptr;
+    mGrallocActor = nullptr;
+    mGraphicBuffer = nullptr;
+    return result;
+  } else {
+    TextureClientData* result = new GrallocTextureClientData(mGrallocActor);
+    mGrallocActor = nullptr;
+    mGraphicBuffer = nullptr;
+    return result;
+  }
+}
+
 GrallocTextureClientOGL::GrallocTextureClientOGL(GrallocBufferActor* aActor,
                                                  gfx::IntSize aSize,
                                                  TextureFlags aFlags)
 : BufferTextureClient(nullptr, gfx::FORMAT_UNKNOWN, aFlags)
 , mGrallocFlags(android::GraphicBuffer::USAGE_SW_READ_OFTEN)
 , mMappedBuffer(nullptr)
 {
   InitWith(aActor, aSize);
@@ -49,16 +119,22 @@ GrallocTextureClientOGL::InitWith(Grallo
   MOZ_ASSERT(aActor);
   MOZ_ASSERT(!IsAllocated());
   MOZ_ASSERT(IsValid());
   mGrallocActor = aActor;
   mGraphicBuffer = aActor->GetGraphicBuffer();
   mSize = aSize;
 }
 
+void
+GrallocTextureClientOGL::SetGraphicBufferLocked(GraphicBufferLocked* aBufferLocked)
+{
+  mBufferLocked = aBufferLocked;
+}
+
 bool
 GrallocTextureClientOGL::ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor)
 {
   MOZ_ASSERT(IsValid());
   if (!IsAllocated()) {
     return false;
   }
 
--- a/gfx/layers/opengl/GrallocTextureClient.h
+++ b/gfx/layers/opengl/GrallocTextureClient.h
@@ -10,16 +10,18 @@
 #include "mozilla/layers/TextureClient.h"
 #include "ISurfaceAllocator.h" // For IsSurfaceDescriptorValid
 #include "mozilla/layers/ShadowLayerUtilsGralloc.h"
 #include <ui/GraphicBuffer.h>
 
 namespace mozilla {
 namespace layers {
 
+class GraphicBufferLocked;
+
 /**
  * A TextureClient implementation based on android::GraphicBuffer (also referred to
  * as "gralloc").
  *
  * Gralloc lets us map texture data in memory (accessible through pointers)
  * and also use it directly as an OpenGL texture without the cost of texture
  * uploads.
  * Gralloc buffers can also be shared accros processes.
@@ -45,16 +47,18 @@ public:
   virtual void Unlock() MOZ_OVERRIDE;
 
   virtual bool ImplementsLocking() const MOZ_OVERRIDE { return true; }
 
   virtual bool IsAllocated() const MOZ_OVERRIDE;
 
   virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) MOZ_OVERRIDE;
 
+  virtual TextureClientData* DropTextureData() MOZ_OVERRIDE;
+
   void InitWith(GrallocBufferActor* aActor, gfx::IntSize aSize);
 
   gfx::IntSize GetSize() const MOZ_OVERRIDE { return mSize; }
 
   android::GraphicBuffer* GetGraphicBuffer()
   {
     return mGraphicBuffer.get();
   }
@@ -83,23 +87,27 @@ public:
                                 StereoMode aStereoMode) MOZ_OVERRIDE;
 
   bool AllocateGralloc(gfx::IntSize aYSize, uint32_t aAndroidFormat, uint32_t aUsage);
 
   virtual bool Allocate(uint32_t aSize) MOZ_OVERRIDE;
 
   virtual size_t GetBufferSize() const MOZ_OVERRIDE;
 
+  void SetGraphicBufferLocked(GraphicBufferLocked* aBufferLocked);
+
 protected:
 
   /**
    * Unfortunately, until bug 879681 is fixed we need to use a GrallocBufferActor.
    */
   GrallocBufferActor* mGrallocActor;
 
+  RefPtr<GraphicBufferLocked> mBufferLocked;
+
   android::sp<android::GraphicBuffer> mGraphicBuffer;
 
   /**
    * Flags that are used when locking the gralloc buffer
    */
   uint32_t mGrallocFlags;
   /**
    * Points to a mapped gralloc buffer between calls to lock and unlock.
--- a/gfx/layers/opengl/TextureClientOGL.h
+++ b/gfx/layers/opengl/TextureClientOGL.h
@@ -36,16 +36,25 @@ public:
 
   void InitWith(gl::SharedTextureHandle aHandle,
                 gfx::IntSize aSize,
                 gl::SharedTextureShareType aShareType,
                 bool aInverted = false);
 
   virtual gfx::IntSize GetSize() const { return mSize; }
 
+  virtual TextureClientData* DropTextureData() MOZ_OVERRIDE
+  {
+    // XXX - right now the code paths using this are managing the shared texture
+    // data, although they should use a TextureClientData for this to ensure that
+    // the destruction sequence is race-free.
+    MarkInvalid();
+    return nullptr;
+  }
+
 protected:
   gl::SharedTextureHandle mHandle;
   gfx::IntSize mSize;
   gl::SharedTextureShareType mShareType;
   bool mInverted;
 };