From b18fc05e364e4c7192df11bc75177798da92b382 Mon Sep 17 00:00:00 2001 From: Andrew Osmond Date: Thu, 22 Feb 2018 14:26:29 -0500 Subject: [PATCH] Bug 1427639 - Part 1. Add preferences to control image frame allocations in volatile memory or the heap. r=tnikkel image.mem.volatile.min_threshold_kb is the minimum buffer allocation for an image frame in KB before it will use volatile memory. If it is less than it will use the heap. This only is set to > 0 on Android. image.mem.animated.use_heap forces image frames to use the heap if it is for an animated image. This is only enabled for Android, and was previously a compile time option also for Android. --- gfx/2d/SourceSurfaceRawData.h | 12 ++++----- gfx/thebes/gfxPrefs.h | 2 ++ image/imgFrame.cpp | 50 ++++++++++++++++++++++++----------- modules/libpref/init/all.js | 16 +++++++++++ 4 files changed, 58 insertions(+), 22 deletions(-) diff --git a/gfx/2d/SourceSurfaceRawData.h b/gfx/2d/SourceSurfaceRawData.h index 250a53d6affc..0db51fb877a6 100644 --- a/gfx/2d/SourceSurfaceRawData.h +++ b/gfx/2d/SourceSurfaceRawData.h @@ -115,6 +115,12 @@ public: MOZ_ASSERT(mMapCount == 0); } + bool Init(const IntSize &aSize, + SurfaceFormat aFormat, + bool aClearMem, + uint8_t aClearValue, + int32_t aStride = 0); + virtual uint8_t* GetData() override { return mArray; } virtual int32_t Stride() override { return mStride; } @@ -142,12 +148,6 @@ public: private: friend class Factory; - bool Init(const IntSize &aSize, - SurfaceFormat aFormat, - bool aClearMem, - uint8_t aClearValue, - int32_t aStride = 0); - AlignedArray mArray; int32_t mStride; SurfaceFormat mFormat; diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index aff69bfa882b..10326be132bf 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -531,11 +531,13 @@ private: 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.animated.discardable", ImageMemAnimatedDiscardable, bool, false); + DECL_GFX_PREF(Live, "image.mem.animated.use_heap", ImageMemAnimatedUseHeap, bool, false); DECL_OVERRIDE_PREF(Live, "image.mem.shared", ImageMemShared, gfxPrefs::WebRenderAll()); 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); DECL_GFX_PREF(Once, "image.mem.surfacecache.min_expiration_ms", ImageMemSurfaceCacheMinExpirationMS, uint32_t, 60*1000); DECL_GFX_PREF(Once, "image.mem.surfacecache.size_factor", ImageMemSurfaceCacheSizeFactor, uint32_t, 64); + DECL_GFX_PREF(Live, "image.mem.volatile.min_threshold_kb", ImageMemVolatileMinThresholdKB, int32_t, -1); DECL_GFX_PREF(Once, "image.multithreaded_decoding.limit", ImageMTDecodingLimit, int32_t, -1); DECL_GFX_PREF(Once, "image.multithreaded_decoding.idle_timeout", ImageMTDecodingIdleTimeout, int32_t, -1); diff --git a/image/imgFrame.cpp b/image/imgFrame.cpp index 0d1350f8bd1b..53bd9f225f1a 100644 --- a/image/imgFrame.cpp +++ b/image/imgFrame.cpp @@ -19,6 +19,7 @@ #include "MainThreadUtils.h" #include "mozilla/CheckedInt.h" #include "mozilla/gfx/Tools.h" +#include "mozilla/gfx/SourceSurfaceRawData.h" #include "mozilla/layers/SourceSurfaceSharedData.h" #include "mozilla/layers/SourceSurfaceVolatileData.h" #include "mozilla/Likely.h" @@ -26,10 +27,6 @@ #include "nsMargin.h" #include "nsThreadUtils.h" -#ifdef ANDROID -#define ANIMATED_FRAMES_USE_HEAP -#endif - namespace mozilla { using namespace gfx; @@ -80,6 +77,33 @@ CreateLockedSurface(DataSourceSurface *aSurface, return nullptr; } +static bool +ShouldUseHeap(const IntSize& aSize, + int32_t aStride, + bool aIsAnimated) +{ + // On some platforms (i.e. Android), a volatile buffer actually keeps a file + // handle active. We would like to avoid too many since we could easily + // exhaust the pool. However, other platforms we do not have the file handle + // problem, and additionally we may avoid a superfluous memset since the + // volatile memory starts out as zero-filled. Hence the knobs below. + + // For as long as an animated image is retained, its frames will never be + // released to let the OS purge volatile buffers. + if (aIsAnimated && gfxPrefs::ImageMemAnimatedUseHeap()) { + return true; + } + + // Lets us avoid too many small images consuming all of the handles. The + // actual allocation checks for overflow. + int32_t bufferSize = (aStride * aSize.width) / 1024; + if (bufferSize < gfxPrefs::ImageMemVolatileMinThresholdKB()) { + return true; + } + + return false; +} + static already_AddRefed AllocateBufferForImage(const IntSize& size, SurfaceFormat format, @@ -87,19 +111,13 @@ AllocateBufferForImage(const IntSize& size, { int32_t stride = VolatileSurfaceStride(size, format); -#ifdef ANIMATED_FRAMES_USE_HEAP - if (aIsAnimated) { - // For as long as an animated image is retained, its frames will never be - // released to let the OS purge volatile buffers. On Android, a volatile - // buffer actually keeps a file handle active, which we would like to avoid - // since many images and frames could easily exhaust the pool. As such, we - // use the heap. On the other platforms we do not have the file handle - // problem, and additionally we may avoid a superfluous memset since the - // volatile memory starts out as zero-filled. - return Factory::CreateDataSourceSurfaceWithStride(size, format, - stride, false); + if (ShouldUseHeap(size, stride, aIsAnimated)) { + RefPtr newSurf = + new SourceSurfaceAlignedRawData(); + if (newSurf->Init(size, format, false, 0, stride)) { + return newSurf.forget(); + } } -#endif if (!aIsAnimated && gfxVars::GetUseWebRenderOrDefault() && gfxPrefs::ImageMemShared()) { diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 40b0196e5f3f..722d98f661a3 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -4733,6 +4733,14 @@ pref("image.mem.discardable", true); // demand from compressed data. Has no effect if image.mem.discardable is false. pref("image.mem.animated.discardable", true); +// Whether the heap should be used for frames from animated images. On Android, +// volatile memory keeps file handles open for each buffer. +#if defined(ANDROID) +pref("image.mem.animated.use_heap", true); +#else +pref("image.mem.animated.use_heap", false); +#endif + // Decodes images into shared memory to allow direct use in separate // rendering processes. pref("image.mem.shared", 2); @@ -4764,6 +4772,14 @@ pref("image.mem.surfacecache.size_factor", 4); // and laptop systems, where we never discard visible images. pref("image.mem.surfacecache.discard_factor", 1); +// What is the minimum buffer size in KB before using volatile memory over the +// heap. On Android, volatile memory keeps file handles open for each buffer. +#if defined(ANDROID) +pref("image.mem.volatile.min_threshold_kb", 100); +#else +pref("image.mem.volatile.min_threshold_kb", -1); +#endif + // How many threads we'll use for multithreaded decoding. If < 0, will be // automatically determined based on the system's number of cores. pref("image.multithreaded_decoding.limit", -1);