Bug 897452 - Part 2 - PTexture deallocation logic - r=sotaro,bjacob
authorNicolas Silva <nical@mozilla.com>
Wed, 11 Dec 2013 20:44:45 -0500
changeset 176025 990c7417b97bebccc35e713c0a7b7eafc2781b3b
parent 176024 969cfcbb1c9c2197c3aceae342ea8899c4fb8def
child 176026 8a3091a7302ee391c2f7e5e47da4c4347b664c5e
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssotaro, bjacob
bugs897452
milestone29.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 897452 - Part 2 - PTexture deallocation logic - r=sotaro,bjacob
gfx/layers/ImageContainer.cpp
gfx/layers/client/CanvasClient.cpp
gfx/layers/client/ClientLayerManager.cpp
gfx/layers/client/CompositableClient.cpp
gfx/layers/client/CompositableClient.h
gfx/layers/client/ImageClient.cpp
gfx/layers/client/TextureClient.cpp
gfx/layers/client/TextureClient.h
gfx/layers/composite/CompositableHost.cpp
gfx/layers/composite/CompositableHost.h
gfx/layers/composite/ImageHost.cpp
gfx/layers/composite/ImageHost.h
gfx/layers/composite/TextureHost.cpp
gfx/layers/composite/TextureHost.h
gfx/layers/ipc/CompositableForwarder.h
gfx/layers/ipc/CompositableTransactionParent.cpp
gfx/layers/ipc/ImageBridgeChild.cpp
gfx/layers/ipc/ImageBridgeChild.h
gfx/layers/ipc/LayersMessages.ipdlh
gfx/layers/ipc/PTexture.ipdl
gfx/layers/ipc/ShadowLayers.cpp
gfx/layers/ipc/ShadowLayers.h
gfx/layers/opengl/GrallocTextureHost.cpp
gfx/layers/opengl/GrallocTextureHost.h
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -212,16 +212,17 @@ ImageContainer::SetCurrentImage(Image *a
  void
 ImageContainer::ClearAllImages()
 {
   if (IsAsync()) {
     // Let ImageClient release all TextureClients.
     ImageBridgeChild::FlushAllImages(mImageClient, this, false);
     return;
   }
+
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   SetCurrentImageInternal(nullptr);
 }
 
 void
 ImageContainer::ClearAllImagesExceptFront()
 {
   if (IsAsync()) {
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -53,17 +53,17 @@ CanvasClient::CreateCanvasClient(CanvasC
   return new CanvasClient2D(aForwarder, aFlags);
 }
 
 void
 CanvasClient2D::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
 {
   if (mBuffer &&
       (mBuffer->IsImmutable() || mBuffer->GetSize() != aSize)) {
-    RemoveTextureClient(mBuffer);
+    mBuffer->ForceRemove();
     mBuffer = nullptr;
   }
 
   bool bufferCreated = false;
   if (!mBuffer) {
     bool isOpaque = (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE);
     gfxContentType contentType = isOpaque
                                                 ? GFX_CONTENT_COLOR
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -363,21 +363,16 @@ ClientLayerManager::ForwardTransaction()
         CompositableChild* compositableChild =
           static_cast<CompositableChild*>(ots.compositableChild());
         MOZ_ASSERT(compositableChild);
 
         compositableChild->GetCompositableClient()
           ->SetDescriptorFromReply(ots.textureId(), ots.image());
         break;
       }
-      case EditReply::TReplyTextureRemoved: {
-        // XXX - The logic to remove textures is implemented in the next patch
-        // of the same bug (897452). They will land together.
-        break;
-      }
 
       default:
         NS_RUNTIMEABORT("not reached");
       }
     }
 
     if (sent) {
       mNeedsComposite = false;
--- a/gfx/layers/client/CompositableClient.cpp
+++ b/gfx/layers/client/CompositableClient.cpp
@@ -249,24 +249,16 @@ CompositableClient::NextTextureID()
 bool
 CompositableClient::AddTextureClient(TextureClient* aClient)
 {
   aClient->SetID(NextTextureID());
   return aClient->InitIPDLActor(mForwarder);
 }
 
 void
-CompositableClient::RemoveTextureClient(TextureClient* aClient)
-{
-  MOZ_ASSERT(aClient);
-  mForwarder->RemoveTexture(aClient);
-  aClient->MarkInvalid();
-}
-
-void
 CompositableClient::OnTransaction()
 {
 }
 
 
 void
 CompositableChild::ActorDestroy(ActorDestroyReason why)
 {
--- a/gfx/layers/client/CompositableClient.h
+++ b/gfx/layers/client/CompositableClient.h
@@ -128,22 +128,16 @@ public:
   uint64_t GetAsyncID() const;
 
   /**
    * Tells the Compositor to create a TextureHost for this TextureClient.
    */
   virtual bool AddTextureClient(TextureClient* aClient);
 
   /**
-   * Tells the Compositor to delete the TextureHost corresponding to this
-   * TextureClient.
-   */
-  virtual void RemoveTextureClient(TextureClient* aClient);
-
-  /**
    * A hook for the Compositable to execute whatever it held off for next transaction.
    */
   virtual void OnTransaction();
 
   /**
    * A hook for the when the Compositable is detached from it's layer.
    */
   virtual void OnDetach() {}
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -96,30 +96,30 @@ TextureInfo ImageClientSingle::GetTextur
 {
   return TextureInfo(COMPOSITABLE_IMAGE);
 }
 
 void
 ImageClientSingle::FlushAllImages(bool aExceptFront)
 {
   if (!aExceptFront && mFrontBuffer) {
-    RemoveTextureClient(mFrontBuffer);
+    mFrontBuffer->ForceRemove();
     mFrontBuffer = nullptr;
   }
 }
 
 void
 ImageClientBuffered::FlushAllImages(bool aExceptFront)
 {
   if (!aExceptFront && mFrontBuffer) {
-    RemoveTextureClient(mFrontBuffer);
+    mFrontBuffer->ForceRemove();
     mFrontBuffer = nullptr;
   }
   if (mBackBuffer) {
-    RemoveTextureClient(mBackBuffer);
+    mBackBuffer->ForceRemove();
     mBackBuffer = nullptr;
   }
 }
 
 bool
 ImageClientSingle::UpdateImage(ImageContainer* aContainer,
                                uint32_t aContentFlags)
 {
@@ -140,34 +140,34 @@ ImageClientSingle::UpdateImage(ImageCont
 
     if (texture->IsSharedWithCompositor()) {
       // XXX - temporary fix for bug 911941
       // This will be changed with bug 912907
       return false;
     }
 
     if (mFrontBuffer) {
-      RemoveTextureClient(mFrontBuffer);
+      mFrontBuffer->ForceRemove();
     }
     mFrontBuffer = texture;
     if (!AddTextureClient(texture)) {
       mFrontBuffer = nullptr;
       return false;
     }
     GetForwarder()->UpdatedTexture(this, texture, nullptr);
     GetForwarder()->UseTexture(this, texture);
   } else if (image->GetFormat() == PLANAR_YCBCR) {
     PlanarYCbCrImage* ycbcr = static_cast<PlanarYCbCrImage*>(image);
     const PlanarYCbCrData* data = ycbcr->GetData();
     if (!data) {
       return false;
     }
 
     if (mFrontBuffer && mFrontBuffer->IsImmutable()) {
-      RemoveTextureClient(mFrontBuffer);
+      mFrontBuffer->ForceRemove();
       mFrontBuffer = nullptr;
     }
 
     bool bufferCreated = false;
     if (!mFrontBuffer) {
       mFrontBuffer = CreateBufferTextureClient(gfx::FORMAT_YUV, TEXTURE_FLAGS_DEFAULT);
       gfx::IntSize ySize(data->mYSize.width, data->mYSize.height);
       gfx::IntSize cbCrSize(data->mCbCrSize.width, data->mCbCrSize.height);
@@ -200,17 +200,17 @@ ImageClientSingle::UpdateImage(ImageCont
     }
 
   } else if (image->GetFormat() == SHARED_TEXTURE) {
     SharedTextureImage* sharedImage = static_cast<SharedTextureImage*>(image);
     const SharedTextureImage::Data *data = sharedImage->GetData();
     gfx::IntSize size = gfx::IntSize(image->GetSize().width, image->GetSize().height);
 
     if (mFrontBuffer) {
-      RemoveTextureClient(mFrontBuffer);
+      mFrontBuffer->ForceRemove();
       mFrontBuffer = nullptr;
     }
 
     RefPtr<SharedTextureClientOGL> buffer = new SharedTextureClientOGL(mTextureFlags);
     buffer->InitWith(data->mHandle, size, data->mShareType, data->mInverted);
     mFrontBuffer = buffer;
     if (!AddTextureClient(mFrontBuffer)) {
       mFrontBuffer = nullptr;
@@ -221,17 +221,17 @@ ImageClientSingle::UpdateImage(ImageCont
   } else {
     nsRefPtr<gfxASurface> surface = image->GetAsSurface();
     MOZ_ASSERT(surface);
 
     gfx::IntSize size = gfx::IntSize(image->GetSize().width, image->GetSize().height);
 
     if (mFrontBuffer &&
         (mFrontBuffer->IsImmutable() || mFrontBuffer->GetSize() != size)) {
-      RemoveTextureClient(mFrontBuffer);
+      mFrontBuffer->ForceRemove();
       mFrontBuffer = nullptr;
     }
 
     bool bufferCreated = false;
     if (!mFrontBuffer) {
       gfxImageFormat format
         = gfxPlatform::GetPlatform()->OptimalFormatForContent(surface->GetContentType());
       mFrontBuffer = CreateBufferTextureClient(gfx::ImageFormatToSurfaceFormat(format),
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -34,22 +34,82 @@
 #endif
 
 using namespace mozilla::gl;
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace layers {
 
+/**
+ * TextureChild is the content-side incarnation of the PTexture IPDL actor.
+ *
+ * TextureChild is used to synchronize a texture client and its corresponding
+ * TextureHost if needed (a TextureClient that is not shared with the compositor
+ * does not have a TextureChild)
+ *
+ * During the deallocation phase, a TextureChild may hold its recently destroyed
+ * TextureClient's data until the compositor side confirmed that it is safe to
+ * deallocte or recycle the it.
+ */
 class TextureChild : public PTextureChild
 {
 public:
+  TextureChild()
+  : mForwarder(nullptr)
+  , mTextureData(nullptr)
+  {
+    MOZ_COUNT_CTOR(TextureChild);
+  }
+
+  ~TextureChild()
+  {
+    MOZ_COUNT_DTOR(TextureChild);
+  }
+
+  bool Recv__delete__() MOZ_OVERRIDE;
+
+  /**
+   * Only used during the deallocation phase iff we need synchronization between
+   * the client and host side for deallocation (that is, when the data is going
+   * to be deallocated or recycled on the client side).
+   */
+  void SetTextureData(TextureClientData* aData)
+  {
+    mTextureData = aData;
+  }
+
+  void DeleteTextureData();
+
+  CompositableForwarder* GetForwarder() { return mForwarder; }
+
+  ISurfaceAllocator* GetAllocator() { return mForwarder; }
+
+  CompositableForwarder* mForwarder;
+  TextureClientData* mTextureData;
 
 };
 
+void
+TextureChild::DeleteTextureData()
+{
+  if (mTextureData) {
+    mTextureData->DeallocateSharedData(GetAllocator());
+    delete mTextureData;
+    mTextureData = nullptr;
+  }
+}
+
+bool
+TextureChild::Recv__delete__()
+{
+  DeleteTextureData();
+  return true;
+}
+
 // static
 PTextureChild*
 TextureClient::CreateIPDLActor()
 {
   return new TextureChild();
 }
 
 // static
@@ -65,16 +125,17 @@ TextureClient::InitIPDLActor(Compositabl
 {
   MOZ_ASSERT(!mActor);
   SurfaceDescriptor desc;
   if (!ToSurfaceDescriptor(desc)) {
     return false;
   }
 
   mActor = static_cast<TextureChild*>(aForwarder->CreateEmptyTextureChild());
+  mActor->mForwarder = aForwarder;
   mShared = true;
   return mActor->SendInit(desc, GetFlags());
 }
 
 PTextureChild*
 TextureClient::GetIPDLActor()
 {
   return mActor;
@@ -117,16 +178,17 @@ public:
   {
     MOZ_ASSERT(!mBuffer, "Forgot to deallocate the shared texture data?");
     MOZ_COUNT_CTOR(MemoryTextureClientData);
   }
 
   virtual void DeallocateSharedData(ISurfaceAllocator*)
   {
     delete[] mBuffer;
+    mBuffer = nullptr;
   }
 
 private:
   uint8_t* mBuffer;
 };
 
 TextureClientData*
 MemoryTextureClient::DropTextureData()
@@ -157,17 +219,43 @@ TextureClient::TextureClient(TextureFlag
   , mActor(nullptr)
   , mID(0)
   , mFlags(aFlags)
   , mShared(false)
   , mValid(true)
 {}
 
 TextureClient::~TextureClient()
-{}
+{
+  // All the destruction code that may lead to virtual method calls must
+  // be in Finalize() which is called just before the destructor.
+}
+
+void TextureClient::ForceRemove()
+{
+  if (mValid && mActor) {
+    if (GetFlags() & TEXTURE_DEALLOCATE_CLIENT) {
+      mActor->SetTextureData(DropTextureData());
+      mActor->SendRemoveTextureSync();
+      mActor->DeleteTextureData();
+    } else {
+      mActor->SendRemoveTexture();
+    }
+  }
+  MarkInvalid();
+}
+
+void
+TextureClient::Finalize()
+{
+  if (mActor) {
+    // this will call ForceRemove in the right thread, using a sync proxy if needed
+    mActor->GetForwarder()->RemoveTexture(this);
+  }
+}
 
 bool
 TextureClient::ShouldDeallocateInDestructor() const
 {
   if (!IsAllocated()) {
     return false;
   }
 
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -108,24 +108,23 @@ public:
 /**
  * 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.
+ * data in the form of a TextureClientData object. This data will be kept alive
+ * until the host side confirms that it is not using the data anymore and that
+ * it is completely safe to deallocate the shared data.
  *
  * See:
- *  - CompositableClient::RemoveTextureClient
- *  - CompositableClient::OnReplyTextureRemoved
+ *  - The PTexture IPDL protocol
+ *  - CompositableChild in TextureClient.cpp
  */
 class TextureClientData {
 public:
   virtual void DeallocateSharedData(ISurfaceAllocator* allocator) = 0;
   virtual ~TextureClientData() {}
 };
 
 /**
@@ -292,29 +291,31 @@ public:
   /**
    * Return a pointer to the IPDLActor.
    *
    * This is to be used with IPDL messages only. Do not store the returned
    * pointer.
    */
   PTextureChild* GetIPDLActor();
 
+  /**
+   * TODO[nical] doc!
+   */
+  void ForceRemove();
+
 private:
   Atomic<int> mRefCount;
 
   /**
    * Called once, just before the destructor.
    *
    * Here goes the shut-down code that uses virtual methods.
    * Must only be called by Release().
    */
-  void Finalize()
-  {
-    // XXX Bug 897452 - Coming soon
-  }
+  void Finalize();
 
 protected:
   void AddFlags(TextureFlags  aFlags)
   {
     MOZ_ASSERT(!IsSharedWithCompositor());
     mFlags |= aFlags;
   }
 
--- a/gfx/layers/composite/CompositableHost.cpp
+++ b/gfx/layers/composite/CompositableHost.cpp
@@ -47,18 +47,18 @@ CompositableHost::~CompositableHost()
 }
 
 void
 CompositableHost::UseTextureHost(TextureHost* aTexture)
 {
   if (!aTexture) {
     return;
   }
+  aTexture->SetCompositor(GetCompositor());
   aTexture->SetCompositableBackendSpecificData(GetCompositableBackendSpecificData());
-  aTexture->SetCompositor(GetCompositor());
 }
 
 
 void
 CompositableHost::AddTextureHost(TextureHost* aTexture)
 {
   MOZ_ASSERT(aTexture);
   MOZ_ASSERT(GetTextureHost(aTexture->GetID()) == nullptr,
--- a/gfx/layers/composite/CompositableHost.h
+++ b/gfx/layers/composite/CompositableHost.h
@@ -107,17 +107,17 @@ public:
   {
     mBackendData = aBackendData;
   }
 
   /**
    * Our IPDL actor is being destroyed, get rid of any shmem resources now and
    * don't worry about compositing anymore.
    */
-  virtual void OnActorDestroy();
+  virtual void OnActorDestroy() {};
 
   // If base class overrides, it should still call the parent implementation
   virtual void SetCompositor(Compositor* aCompositor);
 
   // composite the contents of this buffer host to the compositor's surface
   virtual void Composite(EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::Matrix4x4& aTransform,
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -37,25 +37,16 @@ ImageHost::~ImageHost() {}
 
 void
 ImageHost::UseTextureHost(TextureHost* aTexture)
 {
   CompositableHost::UseTextureHost(aTexture);
   mFrontBuffer = aTexture;
 }
 
-void
-ImageHost::RemoveTextureHost(TextureHost* aTexture)
-{
-  CompositableHost::RemoveTextureHost(aTexture);
-  if (mFrontBuffer && mFrontBuffer->GetID() == aTexture->GetID()) {
-    mFrontBuffer = nullptr;
-  }
-}
-
 TextureHost*
 ImageHost::GetAsTextureHost()
 {
   return mFrontBuffer;
 }
 
 void
 ImageHost::Composite(EffectChain& aEffectChain,
--- a/gfx/layers/composite/ImageHost.h
+++ b/gfx/layers/composite/ImageHost.h
@@ -50,35 +50,26 @@ public:
                          const gfx::Matrix4x4& aTransform,
                          const gfx::Filter& aFilter,
                          const gfx::Rect& aClipRect,
                          const nsIntRegion* aVisibleRegion = nullptr,
                          TiledLayerProperties* aLayerProperties = nullptr) MOZ_OVERRIDE;
 
   virtual void UseTextureHost(TextureHost* aTexture) MOZ_OVERRIDE;
 
-  virtual void RemoveTextureHost(TextureHost* aTexture) MOZ_OVERRIDE;
-
   virtual TextureHost* GetAsTextureHost() MOZ_OVERRIDE;
 
   virtual void SetPictureRect(const nsIntRect& aPictureRect) MOZ_OVERRIDE
   {
     mPictureRect = aPictureRect;
     mHasPictureRect = true;
   }
 
   virtual LayerRenderState GetRenderState() MOZ_OVERRIDE;
 
-  virtual void OnActorDestroy() MOZ_OVERRIDE
-  {
-    if (mFrontBuffer) {
-      mFrontBuffer->OnActorDestroy();
-    }
-  }
-
   virtual void PrintInfo(nsACString& aTo, const char* aPrefix);
 
 #ifdef MOZ_DUMP_PAINTING
   virtual void Dump(FILE* aFile=NULL,
                     const char* aPrefix="",
                     bool aDumpHtml=false) MOZ_OVERRIDE;
 
   virtual TemporaryRef<gfx::DataSourceSurface> GetAsSurface() MOZ_OVERRIDE;
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -34,16 +34,22 @@ class TextureParent : public PTexturePar
 public:
   TextureParent(ISurfaceAllocator* aAllocator);
 
   ~TextureParent();
 
   bool RecvInit(const SurfaceDescriptor& aSharedData,
                 const TextureFlags& aFlags) MOZ_OVERRIDE;
 
+  virtual bool RecvRemoveTexture() MOZ_OVERRIDE;
+
+  virtual bool RecvRemoveTextureSync() MOZ_OVERRIDE;
+
+  virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
+
   TextureHost* GetTextureHost() { return mTextureHost; }
 
   ISurfaceAllocator* mAllocator;
   RefPtr<TextureHost> mTextureHost;
 };
 
 // static
 PTextureParent*
@@ -572,16 +578,26 @@ ShmemTextureHost::~ShmemTextureHost()
 
 void
 ShmemTextureHost::DeallocateSharedData()
 {
   if (mShmem) {
     MOZ_ASSERT(mDeallocator,
                "Shared memory would leak without a ISurfaceAllocator");
     mDeallocator->DeallocShmem(*mShmem);
+    delete mShmem;
+    mShmem = nullptr;
+  }
+}
+
+void
+ShmemTextureHost::ForgetSharedData()
+{
+  if (mShmem) {
+    delete mShmem;
     mShmem = nullptr;
   }
 }
 
 void
 ShmemTextureHost::OnActorDestroy()
 {
   delete mShmem;
@@ -616,16 +632,22 @@ MemoryTextureHost::DeallocateSharedData(
 {
   if (mBuffer) {
     GfxMemoryImageReporter::WillFree(mBuffer);
   }
   delete[] mBuffer;
   mBuffer = nullptr;
 }
 
+void
+MemoryTextureHost::ForgetSharedData()
+{
+  mBuffer = nullptr;
+}
+
 uint8_t* MemoryTextureHost::GetBuffer()
 {
   return mBuffer;
 }
 
 TextureParent::TextureParent(ISurfaceAllocator* aAllocator)
 : mAllocator(aAllocator)
 {
@@ -644,10 +666,51 @@ TextureParent::RecvInit(const SurfaceDes
 {
   mTextureHost = TextureHost::Create(0, // XXX legacy texture id, see subsequent patch
                                      aSharedData,
                                      mAllocator,
                                      aFlags);
   return !!mTextureHost;
 }
 
+bool
+TextureParent::RecvRemoveTexture()
+{
+  return PTextureParent::Send__delete__(this);
+}
+
+bool
+TextureParent::RecvRemoveTextureSync()
+{
+  // we don't need to send a reply in the synchronous case since the child side
+  // has the guarantee that this message has been handled synchronously.
+  return PTextureParent::Send__delete__(this);
+}
+
+void
+TextureParent::ActorDestroy(ActorDestroyReason why)
+{
+  switch (why) {
+  case AncestorDeletion:
+    NS_WARNING("PTexture deleted after ancestor");
+    // fall-through to deletion path
+  case Deletion:
+    if (mTextureHost && mTextureHost->GetFlags() & !TEXTURE_DEALLOCATE_CLIENT) {
+      mTextureHost->DeallocateSharedData();
+    }
+    break;
+
+  case NormalShutdown:
+  case AbnormalShutdown:
+    if (mTextureHost) {
+      mTextureHost->OnActorDestroy();
+    }
+    break;
+
+  case FailedConstructor:
+    NS_RUNTIMEABORT("FailedConstructor isn't possible in PTexture");
+  }
+  mTextureHost->ForgetSharedData();
+  mTextureHost = nullptr;
+}
+
 } // namespace
 } // namespace
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -328,16 +328,24 @@ public:
 
   /**
    * Should be overridden in order to deallocate the data that is shared with
    * the content side, such as shared memory.
    */
   virtual void DeallocateSharedData() {}
 
   /**
+   * Should be overridden in order to force the TextureHost to drop all references
+   * to it's shared data.
+   *
+   * This is important to ensure the correctness of the deallocation protocol.
+   */
+  virtual void ForgetSharedData() {}
+
+  /**
    * An ID to differentiate TextureHosts of a given CompositableHost.
    *
    * A TextureHost and its corresponding TextureClient always have the same ID.
    * TextureHosts of a given CompositableHost always have different IDs.
    * TextureHosts of different CompositableHosts, may have the same ID.
    * Zero is always an invalid ID.
    */
   uint64_t GetID() const { return mID; }
@@ -497,16 +505,18 @@ public:
                    gfx::SurfaceFormat aFormat,
                    ISurfaceAllocator* aDeallocator,
                    TextureFlags aFlags);
 
   ~ShmemTextureHost();
 
   virtual void DeallocateSharedData() MOZ_OVERRIDE;
 
+  virtual void ForgetSharedData() MOZ_OVERRIDE;
+
   virtual uint8_t* GetBuffer() MOZ_OVERRIDE;
 
   virtual const char *Name() MOZ_OVERRIDE { return "ShmemTextureHost"; }
 
   virtual void OnActorDestroy() MOZ_OVERRIDE;
 
 protected:
   mozilla::ipc::Shmem* mShmem;
@@ -526,16 +536,18 @@ public:
                     uint8_t* aBuffer,
                     gfx::SurfaceFormat aFormat,
                     TextureFlags aFlags);
 
   ~MemoryTextureHost();
 
   virtual void DeallocateSharedData() MOZ_OVERRIDE;
 
+  virtual void ForgetSharedData() MOZ_OVERRIDE;
+
   virtual uint8_t* GetBuffer() MOZ_OVERRIDE;
 
   virtual const char *Name() MOZ_OVERRIDE { return "MemoryTextureHost"; }
 
 protected:
   uint8_t* mBuffer;
 };
 
--- a/gfx/layers/ipc/CompositableForwarder.h
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -158,21 +158,17 @@ public:
    * resize, e.g.
    */
   virtual void DestroyedThebesBuffer(const SurfaceDescriptor& aBackBufferToDestroy) = 0;
 
   /**
    * Tell the compositor side to delete the TextureHost corresponding to the
    * TextureClient passed in parameter.
    */
-  virtual void RemoveTexture(TextureClient* aTexture)
-  {
-    // XXX - this part is incomplete. The removing textures is implemented
-    // in a subsequent patch of the same bug (897452).
-  }
+  virtual void RemoveTexture(TextureClient* aTexture) = 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/CompositableTransactionParent.cpp
+++ b/gfx/layers/ipc/CompositableTransactionParent.cpp
@@ -228,54 +228,27 @@ CompositableParentManager::ReceiveCompos
       MOZ_ASSERT(tex.get());
       compositable->UseTextureHost(tex);
 
       if (IsAsync()) {
         ScheduleComposition(op);
       }
       break;
     }
-    case CompositableOperation::TOpRemoveTexture: {
-      const OpRemoveTexture& op = aEdit.get_OpRemoveTexture();
-
-      RefPtr<TextureHost> texture = TextureHost::AsTextureHost(op.textureParent());
-      MOZ_ASSERT(texture);
-
-      TextureFlags flags = texture->GetFlags();
-
-      if (!(flags & TEXTURE_DEALLOCATE_CLIENT) &&
-          !(flags & TEXTURE_DEALLOCATE_DEFERRED)) {
-        texture->DeallocateSharedData();
-      }
-
-      // if it is not the host that deallocates the shared data, then we need
-      // to notfy the client side to tell when it is safe to deallocate or
-      // reuse it.
-      if (flags & TEXTURE_DEALLOCATE_CLIENT) {
-        replyv.push_back(ReplyTextureRemoved(op.callbackID()));
-      }
-
-      TextureHost::SendDeleteIPDLActor(op.textureParent());
-
-      break;
-    }
     case CompositableOperation::TOpUpdateTexture: {
       const OpUpdateTexture& op = aEdit.get_OpUpdateTexture();
       CompositableHost* compositable = AsCompositable(op);
       MOZ_ASSERT(compositable);
       RefPtr<TextureHost> texture = TextureHost::AsTextureHost(op.textureParent());
       MOZ_ASSERT(texture);
 
       texture->Updated(op.region().type() == MaybeRegion::TnsIntRegion
                        ? &op.region().get_nsIntRegion()
                        : nullptr); // no region means invalidate the entire surface
 
-
-      compositable->UseTextureHost(texture);
-
       break;
     }
 
     default: {
       MOZ_ASSERT(false, "bad type");
     }
   }
 
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -484,20 +484,16 @@ ImageBridgeChild::EndTransaction()
           static_cast<CompositableChild*>(ots.compositableChild());
 
       MOZ_ASSERT(compositableChild);
 
       compositableChild->GetCompositableClient()
         ->SetDescriptorFromReply(ots.textureId(), ots.image());
       break;
     }
-    case EditReply::TReplyTextureRemoved: {
-      // XXX - implemented in another patch of the same bug (897452).
-      break;
-    }
     default:
       NS_RUNTIMEABORT("not reached");
     }
   }
 }
 
 
 PImageBridgeChild*
@@ -911,10 +907,41 @@ ImageBridgeChild::DeallocPTextureChild(P
 }
 
 PTextureChild*
 ImageBridgeChild::CreateEmptyTextureChild()
 {
   return SendPTextureConstructor();
 }
 
+static void RemoveTextureSync(TextureClient* aTexture, ReentrantMonitor* aBarrier, bool* aDone)
+{
+  aTexture->ForceRemove();
+
+  ReentrantMonitorAutoEnter autoMon(*aBarrier);
+  *aDone = true;
+  aBarrier->NotifyAll();
+}
+
+void ImageBridgeChild::RemoveTexture(TextureClient* aTexture)
+{
+  if (InImageBridgeChildThread()) {
+    aTexture->ForceRemove();
+    return;
+  }
+
+  ReentrantMonitor barrier("RemoveTexture Lock");
+  ReentrantMonitorAutoEnter autoMon(barrier);
+  bool done = false;
+
+  sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
+    FROM_HERE,
+    NewRunnableFunction(&RemoveTextureSync, aTexture, &barrier, &done));
+
+  // should stop the thread until the ImageClient has been created on
+  // the other thread
+  while (!done) {
+    barrier.Wait();
+  }
+}
+
 } // layers
 } // mozilla
--- a/gfx/layers/ipc/ImageBridgeChild.h
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -268,16 +268,18 @@ public:
                               nsIntRegion* aRegion) MOZ_OVERRIDE;
 
   /**
    * See CompositableForwarder::UseTexture
    */
   virtual void UseTexture(CompositableClient* aCompositable,
                           TextureClient* aClient) MOZ_OVERRIDE;
 
+  virtual void RemoveTexture(TextureClient* aTexture) MOZ_OVERRIDE;
+
   virtual void PaintedTiledLayerBuffer(CompositableClient* aCompositable,
                                        const SurfaceDescriptorTiles& aTileLayerDescriptor) MOZ_OVERRIDE
   {
     NS_RUNTIMEABORT("should not be called");
   }
 
   /**
    * Communicate to the compositor that the texture identified by aCompositable
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -297,25 +297,16 @@ struct OpPaintTextureIncremental {
 };
 
 struct OpUpdatePictureRect {
   PCompositable compositable;
   nsIntRect picture;
 };
 
 /**
- * Tells the compositor-side to remove the corresponding TextureHost and
- * deallocate its data.
- */
-struct OpRemoveTexture {
-  PTexture texture;
-  uint64_t callbackID;
-};
-
-/**
  * Tells the compositor-side which texture to use (for example, as front buffer
  * if there is several textures for double buffering)
  */
 struct OpUseTexture {
   PCompositable compositable;
   PTexture texture;
 };
 
@@ -338,17 +329,16 @@ union CompositableOperation {
   OpDestroyThebesBuffer;
 
   OpPaintTexture;
   OpPaintTextureRegion;
   OpPaintTextureIncremental;
 
   OpPaintTiledLayerBuffer;
 
-  OpRemoveTexture;
   OpUpdateTexture;
   OpUseTexture;
 };
 
 // A unit of a changeset; a set of these comprise a changeset
 union Edit {
   OpCreateThebesLayer;
   OpCreateContainerLayer;
@@ -382,23 +372,17 @@ struct OpContentBufferSwap {
 };
 
 struct OpTextureSwap {
   PCompositable compositable;
   uint32_t textureId;
   SurfaceDescriptor image;
 };
 
-struct ReplyTextureRemoved {
-  uint64_t callbackID;
-};
-
 // Unit of a "changeset reply".  This is a weird abstraction, probably
 // only to be used for buffer swapping.
 union EditReply {
   OpContentBufferSwap;
   OpTextureSwap;
-
-  ReplyTextureRemoved;
 };
 
 } // namespace
 } // namespace
--- a/gfx/layers/ipc/PTexture.ipdl
+++ b/gfx/layers/ipc/PTexture.ipdl
@@ -25,12 +25,22 @@ sync protocol PTexture {
 child:
     async __delete__();
 
 parent:
     /**
      * Set the shared data and create the TextureHost on the parent side.
      */
     async Init(SurfaceDescriptor aSharedData, uint32_t aTextureFlags);
+
+    /**
+     * Asynchronously tell the Compositor side to remove the texture.
+     */
+    async RemoveTexture();
+
+    /**
+     * Synchronously tell the compositor side to remove the texture.
+     */
+    sync RemoveTextureSync();
 };
 
 } // layers
 } // mozilla
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -428,16 +428,22 @@ ShadowLayerForwarder::UpdatedTexture(Com
 void
 ShadowLayerForwarder::UseTexture(CompositableClient* aCompositable,
                                  TextureClient* aTexture)
 {
   mTxn->AddEdit(OpUseTexture(nullptr, aCompositable->GetIPDLActor(),
                              nullptr, aTexture->GetIPDLActor()));
 }
 
+void
+ShadowLayerForwarder::RemoveTexture(TextureClient* aTexture)
+{
+  aTexture->ForceRemove();
+}
+
 bool
 ShadowLayerForwarder::EndTransaction(InfallibleTArray<EditReply>* aReplies, bool* aSent)
 {
   *aSent = false;
 
   PROFILER_LABEL("ShadowLayerForwarder", "EndTranscation");
   RenderTraceScope rendertrace("Foward Transaction", "000091");
   NS_ABORT_IF_FALSE(HasShadowManager(), "no manager to forward to");
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -274,16 +274,18 @@ public:
   /**
    * Communicate to the compositor that the texture identified by aLayer
    * and aIdentifier has been updated to aImage.
    */
   virtual void UpdateTexture(CompositableClient* aCompositable,
                              TextureIdentifier aTextureId,
                              SurfaceDescriptor* aDescriptor) MOZ_OVERRIDE;
 
+  virtual void RemoveTexture(TextureClient* aTexture) MOZ_OVERRIDE;
+
   /**
    * Same as above, but performs an asynchronous layer transaction
    */
   virtual void UpdateTextureNoSwap(CompositableClient* aCompositable,
                                    TextureIdentifier aTextureId,
                                    SurfaceDescriptor* aDescriptor) MOZ_OVERRIDE;
 
   /**
--- a/gfx/layers/opengl/GrallocTextureHost.cpp
+++ b/gfx/layers/opengl/GrallocTextureHost.cpp
@@ -90,16 +90,17 @@ TextureTargetForAndroidPixelFormat(andro
 
 GrallocTextureSourceOGL::GrallocTextureSourceOGL(CompositorOGL* aCompositor,
                                                  android::GraphicBuffer* aGraphicBuffer,
                                                  gfx::SurfaceFormat aFormat)
   : mCompositor(aCompositor)
   , mGraphicBuffer(aGraphicBuffer)
   , mEGLImage(0)
   , mFormat(aFormat)
+  , mNeedsReset(true)
 {
   MOZ_ASSERT(mGraphicBuffer.get());
 }
 
 GrallocTextureSourceOGL::~GrallocTextureSourceOGL()
 {
   DeallocateDeviceData();
   mCompositor = nullptr;
@@ -164,16 +165,24 @@ GrallocTextureSourceOGL::GetFormat() con
     return gfx::FORMAT_R8G8B8A8;
   }
   return mFormat;
 }
 
 void
 GrallocTextureSourceOGL::SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData)
 {
+  if (mCompositableBackendData != aBackendData) {
+    mNeedsReset = true;
+  }
+
+  if (!mNeedsReset) {
+    return;
+  }
+
   mCompositableBackendData = aBackendData;
 
   if (!mCompositor) {
     return;
   }
 
   // delete old EGLImage
   DeallocateDeviceData();
@@ -182,16 +191,17 @@ GrallocTextureSourceOGL::SetCompositable
   GLuint tex = GetGLTexture();
   GLuint textureTarget = GetTextureTarget();
 
   gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
   gl()->fBindTexture(textureTarget, tex);
   // create new EGLImage
   mEGLImage = gl()->CreateEGLImageForNativeBuffer(mGraphicBuffer->getNativeBuffer());
   gl()->fEGLImageTargetTexture2D(textureTarget, mEGLImage);
+  mNeedsReset = false;
 }
 
 gfx::IntSize
 GrallocTextureSourceOGL::GetSize() const
 {
   if (!IsValid()) {
     NS_WARNING("Trying to access the size of an invalid GrallocTextureSourceOGL");
     return gfx::IntSize(0, 0);
@@ -269,16 +279,24 @@ GrallocTextureHostOGL::DeallocateSharedD
 {
   if (mTextureSource) {
     mTextureSource->ForgetBuffer();
   }
   PGrallocBufferParent::Send__delete__(mGrallocActor);
 }
 
 void
+GrallocTextureHostOGL::ForgetSharedData()
+{
+  if (mTextureSource) {
+    mTextureSource->ForgetBuffer();
+  }
+}
+
+void
 GrallocTextureHostOGL::DeallocateDeviceData()
 {
   mTextureSource->DeallocateDeviceData();
 }
 
 LayerRenderState
 GrallocTextureHostOGL::GetRenderState()
 {
--- a/gfx/layers/opengl/GrallocTextureHost.h
+++ b/gfx/layers/opengl/GrallocTextureHost.h
@@ -64,16 +64,17 @@ public:
 
   GLuint GetGLTexture();
 
 protected:
   CompositorOGL* mCompositor;
   android::sp<android::GraphicBuffer> mGraphicBuffer;
   EGLImage mEGLImage;
   gfx::SurfaceFormat mFormat;
+  bool mNeedsReset;
 };
 
 class GrallocTextureHostOGL : public TextureHost
 {
   friend class GrallocBufferActor;
 public:
   GrallocTextureHostOGL(uint64_t aID,
                         TextureFlags aFlags,
@@ -86,16 +87,18 @@ public:
   virtual bool Lock() MOZ_OVERRIDE;
 
   virtual void Unlock() MOZ_OVERRIDE;
 
   virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
 
   virtual void DeallocateSharedData() MOZ_OVERRIDE;
 
+  virtual void ForgetSharedData() MOZ_OVERRIDE;
+
   virtual void DeallocateDeviceData() MOZ_OVERRIDE;
 
   virtual gfx::SurfaceFormat GetFormat() const;
 
   virtual gfx::IntSize GetSize() const MOZ_OVERRIDE { return mSize; }
 
   virtual LayerRenderState GetRenderState() MOZ_OVERRIDE;