Bug 795940 - Part 0.3 - Support stopping requests (on a best-effort basis). r=jrmuizel
authorJoe Drew <joe@drew.ca>
Fri, 12 Oct 2012 18:20:22 -0400
changeset 118494 510cab1158baced8c9dd8903a6e03899e57b1c86
parent 118493 05a2876b0d0264e02f424751558bae6d5024cf0d
child 118495 6bd34e2bfafc0ef93ccc7eae46f8a91204ce578b
push id1997
push userakeybl@mozilla.com
push dateMon, 07 Jan 2013 21:25:26 +0000
treeherdermozilla-beta@4baf45cdcf21 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs795940
milestone19.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 795940 - Part 0.3 - Support stopping requests (on a best-effort basis). r=jrmuizel
image/src/RasterImage.cpp
image/src/RasterImage.h
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -174,17 +174,18 @@ RasterImage::RasterImage(imgStatusTracke
   mMultipart(false),
   mDiscardable(false),
   mHasSourceData(false),
   mDecoded(false),
   mHasBeenDecoded(false),
   mInDecoder(false),
   mAnimationFinished(false),
   mFinishing(false),
-  mInUpdateImageContainer(false)
+  mInUpdateImageContainer(false),
+  mScaleRequest(nullptr)
 {
   // Set up the discard tracker node.
   mDiscardTrackerNode.img = this;
   Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(0);
 
   // Statistics
   num_containers++;
 }
@@ -2618,19 +2619,23 @@ RasterImage::SyncDecode()
 }
 
 nsresult
 RasterImage::ScaleRunner::Run()
 {
   // An alias just for ease of typing
   ScaleRequest* request = mScaleRequest;
 
-  request->done = mozilla::gfx::Scale(request->srcData, request->srcRect.width, request->srcRect.height, request->srcStride,
-                                      request->dstData, request->dstSize.width, request->dstSize.height, request->dstStride,
-                                      request->srcFormat);
+  if (!request->stopped) {
+    request->done = mozilla::gfx::Scale(request->srcData, request->srcRect.width, request->srcRect.height, request->srcStride,
+                                        request->dstData, request->dstSize.width, request->dstSize.height, request->dstStride,
+                                        request->srcFormat);
+  } else {
+    request->done = false;
+  }
 
   // OK, we've got a new scaled image. Let's get the main thread to unlock and
   // redraw it.
   DrawRunner* runner = new DrawRunner(mScaleRequest.forget());
   NS_DispatchToMainThread(runner, NS_DISPATCH_NORMAL);
 
   return NS_OK;
 }
