Bug 1054079 (Part 3) - Store imgFrame objects, not SourceSurface objects, in the SurfaceCache. r=dholbert
authorSeth Fowler <seth@mozilla.com>
Sun, 14 Sep 2014 15:22:45 -0700
changeset 205278 3bfa3ac98ba1fb74a8dcea08e0af1a3c39f5b731
parent 205277 721e6ae47dbc41804480f7c66debdd9efaaabc11
child 205279 5876e536d6289badbb787b8261151a302af66e99
push id27483
push usercbook@mozilla.com
push dateMon, 15 Sep 2014 12:07:08 +0000
treeherdermozilla-central@77127f875559 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdholbert
bugs1054079
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 1054079 (Part 3) - Store imgFrame objects, not SourceSurface objects, in the SurfaceCache. r=dholbert
image/src/SurfaceCache.cpp
image/src/SurfaceCache.h
image/src/VectorImage.cpp
image/src/VectorImage.h
--- a/image/src/SurfaceCache.cpp
+++ b/image/src/SurfaceCache.cpp
@@ -7,24 +7,25 @@
  * SurfaceCache is a service for caching temporary surfaces in imagelib.
  */
 
 #include "SurfaceCache.h"
 
 #include <algorithm>
 #include "mozilla/Attributes.h"  // for MOZ_THIS_IN_INITIALIZER_LIST
 #include "mozilla/DebugOnly.h"
+#include "mozilla/Move.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/StaticPtr.h"
 #include "nsIMemoryReporter.h"
 #include "gfx2DGlue.h"
 #include "gfxPattern.h"  // Workaround for flaw in bug 921753 part 2.
-#include "gfxDrawable.h"
 #include "gfxPlatform.h"
+#include "imgFrame.h"
 #include "nsAutoPtr.h"
 #include "nsExpirationTracker.h"
 #include "nsHashKeys.h"
 #include "nsRefPtrHashtable.h"
 #include "nsSize.h"
 #include "nsTArray.h"
 #include "prsystem.h"
 #include "SVGImageContext.h"
@@ -112,50 +113,48 @@ private:
  * surface.
  */
 class CachedSurface
 {
   ~CachedSurface() {}
 public:
   NS_INLINE_DECL_REFCOUNTING(CachedSurface)
 
-  CachedSurface(SourceSurface*    aSurface,
+  CachedSurface(imgFrame*         aSurface,
                 const IntSize     aTargetSize,
                 const Cost        aCost,
                 const ImageKey    aImageKey,
                 const SurfaceKey& aSurfaceKey)
     : mSurface(aSurface)
     , mTargetSize(aTargetSize)
     , mCost(aCost)
     , mImageKey(aImageKey)
     , mSurfaceKey(aSurfaceKey)
   {
     MOZ_ASSERT(mSurface, "Must have a valid SourceSurface");
     MOZ_ASSERT(mImageKey, "Must have a valid image key");
   }
 
-  already_AddRefed<gfxDrawable> Drawable() const
+  DrawableFrameRef DrawableRef() const
   {
-    nsRefPtr<gfxDrawable> drawable =
-      new gfxSurfaceDrawable(mSurface, ThebesIntSize(mTargetSize));
-    return drawable.forget();
+    return mSurface->DrawableRef();
   }
 
   ImageKey GetImageKey() const { return mImageKey; }
   SurfaceKey GetSurfaceKey() const { return mSurfaceKey; }
   CostEntry GetCostEntry() { return image::CostEntry(this, mCost); }
   nsExpirationState* GetExpirationState() { return &mExpirationState; }
 
 private:
-  nsExpirationState       mExpirationState;
-  nsRefPtr<SourceSurface> mSurface;
-  const IntSize           mTargetSize;
-  const Cost              mCost;
-  const ImageKey          mImageKey;
-  const SurfaceKey        mSurfaceKey;
+  nsExpirationState  mExpirationState;
+  nsRefPtr<imgFrame> mSurface;
+  const IntSize      mTargetSize;
+  const Cost         mCost;
+  const ImageKey     mImageKey;
+  const SurfaceKey   mSurfaceKey;
 };
 
 /*
  * An ImageSurfaceCache is a per-image surface cache. For correctness we must be
  * able to remove all surfaces associated with an image when the image is
  * destroyed or invalidated. Since this will happen frequently, it makes sense
  * to make it cheap by storing the surfaces for each image separately.
  */
@@ -235,24 +234,24 @@ private:
     UnregisterWeakMemoryReporter(this);
   }
 
 public:
   void InitMemoryReporter() {
     RegisterWeakMemoryReporter(this);
   }
 
