Bug 980037 - Store scaled images in volatile buffers, r=seth
authorMichael Wu <mwu@mozilla.com>
Sun, 02 Mar 2014 11:17:26 -0500
changeset 191858 e09f4b94cf29e637326767738cb7727975707b35
parent 191857 efba9e32aca15069e1df351a988a66f67ee27e4d
child 191859 df384c7c679a925ea328b43cc1c1c27b37bd4bff
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersseth
bugs980037
milestone30.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 980037 - Store scaled images in volatile buffers, r=seth
image/src/RasterImage.cpp
image/src/RasterImage.h
image/src/imgFrame.cpp
image/src/imgFrame.h
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -244,16 +244,18 @@ public:
 
     nsRefPtr<RasterImage> image = weakImage.get();
     if (!image) {
       return false;
     }
 
     bool success = false;
     if (dstLocked) {
+      if (DiscardingEnabled())
+        dstFrame->SetDiscardable();
       success = NS_SUCCEEDED(dstFrame->UnlockImageData());
 
       dstLocked = false;
       srcData = nullptr;
       dstData = nullptr;
       srcSurface = nullptr;
       dstSurface = nullptr;
     }
@@ -2558,80 +2560,96 @@ RasterImage::ScalingDone(ScaleRequest* r
   // If we were waiting for this scale to come through, forget the scale
   // request. Otherwise, we still have a scale outstanding that it's possible
   // for us to (want to) stop.
   if (mScaleRequest == request) {
     mScaleRequest = nullptr;
   }
 }
 
