Bug 1302380 - Report canvas memory usage through the buffer provider. r=gw280
☠☠ backed out by b401cb17167b ☠ ☠
authorNicolas Silva <nsilva@mozilla.com>
Fri, 16 Sep 2016 10:13:19 +0200
changeset 314240 e0d755ab4cbd6b2401fbbc592ef417f2fb0fda46
parent 314239 88570c6adbe21830848a2e3f2f88cb1557e0dd29
child 314241 8b08565f9815d0a3edbb6a8c734aecc7d2c660bc
push id32351
push userkwierso@gmail.com
push dateFri, 16 Sep 2016 21:35:37 +0000
treeherderautoland@aff0c07fa3dc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgw280
bugs1302380
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 1302380 - Report canvas memory usage through the buffer provider. r=gw280
dom/canvas/CanvasRenderingContext2D.cpp
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/PersistentBufferProvider.cpp
gfx/layers/PersistentBufferProvider.h
gfx/layers/client/ClientLayerManager.cpp
gfx/layers/client/ClientLayerManager.h
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1154,22 +1154,16 @@ CanvasRenderingContext2D::ParseColor(con
 
 nsresult
 CanvasRenderingContext2D::Reset()
 {
   if (mCanvasElement) {
     mCanvasElement->InvalidateCanvas();
   }
 
-  // only do this for non-docshell created contexts,
-  // since those are the ones that we created a surface for
-  if (mTarget && IsTargetValid() && !mDocShell) {
-    gCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
-  }
-
   bool forceReset = true;
   ReturnTarget(forceReset);
   mTarget = nullptr;
   mBufferProvider = nullptr;
 
   // reset hit regions
   mHitRegionsOptions.ClearAndRetainStorage();
 
@@ -1622,18 +1616,16 @@ CanvasRenderingContext2D::EnsureTarget(c
   }
 
   MOZ_ASSERT(newTarget);
   MOZ_ASSERT(newProvider);
 
   mTarget = newTarget.forget();
   mBufferProvider = newProvider.forget();
 
-  RegisterAllocation();
-
   // Skia expects the unused X channel to contains 0 even for opaque operations
   // so we can't skip clearing in that case, even if we are going to cover the
   // entire canvas in the next drawing operation.
   if (!canDiscardContent || mTarget->GetBackendType() == gfx::BackendType::SKIA) {
     mTarget->ClearRect(canvasRect);
   }
 
   RestoreClipsAndTransformToTarget();
@@ -1667,44 +1659,31 @@ CanvasRenderingContext2D::SetInitialStat
   state->shadowColor = NS_RGBA(0,0,0,0);
 }
 
 void
 CanvasRenderingContext2D::SetErrorState()
 {
   EnsureErrorTarget();
 
-  if (mTarget && mTarget != sErrorTarget) {
-    gCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
-  }
-
   mTarget = sErrorTarget;
   mBufferProvider = nullptr;
 
   // clear transforms, clips, etc.
   SetInitialState();
 }
 
 void
-CanvasRenderingContext2D::RegisterAllocation()
-{
-  // XXX - It would make more sense to track the allocation in
-  // PeristentBufferProvider, rather than here.
+EnsureCanvasMemoryReporter()
+{
   static bool registered = false;
-  // FIXME: Disable the reporter for now, see bug 1241865
-  if (!registered && false) {
+  if (!registered) {
     registered = true;
     RegisterStrongMemoryReporter(new Canvas2dPixelsReporter());
   }
-
-  gCanvasAzureMemoryUsed += mWidth * mHeight * 4;
-  JSContext* context = nsContentUtils::GetCurrentJSContext();
-  if (context) {
-    JS_updateMallocCounter(context, mWidth * mHeight * 4);
-  }
 }
 
 static already_AddRefed<LayerManager>
 LayerManagerFromCanvasElement(nsINode* aCanvasElement)
 {
   if (!aCanvasElement || !aCanvasElement->OwnerDoc()) {
     return nullptr;
   }
@@ -1778,17 +1757,19 @@ CanvasRenderingContext2D::TrySharedTarge
   }
 
   RefPtr<LayerManager> layerManager = LayerManagerFromCanvasElement(mCanvasElement);
 
   if (!layerManager) {
     return false;
   }
 
-  aOutProvider = layerManager->CreatePersistentBufferProvider(GetSize(), GetSurfaceFormat());
+  EnsureCanvasMemoryReporter();
+  aOutProvider = layerManager->CreatePersistentBufferProvider(GetSize(), GetSurfaceFormat(),
+                                                              &gCanvasAzureMemoryUsed);
 
   if (!aOutProvider) {
     return false;
   }
 
   // We can pass an empty persisted rect since we just created the buffer
   // provider (nothing to restore).
   aOutDT = aOutProvider->BorrowDrawTarget(IntRect());
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -197,25 +197,28 @@ LayerManager::CreateDrawTarget(const Int
                                SurfaceFormat aFormat)
 {
   return gfxPlatform::GetPlatform()->
     CreateOffscreenCanvasDrawTarget(aSize, aFormat);
 }
 
 already_AddRefed<PersistentBufferProvider>
 LayerManager::CreatePersistentBufferProvider(const mozilla::gfx::IntSize &aSize,
-                                             mozilla::gfx::SurfaceFormat aFormat)
+                                             mozilla::gfx::SurfaceFormat aFormat,
+                                             int64_t* aMemoryCounter)
 {
   RefPtr<PersistentBufferProviderBasic> bufferProvider =
     PersistentBufferProviderBasic::Create(aSize, aFormat,
-      gfxPlatform::GetPlatform()->GetPreferredCanvasBackend());
+      gfxPlatform::GetPlatform()->GetPreferredCanvasBackend(),
+      aMemoryCounter
+    );
 
   if (!bufferProvider) {
     bufferProvider = PersistentBufferProviderBasic::Create(aSize, aFormat,
-      gfxPlatform::GetPlatform()->GetFallbackCanvasBackend());
+      gfxPlatform::GetPlatform()->GetFallbackCanvasBackend(), aMemoryCounter);
   }
 
   return bufferProvider.forget();
 }
 
 #ifdef DEBUG
 void
 LayerManager::Mutated(Layer* aLayer)
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -480,17 +480,18 @@ public:
                      mozilla::gfx::SurfaceFormat aFormat);
 
   /**
    * Creates a PersistentBufferProvider for use with canvas which is optimized for
    * inter-operating with this layermanager.
    */
   virtual already_AddRefed<PersistentBufferProvider>
     CreatePersistentBufferProvider(const mozilla::gfx::IntSize &aSize,
-                                   mozilla::gfx::SurfaceFormat aFormat);
+                                   mozilla::gfx::SurfaceFormat aFormat,
+                                   int64_t* aMemoryCounter);
 
   virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) { return true; }
 
   /**
    * returns the maximum texture size on this layer backend, or INT32_MAX
    * if there is no maximum
    */
   virtual int32_t GetMaxTextureSize() const = 0;