-  void Insert(SourceSurface*    aSurface,
+  void Insert(imgFrame*         aSurface,
               IntSize           aTargetSize,
               const Cost        aCost,
               const ImageKey    aImageKey,
               const SurfaceKey& aSurfaceKey)
   {
-    MOZ_ASSERT(!Lookup(aImageKey, aSurfaceKey).take(),
-               "Inserting a duplicate drawable into the SurfaceCache");
+    MOZ_ASSERT(!Lookup(aImageKey, aSurfaceKey),
+               "Inserting a duplicate surface into the SurfaceCache");
 
     // If this is bigger than the maximum cache size, refuse to cache it.
     if (!CanHold(aCost))
       return;
 
     nsRefPtr<CachedSurface> surface =
       new CachedSurface(aSurface, aTargetSize, aCost, aImageKey, aSurfaceKey);
 
@@ -312,29 +311,37 @@ public:
     mExpirationTracker.RemoveObject(aSurface);
     DebugOnly<bool> foundInCosts = mCosts.RemoveElementSorted(costEntry);
     mAvailableCost += costEntry.GetCost();
 
     MOZ_ASSERT(foundInCosts, "Lost track of costs for this surface");
     MOZ_ASSERT(mAvailableCost <= mMaxCost, "More available cost than we started with");
   }
 
-  already_AddRefed<gfxDrawable> Lookup(const ImageKey    aImageKey,
-                                       const SurfaceKey& aSurfaceKey)
+  DrawableFrameRef Lookup(const ImageKey    aImageKey,
+                          const SurfaceKey& aSurfaceKey)
   {
     nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache)
-      return nullptr;  // No cached surfaces for this image.
-    
+      return DrawableFrameRef();  // No cached surfaces for this image.
+
     nsRefPtr<CachedSurface> surface = cache->Lookup(aSurfaceKey);
     if (!surface)
-      return nullptr;  // Lookup in the per-image cache missed.
-    
+      return DrawableFrameRef();  // Lookup in the per-image cache missed.
+
+    DrawableFrameRef ref = surface->DrawableRef();
+    if (!ref) {
+      // The surface was released by the operating system. Remove the cache
+      // entry as well.
+      Remove(surface);
+      return DrawableFrameRef();
+    }
+
     mExpirationTracker.MarkUsed(surface);
-    return surface->Drawable();
+    return ref;
   }
 
   bool CanHold(const Cost aCost) const
   {
     return aCost <= mMaxCost;
   }
 
   void Discard(const ImageKey aImageKey)
@@ -492,28 +499,28 @@ SurfaceCache::Initialize()
 
 /* static */ void
 SurfaceCache::Shutdown()
 {
   MOZ_ASSERT(sInstance, "No singleton - was Shutdown() called twice?");
   sInstance = nullptr;
 }
 
-/* static */ already_AddRefed<gfxDrawable>
+/* static */ DrawableFrameRef
 SurfaceCache::Lookup(const ImageKey    aImageKey,
                      const SurfaceKey& aSurfaceKey)
 {
   MOZ_ASSERT(sInstance, "Should be initialized");
   MOZ_ASSERT(NS_IsMainThread());
 
   return sInstance->Lookup(aImageKey, aSurfaceKey);
 }
 
 /* static */ void
