Bug 923302 - Add explicit memory reporting for SurfaceCache. r=njn
authorSeth Fowler <seth@mozilla.com>
Wed, 01 Oct 2014 17:16:42 -0700
changeset 208295 a3e9214bfcce9698f99c08ac6795078759819b6e
parent 208294 60c79ffef24f4856c861ae432c447ef1c579fc70
child 208296 fce56d505b2e116bb1e5bd453905a9c8fa92478d
push id49885
push usermfowler@mozilla.com
push dateThu, 02 Oct 2014 00:21:01 +0000
treeherdermozilla-inbound@a3e9214bfcce [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnjn
bugs923302
milestone35.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 923302 - Add explicit memory reporting for SurfaceCache. r=njn
image/src/RasterImage.cpp
image/src/SurfaceCache.cpp
image/src/SurfaceCache.h
image/src/VectorImage.cpp
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -1005,20 +1005,23 @@ RasterImage::HeapSizeOfSourceWithCompute
   }
   return n;
 }
 
 size_t
 RasterImage::SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
                                                      MallocSizeOf aMallocSizeOf) const
 {
-  return mFrameBlender
-       ? mFrameBlender->SizeOfDecodedWithComputedFallbackIfHeap(aLocation,
-                                                                aMallocSizeOf)
-       : 0;
+  size_t n = 0;
+  n += SurfaceCache::SizeOfSurfaces(ImageKey(this), aLocation, aMallocSizeOf);
+  if (mFrameBlender) {
+    n += mFrameBlender->SizeOfDecodedWithComputedFallbackIfHeap(aLocation,
+                                                                aMallocSizeOf);
+  }
+  return n;
 }
 
 size_t
 RasterImage::HeapSizeOfDecodedWithComputedFallback(MallocSizeOf aMallocSizeOf) const
 {
   return SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation::IN_PROCESS_HEAP,
                                                  aMallocSizeOf);
 }
--- a/image/src/SurfaceCache.cpp
+++ b/image/src/SurfaceCache.cpp
@@ -49,30 +49,30 @@ class SurfaceCacheImpl;
 // The single surface cache instance.
 static StaticRefPtr<SurfaceCacheImpl> sInstance;
 
 
 ///////////////////////////////////////////////////////////////////////////////
 // SurfaceCache Implementation
 ///////////////////////////////////////////////////////////////////////////////
 
-/*
+/**
  * Cost models the cost of storing a surface in the cache. Right now, this is
  * simply an estimate of the size of the surface in bytes, but in the future it
  * may be worth taking into account the cost of rematerializing the surface as
  * well.
  */
 typedef size_t Cost;
 
 static Cost ComputeCost(const IntSize& aSize)
 {
   return aSize.width * aSize.height * 4;  // width * height * 4 bytes (32bpp)
 }
 
-/*
+/**
  * Since we want to be able to make eviction decisions based on cost, we need to
  * be able to look up the CachedSurface which has a certain cost as well as the
  * cost associated with a certain CachedSurface. To make this possible, in data
  * structures we actually store a CostEntry, which contains a weak pointer to
  * its associated surface.
  *
  * To make usage of the weak pointer safe, SurfaceCacheImpl always calls
  * StartTracking after a surface is stored in the cache and StopTracking before
@@ -103,17 +103,17 @@ public:
            (mCost == aOther.mCost && mSurface < aOther.mSurface);
   }
 
 private:
   CachedSurface* mSurface;
   Cost           mCost;
 };
 
-/*
+/**
  * A CachedSurface associates a surface with a key that uniquely identifies that
  * surface.
  */
 class CachedSurface
 {
   ~CachedSurface() {}
 public:
   NS_INLINE_DECL_REFCOUNTING(CachedSurface)
@@ -153,16 +153,44 @@ public:
   bool IsLocked() const { return bool(mDrawableRef); }
 
   ImageKey GetImageKey() const { return mImageKey; }
   SurfaceKey GetSurfaceKey() const { return mSurfaceKey; }
   CostEntry GetCostEntry() { return image::CostEntry(this, mCost); }
   nsExpirationState* GetExpirationState() { return &mExpirationState; }
   Lifetime GetLifetime() const { return mLifetime; }
 
+  // A helper type used by SurfaceCacheImpl::SizeOfSurfacesSum.
+  struct SizeOfSurfacesSum
+  {
+    SizeOfSurfacesSum(gfxMemoryLocation aLocation,
+                      MallocSizeOf      aMallocSizeOf)
+      : mLocation(aLocation)
+      , mMallocSizeOf(aMallocSizeOf)
+      , mSum(0)
+    { }
+
+    void Add(CachedSurface* aCachedSurface)
+    {
+      if (!aCachedSurface || !aCachedSurface->mSurface) {
+        return;
+      }
+
+      mSum += aCachedSurface->mSurface->
+        SizeOfExcludingThisWithComputedFallbackIfHeap(mLocation, mMallocSizeOf);
+    }
+
+    size_t Result() const { return mSum; }
+
+  private:
+    gfxMemoryLocation mLocation;
+    MallocSizeOf      mMallocSizeOf;
+    size_t            mSum;
+  };
+
 private:
   nsExpirationState  mExpirationState;
   nsRefPtr<imgFrame> mSurface;
   DrawableFrameRef   mDrawableRef;
   const Cost         mCost;
   const ImageKey     mImageKey;
   const SurfaceKey   mSurfaceKey;
   const Lifetime     mLifetime;
@@ -517,48 +545,63 @@ public:
     cache->StartTracking(aSurface);
 
     return PL_DHASH_NEXT;
   }
 
   NS_IMETHOD
   CollectReports(nsIHandleReportCallback* aHandleReport,
                  nsISupports*             aData,
-                 bool                     aAnonymize)
+                 bool                     aAnonymize) MOZ_OVERRIDE
   {
+    // We have explicit memory reporting for the surface cache which is more
+    // accurate than the cost metrics we report here, but these metrics are
+    // still useful to report, since they control the cache's behavior.
     nsresult rv;
 
-    rv = MOZ_COLLECT_REPORT("imagelib-surface-cache-total",
+    rv = MOZ_COLLECT_REPORT("imagelib-surface-cache-estimated-total",
                             KIND_OTHER, UNITS_BYTES,
-                            SizeOfSurfacesEstimate(),
-                            "Total memory used by the imagelib surface cache.");
+                            (mMaxCost - mAvailableCost),
+                            "Estimated total memory used by the imagelib "
+                            "surface cache.");
     NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = MOZ_COLLECT_REPORT("imagelib-surface-cache-locked",
+    rv = MOZ_COLLECT_REPORT("imagelib-surface-cache-estimated-locked",
                             KIND_OTHER, UNITS_BYTES,
-                            SizeOfLockedSurfacesEstimate(),
-                            "Memory used by locked surfaces in the imagelib "
-                            "surface cache.");
+                            mLockedCost,
+                            "Estimated memory used by locked surfaces in the "
+                            "imagelib surface cache.");
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
   }
 