--- a/gfx/layers/PersistentBufferProvider.cpp
+++ b/gfx/layers/PersistentBufferProvider.cpp
@@ -11,24 +11,33 @@
 #include "gfxPlatform.h"
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
 
-PersistentBufferProviderBasic::PersistentBufferProviderBasic(DrawTarget* aDt)
+PersistentBufferProviderBasic::PersistentBufferProviderBasic(DrawTarget* aDt, int64_t* aMemoryCounter)
 : mDrawTarget(aDt)
+, mMemoryCounter(aMemoryCounter)
 {
+  if (mMemoryCounter) {
+    IntSize size = mDrawTarget->GetSize();
+    *mMemoryCounter += size.width * size.height * gfx::BytesPerPixel(mDrawTarget->GetFormat());
+  }
   MOZ_COUNT_CTOR(PersistentBufferProviderBasic);
 }
 
 PersistentBufferProviderBasic::~PersistentBufferProviderBasic()
 {
+  if (mMemoryCounter) {
+    IntSize size = mDrawTarget->GetSize();
+    *mMemoryCounter -= size.width * size.height * gfx::BytesPerPixel(mDrawTarget->GetFormat());
+  }
   MOZ_COUNT_DTOR(PersistentBufferProviderBasic);
 }
 
 already_AddRefed<gfx::DrawTarget>
 PersistentBufferProviderBasic::BorrowDrawTarget(const gfx::IntRect& aPersistedRect)
 {
   MOZ_ASSERT(!mSnapshot);
   RefPtr<gfx::DrawTarget> dt(mDrawTarget);
@@ -62,36 +71,37 @@ PersistentBufferProviderBasic::ReturnSna
   RefPtr<SourceSurface> snapshot = aSnapshot;
   MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
   mSnapshot = nullptr;
 }
 
 //static
 already_AddRefed<PersistentBufferProviderBasic>
 PersistentBufferProviderBasic::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
