diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index 3cf84cf0d10f..a18e52f46dad 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -332,6 +332,7 @@ pref("media.video-queue.default-size", 3); 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 diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index fce4936d812f..fbb6a33acfed 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -256,6 +256,7 @@ private: 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); diff --git a/image/RasterImage.cpp b/image/RasterImage.cpp index 939d9c84ef3c..ec026805c748 100644 --- a/image/RasterImage.cpp +++ b/image/RasterImage.cpp @@ -262,6 +262,7 @@ RasterImage::RasterImage(ImageURL* aURI /* = nullptr */) : #endif mSourceBuffer(new SourceBuffer()), mFrameCount(0), + mRetryCount(0), mHasSize(false), mDecodeOnlyOnDraw(false), mTransient(false), @@ -1342,6 +1343,31 @@ RasterImage::CanDiscard() { !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 mImage; + const IntSize mSize; + const uint32_t mFlags; +}; + // Sets up a decoder for this image. already_AddRefed RasterImage::CreateDecoder(const Maybe& aSize, uint32_t aFlags) @@ -1418,6 +1444,33 @@ RasterImage::CreateDecoder(const Maybe& aSize, uint32_t aFlags) 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 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. diff --git a/image/RasterImage.h b/image/RasterImage.h index 0075b39646f5..5fd16e3c3521 100644 --- a/image/RasterImage.h +++ b/image/RasterImage.h @@ -400,6 +400,9 @@ private: // data // 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?