-SurfaceCache::Insert(SourceSurface*    aSurface,
+SurfaceCache::Insert(imgFrame*         aSurface,
                      const ImageKey    aImageKey,
                      const SurfaceKey& aSurfaceKey)
 {
   MOZ_ASSERT(sInstance, "Should be initialized");
   MOZ_ASSERT(NS_IsMainThread());
 
   Cost cost = ComputeCost(aSurfaceKey.Size());
   return sInstance->Insert(aSurface, aSurfaceKey.Size(), cost, aImageKey,
--- a/image/src/SurfaceCache.h
+++ b/image/src/SurfaceCache.h
@@ -1,38 +1,35 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /**
- * SurfaceCache is a service for caching temporary surfaces in imagelib.
+ * 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
 
-class gfxDrawable;
-
 namespace mozilla {
-
-namespace gfx {
-class DrawTarget;
-} // namespace gfx
-
 namespace image {
 
+class DrawableFrameRef;
 class Image;
+class imgFrame;
 
 /*
  * ImageKey contains the information we need to look up all cached surfaces for
  * a particular image.
  */
 typedef Image* ImageKey;
 
 /*
@@ -85,16 +82,21 @@ private:
   uint32_t               mFlags;
 };
 
 /**
  * SurfaceCache is an imagelib-global service that allows caching of temporary
  * surfaces. Surfaces expire from the cache automatically if they go too long
  * without being accessed.
  *
+ * SurfaceCache does not hold surfaces directly; instead, it holds imgFrame
+ * objects, which hold surfaces but also layer on additional features specific
+ * to imagelib's needs like animation, padding support, and transparent support
+ * for volatile buffers.
+ *
  * SurfaceCache is not thread-safe; it should only be accessed from the main
  * thread.
  */
 struct SurfaceCache
 {
   typedef gfx::IntSize IntSize;
 
   /*
@@ -103,37 +105,43 @@ struct SurfaceCache
   static void Initialize();
 
   /*
    * Release static data. Called during imagelib module shutdown.
    */
   static void Shutdown();
 
   /*
-   * Look up a surface in the cache.
+   * Look up the imgFrame containing a surface in the cache and returns a
+   * drawable reference to that imgFrame.
+   *
+   * If the imgFrame was found in the cache, but had stored its surface in a
+   * volatile buffer which was discarded by the OS, then it is automatically
+   * removed from the cache and an empty DrawableFrameRef is returned.
    *
    * @param aImageKey    Key data identifying which image the surface belongs to.
    * @param aSurfaceKey  Key data which uniquely identifies the requested surface.
    *
-   * @return the requested surface, or nullptr if not found.
+   * @return a DrawableFrameRef to the imgFrame wrapping the requested surface,
+   *         or an empty DrawableFrameRef if not found.
    */
-  static already_AddRefed<gfxDrawable> Lookup(const ImageKey    aImageKey,
-                                              const SurfaceKey& aSurfaceKey);
+  static DrawableFrameRef Lookup(const ImageKey    aImageKey,
+                                 const SurfaceKey& aSurfaceKey);
 
   /*
    * Insert a surface into the cache. It is an error to call this function
    * without first calling Lookup to verify that the surface is not already in
    * the cache.
    *
-   * @param aTarget      The new surface (in the form of a DrawTarget) to insert
-   *                     into the cache.
+   * @param aTarget      The new surface (wrapped in an imgFrame) to insert into
+   *                     the cache.
    * @param aImageKey    Key data identifying which image the surface belongs to.
    * @param aSurfaceKey  Key data which uniquely identifies the requested surface.
    */
-  static void Insert(gfx::SourceSurface*  aSurface,
+  static void Insert(imgFrame*         aSurface,
                      const ImageKey    aImageKey,
                      const SurfaceKey& aSurfaceKey);
 
   /*
    * Checks if a surface of a given size could possibly be stored in the cache.
    * If CanHold() returns false, Insert() will always fail to insert the
    * surface, but the inverse is not true: Insert() may take more information
    * into account than just image size when deciding whether to cache the
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -6,16 +6,17 @@
 #include "VectorImage.h"
 
 #include "gfx2DGlue.h"
 #include "gfxContext.h"
 #include "gfxDrawable.h"
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
 #include "imgDecoderObserver.h"
+#include "imgFrame.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/SVGSVGElement.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
 #include "nsIDOMEvent.h"
 #include "nsIPresShell.h"
 #include "nsIStreamListener.h"
@@ -837,37 +838,40 @@ VectorImage::Draw(gfxContext* aContext,
   AutoSVGRenderingState autoSVGState(aSVGContext, animTime,
                                      mSVGDocumentWrapper->GetRootSVGElem());
 
   // Pack up the drawing parameters.
   SVGDrawingParameters params(aContext, aSize, aRegion, aFilter,
                               aSVGContext, animTime, aFlags);
 
   if (aFlags & FLAG_BYPASS_SURFACE_CACHE) {
-    CreateDrawableAndShow(params);
+    CreateSurfaceAndShow(params);
     return NS_OK;
   }
 
-  nsRefPtr<gfxDrawable> drawable =
+  DrawableFrameRef frameRef =
     SurfaceCache::Lookup(ImageKey(this),
                          SurfaceKey(params.size, aSVGContext,
                                     animTime, aFlags));
 
   // Draw.
-  if (drawable) {
-    Show(drawable, params);
+  if (frameRef) {
+    RefPtr<SourceSurface> surface = frameRef->GetSurface();
+    nsRefPtr<gfxDrawable> svgDrawable =
+      new gfxSurfaceDrawable(surface, ThebesIntSize(frameRef->GetSize()));
+    Show(svgDrawable, params);
   } else {
-    CreateDrawableAndShow(params);
+    CreateSurfaceAndShow(params);
   }
 
   return NS_OK;
 }
 
 void
-VectorImage::CreateDrawableAndShow(const SVGDrawingParameters& aParams)
+VectorImage::CreateSurfaceAndShow(const SVGDrawingParameters& aParams)
 {
   mSVGDocumentWrapper->UpdateViewportBounds(aParams.viewportSize);
   mSVGDocumentWrapper->FlushImageTransformInvalidation();
 
   nsRefPtr<gfxDrawingCallback> cb =
     new SVGDrawingCallback(mSVGDocumentWrapper,
                            nsIntRect(nsIntPoint(0, 0), aParams.viewportSize),
                            aParams.size,
@@ -880,48 +884,42 @@ VectorImage::CreateDrawableAndShow(const
                      // Refuse to cache animated images:
                      // XXX(seth): We may remove this restriction in bug 922893.
                      mHaveAnimations ||
                      // The image is too big to fit in the cache:
                      !SurfaceCache::CanHold(aParams.size);
   if (bypassCache)
     return Show(svgDrawable, aParams);
 
-  // Try to create an offscreen surface.
-  RefPtr<gfx::DrawTarget> target =
-    gfxPlatform::GetPlatform()->
-      CreateOffscreenContentDrawTarget(aParams.size,
-                                       gfx::SurfaceFormat::B8G8R8A8);
+  // Try to create an imgFrame, initializing the surface it contains by drawing
+  // our gfxDrawable into it. (We use FILTER_NEAREST since we never scale here.)
+  nsRefPtr<imgFrame> frame = new imgFrame;
+  nsresult rv =
+    frame->InitWithDrawable(svgDrawable, ThebesIntSize(aParams.size),
+                            SurfaceFormat::B8G8R8A8,
+                            GraphicsFilter::FILTER_NEAREST, aParams.flags);
 
-  // If we couldn't create the draw target, it was probably because it would end
+  // If we couldn't create the frame, it was probably because it would end
   // up way too big. Generally it also wouldn't fit in the cache, but the prefs
   // could be set such that the cache isn't the limiting factor.
-  if (!target)
+  if (NS_FAILED(rv))
     return Show(svgDrawable, aParams);
 
-  nsRefPtr<gfxContext> ctx = new gfxContext(target);
+  // Take a strong reference to the frame's surface and make sure it hasn't
+  // already been purged by the operating system.
+  RefPtr<SourceSurface> surface = frame->GetSurface();
+  if (!surface)
+    return Show(svgDrawable, aParams);
 
-  // Actually draw. (We use FILTER_NEAREST since we never scale here.)
-  nsIntRect imageRect(ThebesIntRect(aParams.imageRect));
-  gfxUtils::DrawPixelSnapped(ctx, svgDrawable,
-                             ThebesIntSize(aParams.size),
-                             ImageRegion::Create(imageRect),
-                             SurfaceFormat::B8G8R8A8,
-                             GraphicsFilter::FILTER_NEAREST, aParams.flags);
-
-  RefPtr<SourceSurface> surface = target->Snapshot();
-
-  // Attempt to cache the resulting surface.
-  SurfaceCache::Insert(surface, ImageKey(this),
+  // Attempt to cache the frame.
+  SurfaceCache::Insert(frame, ImageKey(this),
                        SurfaceKey(aParams.size, aParams.svgContext,
                                   aParams.animationTime, aParams.flags));
 
-  // Draw. Note that if SurfaceCache::Insert failed for whatever reason,
-  // then |target| is all that is keeping the pixel data alive, so we have
-  // to draw before returning from this function.
+  // Draw.
   nsRefPtr<gfxDrawable> drawable =
     new gfxSurfaceDrawable(surface, ThebesIntSize(aParams.size));
   Show(drawable, aParams);
 }
 
 
 void
 VectorImage::Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams)
--- a/image/src/VectorImage.h
+++ b/image/src/VectorImage.h
@@ -81,17 +81,17 @@ protected:
   explicit VectorImage(imgStatusTracker* aStatusTracker = nullptr,
                        ImageURL* aURI = nullptr);
   virtual ~VectorImage();
 
   virtual nsresult StartAnimation();
   virtual nsresult StopAnimation();
   virtual bool     ShouldAnimate();
 
-  void CreateDrawableAndShow(const SVGDrawingParameters& aParams);
+  void CreateSurfaceAndShow(const SVGDrawingParameters& aParams);
   void Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams);
 
 private:
   void CancelAllListeners();
   void SendInvalidationNotifications();
 
   nsRefPtr<SVGDocumentWrapper>       mSVGDocumentWrapper;
   nsRefPtr<SVGRootRenderingObserver> mRenderingObserver;