-  // XXX(seth): This is currently only an estimate and, since we don't know
-  // which surfaces are in GPU memory and which aren't, it's reported as
-  // KIND_OTHER and will also show up in heap-unclassified. Bug 923302 will
-  // make this nicer.
-  Cost SizeOfSurfacesEstimate() const
+  size_t SizeOfSurfaces(const ImageKey    aImageKey,
+                        gfxMemoryLocation aLocation,
+                        MallocSizeOf      aMallocSizeOf)
   {
-    return mMaxCost - mAvailableCost;
+    nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
+    if (!cache) {
+      return 0;  // No surfaces for this image.
+    }
+
+    // Sum the size of all surfaces in the per-image cache.
+    CachedSurface::SizeOfSurfacesSum sum(aLocation, aMallocSizeOf);
+    cache->ForEach(DoSizeOfSurfacesSum, &sum);
+
+    return sum.Result();
   }
 
-  Cost SizeOfLockedSurfacesEstimate() const
+  static PLDHashOperator DoSizeOfSurfacesSum(const SurfaceKey&,
+                                             CachedSurface*    aSurface,
+                                             void*             aSum)
   {
-    return mLockedCost;
+    auto sum = static_cast<CachedSurface::SizeOfSurfacesSum*>(aSum);
+    sum->Add(aSurface);
+    return PL_DHASH_NEXT;
   }
 
 private:
   already_AddRefed<ImageSurfaceCache> GetImageCache(const ImageKey aImageKey)
   {
     nsRefPtr<ImageSurfaceCache> imageCache;
     mImageCaches.Get(aImageKey, getter_AddRefs(imageCache));
     return imageCache.forget();
@@ -592,17 +635,19 @@ private:
   private:
     SurfaceCacheImpl* const mCache;  // Weak pointer to owner.
   };
 
   struct MemoryPressureObserver : public nsIObserver
   {
     NS_DECL_ISUPPORTS
 
-    NS_IMETHOD Observe(nsISupports*, const char* aTopic, const char16_t*)
+    NS_IMETHOD Observe(nsISupports*,
+                       const char* aTopic,
+                       const char16_t*) MOZ_OVERRIDE
     {
       if (sInstance && strcmp(aTopic, "memory-pressure") == 0) {
         sInstance->DiscardAll();
       }
       return NS_OK;
     }
 
   private:
@@ -751,10 +796,23 @@ SurfaceCache::RemoveImage(Image* aImageK
 SurfaceCache::DiscardAll()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (sInstance) {
     sInstance->DiscardAll();
   }
 }
 
+/* static */ size_t
+SurfaceCache::SizeOfSurfaces(const ImageKey    aImageKey,
+                             gfxMemoryLocation aLocation,
+                             MallocSizeOf      aMallocSizeOf)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!sInstance) {
+    return 0;
+  }
+
+  return sInstance->SizeOfSurfaces(aImageKey, aLocation, aMallocSizeOf);
+}
+
 } // namespace image
 } // namespace mozilla
