Bug 1167235 - Part 3 - Use TextureReadLock to optimize canvas copy-on-writes. r=Bas
authorNicolas Silva <nsilva@mozilla.com>
Fri, 01 Jul 2016 10:58:16 +0200
changeset 343434 31f3f2c5a759b92ac41f55c9616fb7a15700abf5
parent 343433 53487e6b475af7829d3aa14a9d99ffe9e443774e
child 343435 f51f78388224856591d67cebd26b46aed715287c
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)
reviewersBas
bugs1167235
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 1167235 - Part 3 - Use TextureReadLock to optimize canvas copy-on-writes. r=Bas
gfx/layers/PersistentBufferProvider.cpp
gfx/layers/PersistentBufferProvider.h
gfx/layers/client/CanvasClient.h
gfx/layers/ipc/CompositableForwarder.h
--- a/gfx/layers/PersistentBufferProvider.cpp
+++ b/gfx/layers/PersistentBufferProvider.cpp
@@ -85,24 +85,26 @@ PersistentBufferProviderShared::Create(g
 {
   if (!aFwd || !aFwd->IPCOpen()) {
     return nullptr;
   }
 
   RefPtr<TextureClient> texture = TextureClient::CreateForDrawing(
     aFwd, aFormat, aSize,
     BackendSelector::Canvas,
-    TextureFlags::IMMEDIATE_UPLOAD,
+    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,
@@ -115,51 +117,79 @@ PersistentBufferProviderShared::Persiste
 {
   MOZ_COUNT_CTOR(PersistentBufferProviderShared);
 }
 
 PersistentBufferProviderShared::~PersistentBufferProviderShared()
 {
   MOZ_COUNT_DTOR(PersistentBufferProviderShared);
 
+  if (IsActivityTracked()) {
+    mFwd->GetActiveResourceTracker().RemoveObject(this);
+  }
+
   mDrawTarget = nullptr;
   if (mBack && mBack->IsLocked()) {
     mBack->Unlock();
   }
   if (mFront && mFront->IsLocked()) {
     mFront->Unlock();
   }
+  if (mBuffer && mBuffer->IsLocked()) {
+    mBuffer->Unlock();
+  }
 }
 
 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) {
-    if (!mBack) {
-      mBack = TextureClient::CreateForDrawing(
-        mFwd, mFormat, mSize,
-        BackendSelector::Canvas,
-        TextureFlags::IMMEDIATE_UPLOAD,
-        TextureAllocationFlags::ALLOC_DEFAULT
-      );
+    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 (!mBack) {
+    if (!mBack || !mBack->Lock(OpenMode::OPEN_READ_WRITE)) {
       return nullptr;
     }
 
-    if (!mBack->Lock(OpenMode::OPEN_READ_WRITE)) {
-      return nullptr;
-    }
-    if (!aPersistedRect.IsEmpty() && mFront
-        && mFront->Lock(OpenMode::OPEN_READ)) {
+    if (changedBackBuffer && !aPersistedRect.IsEmpty()
+        && mFront && mFront->Lock(OpenMode::OPEN_READ)) {
 
       DebugOnly<bool> success = mFront->CopyToTextureClient(mBack, &aPersistedRect, nullptr);
       MOZ_ASSERT(success);
 
       mFront->Unlock();
     }
 
     mDrawTarget = mBack->BorrowDrawTarget();
@@ -172,31 +202,39 @@ PersistentBufferProviderShared::BorrowDr
   return dt.forget();
 }
 
 bool
 PersistentBufferProviderShared::ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT)
 {
   RefPtr<gfx::DrawTarget> dt(aDT);
   MOZ_ASSERT(mDrawTarget == dt);
+  MOZ_ASSERT(!mSnapshot);
 
   mDrawTarget = nullptr;
   dt = nullptr;
 
   mBack->Unlock();
 
+  if (!mBuffer && mFront && !mFront->IsLocked()) {
+    mBuffer.swap(mFront);
+  }
+
   mFront = mBack;
-  mBack = nullptr;
 
   return true;
 }
 
 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()) {
     MOZ_ASSERT(false);
     return nullptr;
   }
 
   if (!mFront->Lock(OpenMode::OPEN_READ)) {
     return nullptr;
   }
@@ -222,10 +260,21 @@ PersistentBufferProviderShared::ReturnSn
   mSnapshot = nullptr;
   snapshot = nullptr;
 
   mDrawTarget = nullptr;
 
   mFront->Unlock();
 }
 
+void
+PersistentBufferProviderShared::NotifyInactive()
+{
+  if (mBuffer && mBuffer->IsLocked()) {
+    // mBuffer should never be locked
+    MOZ_ASSERT(false);
+    mBuffer->Unlock();
+  }
+  mBuffer = nullptr;
+}
+
 } // namespace layers
 } // namespace mozilla
\ No newline at end of file
--- a/gfx/layers/PersistentBufferProvider.h
+++ b/gfx/layers/PersistentBufferProvider.h
@@ -91,16 +91,17 @@ private:
 };
 
 
 /**
  * Provides access to a buffer which can be sent to the compositor without
  * requiring a copy.
  */
 class PersistentBufferProviderShared : public PersistentBufferProvider