-void
+bool
 RasterImage::DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
                                           gfxContext *aContext,
                                           GraphicsFilter aFilter,
                                           const gfxMatrix &aUserSpaceToImageSpace,
                                           const gfxRect &aFill,
                                           const nsIntRect &aSubimage,
                                           uint32_t aFlags)
 {
   imgFrame *frame = aFrame;
   nsIntRect framerect = frame->GetRect();
   gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
   gfxMatrix imageSpaceToUserSpace = aUserSpaceToImageSpace;
   imageSpaceToUserSpace.Invert();
   gfx::Size scale = ToSize(imageSpaceToUserSpace.ScaleFactors(true));
   nsIntRect subimage = aSubimage;
-
-  if (CanScale(aFilter, scale, aFlags)) {
+  nsRefPtr<gfxASurface> surf;
+
+  if (CanScale(aFilter, scale, aFlags) && !frame->IsSinglePixel()) {
     // If scale factor is still the same that we scaled for and
     // ScaleWorker isn't still working, then we can use pre-downscaled frame.
     // If scale factor has changed, order new request.
     // FIXME: Current implementation doesn't support pre-downscale
     // mechanism for multiple sizes from same src, since we cache
     // pre-downscaled frame only for the latest requested scale.
     // The solution is to cache more than one scaled image frame
     // for each RasterImage.
+    bool needScaleReq;
     if (mScaleResult.status == SCALE_DONE && mScaleResult.scale == scale) {
-      frame = mScaleResult.frame;
-      userSpaceToImageSpace.Multiply(gfxMatrix().Scale(scale.width, scale.height));
-
-      // Since we're switching to a scaled image, we need to transform the
-      // area of the subimage to draw accordingly, since imgFrame::Draw()
-      // doesn't know about scaled frames.
-      subimage.ScaleRoundOut(scale.width, scale.height);
+      // Grab and hold the surface to make sure the OS didn't destroy it
+      mScaleResult.frame->GetSurface(getter_AddRefs(surf));
+      needScaleReq = !surf;
+      if (surf) {
+        frame = mScaleResult.frame;
+        userSpaceToImageSpace.Multiply(gfxMatrix().Scale(scale.width,
+                                                         scale.height));
+
+        // Since we're switching to a scaled image, we need to transform the
+        // area of the subimage to draw accordingly, since imgFrame::Draw()
+        // doesn't know about scaled frames.
+        subimage.ScaleRoundOut(scale.width, scale.height);
+      }
+    } else {
+      needScaleReq = !(mScaleResult.status == SCALE_PENDING &&
+                       mScaleResult.scale == scale);
     }
 
     // If we're not waiting for exactly this result, and there's only one
     // instance of this image on this page, ask for a scale.
-    else if (!(mScaleResult.status == SCALE_PENDING && mScaleResult.scale == scale) &&
-             mLockCount == 1) {
-      // If we have an oustanding request, signal it to stop (if it can).
+    if (needScaleReq && mLockCount == 1) {
+      if (NS_FAILED(frame->LockImageData())) {
+        frame->UnlockImageData();
+        return false;
+      }
+
+      // If we have an outstanding request, signal it to stop (if it can).
       if (mScaleRequest) {
         mScaleRequest->stopped = true;
       }
 
       nsRefPtr<ScaleRunner> runner = new ScaleRunner(this, scale, frame);
       if (runner->IsOK()) {
         if (!sScaleWorkerThread) {
           NS_NewNamedThread("Image Scaler", getter_AddRefs(sScaleWorkerThread));
           ClearOnShutdown(&sScaleWorkerThread);
         }
 
         sScaleWorkerThread->Dispatch(runner, NS_DISPATCH_NORMAL);
       }
+      frame->UnlockImageData();
     }
   }
 
   nsIntMargin padding(framerect.y,
                       mSize.width - framerect.XMost(),
                       mSize.height - framerect.YMost(),
                       framerect.x);
 
-  frame->Draw(aContext, aFilter, userSpaceToImageSpace, aFill, padding, subimage,
-              aFlags);
+  return frame->Draw(aContext, aFilter, userSpaceToImageSpace,
+                     aFill, padding, subimage, aFlags);
 }
 
 //******************************************************************************
 /* [noscript] void draw(in gfxContext aContext,
  *                      in gfxGraphicsFilter aFilter,
  *                      [const] in gfxMatrix aUserSpaceToImageSpace,
  *                      [const] in gfxRect aFill,
  *                      [const] in nsIntRect aSubimage,
@@ -2713,29 +2731,26 @@ RasterImage::Draw(gfxContext *aContext,
 
   uint32_t frameIndex = aWhichFrame == FRAME_FIRST ? 0
                                                    : GetCurrentImgFrameIndex();
   imgFrame* frame = GetDrawableImgFrame(frameIndex);
   if (!frame) {
     return NS_OK; // Getting the frame (above) touches the image and kicks off decoding
   }
 
-  nsRefPtr<gfxASurface> surf;
-  if (!frame->IsSinglePixel()) {
-    frame->GetSurface(getter_AddRefs(surf));
-    if (!surf) {
-      // The OS threw out some or all of our buffer. Start decoding again.
-      ForceDiscard();
-      WantDecodedFrames();
-      return NS_OK;
-    }
+  bool drawn = DrawWithPreDownscaleIfNeeded(frame, aContext, aFilter,
+                                            aUserSpaceToImageSpace, aFill,
+                                            aSubimage, aFlags);
+  if (!drawn) {
+    // The OS threw out some or all of our buffer. Start decoding again.
+    ForceDiscard();
+    WantDecodedFrames();
+    return NS_OK;
   }
 
-  DrawWithPreDownscaleIfNeeded(frame, aContext, aFilter, aUserSpaceToImageSpace, aFill, aSubimage, aFlags);
-
   if (mDecoded && !mDrawStartTime.IsNull()) {
       TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY, int32_t(drawLatency.ToMicroseconds()));
       // clear the value of mDrawStartTime
       mDrawStartTime = TimeStamp();
   }
 
   return NS_OK;
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -540,17 +540,17 @@ private:
   private: /* members */
 
     nsRefPtr<RasterImage> mImage;
   };
 
   nsresult FinishedSomeDecoding(eShutdownIntent intent = eShutdownIntent_Done,
                                 DecodeRequest* request = nullptr);
 
-  void DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
+  bool DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
                                     gfxContext *aContext,
                                     GraphicsFilter aFilter,
                                     const gfxMatrix &aUserSpaceToImageSpace,
                                     const gfxRect &aFill,
                                     const nsIntRect &aSubimage,
                                     uint32_t aFlags);
 
   nsresult CopyFrame(uint32_t aWhichFrame,
--- a/image/src/imgFrame.cpp
+++ b/image/src/imgFrame.cpp
@@ -414,22 +414,23 @@ imgFrame::SurfaceWithFormat
 imgFrame::SurfaceForDrawing(bool               aDoPadding,
                             bool               aDoPartialDecode,
                             bool               aDoTile,
                             const nsIntMargin& aPadding,
                             gfxMatrix&         aUserSpaceToImageSpace,
                             gfxRect&           aFill,
                             gfxRect&           aSubimage,
                             gfxRect&           aSourceRect,
-                            gfxRect&           aImageRect)
+                            gfxRect&           aImageRect,
+                            gfxASurface*       aSurface)
 {
   IntSize size(int32_t(aImageRect.Width()), int32_t(aImageRect.Height()));
   if (!aDoPadding && !aDoPartialDecode) {
     NS_ASSERTION(!mSinglePixel, "This should already have been handled");
-    return SurfaceWithFormat(new gfxSurfaceDrawable(ThebesSurface(), ThebesIntSize(size)), mFormat);
+    return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, ThebesIntSize(size)), mFormat);
   }
 
   gfxRect available = gfxRect(mDecoded.x, mDecoded.y, mDecoded.width, mDecoded.height);
 
   if (aDoTile || mSinglePixel) {
     // Create a temporary surface.
     // Give this surface an alpha channel because there are
     // transparent pixels in the padding or undecoded area
@@ -440,17 +441,17 @@ imgFrame::SurfaceForDrawing(bool        
       return SurfaceWithFormat();
 
     // Fill 'available' with whatever we've got
     gfxContext tmpCtx(surface);
     tmpCtx.SetOperator(gfxContext::OPERATOR_SOURCE);
     if (mSinglePixel) {
       tmpCtx.SetDeviceColor(mSinglePixelColor);
     } else {
-      tmpCtx.SetSource(ThebesSurface(), gfxPoint(aPadding.left, aPadding.top));
+      tmpCtx.SetSource(aSurface, gfxPoint(aPadding.left, aPadding.top));
     }
     tmpCtx.Rectangle(available);
     tmpCtx.Fill();
 
     return SurfaceWithFormat(new gfxSurfaceDrawable(surface, ThebesIntSize(size)), format);
   }
 
   // Not tiling, and we have a surface, so we can account for
@@ -462,62 +463,69 @@ imgFrame::SurfaceForDrawing(bool        
   aFill = imageSpaceToUserSpace.Transform(aSourceRect);
 
   aSubimage = aSubimage.Intersect(available) - gfxPoint(aPadding.left, aPadding.top);
   aUserSpaceToImageSpace.Multiply(gfxMatrix().Translate(-gfxPoint(aPadding.left, aPadding.top)));
   aSourceRect = aSourceRect - gfxPoint(aPadding.left, aPadding.top);
   aImageRect = gfxRect(0, 0, mSize.width, mSize.height);
 
   gfxIntSize availableSize(mDecoded.width, mDecoded.height);
-  return SurfaceWithFormat(new gfxSurfaceDrawable(ThebesSurface(),
-                                                  availableSize),
+  return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, availableSize),
                            mFormat);
 }
 