-                                      gfx::BackendType aBackend)
+                                      gfx::BackendType aBackend, int64_t* aMemoryCounter)
 {
   RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(aBackend, aSize, aFormat);
 
   if (!dt) {
     return nullptr;
   }
 
   RefPtr<PersistentBufferProviderBasic> provider =
-    new PersistentBufferProviderBasic(dt);
+    new PersistentBufferProviderBasic(dt, aMemoryCounter);
 
   return provider.forget();
 }
 
 
 //static
 already_AddRefed<PersistentBufferProviderShared>
 PersistentBufferProviderShared::Create(gfx::IntSize aSize,
                                        gfx::SurfaceFormat aFormat,
-                                       CompositableForwarder* aFwd)
+                                       CompositableForwarder* aFwd,
+                                       int64_t* aMemoryCounter)
 {
   if (!aFwd || !aFwd->IPCOpen()) {
     return nullptr;
   }
 
   RefPtr<TextureClient> texture = TextureClient::CreateForDrawing(
     aFwd, aFormat, aSize,
     BackendSelector::Canvas,
@@ -99,33 +109,37 @@ PersistentBufferProviderShared::Create(g
     TextureAllocationFlags::ALLOC_DEFAULT
   );
 
   if (!texture) {
     return nullptr;
   }
 
   RefPtr<PersistentBufferProviderShared> provider =
-    new PersistentBufferProviderShared(aSize, aFormat, aFwd, texture);
+    new PersistentBufferProviderShared(aSize, aFormat, aFwd, texture, aMemoryCounter);
+
   return provider.forget();
 }
 
 PersistentBufferProviderShared::PersistentBufferProviderShared(gfx::IntSize aSize,
                                                                gfx::SurfaceFormat aFormat,
                                                                CompositableForwarder* aFwd,
-                                                               RefPtr<TextureClient>& aTexture)
+                                                               RefPtr<TextureClient>& aTexture,
+                                                               int64_t* aMemoryCounter)
 
 : mSize(aSize)
 , mFormat(aFormat)
 , mFwd(aFwd)
 , mFront(Nothing())
+, mMemoryCounter(aMemoryCounter)
 {
   if (mTextures.append(aTexture)) {
     mBack = Some<uint32_t>(0);
   }
+  OnAllocation();
   MOZ_COUNT_CTOR(PersistentBufferProviderShared);
 }
 
 PersistentBufferProviderShared::~PersistentBufferProviderShared()
 {
   MOZ_COUNT_DTOR(PersistentBufferProviderShared);
 
   if (IsActivityTracked()) {
@@ -288,16 +302,17 @@ PersistentBufferProviderShared::BorrowDr
       mFwd, mFormat, mSize,
       BackendSelector::Canvas,
       TextureFlags::DEFAULT,
       TextureAllocationFlags::ALLOC_DEFAULT
     );
 
     MOZ_ASSERT(newTexture);
     if (newTexture) {
+      OnAllocation();
       if (mTextures.append(newTexture)) {
         tex = newTexture;
         mBack = Some<uint32_t>(mTextures.length() - 1);
       }
     }
   }
 
   if (!tex || !tex->Lock(OpenMode::OPEN_READ_WRITE)) {
@@ -400,46 +415,71 @@ PersistentBufferProviderShared::ReturnSn
 }
 
 void
 PersistentBufferProviderShared::NotifyInactive()
 {
   RefPtr<TextureClient> front = GetTexture(mFront);
   RefPtr<TextureClient> back = GetTexture(mBack);
 
+  for (unsigned i = 0; i < mTextures.length(); ++i) {
+    OnDeallocation();
+  }
+
   // 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);
+      OnAllocation();
     }
     if (front == back) {
       mFront = mBack;
     }
   }
 
   if (front && front != back) {
     if (mTextures.append(front)) {
       mFront = Some<uint32_t>(mTextures.length() - 1);
+      OnAllocation();
     }
   }
 }
 
 void
 PersistentBufferProviderShared::Destroy()
 {
   mSnapshot = nullptr;
   mDrawTarget = nullptr;
 
   for (uint32_t i = 0; i < mTextures.length(); ++i) {
     TextureClient* texture = mTextures[i];
     if (texture && texture->IsLocked()) {
       MOZ_ASSERT(false);
       texture->Unlock();
     }
+    if (texture) {
+      OnDeallocation();
+    }
   }
 
   mTextures.clear();
 }
 
+void
+PersistentBufferProviderShared::OnAllocation()
+{
+  if (mMemoryCounter) {
+    *mMemoryCounter += mSize.width * mSize.height * gfx::BytesPerPixel(mFormat);
+  }
+}
+
+void
+PersistentBufferProviderShared::OnDeallocation()
+{
+  if (mMemoryCounter) {
+    *mMemoryCounter -= mSize.width * mSize.height * gfx::BytesPerPixel(mFormat);
+  }
+}
+
 } // namespace layers
 } // namespace mozilla