@@ -2644,52 +2649,52 @@ RasterImage::ScaleRunner::ScaleRunner(Ra
   request->dstFrame = new imgFrame();
   nsresult rv = request->dstFrame->Init(0, 0, request->dstSize.width, request->dstSize.height,
                                         gfxASurface::ImageFormatARGB32);
 
   if (NS_FAILED(rv) || !request->GetSurfaces(aSrcFrame)) {
     return;
   }
 
-  aImage->SetResultPending(request);
+  aImage->ScalingStart(request);
 
   mScaleRequest = request;
 }
 
 RasterImage::DrawRunner::DrawRunner(ScaleRequest* request)
  : mScaleRequest(request)
 {}
 
 nsresult
 RasterImage::DrawRunner::Run()
 {
   // ScaleWorker is finished with this request, so we can unlock the data now.
   mScaleRequest->ReleaseSurfaces();
 
+  nsRefPtr<RasterImage> image = mScaleRequest->weakImage.get();
+
   // Only set the scale result if the request finished successfully.
-  if (mScaleRequest->done) {
-    RasterImage* image = mScaleRequest->weakImage;
-    if (image) {
-      nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(image->mObserver));
-      if (observer) {
-        imgFrame *scaledFrame = mScaleRequest->dstFrame.get();
-        scaledFrame->ImageUpdated(scaledFrame->GetRect());
-        observer->FrameChanged(&mScaleRequest->srcRect);
-      }
-
-      image->SetScaleResult(mScaleRequest);
+  if (mScaleRequest->done && image) {
+    nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(image->mObserver));
+    if (observer) {
+      imgFrame *scaledFrame = mScaleRequest->dstFrame.get();
+      scaledFrame->ImageUpdated(scaledFrame->GetRect());
+      observer->FrameChanged(&mScaleRequest->srcRect);
     }
   }
 
-  // Scaling failed. Reset the scale result on our image.
-  else {
-    RasterImage* image = mScaleRequest->weakImage;
-    if (image) {
-      image->SetScaleResult(nullptr);
+  if (image) {
+    ScaleStatus status;
+    if (mScaleRequest->done) {
+      status = SCALE_DONE;
+    } else {
+      status = SCALE_INVALID;
     }
+
+    image->ScalingDone(mScaleRequest, status);
   }
 
   return NS_OK;
 }
 
 static inline bool
 IsDownscale(const gfxSize& scale)
 {
@@ -2715,35 +2720,46 @@ RasterImage::CanScale(gfxPattern::Graphi
     return (aScale.width < factor || aScale.height < factor);
   }
 #endif
 
   return false;
 }
 
 void
-RasterImage::SetScaleResult(ScaleRequest* request)
+RasterImage::ScalingStart(ScaleRequest* request)
 {
-  if (request) {
+  MOZ_ASSERT(request);
+  mScaleResult.scale = request->scale;
+  mScaleResult.status = SCALE_PENDING;
+  mScaleRequest = request;
+}
+
+void
+RasterImage::ScalingDone(ScaleRequest* request, ScaleStatus status)
+{
+  MOZ_ASSERT(status == SCALE_DONE || status == SCALE_INVALID);
+  MOZ_ASSERT(request);
+
+  if (status == SCALE_DONE) {
     MOZ_ASSERT(request->done);
     mScaleResult.status = SCALE_DONE;
     mScaleResult.frame = request->dstFrame;
     mScaleResult.scale = request->scale;
   } else {
     mScaleResult.status = SCALE_INVALID;
     mScaleResult.frame = nullptr;
   }
-}
-
-void
-RasterImage::SetResultPending(ScaleRequest* request)
-{
-  MOZ_ASSERT(request);
-  mScaleResult.scale = request->scale;
-  mScaleResult.status = SCALE_PENDING;
+
+  // 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
 RasterImage::DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
                                           gfxContext *aContext,
                                           gfxPattern::GraphicsFilter aFilter,
                                           const gfxMatrix &aUserSpaceToImageSpace,
                                           const gfxRect &aFill,
@@ -2773,16 +2789,21 @@ RasterImage::DrawWithPreDownscaleIfNeede
       // Since we're switching to a scaled image, we 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);
     }
 
     // If we're not waiting for exactly this result, ask for it.
     else if (!(mScaleResult.status == SCALE_PENDING && mScaleResult.scale == scale)) {
+      // If we have an oustanding 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);
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -474,16 +474,17 @@ private:
   };
 
   struct ScaleRequest
   {
     ScaleRequest(RasterImage* aImage, const gfxSize& aScale, imgFrame* aSrcFrame)
       : scale(aScale)
       , dstLocked(false)
       , done(false)
+      , stopped(false)
     {
       MOZ_ASSERT(!aSrcFrame->GetIsPaletted());
       MOZ_ASSERT(aScale.width > 0 && aScale.height > 0);
 
       weakImage = aImage->asWeakPtr();
       srcRect = aSrcFrame->GetRect();
       dstSize.width = NSToIntRoundUp(srcRect.width * scale.width);
       dstSize.height = NSToIntRoundUp(srcRect.height * scale.height);
@@ -569,16 +570,20 @@ private:
     uint8_t* dstData;
     nsIntRect srcRect;
     gfxIntSize dstSize;
     uint32_t srcStride;
     uint32_t dstStride;
     mozilla::gfx::SurfaceFormat srcFormat;
     bool dstLocked;
     bool done;
+    // This boolean is accessed from both threads simultaneously without locking.
+    // That's safe because stopping a ScaleRequest is strictly an optimization;
+    // if we're not cache-coherent, at worst we'll do extra work.
+    bool stopped;
   };
 
   enum ScaleStatus
   {
     SCALE_INVALID,
     SCALE_PENDING,
     SCALE_DONE
   };
@@ -620,23 +625,25 @@ private:
 
   void DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
                                     gfxContext *aContext,
                                     gfxPattern::GraphicsFilter aFilter,
                                     const gfxMatrix &aUserSpaceToImageSpace,
                                     const gfxRect &aFill,
                                     const nsIntRect &aSubimage);
 
-  // Call this with a finished ScaleRequest to set this RasterImage's scale
-  // result, or nullptr to reset the scale result.
-  void SetScaleResult(ScaleRequest* request);
+  // Call this with a new ScaleRequest to mark this RasterImage's scale result
+  // as waiting for the results of this request. You call to ScalingDone before
+  // request is destroyed!
+  void ScalingStart(ScaleRequest* request);
 
-  // Call this with a new ScaleRequest to mark this RasterImage's scale result
-  // as waiting for the results of this request.
-  void SetResultPending(ScaleRequest* request);
+  // Call this with a finished ScaleRequest to set this RasterImage's scale
+  // result. Give it a ScaleStatus of SCALE_DONE if everything succeeded, and
+  // SCALE_INVALID otherwise.
+  void ScalingDone(ScaleRequest* request, ScaleStatus status);
 
   /**
    * Advances the animation. Typically, this will advance a single frame, but it
    * may advance multiple frames. This may happen if we have infrequently
    * "ticking" refresh drivers (e.g. in background tabs), or extremely short-
    * lived animation frames.
    *
    * @param aTime the time that the animation should advance to. This will
@@ -841,16 +848,21 @@ private: // data
   nsresult WriteToDecoder(const char *aBuffer, uint32_t aCount);
   nsresult DecodeSomeData(uint32_t aMaxBytes);
   bool     IsDecodeFinished();
   TimeStamp mDrawStartTime;
 
   inline bool CanScale(gfxPattern::GraphicsFilter aFilter, gfxSize aScale);
   ScaleResult mScaleResult;
 
+  // We hold on to a bare pointer to a ScaleRequest while it's outstanding so
+  // we can mark it as stopped if necessary. The ScaleWorker/DrawWorker duo
+  // will inform us when to let go of this pointer.
+  ScaleRequest* mScaleRequest;
+
   // Decoder shutdown
   enum eShutdownIntent {
     eShutdownIntent_Done        = 0,
     eShutdownIntent_Interrupted = 1,
     eShutdownIntent_Error       = 2,
     eShutdownIntent_AllCount    = 3
   };
   nsresult ShutdownDecoder(eShutdownIntent aIntent);