Bug 1291296 - Make it possible to change a PersistentBufferProvider's Forwarder without crashing. r=ethlin
authorNicolas Silva <nsilva@mozilla.com>
Tue, 09 Aug 2016 14:42:15 +0200
changeset 308821 d59fd1ff4bec47d4f49e9ce4edc210b9548aab0c
parent 308820 adc1960091eb58583bd65d45fe65e38cbdcd78d8
child 308822 210edbef152a89dbeebb031db918e47b0c99b210
push id31218
push usercbook@mozilla.com
push dateWed, 10 Aug 2016 14:05:51 +0000
treeherderautoland@d44be6871ba0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersethlin
bugs1291296
milestone51.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 1291296 - Make it possible to change a PersistentBufferProvider's Forwarder without crashing. r=ethlin
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);