+                                     , public ActiveResource
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PersistentBufferProviderShared, override)
 
   static already_AddRefed<PersistentBufferProviderShared>
   Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
          CompositableForwarder* aFwd);
 
@@ -113,28 +114,34 @@ public:
   virtual already_AddRefed<gfx::SourceSurface> BorrowSnapshot() override;
 
   virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) override;
 
   TextureClient* GetTextureClient() override {
     return mFront;
   }
 
+  virtual void NotifyInactive() override;
+
 protected:
   PersistentBufferProviderShared(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                                  CompositableForwarder* aFwd,
                                  RefPtr<TextureClient>& aTexture);
 
   ~PersistentBufferProviderShared();
 
   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;
   RefPtr<gfx::DrawTarget> mDrawTarget;
   RefPtr<gfx::SourceSurface > mSnapshot;
 };
 
 struct AutoReturnSnapshot
 {
   PersistentBufferProvider* mBufferProvider;
   RefPtr<gfx::SourceSurface>* mSnapshot;
--- a/gfx/layers/client/CanvasClient.h
+++ b/gfx/layers/client/CanvasClient.h
@@ -106,17 +106,16 @@ public:
   }
 
   virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) override;
 
   virtual void UpdateFromTexture(TextureClient* aBuffer) override;
 
   virtual bool AddTextureClient(TextureClient* aTexture) override
   {
-    MOZ_ASSERT((mTextureFlags & aTexture->GetFlags()) == mTextureFlags);
     return CanvasClient::AddTextureClient(aTexture);
   }
 
   virtual void OnDetach() override
   {
     mBackBuffer = mFrontBuffer = nullptr;
   }
 
--- a/gfx/layers/ipc/CompositableForwarder.h
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -13,47 +13,79 @@
 #include "mozilla/layers/CompositableClient.h"  // for CompositableClient
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator
 #include "mozilla/layers/LayersTypes.h"  // for LayersBackend
 #include "mozilla/layers/TextureClient.h"  // for TextureClient
 #include "mozilla/layers/TextureForwarder.h"  // for TextureForwarder
 #include "nsRegion.h"                   // for nsIntRegion
 #include "mozilla/gfx/Rect.h"
+#include "nsExpirationTracker.h"
 #include "nsHashKeys.h"
 #include "nsTHashtable.h"
 
 namespace mozilla {
 namespace layers {
 
 class CompositableClient;
 class AsyncTransactionTracker;
 class ImageContainer;
 struct TextureFactoryIdentifier;
 class SurfaceDescriptor;
 class SurfaceDescriptorTiles;
 class ThebesBufferData;
 class PTextureChild;
 
 /**
+ * See ActiveResourceTracker below.
+ */
+class ActiveResource
+{
+public:
+ virtual void NotifyInactive() = 0;
+  nsExpirationState* GetExpirationState() { return &mExpirationState; }
+  bool IsActivityTracked() { return mExpirationState.IsTracked(); }
+private:
+  nsExpirationState mExpirationState;
+};
+
+/**
+ * A convenience class on top of nsExpirationTracker
+ */
+class ActiveResourceTracker : public nsExpirationTracker<ActiveResource, 3>
+{
+public:
+  ActiveResourceTracker(uint32_t aExpirationCycle, const char* aName)
+  : nsExpirationTracker(aExpirationCycle, aName)
+  {}
+
+  virtual void NotifyExpired(ActiveResource* aResource) override
+  {
+    RemoveObject(aResource);
+    aResource->NotifyInactive();
+  }
+};
+
+/**
  * A transaction is a set of changes that happenned on the content side, that
  * should be sent to the compositor side.
  * CompositableForwarder is an interface to manage a transaction of
  * compositable objetcs.
  *
  * ShadowLayerForwarder is an example of a CompositableForwarder (that can
  * additionally forward modifications of the Layer tree).
  * ImageBridgeChild is another CompositableForwarder.
  */
 class CompositableForwarder : public TextureForwarder
 {
 public:
 
   CompositableForwarder(const char* aName)
     : TextureForwarder(aName)
+    , mActiveResourceTracker(1000, "CompositableForwarder")
     , mSerial(++sSerialCounter)
   {}
 
   /**
    * Setup the IPDL actor for aCompositable to be part of layers
    * transactions.
    */
   virtual void Connect(CompositableClient* aCompositable,
@@ -164,23 +196,27 @@ public:
     return mTextureFactoryIdentifier.mSupportsPartialUploads;
   }
 
   const TextureFactoryIdentifier& GetTextureFactoryIdentifier() const
   {
     return mTextureFactoryIdentifier;
   }
 
+  ActiveResourceTracker& GetActiveResourceTracker() { return mActiveResourceTracker; }
+
 protected:
   TextureFactoryIdentifier mTextureFactoryIdentifier;
 
   nsTArray<RefPtr<TextureClient> > mTexturesToRemove;
   nsTArray<RefPtr<CompositableClient>> mCompositableClientsToRemove;
   RefPtr<SyncObject> mSyncObject;
 
+  ActiveResourceTracker mActiveResourceTracker;
+
   const int32_t mSerial;
   static mozilla::Atomic<int32_t> sSerialCounter;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif