Bug 1171356 - On B2G, retry image decodes that fail because allocation of the first frame failed. r=tn
authorSeth Fowler <mark.seth.fowler@gmail.com>
Thu, 04 Jun 2015 11:08:17 -0700
changeset 247182 e4b22fb2ac017c1a30ffd11cc3d863d0b26d8c7c
parent 247181 89ac61464a450e47d2d6586886fcf486f74c2e9b
child 247183 061363215959d2a92aaa1eb72515c8c80af93844
push id28855
push userkwierso@gmail.com
push dateFri, 05 Jun 2015 01:19:30 +0000
treeherdermozilla-central@227d356ac030 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn
bugs1171356
milestone41.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 1171356 - On B2G, retry image decodes that fail because allocation of the first frame failed. r=tn
b2g/app/b2g.js
gfx/thebes/gfxPrefs.h
image/RasterImage.cpp
image/RasterImage.h
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -327,16 +327,17 @@ pref("media.eme.apiVisible", true);
 // The default number of decoded video frames that are enqueued in
 // MediaDecoderReader's mVideoQueue.
 pref("media.video-queue.default-size", 3);
 
 // optimize images' memory usage
 pref("image.downscale-during-decode.enabled", true);
 pref("image.decode-only-on-draw.enabled", false);
 pref("image.mem.allow_locking_in_content_processes", true);
+pref("image.decode.retry-on-alloc-failure", true);
 // Limit the surface cache to 1/8 of main memory or 128MB, whichever is smaller.
 // Almost everything that was factored into 'max_decoded_image_kb' is now stored
 // in the surface cache.  1/8 of main memory is 32MB on a 256MB device, which is
 // about the same as the old 'max_decoded_image_kb'.
 pref("image.mem.surfacecache.max_size_kb", 131072);  // 128MB
 pref("image.mem.surfacecache.size_factor", 8);  // 1/8 of main memory
 pref("image.mem.surfacecache.discard_factor", 2);  // Discard 1/2 of the surface cache at a time.
 pref("image.mem.surfacecache.min_expiration_ms", 86400000); // 24h, we rely on the out of memory hook
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -251,16 +251,17 @@ private:
 
   DECL_GFX_PREF(Live, "gl.msaa-level",                         MSAALevel, uint32_t, 2);
   DECL_GFX_PREF(Live, "gl.require-hardware",                   RequireHardwareGL, bool, false);
 
   DECL_GFX_PREF(Once, "image.cache.size",                      ImageCacheSize, int32_t, 5*1024*1024);
   DECL_GFX_PREF(Once, "image.cache.timeweight",                ImageCacheTimeWeight, int32_t, 500);
   DECL_GFX_PREF(Live, "image.decode-only-on-draw.enabled",     ImageDecodeOnlyOnDrawEnabled, bool, true);
   DECL_GFX_PREF(Live, "image.decode-immediately.enabled",      ImageDecodeImmediatelyEnabled, bool, false);
+  DECL_GFX_PREF(Once, "image.decode.retry-on-alloc-failure",   ImageDecodeRetryOnAllocFailure, bool, false);
   DECL_GFX_PREF(Live, "image.downscale-during-decode.enabled", ImageDownscaleDuringDecodeEnabled, bool, true);
   DECL_GFX_PREF(Live, "image.high_quality_downscaling.enabled", ImageHQDownscalingEnabled, bool, false);
   DECL_GFX_PREF(Live, "image.high_quality_downscaling.min_factor", ImageHQDownscalingMinFactor, uint32_t, 1000);
   DECL_GFX_PREF(Live, "image.high_quality_upscaling.max_size", ImageHQUpscalingMaxSize, uint32_t, 20971520);
   DECL_GFX_PREF(Once, "image.mem.decode_bytes_at_a_time",      ImageMemDecodeBytesAtATime, uint32_t, 200000);
   DECL_GFX_PREF(Live, "image.mem.discardable",                 ImageMemDiscardable, bool, false);
   DECL_GFX_PREF(Once, "image.mem.surfacecache.discard_factor", ImageMemSurfaceCacheDiscardFactor, uint32_t, 1);
   DECL_GFX_PREF(Once, "image.mem.surfacecache.max_size_kb",    ImageMemSurfaceCacheMaxSizeKB, uint32_t, 100 * 1024);
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -257,16 +257,17 @@ RasterImage::RasterImage(ImageURL* aURI 
   mDecodeCount(0),
   mRequestedSampleSize(0),
   mLastImageContainerDrawResult(DrawResult::NOT_READY),
 #ifdef DEBUG
   mFramesNotified(0),
 #endif
   mSourceBuffer(new SourceBuffer()),
   mFrameCount(0),
+  mRetryCount(0),
   mHasSize(false),
   mDecodeOnlyOnDraw(false),
   mTransient(false),
   mDiscardable(false),
   mHasSourceData(false),
   mHasBeenDecoded(false),
   mPendingAnimation(false),
   mAnimationFinished(false),
@@ -1337,16 +1338,41 @@ RasterImage::Discard()
 }
 
 bool
 RasterImage::CanDiscard() {
   return mHasSourceData &&       // ...have the source data...
          !mAnim;                 // Can never discard animated images
 }
 
+class RetryDecodeRunnable : public nsRunnable
+{
+public:
+  RetryDecodeRunnable(RasterImage* aImage,
+                      const IntSize& aSize,
+                      uint32_t aFlags)
+    : mImage(aImage)
+    , mSize(aSize)
+    , mFlags(aFlags)
+  {
+    MOZ_ASSERT(aImage);
+  }
+
+  NS_IMETHOD Run()
+  {
+    mImage->RequestDecodeForSize(mSize, mFlags);
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<RasterImage> mImage;
+  const IntSize mSize;
+  const uint32_t mFlags;
+};
+
 // Sets up a decoder for this image.
 already_AddRefed<Decoder>
 RasterImage::CreateDecoder(const Maybe<IntSize>& aSize, uint32_t aFlags)
 {
   // Make sure we actually get size before doing a full decode.
   if (aSize) {
     MOZ_ASSERT(mHasSize, "Must do a size decode before a full decode!");
     MOZ_ASSERT(mDownscaleDuringDecode || *aSize == mSize,
@@ -1413,16 +1439,43 @@ RasterImage::CreateDecoder(const Maybe<I
     // need a different type, they need to ask for it themselves.
     // XXX(seth): Note that we call SetSize() and NeedNewFrame() with the
     // image's intrinsic size, but AllocateFrame with our target size.
     decoder->SetSize(mSize, mOrientation);
     decoder->NeedNewFrame(0, 0, 0, aSize->width, aSize->height,
                           SurfaceFormat::B8G8R8A8);
     decoder->AllocateFrame(*aSize);
   }
+
+  if (aSize && decoder->HasError()) {
+    if (gfxPrefs::ImageDecodeRetryOnAllocFailure() &&
+        mRetryCount < 10) {
+      // We couldn't allocate the first frame for this image. We're probably in
+      // a temporary low-memory situation, so fire off a runnable and hope that
+      // things have improved when it runs. (Unless we've already retried 10
+      // times in a row, in which case just give up.)
+      mRetryCount++;
+
+      if (decoder->ImageIsLocked()) {
+        UnlockImage();
+      }
+      decoder->TakeProgress();
+      decoder->TakeInvalidRect();
+
+      nsCOMPtr<nsIRunnable> runnable =
+        new RetryDecodeRunnable(this, *aSize, aFlags);
+      NS_DispatchToMainThread(runnable);
+
+      return nullptr;
+    }
+  } else {
+    // We didn't encounter an error when allocating the first frame.
+    mRetryCount = 0;
+  }
+
   decoder->SetIterator(mSourceBuffer->Iterator());
 
   // Set a target size for downscale-during-decode if applicable.
   if (mDownscaleDuringDecode && aSize && *aSize != mSize) {
     DebugOnly<nsresult> rv = decoder->SetTargetSize(*aSize);
     MOZ_ASSERT(nsresult(rv) != NS_ERROR_NOT_AVAILABLE,
                "We're downscale-during-decode but decoder doesn't support it?");
     MOZ_ASSERT(NS_SUCCEEDED(rv), "Bad downscale-during-decode target size?");
--- a/image/RasterImage.h
+++ b/image/RasterImage.h
@@ -395,16 +395,19 @@ private: // data
 #endif
 
   // The source data for this image.
   nsRefPtr<SourceBuffer>     mSourceBuffer;
 
   // The number of frames this image has.
   uint32_t                   mFrameCount;
 
+  // The number of times we've retried decoding this image.
+  uint8_t                    mRetryCount;
+
   // Boolean flags (clustered together to conserve space):
   bool                       mHasSize:1;       // Has SetSize() been called?
   bool                       mDecodeOnlyOnDraw:1; // Decoding only on draw?
   bool                       mTransient:1;     // Is the image short-lived?
   bool                       mDiscardable:1;   // Is container discardable?
   bool                       mHasSourceData:1; // Do we have source data?
   bool                       mHasBeenDecoded:1; // Decoded at least once?
   bool                       mDownscaleDuringDecode:1;