-void imgFrame::Draw(gfxContext *aContext, GraphicsFilter aFilter,
+bool imgFrame::Draw(gfxContext *aContext, GraphicsFilter aFilter,
                     const gfxMatrix &aUserSpaceToImageSpace, const gfxRect& aFill,
                     const nsIntMargin &aPadding, const nsIntRect &aSubimage,
                     uint32_t aImageFlags)
 {
   PROFILER_LABEL("image", "imgFrame::Draw");
   NS_ASSERTION(!aFill.IsEmpty(), "zero dest size --- fix caller");
   NS_ASSERTION(!aSubimage.IsEmpty(), "zero source size --- fix caller");
   NS_ASSERTION(!mPalettedImageData, "Directly drawing a paletted image!");
 
   bool doPadding = aPadding != nsIntMargin(0,0,0,0);
   bool doPartialDecode = !ImageComplete();
 
   if (mSinglePixel && !doPadding && !doPartialDecode) {
     DoSingleColorFastPath(aContext, mSinglePixelColor, aFill);
-    return;
+    return true;
   }
 
   gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
   gfxRect sourceRect = userSpaceToImageSpace.TransformBounds(aFill);
   gfxRect imageRect(0, 0, mSize.width + aPadding.LeftRight(),
                     mSize.height + aPadding.TopBottom());
   gfxRect subimage(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
   gfxRect fill = aFill;
 
   NS_ASSERTION(!sourceRect.Intersect(subimage).IsEmpty(),
                "We must be allowed to sample *some* source pixels!");
 
+  nsRefPtr<gfxASurface> surf;
+  if (!mSinglePixel) {
+    surf = ThebesSurface();
+    if (!surf)
+      return false;
+  }
+
   bool doTile = !imageRect.Contains(sourceRect) &&
                 !(aImageFlags & imgIContainer::FLAG_CLAMP);
   SurfaceWithFormat surfaceResult =
     SurfaceForDrawing(doPadding, doPartialDecode, doTile, aPadding,
                       userSpaceToImageSpace, fill, subimage, sourceRect,
-                      imageRect);
+                      imageRect, surf);
 
   if (surfaceResult.IsValid()) {
     gfxUtils::DrawPixelSnapped(aContext, surfaceResult.mDrawable,
                                userSpaceToImageSpace,
                                subimage, sourceRect, imageRect, fill,
                                surfaceResult.mFormat, aFilter, aImageFlags);
   }
+  return true;
 }
 
 // This can be called from any thread, but not simultaneously.
 nsresult imgFrame::ImageUpdated(const nsIntRect &aUpdateRect)
 {
   MutexAutoLock lock(mDirtyMutex);
 
   mDecoded.UnionRect(mDecoded, aUpdateRect);
--- a/image/src/imgFrame.h
+++ b/image/src/imgFrame.h
@@ -44,17 +44,17 @@ class imgFrame
 {
 public:
   imgFrame();
   ~imgFrame();
 
   nsresult Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, gfxImageFormat aFormat, uint8_t aPaletteDepth = 0);
   nsresult Optimize();
 
-  void Draw(gfxContext *aContext, GraphicsFilter aFilter,
+  bool Draw(gfxContext *aContext, GraphicsFilter aFilter,
             const gfxMatrix &aUserSpaceToImageSpace, const gfxRect& aFill,
             const nsIntMargin &aPadding, const nsIntRect &aSubimage,
             uint32_t aImageFlags = imgIContainer::FLAG_NONE);
 
   nsresult ImageUpdated(const nsIntRect &aUpdateRect);
   bool GetIsDirty() const;
 
   nsIntRect GetRect() const;
@@ -177,17 +177,18 @@ private: // methods
   SurfaceWithFormat SurfaceForDrawing(bool               aDoPadding,
                                       bool               aDoPartialDecode,
                                       bool               aDoTile,
                                       const nsIntMargin& aPadding,
                                       gfxMatrix&         aUserSpaceToImageSpace,
                                       gfxRect&           aFill,
                                       gfxRect&           aSubimage,
                                       gfxRect&           aSourceRect,
-                                      gfxRect&           aImageRect);
+                                      gfxRect&           aImageRect,
+                                      gfxASurface*       aSurface);
 
 private: // data
   nsRefPtr<gfxImageSurface> mImageSurface;
   nsRefPtr<gfxASurface> mOptSurface;
 #if defined(XP_WIN)
   nsRefPtr<gfxWindowsSurface> mWinSurface;
 #elif defined(XP_MACOSX)
   nsRefPtr<gfxQuartzImageSurface> mQuartzSurface;