Bug 769021; fix a memory leak with mask sharing. r=khuey
authorNicholas Cameron <ncameron@mozilla.com>
Tue, 04 Sep 2012 13:02:56 +1200
changeset 104173 61c0826e890302972e8163b61282d841470b593a
parent 104172 113d57c8e8b72fafb6f4c092e7ba14cfeb243df4
child 104174 d155eefe75f326b242b282b25a4bb370c177a9b1
push id23402
push useremorley@mozilla.com
push dateTue, 04 Sep 2012 09:31:09 +0000
treeherdermozilla-central@500dac3a151b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs769021
milestone18.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 769021; fix a memory leak with mask sharing. r=khuey
layout/base/FrameLayerBuilder.cpp
layout/base/MaskLayerImageCache.cpp
layout/base/MaskLayerImageCache.h
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -3291,38 +3291,34 @@ ContainerState::SetupMaskLayer(Layer *aL
   gfxMatrix maskTransform;
   maskTransform.Scale(float(surfaceSize.width)/float(boundingRect.Width()),
                       float(surfaceSize.height)/float(boundingRect.Height()));
   maskTransform.Translate(-boundingRect.TopLeft());
   // imageTransform is only used when the clip is painted to the mask
   gfxMatrix imageTransform = maskTransform;
   imageTransform.Scale(mParameters.mXScale, mParameters.mYScale);
 
+  nsAutoPtr<MaskLayerImageCache::MaskLayerImageKey> newKey(
+    new MaskLayerImageCache::MaskLayerImageKey(aLayer->Manager()->GetBackendType()));
+
   // copy and transform the rounded rects
-  nsTArray<MaskLayerImageCache::PixelRoundedRect> roundedRects;
   for (uint32_t i = 0; i < newData.mRoundedClipRects.Length(); ++i) {
-    roundedRects.AppendElement(
+    newKey->mRoundedClipRects.AppendElement(
       MaskLayerImageCache::PixelRoundedRect(newData.mRoundedClipRects[i],
                                             mContainerFrame->PresContext()));
-    roundedRects[i].ScaleAndTranslate(imageTransform);
+    newKey->mRoundedClipRects[i].ScaleAndTranslate(imageTransform);
   }
  
+  const MaskLayerImageCache::MaskLayerImageKey* lookupKey = newKey;
+
   // check to see if we can reuse a mask image
-  const MaskLayerImageCache::MaskLayerImageKey* key =
-    new MaskLayerImageCache::MaskLayerImageKey(roundedRects, aLayer->Manager()->GetBackendType());
-  const MaskLayerImageCache::MaskLayerImageKey* lookupKey = key;
-
   nsRefPtr<ImageContainer> container =
     GetMaskLayerImageCache()->FindImageFor(&lookupKey);
 
-  if (container) {
-    // track the returned key for the mask image
-    delete key;
-    key = lookupKey;
-  } else {
+  if (!container) {
     // no existing mask image, so build a new one
     nsRefPtr<gfxASurface> surface =
       aLayer->Manager()->CreateOptimalMaskSurface(surfaceSize);
 
     // fail if we can't get the right surface
     if (!surface || surface->CairoStatus()) {
       NS_WARNING("Could not create surface for mask layer.");
       SetClipCount(thebesData, 0);
@@ -3343,26 +3339,26 @@ ContainerState::SetupMaskLayer(Layer *aL
     nsRefPtr<Image> image = container->CreateImage(&format, 1);
     NS_ASSERTION(image, "Could not create image container for mask layer.");
     CairoImage::Data data;
     data.mSurface = surface;
     data.mSize = surfaceSize;
     static_cast<CairoImage*>(image.get())->SetData(data);
     container->SetCurrentImageInTransaction(image);
 
-    GetMaskLayerImageCache()->PutImage(key, container);
+    GetMaskLayerImageCache()->PutImage(newKey.forget(), container);
   }
 
   maskLayer->SetContainer(container);
   maskLayer->SetBaseTransform(gfx3DMatrix::From2D(maskTransform.Invert()));
 
   // save the details of the clip in user data
   userData->mScaleX = newData.mScaleX;
   userData->mScaleY = newData.mScaleY;
   userData->mRoundedClipRects.SwapElements(newData.mRoundedClipRects);
-  userData->mImageKey = key;
+  userData->mImageKey = lookupKey;
 
   aLayer->SetMaskLayer(maskLayer);
   SetClipCount(thebesData, aRoundedRectClipCount);
   return;
 }
 
 } // namespace mozilla
--- a/layout/base/MaskLayerImageCache.cpp
+++ b/layout/base/MaskLayerImageCache.cpp
@@ -7,20 +7,23 @@
 #include "ImageContainer.h"
 
 using namespace mozilla::layers;
 
 namespace mozilla {
 
 MaskLayerImageCache::MaskLayerImageCache()
 {
+  MOZ_COUNT_CTOR(MaskLayerImageCache);
   mMaskImageContainers.Init();
 }
 MaskLayerImageCache::~MaskLayerImageCache()
-{}
+{
+  MOZ_COUNT_DTOR(MaskLayerImageCache);
+}
 
 
 /* static */ PLDHashOperator
 MaskLayerImageCache::SweepFunc(MaskLayerImageEntry* aEntry,
                                void* aUserArg)
 {
   const MaskLayerImageCache::MaskLayerImageKey* key = aEntry->mKey;
 
--- a/layout/base/MaskLayerImageCache.h
+++ b/layout/base/MaskLayerImageCache.h
@@ -45,20 +45,34 @@ public:
   {
     PixelRoundedRect(const FrameLayerBuilder::Clip::RoundedRect& aRRect,
                      nsPresContext* aPresContext)
       : mRect(aPresContext->AppUnitsToGfxUnits(aRRect.mRect.x),
               aPresContext->AppUnitsToGfxUnits(aRRect.mRect.y),
               aPresContext->AppUnitsToGfxUnits(aRRect.mRect.width),
               aPresContext->AppUnitsToGfxUnits(aRRect.mRect.height))
     {
+      MOZ_COUNT_CTOR(PixelRoundedRect);
       NS_FOR_CSS_HALF_CORNERS(corner) {
         mRadii[corner] = aPresContext->AppUnitsToGfxUnits(aRRect.mRadii[corner]);
       }
     }
+    PixelRoundedRect(const PixelRoundedRect& aPRR)
+      : mRect(aPRR.mRect)
+    {
+      MOZ_COUNT_CTOR(PixelRoundedRect);
+      NS_FOR_CSS_HALF_CORNERS(corner) {
+        mRadii[corner] = aPRR.mRadii[corner];
+      }
+    }
+
+    ~PixelRoundedRect()
+    {
+      MOZ_COUNT_DTOR(PixelRoundedRect);
+    }
 
     // Applies the scale and translate components of aTransform.
     // It is an error to pass a matrix which does more than just scale
     // and translate.
     void ScaleAndTranslate(const gfxMatrix& aTransform)
     {
       NS_ASSERTION(aTransform.xy == 0 && aTransform.yx == 0,
                    "Transform has a component other than scale and translate");
@@ -94,36 +108,52 @@ public:
       hash = AddToHash(hash, HashBytes(mRadii, 8*sizeof(gfxFloat)));
 
       return hash;
     }
 
     gfxRect mRect;
     // Indices into mRadii are the NS_CORNER_* constants in nsStyleConsts.h
     gfxFloat mRadii[8];
+
+  private:
+    PixelRoundedRect() MOZ_DELETE;
   };
 
   /**
    * A key to identify cached image containers.
    * The const-ness of this class is with respect to its use as a key into a
    * hashtable, so anything not used to create the hash is mutable.
    * mLayerCount counts the number of mask layers which have a reference to
    * MaskLayerImageEntry::mContainer; it is maintained by MaskLayerUserData,
    * which keeps a reference to the key. There will usually be mLayerCount + 1
    * pointers to a key object (the +1 being from the hashtable entry), but this
    * invariant may be temporarily broken.
    */
-  class MaskLayerImageKey
+  struct MaskLayerImageKey
   {
-  public:
-    MaskLayerImageKey(const nsTArray<PixelRoundedRect>& aRoundedClipRects, layers::LayersBackend aBackend)
+    MaskLayerImageKey(layers::LayersBackend aBackend)
       : mBackend(aBackend)
       , mLayerCount(0)
-      , mRoundedClipRects(aRoundedClipRects)
-    {}
+      , mRoundedClipRects()
+    {
+      MOZ_COUNT_CTOR(MaskLayerImageKey);
+    }
+    MaskLayerImageKey(const MaskLayerImageKey& aKey)
+      : mBackend(aKey.mBackend)
+      , mLayerCount(aKey.mLayerCount)
+      , mRoundedClipRects(aKey.mRoundedClipRects)
+    {
+      MOZ_COUNT_CTOR(MaskLayerImageKey);
+    }
+
+    ~MaskLayerImageKey()
+    {
+      MOZ_COUNT_DTOR(MaskLayerImageKey);
+    }
 
     void AddRef() const { ++mLayerCount; }
     void Release() const
     {
       NS_ASSERTION(mLayerCount > 0, "Inconsistent layer count");
       --mLayerCount;
     }
 
@@ -167,22 +197,29 @@ public:
 protected:
 
   class MaskLayerImageEntry : public PLDHashEntryHdr
   {
   public:
     typedef const MaskLayerImageKey& KeyType;
     typedef const MaskLayerImageKey* KeyTypePointer;
 
-    MaskLayerImageEntry(KeyTypePointer aKey) : mKey(aKey) {}
+    MaskLayerImageEntry(KeyTypePointer aKey) : mKey(aKey)
+    {
+      MOZ_COUNT_CTOR(MaskLayerImageEntry);
+    }
     MaskLayerImageEntry(const MaskLayerImageEntry& aOther)
       : mKey(aOther.mKey.get())
     {
       NS_ERROR("ALLOW_MEMMOVE == true, should never be called");
     }
+    ~MaskLayerImageEntry()
+    {
+      MOZ_COUNT_DTOR(MaskLayerImageEntry);
+    }
 
     // KeyEquals(): does this entry match this key?
     bool KeyEquals(KeyTypePointer aKey) const
     {
       return *mKey == *aKey;
     }
 
     // KeyToPointer(): Convert KeyType to KeyTypePointer