\ No newline at end of file
--- a/gfx/layers/PersistentBufferProvider.h
+++ b/gfx/layers/PersistentBufferProvider.h
@@ -77,19 +77,20 @@ public:
 
 
 class PersistentBufferProviderBasic : public PersistentBufferProvider
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PersistentBufferProviderBasic, override)
 
   static already_AddRefed<PersistentBufferProviderBasic>
-  Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, gfx::BackendType aBackend);
+  Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, gfx::BackendType aBackend,
+         int64_t* aMemoryCounter);
 
-  explicit PersistentBufferProviderBasic(gfx::DrawTarget* aTarget);
+  explicit PersistentBufferProviderBasic(gfx::DrawTarget* aTarget, int64_t* aMemoryCounter = nullptr);
 
   virtual LayersBackend GetType() override { return LayersBackend::LAYERS_BASIC; }
 
   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;
@@ -97,32 +98,34 @@ public:
   virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) override;
 
   virtual bool PreservesDrawingState() const override { return true; }
 private:
   ~PersistentBufferProviderBasic();
 
   RefPtr<gfx::DrawTarget> mDrawTarget;
   RefPtr<gfx::SourceSurface> mSnapshot;
+  int64_t* mMemoryCounter;
 };
 
 
 /**
  * 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);
+         CompositableForwarder* aFwd,
+         int64_t* aMemoryCounter);
 
   virtual LayersBackend GetType() override { return LayersBackend::LAYERS_CLIENT; }
 
   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;
@@ -136,36 +139,41 @@ public:
   virtual void OnShutdown() override { Destroy(); }
 
   virtual bool SetForwarder(CompositableForwarder* aFwd) override;
 
   virtual bool PreservesDrawingState() const override { return false; }
 protected:
   PersistentBufferProviderShared(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                                  CompositableForwarder* aFwd,
-                                 RefPtr<TextureClient>& aTexture);
+                                 RefPtr<TextureClient>& aTexture,
+                                 int64_t* aMemoryCounter);
 
   ~PersistentBufferProviderShared();
 
+  void OnAllocation();
+  void OnDeallocation();
+
   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;
   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;
+  int64_t* mMemoryCounter;
 };
 
 struct AutoReturnSnapshot
 {
   PersistentBufferProvider* mBufferProvider;
   RefPtr<gfx::SourceSurface>* mSnapshot;
 
   explicit AutoReturnSnapshot(PersistentBufferProvider* aProvider = nullptr)
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -835,32 +835,33 @@ bool
 ClientLayerManager::DependsOnStaleDevice() const
 {
   return gfxPlatform::GetPlatform()->GetDeviceCounter() != mDeviceCounter;
 }
 
 
 already_AddRefed<PersistentBufferProvider>
 ClientLayerManager::CreatePersistentBufferProvider(const gfx::IntSize& aSize,
-                                                   gfx::SurfaceFormat aFormat)
+                                                   gfx::SurfaceFormat aFormat,
+                                                   int64_t* aMemoryCounter)
 {
   // Don't use a shared buffer provider if compositing is considered "not cheap"
   // because the canvas will most likely be flattened into a thebes layer instead
   // of being sent to the compositor, in which case rendering into shared memory
   // is wasteful.
   if (IsCompositingCheap() &&
       gfxPrefs::PersistentBufferProviderSharedEnabled()) {
     RefPtr<PersistentBufferProvider> provider
-      = PersistentBufferProviderShared::Create(aSize, aFormat, AsShadowForwarder());
+      = PersistentBufferProviderShared::Create(aSize, aFormat, AsShadowForwarder(), aMemoryCounter);
     if (provider) {
       return provider.forget();
     }
   }
 
-  return LayerManager::CreatePersistentBufferProvider(aSize, aFormat);
+  return LayerManager::CreatePersistentBufferProvider(aSize, aFormat, aMemoryCounter);
 }
 
 
 ClientLayer::~ClientLayer()
 {
   if (HasShadow()) {
     PLayerChild::Send__delete__(GetShadow());
   }
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -226,17 +226,18 @@ public:
   public:
     virtual void DidComposite() = 0;
   };
 
   void AddDidCompositeObserver(DidCompositeObserver* aObserver);
   void RemoveDidCompositeObserver(DidCompositeObserver* aObserver);
 
   virtual already_AddRefed<PersistentBufferProvider>
-  CreatePersistentBufferProvider(const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) override;
+  CreatePersistentBufferProvider(const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat,
+                                 int64_t* aMemoryCounter) override;
 
 protected:
   enum TransactionPhase {
     PHASE_NONE, PHASE_CONSTRUCTION, PHASE_DRAWING, PHASE_FORWARD
   };
   TransactionPhase mPhase;
 
 private: