Bug 1289380 - Simplify PersistentBufferProviderShared. r=edwin
authorNicolas Silva <nsilva@mozilla.com>
Wed, 27 Jul 2016 16:50:20 +0200
changeset 346924 fc5b4f286716bf47199435415f945695c1c20d0b
parent 346923 950a962b75eae61cb9f0c5638b6b362427f7ebe6
child 346925 0e77ceaa87d957ffee81426d0789b7d39b173ca5
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)
reviewersedwin
bugs1289380
milestone50.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 1289380 - Simplify PersistentBufferProviderShared. r=edwin
dom/canvas/CanvasRenderingContext2D.cpp
gfx/layers/PersistentBufferProvider.cpp
gfx/layers/PersistentBufferProvider.h
gfx/layers/client/CanvasClient.cpp
gfx/layers/client/CanvasClient.h
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -900,17 +900,17 @@ public:
   static void PreTransactionCallback(void* aData)
   {
     CanvasRenderingContext2DUserData* self =
       static_cast<CanvasRenderingContext2DUserData*>(aData);
     CanvasRenderingContext2D* context = self->mContext;
     if (!context || !context->mTarget)
       return;
 
-    context->ReturnTarget();
+    context->OnStableState();
   }
 
   static void DidTransactionCallback(void* aData)
   {
     CanvasRenderingContext2DUserData* self =
       static_cast<CanvasRenderingContext2DUserData*>(aData);
     if (self->mContext) {
       self->mContext->MarkContextClean();
@@ -1499,16 +1499,20 @@ CanvasRenderingContext2D::ScheduleStable
   nsContentUtils::RunInStableState(
     NewRunnableMethod(this, &CanvasRenderingContext2D::OnStableState)
   );
 }
 
 void
 CanvasRenderingContext2D::OnStableState()
 {
+  if (!mHasPendingStableStateCallback) {
+    return;
+  }
+
   ReturnTarget();
 
   mHasPendingStableStateCallback = false;
 }
 
 CanvasRenderingContext2D::RenderingMode
 CanvasRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect,
                                        RenderingMode aRenderingMode)
@@ -1524,26 +1528,26 @@ CanvasRenderingContext2D::EnsureTarget(c
   MOZ_ASSERT(mRenderingMode != RenderingMode::DefaultBackendMode);
 
   RenderingMode mode = (aRenderingMode == RenderingMode::DefaultBackendMode) ? mRenderingMode : aRenderingMode;
 
   if (mTarget && mode == mRenderingMode) {
     return mRenderingMode;
   }
 
+  ScheduleStableStateCallback();
+
   if (mBufferProvider && mode == mRenderingMode) {
     gfx::Rect rect(0, 0, mWidth, mHeight);
     if (aCoveredRect && CurrentState().transform.TransformBounds(*aCoveredRect).Contains(rect)) {
       mTarget = mBufferProvider->BorrowDrawTarget(IntRect());
     } else {
       mTarget = mBufferProvider->BorrowDrawTarget(IntRect(0, 0, mWidth, mHeight));
     }
 
-    ScheduleStableStateCallback();
-
     if (mTarget) {
       // Restore clip and transform.
       for (uint32_t i = 0; i < mStyleStack.Length(); i++) {
         mTarget->SetTransform(mStyleStack[i].transform);
         for (uint32_t c = 0; c < mStyleStack[i].clipsPushed.Length(); c++) {
           mTarget->PushClip(mStyleStack[i].clipsPushed[c]);
         }
       }
@@ -1726,17 +1730,17 @@ CanvasRenderingContext2D::ClearTarget()
       }
     }
   }
 }
 
 void
 CanvasRenderingContext2D::ReturnTarget()
 {
-  if (mTarget && mBufferProvider) {
+  if (mTarget && mBufferProvider && mTarget != sErrorTarget) {
     CurrentState().transform = mTarget->GetTransform();
     for (uint32_t i = 0; i < mStyleStack.Length(); i++) {
       for (uint32_t c = 0; c < mStyleStack[i].clipsPushed.Length(); c++) {
         mTarget->PopClip();
       }
     }
     mBufferProvider->ReturnDrawTarget(mTarget.forget());
   }
@@ -5688,17 +5692,17 @@ CanvasRenderingContext2D::GetBufferProvi
     return mBufferProvider;
   }
 
   if (mTarget) {
     mBufferProvider = new PersistentBufferProviderBasic(mTarget);
     return mBufferProvider;
   }
 
-  if (aManager) {
+  if (aManager && !mIsSkiaGL) {
     mBufferProvider = aManager->CreatePersistentBufferProvider(gfx::IntSize(mWidth, mHeight),
                                                                GetSurfaceFormat());
   }
 
   return mBufferProvider;
 }
 
 already_AddRefed<Layer>
--- a/gfx/layers/PersistentBufferProvider.cpp
+++ b/gfx/layers/PersistentBufferProvider.cpp
@@ -98,202 +98,261 @@ PersistentBufferProviderShared::Create(g
     TextureFlags::DEFAULT,
     TextureAllocationFlags::ALLOC_DEFAULT
   );
 
   if (!texture) {
     return nullptr;
   }
 
-  texture->EnableReadLock();
-
   RefPtr<PersistentBufferProviderShared> provider =
     new PersistentBufferProviderShared(aSize, aFormat, aFwd, texture);
   return provider.forget();
 }
 
 PersistentBufferProviderShared::PersistentBufferProviderShared(gfx::IntSize aSize,
                                                                gfx::SurfaceFormat aFormat,
                                                                CompositableForwarder* aFwd,
                                                                RefPtr<TextureClient>& aTexture)
 
 : mSize(aSize)
 , mFormat(aFormat)
 , mFwd(aFwd)
