Bug 1291296 - Make it possible to change a PersistentBufferProvider's Forwarder without crashing. r=ethlin, a=ritu
authorNicolas Silva <nsilva@mozilla.com>
Tue, 09 Aug 2016 14:42:15 +0200
changeset 347721 2bdd8158b05656d15e385246d06e18926cec13b0
parent 347720 5872b231bb7af5bd859a1113529a30a183761650
child 347722 e2be3bbce44a9f118a1cfe17961fad25f0ffe8dd
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersethlin, ritu
bugs1291296
milestone50.0a2
Bug 1291296 - Make it possible to change a PersistentBufferProvider's Forwarder without crashing. r=ethlin, a=ritu
gfx/layers/PersistentBufferProvider.cpp
gfx/layers/PersistentBufferProvider.h
gfx/layers/client/ClientCanvasLayer.cpp
--- a/gfx/layers/PersistentBufferProvider.cpp
+++ b/gfx/layers/PersistentBufferProvider.cpp
@@ -130,16 +130,94 @@ PersistentBufferProviderShared::~Persist
 
   if (IsActivityTracked()) {
     mFwd->GetActiveResourceTracker().RemoveObject(this);
   }
 
   Destroy();
 }
 
+bool
+PersistentBufferProviderShared::SetForwarder(CompositableForwarder* aFwd)
+{
+  MOZ_ASSERT(aFwd);
+  if (!aFwd) {
+    return false;
+  }
+
+  if (mFwd == aFwd) {
+    // The forwarder should not change most of the time.
+    return true;
+  }
+
+  if (IsActivityTracked()) {
+    mFwd->GetActiveResourceTracker().RemoveObject(this);
+  }
+
+  if (mFwd->AsTextureForwarder() != aFwd->AsTextureForwarder() ||
+      mFwd->GetCompositorBackendType() != aFwd->GetCompositorBackendType()) {
+    // We are going to be used with an different and/or incompatible forwarder.
+    // This should be extremely rare. We have to copy the front buffer into a
+    // texture that is compatible with the new forwarder.
+
+    // Grab the current front buffer.
+    RefPtr<TextureClient> prevTexture = GetTexture(mFront);
+
+    // Get rid of everything else
+    Destroy();
+
+    if (prevTexture) {
+      RefPtr<TextureClient> newTexture = TextureClient::CreateForDrawing(
+        aFwd, mFormat, mSize,
+        BackendSelector::Canvas,
+        TextureFlags::DEFAULT,
+        TextureAllocationFlags::ALLOC_DEFAULT
+      );
+
+      MOZ_ASSERT(newTexture);
+      if (!newTexture) {
+        return false;
+      }
+
+      // If we early-return in one of the following branches, we will
+      // leave the buffer provider in an empty state, since we called
+      // Destroy. Not ideal but at least we won't try to use it with a
+      // an incompatible ipc channel.
+
+      if (!newTexture->Lock(OpenMode::OPEN_WRITE)) {
+        return false;
+      }
+
+      if (!prevTexture->Lock(OpenMode::OPEN_READ)) {
+        newTexture->Unlock();
+        return false;
+      }
+
+      bool success = prevTexture->CopyToTextureClient(newTexture, nullptr, nullptr);
+
+      prevTexture->Unlock();
+      newTexture->Unlock();
+
+      if (!success) {
+        return false;
+      }
+
+      if (!mTextures.append(newTexture)) {
+        return false;
+      }
+      mFront = Some<uint32_t>(mTextures.length() - 1);
+      mBack = mFront;
+    }
+  }
+
+  mFwd = aFwd;
+
+  return true;
+}
+
 TextureClient*
 PersistentBufferProviderShared::GetTexture(Maybe<uint32_t> aIndex)
 {
   if (aIndex.isNothing() || !CheckIndex(aIndex.value())) {
     return nullptr;
   }
   return mTextures[aIndex.value()];
 }
--- a/gfx/layers/PersistentBufferProvider.h
+++ b/gfx/layers/PersistentBufferProvider.h
@@ -58,16 +58,18 @@ public:
 
   virtual already_AddRefed<gfx::SourceSurface> BorrowSnapshot() = 0;
 
   virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) = 0;
 
   virtual TextureClient* GetTextureClient() { return nullptr; }
 
   virtual void OnShutdown() {}
+
+  virtual bool SetForwarder(CompositableForwarder* aFwd) { return true; }
 };
 
 
 class PersistentBufferProviderBasic : public PersistentBufferProvider
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PersistentBufferProviderBasic, override)
 
@@ -119,16 +121,18 @@ public:
   virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) override;
 
   virtual TextureClient* GetTextureClient() override;
 
   virtual void NotifyInactive() override;
 
   virtual void OnShutdown() override { Destroy(); }
 
+  virtual bool SetForwarder(CompositableForwarder* aFwd) override;
+
 protected:
   PersistentBufferProviderShared(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                                  CompositableForwarder* aFwd,
                                  RefPtr<TextureClient>& aTexture);
 
   ~PersistentBufferProviderShared();
 
   TextureClient* GetTexture(Maybe<uint32_t> aIndex);
--- a/gfx/layers/client/ClientCanvasLayer.cpp
+++ b/gfx/layers/client/ClientCanvasLayer.cpp
@@ -121,16 +121,20 @@ ClientCanvasLayer::RenderLayer()
 
   if (!IsDirty()) {
     return;
   }
   Painted();
 
   FirePreTransactionCallback();
   if (mBufferProvider && mBufferProvider->GetTextureClient()) {
+    if (!mBufferProvider->SetForwarder(mCanvasClient->GetForwarder())) {
+      gfxCriticalNote << "BufferProvider::SetForwarder failed";
+      return;
+    }
     mCanvasClient->UpdateFromTexture(mBufferProvider->GetTextureClient());
   } else {
     mCanvasClient->Update(gfx::IntSize(mBounds.width, mBounds.height), this);
   }
 
   FireDidTransactionCallback();
 
   ClientManager()->Hold(this);