--- a/image/src/SurfaceCache.h
+++ b/image/src/SurfaceCache.h
@@ -6,23 +6,25 @@
 /**
  * SurfaceCache is a service for caching temporary surfaces and decoded image
  * data in imagelib.
  */
 
 #ifndef MOZILLA_IMAGELIB_SURFACECACHE_H_
 #define MOZILLA_IMAGELIB_SURFACECACHE_H_
 
-#include "mozilla/Maybe.h"          // for Maybe
-#include "mozilla/HashFunctions.h"  // for HashGeneric and AddToHash
-#include "gfxPoint.h"               // for gfxSize
-#include "nsCOMPtr.h"               // for already_AddRefed
-#include "mozilla/gfx/Point.h"      // for mozilla::gfx::IntSize
-#include "mozilla/gfx/2D.h"         // for SourceSurface
-#include "SVGImageContext.h"        // for SVGImageContext
+#include "mozilla/Maybe.h"           // for Maybe
+#include "mozilla/MemoryReporting.h" // for MallocSizeOf
+#include "mozilla/HashFunctions.h"   // for HashGeneric and AddToHash
+#include "gfx2DGlue.h"               // for gfxMemoryLocation
+#include "gfxPoint.h"                // for gfxSize
+#include "nsCOMPtr.h"                // for already_AddRefed
+#include "mozilla/gfx/Point.h"       // for mozilla::gfx::IntSize
+#include "mozilla/gfx/2D.h"          // for SourceSurface
+#include "SVGImageContext.h"         // for SVGImageContext
 
 namespace mozilla {
 namespace image {
 
 class DrawableFrameRef;
 class Image;
 class imgFrame;
 
@@ -288,16 +290,33 @@ struct SurfaceCache
    * Evicts all evictable surfaces from the cache.
    *
    * All surfaces are evictable except for persistent surfaces associated with
    * locked images. Non-evictable surfaces can only be removed by
    * RemoveSurface() or RemoveImage().
    */
   static void DiscardAll();
 
+  /**
+   * Computes the size of the surfaces stored for the given image at the given
+   * memory location.
+   *
+   * This is intended for use with memory reporting.
+   *
+   * @param aImageKey     The image to report memory usage for.
+   * @param aLocation     The location (heap, nonheap, etc.) of the memory to
+   *                      report on.
+   * @param aMallocSizeOf A fallback malloc memory reporting function. This
+   *                      should be null unless we're reporting on in-process
+   *                      heap memory.
+   */
+  static size_t SizeOfSurfaces(const ImageKey    aImageKey,
+                               gfxMemoryLocation aLocation,
+                               MallocSizeOf      aMallocSizeOf);
+
 private:
   virtual ~SurfaceCache() = 0;  // Forbid instantiation.
 };
 
 } // namespace image
 } // namespace mozilla
 
 #endif // MOZILLA_IMAGELIB_SURFACECACHE_H_
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -379,35 +379,40 @@ VectorImage::HeapSizeOfSourceWithCompute
 }
 
 size_t
 VectorImage::HeapSizeOfDecodedWithComputedFallback(MallocSizeOf aMallocSizeOf) const
 {
   // If implementing this, we'll need to restructure our callers to make sure
   // any amount we return is attributed to the vector images measure (i.e.
   // "explicit/images/{content,chrome}/vector/{used,unused}/...")
-  return 0;
+  // XXX(seth): Same goes for the other *SizeOfDecoded() methods. We'll do this
+  // in bug 921300 or one of its blockers. For now it seems worthwhile to get
+  // this memory accounted for, even if it gets listed under 'raster'. It does
+  // make some perverse sense, since we are after all reporting on raster data
+  // here - it just happens to be computed from a vector document.
+  return SurfaceCache::SizeOfSurfaces(ImageKey(this),
+                                      gfxMemoryLocation::IN_PROCESS_HEAP,
+                                      aMallocSizeOf);
 }
 
 size_t
 VectorImage::NonHeapSizeOfDecoded() const
 {
-  // If implementing this, we'll need to restructure our callers to make sure
-  // any amount we return is attributed to the vector images measure (i.e.
-  // "explicit/images/{content,chrome}/vector/{used,unused}/...")
-  return 0;
+  return SurfaceCache::SizeOfSurfaces(ImageKey(this),
+                                      gfxMemoryLocation::IN_PROCESS_NONHEAP,
+                                      nullptr);
 }
 
 size_t
 VectorImage::OutOfProcessSizeOfDecoded() const
 {
-  // If implementing this, we'll need to restructure our callers to make sure
-  // any amount we return is attributed to the vector images measure (i.e.
-  // "explicit/images/{content,chrome}/vector/{used,unused}/...")
-  return 0;
+  return SurfaceCache::SizeOfSurfaces(ImageKey(this),
+                                      gfxMemoryLocation::OUT_OF_PROCESS,
+                                      nullptr);
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(WindowsMallocSizeOf);
 
 size_t
 VectorImage::HeapSizeOfVectorImageDocument(nsACString* aDocURL) const
 {
   nsIDocument* doc = mSVGDocumentWrapper->GetDocument();