-, mBack(aTexture.forget())
+, mFront(Nothing())
 {
+  if (mTextures.append(aTexture)) {
+    mBack = Some<uint32_t>(0);
+  }
   MOZ_COUNT_CTOR(PersistentBufferProviderShared);
 }
 
 PersistentBufferProviderShared::~PersistentBufferProviderShared()
 {
   MOZ_COUNT_DTOR(PersistentBufferProviderShared);
 
   if (IsActivityTracked()) {
     mFwd->GetActiveResourceTracker().RemoveObject(this);
   }
 
   Destroy();
 }
 
+TextureClient*
+PersistentBufferProviderShared::GetTexture(Maybe<uint32_t> aIndex)
+{
+  if (aIndex.isNothing() || !CheckIndex(aIndex.value())) {
+    return nullptr;
+  }
+  return mTextures[aIndex.value()];
+}
+
 already_AddRefed<gfx::DrawTarget>
 PersistentBufferProviderShared::BorrowDrawTarget(const gfx::IntRect& aPersistedRect)
 {
   if (!mFwd->IPCOpen()) {
     return nullptr;
   }
 
   MOZ_ASSERT(!mSnapshot);
 
   if (IsActivityTracked()) {
     mFwd->GetActiveResourceTracker().MarkUsed(this);
   } else {
     mFwd->GetActiveResourceTracker().AddObject(this);
   }
 
-  if (!mDrawTarget) {
-    bool changedBackBuffer = false;
-    if (!mBack || mBack->IsReadLocked()) {
-      if (mBuffer && !mBuffer->IsReadLocked()) {
-        mBack.swap(mBuffer);
-      } else {
-        mBack = TextureClient::CreateForDrawing(
-          mFwd, mFormat, mSize,
-          BackendSelector::Canvas,
-          TextureFlags::DEFAULT,
-          TextureAllocationFlags::ALLOC_DEFAULT
-        );
-        if (mBack) {
-          mBack->EnableReadLock();
-        }
-      }
-      changedBackBuffer = true;
-    } else {
-      // Fast path, our front buffer is already writable because the texture upload
-      // has completed on the compositor side.
-      if (mBack->HasIntermediateBuffer()) {
-        // No need to keep an extra buffer around
-        mBuffer = nullptr;
+  if (mDrawTarget) {
+    RefPtr<gfx::DrawTarget> dt(mDrawTarget);
+    return dt.forget();
+  }
+
+  mFront = Nothing();
+
+  auto previousBackBuffer = mBack;
+
+  TextureClient* tex = GetTexture(mBack);
+
+  // First try to reuse the current back buffer. If we can do that it means
+  // we can skip copying its content to the new back buffer.
+  if (tex && tex->IsReadLocked()) {
+    // The back buffer is currently used by the compositor, we can't draw
+    // into it.
+    tex = nullptr;
+  }
+
+  if (!tex) {
+    // Try to grab an already allocated texture if any is available.
+    for (uint32_t i = 0; i < mTextures.length(); ++i) {
+      if (!mTextures[i]->IsReadLocked()) {
+        mBack = Some(i);
+        tex = mTextures[i];
+        break;
       }
     }
+  }
 
-    if (!mBack || !mBack->Lock(OpenMode::OPEN_READ_WRITE)) {
+  if (!tex) {
+    // We have to allocate a new texture.
+    if (mTextures.length() >= 4) {
+      // We should never need to buffer that many textures, something's wrong.
+      MOZ_ASSERT(false);
       return nullptr;
     }
 
-    if (changedBackBuffer && !aPersistedRect.IsEmpty()
-        && mFront && mFront->Lock(OpenMode::OPEN_READ)) {
+    RefPtr<TextureClient> newTexture = TextureClient::CreateForDrawing(
+      mFwd, mFormat, mSize,
+      BackendSelector::Canvas,
+      TextureFlags::DEFAULT,
+      TextureAllocationFlags::ALLOC_DEFAULT
+    );
 
-      DebugOnly<bool> success = mFront->CopyToTextureClient(mBack, &aPersistedRect, nullptr);
+    MOZ_ASSERT(newTexture);
+    if (newTexture) {
+      if (mTextures.append(newTexture)) {
+        tex = newTexture;
+        mBack = Some<uint32_t>(mTextures.length() - 1);
+      }
+    }
+  }
+
+  if (!tex || !tex->Lock(OpenMode::OPEN_READ_WRITE)) {
+    return nullptr;
+  }
+
+  if (mBack != previousBackBuffer && !aPersistedRect.IsEmpty()) {
+    TextureClient* previous = GetTexture(previousBackBuffer);
+    if (previous && previous->Lock(OpenMode::OPEN_READ)) {
+      DebugOnly<bool> success = previous->CopyToTextureClient(tex, &aPersistedRect, nullptr);
       MOZ_ASSERT(success);
 
-      mFront->Unlock();
-    }
-
-    mDrawTarget = mBack->BorrowDrawTarget();
-    if (!mDrawTarget) {
-      return nullptr;
+      previous->Unlock();
     }
   }
 
+  mDrawTarget = tex->BorrowDrawTarget();
+
   RefPtr<gfx::DrawTarget> dt(mDrawTarget);
   return dt.forget();
 }
 
 bool
 PersistentBufferProviderShared::ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT)
 {
   RefPtr<gfx::DrawTarget> dt(aDT);
   MOZ_ASSERT(mDrawTarget == dt);
+  // Can't change the current front buffer while its snapshot is borrowed!
   MOZ_ASSERT(!mSnapshot);
 
   mDrawTarget = nullptr;
   dt = nullptr;
 
-  mBack->Unlock();
+  TextureClient* back = GetTexture(mBack);
+  MOZ_ASSERT(back);
 
-  if (!mBuffer && mFront && !mFront->IsLocked()) {
-    mBuffer.swap(mFront);
+  if (back) {
+    back->Unlock();
+    mFront = mBack;
   }
 
-  mFront = mBack;
+  return !!back;
+}
 
-  return true;
+TextureClient*
+PersistentBufferProviderShared::GetTextureClient()
+{
+  // Can't access the front buffer while drawing.
+  MOZ_ASSERT(!mDrawTarget);
+  TextureClient* texture = GetTexture(mFront);
+  if (texture) {
+    texture->EnableReadLock();
+  } else {
+    gfxCriticalNote << "PersistentBufferProviderShared: front buffer unavailable";
+  }
+  return texture;
 }
 
 already_AddRefed<gfx::SourceSurface>
 PersistentBufferProviderShared::BorrowSnapshot()
 {
-  // TODO[nical] currently we can't snapshot while drawing, looks like it does
-  // the job but I am not sure whether we want to be able to do that.
   MOZ_ASSERT(!mDrawTarget);
 
-  if (!mFront || mFront->IsLocked()) {
+  auto front = GetTexture(mFront);
+  if (!front || front->IsLocked()) {
     MOZ_ASSERT(false);
     return nullptr;
   }
 
-  if (!mFront->Lock(OpenMode::OPEN_READ)) {
+  if (!front->Lock(OpenMode::OPEN_READ)) {
     return nullptr;
   }
 
-  mDrawTarget = mFront->BorrowDrawTarget();
+  RefPtr<DrawTarget> dt = front->BorrowDrawTarget();
 
-  if (!mDrawTarget) {
-    mFront->Unlock();
+  if (!dt) {
+    front->Unlock();
+    return nullptr;
   }
 
-  mSnapshot = mDrawTarget->Snapshot();
+  mSnapshot = dt->Snapshot();
 
   RefPtr<SourceSurface> snapshot = mSnapshot;
   return snapshot.forget();
 }
 
 void
 PersistentBufferProviderShared::ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot)
 {
   RefPtr<SourceSurface> snapshot = aSnapshot;
   MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
 
   mSnapshot = nullptr;
   snapshot = nullptr;
 
-  mDrawTarget = nullptr;
-
-  mFront->Unlock();
+  auto front = GetTexture(mFront);
+  if (front) {
+    front->Unlock();
+  }
 }
 
 void
 PersistentBufferProviderShared::NotifyInactive()
 {
-  if (mBuffer && mBuffer->IsLocked()) {
-    // mBuffer should never be locked
-    MOZ_ASSERT(false);
-    mBuffer->Unlock();
+  RefPtr<TextureClient> front = GetTexture(mFront);
+  RefPtr<TextureClient> back = GetTexture(mBack);
+
+  // Clear all textures (except the front and back ones that we just kept).
+  mTextures.clear();
+
+  if (back) {
+    if (mTextures.append(back)) {
+      mBack = Some<uint32_t>(0);
+    }
+    if (front == back) {
+      mFront = mBack;
+    }
   }
-  mBuffer = nullptr;
+
+  if (front && front != back) {
+    if (mTextures.append(front)) {
+      mFront = Some<uint32_t>(mTextures.length() - 1);
+    }
+  }
 }
 
 void
 PersistentBufferProviderShared::Destroy()
 {
   mSnapshot = nullptr;
   mDrawTarget = nullptr;
 
-  if (mFront && mFront->IsLocked()) {
-    mFront->Unlock();
-  }
-
-  if (mBack && mBack->IsLocked()) {
-    mBack->Unlock();
+  for (uint32_t i = 0; i < mTextures.length(); ++i) {
+    TextureClient* texture = mTextures[i];
+    if (texture && texture->IsLocked()) {
+      MOZ_ASSERT(false);
+      texture->Unlock();
+    }
   }
 
-  if (mBuffer && mBuffer->IsLocked()) {
-    mBuffer->Unlock();
-  }
-
-  mFront = nullptr;
-  mBack = nullptr;
-  mBuffer = nullptr;
+  mTextures.clear();
 }
 
 } // namespace layers
 } // namespace mozilla
\ No newline at end of file
--- a/gfx/layers/PersistentBufferProvider.h
+++ b/gfx/layers/PersistentBufferProvider.h
@@ -6,16 +6,17 @@
 #ifndef MOZILLA_GFX_PersistentBUFFERPROVIDER_H
 #define MOZILLA_GFX_PersistentBUFFERPROVIDER_H
 
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/RefPtr.h"             // for RefPtr, already_AddRefed, etc
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/layers/CompositableForwarder.h"
 #include "mozilla/gfx/Types.h"
+#include "mozilla/Vector.h"
 
 namespace mozilla {
 
 namespace gfx {
   class SourceSurface;
   class DrawTarget;
 }
 
@@ -112,42 +113,43 @@ public:
   virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget(const gfx::IntRect& aPersistedRect) override;
 
   virtual bool ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT) override;
 
   virtual already_AddRefed<gfx::SourceSurface> BorrowSnapshot() override;
 
   virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) override;
 
-  TextureClient* GetTextureClient() override {
-    return mFront;
-  }
+  virtual TextureClient* GetTextureClient() override;
 
   virtual void NotifyInactive() override;
 
   virtual void OnShutdown() override { Destroy(); }
 
 protected:
   PersistentBufferProviderShared(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                                  CompositableForwarder* aFwd,
                                  RefPtr<TextureClient>& aTexture);
 
   ~PersistentBufferProviderShared();
 
+  TextureClient* GetTexture(Maybe<uint32_t> aIndex);
+  bool CheckIndex(uint32_t aIndex) { return aIndex < mTextures.length(); }
+
   void Destroy();
 
   gfx::IntSize mSize;
   gfx::SurfaceFormat mFormat;
   RefPtr<CompositableForwarder> mFwd;
-  // The texture presented to the compositor.
-  RefPtr<TextureClient> mFront;
-  // The texture that the canvas uses.
-  RefPtr<TextureClient> mBack;
-  // An extra texture we keep around temporarily to avoid allocating.
-  RefPtr<TextureClient> mBuffer;
+  Vector<RefPtr<TextureClient>, 4> mTextures;
+  // Offset of the texture in mTextures that the canvas uses.
+  Maybe<uint32_t> mBack;
+  // Offset of the texture in mTextures that is presented to the compositor.
+  Maybe<uint32_t> mFront;
+
   RefPtr<gfx::DrawTarget> mDrawTarget;
   RefPtr<gfx::SourceSurface > mSnapshot;
 };
 
 struct AutoReturnSnapshot
 {
   PersistentBufferProvider* mBufferProvider;
   RefPtr<gfx::SourceSurface>* mSnapshot;
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -72,32 +72,36 @@ CanvasClient2D::UpdateFromTexture(Textur
   MOZ_ASSERT(aTexture);
 
   if (!aTexture->IsSharedWithCompositor()) {
     if (!AddTextureClient(aTexture)) {
       return;
     }
   }
 
-  mBackBuffer = aTexture;
+  mBackBuffer = nullptr;
+  mFrontBuffer = nullptr;
+  mBufferProviderTexture = aTexture;
 
   AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
   CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
-  t->mTextureClient = mBackBuffer;
+  t->mTextureClient = aTexture;
   t->mPictureRect = nsIntRect(nsIntPoint(0, 0), aTexture->GetSize());
   t->mFrameID = mFrameID;
   t->mInputFrameID = VRManagerChild::Get()->GetInputFrameID();
 
   GetForwarder()->UseTextures(this, textures);
   aTexture->SyncWithObject(GetForwarder()->GetSyncObject());
 }
 
 void
 CanvasClient2D::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
 {
+  mBufferProviderTexture = nullptr;
+
   AutoRemoveTexture autoRemove(this);
   if (mBackBuffer &&
       (mBackBuffer->IsImmutable() || mBackBuffer->GetSize() != aSize)) {
     autoRemove.mTexture = mBackBuffer;
     mBackBuffer = nullptr;
   }
 
   bool bufferCreated = false;
--- a/gfx/layers/client/CanvasClient.h
+++ b/gfx/layers/client/CanvasClient.h
@@ -123,16 +123,22 @@ private:
   already_AddRefed<TextureClient>
     CreateTextureClientForCanvas(gfx::SurfaceFormat aFormat,
                                  gfx::IntSize aSize,
                                  TextureFlags aFlags,
                                  ClientCanvasLayer* aLayer);
 
   RefPtr<TextureClient> mBackBuffer;
   RefPtr<TextureClient> mFrontBuffer;
+  // We store this texture separately to make sure it is not written into
+  // in Update() if for some silly reason we end up alternating between
+  // UpdateFromTexture and Update.
+  // This code is begging for a cleanup. The situation described above should
+  // not be made possible.
+  RefPtr<TextureClient> mBufferProviderTexture;
 };
 
 // Used for GL canvases where we don't need to do any readback, i.e., with a
 // GL backend.
 class CanvasClientSharedSurface : public CanvasClient
 {
 private:
   RefPtr<SharedSurfaceTextureClient> mShSurfClient;