Bug 1060869 (Part 3) - Make the SurfaceCache free only a fraction of its data on memory pressure. r=dholbert

--HG--
extra : rebase_source : 432056ddc161ca128aabfa7adeef763a3250a070
This commit is contained in:
Seth Fowler 2014-11-25 00:10:11 -08:00
parent 5e2c77546a
commit 3c52847f5b
3 changed files with 53 additions and 8 deletions

View File

@ -239,6 +239,7 @@ private:
DECL_GFX_PREF(Live, "image.mem.hard_limit_decoded_image_kb", ImageMemHardLimitDecodedImageKB, uint32_t, 0);
DECL_GFX_PREF(Live, "image.mem.max_decoded_image_kb", ImageMemMaxDecodedImageKB, uint32_t, 50*1024);
DECL_GFX_PREF(Live, "image.mem.max_ms_before_yield", ImageMemMaxMSBeforeYield, uint32_t, 400);
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);

View File

@ -240,10 +240,12 @@ public:
NS_DECL_ISUPPORTS
SurfaceCacheImpl(uint32_t aSurfaceCacheExpirationTimeMS,
uint32_t aSurfaceCacheDiscardFactor,
uint32_t aSurfaceCacheSize)
: mExpirationTracker(MOZ_THIS_IN_INITIALIZER_LIST(),
aSurfaceCacheExpirationTimeMS)
, mMemoryPressureObserver(new MemoryPressureObserver)
, mDiscardFactor(aSurfaceCacheDiscardFactor)
, mMaxCost(aSurfaceCacheSize)
, mAvailableCost(aSurfaceCacheSize)
, mLockedCost(0)
@ -474,6 +476,32 @@ public:
}
}
void DiscardForMemoryPressure()
{
// Compute our discardable cost. Since locked surfaces aren't discardable,
// we exclude them.
const Cost discardableCost = (mMaxCost - mAvailableCost) - mLockedCost;
MOZ_ASSERT(discardableCost <= mMaxCost, "Discardable cost doesn't add up");
// Our target is to raise our available cost by (1 / mDiscardFactor) of our
// discardable cost - in other words, we want to end up with about
// (discardableCost / mDiscardFactor) fewer bytes stored in the surface
// cache after we're done.
const Cost targetCost = mAvailableCost + (discardableCost / mDiscardFactor);
if (targetCost > mMaxCost - mLockedCost) {
MOZ_ASSERT_UNREACHABLE("Target cost is more than we can discard");
DiscardAll();
return;
}
// Discard surfaces until we've reduced our cost to our target cost.
while (mAvailableCost < targetCost) {
MOZ_ASSERT(!mCosts.IsEmpty(), "Removed everything and still not done");
Remove(mCosts.LastElement().GetSurface());
}
}
static PLDHashOperator DoStopTracking(const SurfaceKey&,
CachedSurface* aSurface,
void* aCache)
@ -600,7 +628,7 @@ private:
NS_IMETHOD Observe(nsISupports*, const char* aTopic, const char16_t*)
{
if (sInstance && strcmp(aTopic, "memory-pressure") == 0) {
sInstance->DiscardAll();
sInstance->DiscardForMemoryPressure();
}
return NS_OK;
}
@ -614,6 +642,7 @@ private:
nsRefPtrHashtable<nsPtrHashKey<Image>, ImageSurfaceCache> mImageCaches;
SurfaceTracker mExpirationTracker;
nsRefPtr<MemoryPressureObserver> mMemoryPressureObserver;
const uint32_t mDiscardFactor;
const Cost mMaxCost;
Cost mAvailableCost;
Cost mLockedCost;
@ -639,6 +668,13 @@ SurfaceCache::Initialize()
uint32_t surfaceCacheExpirationTimeMS =
gfxPrefs::ImageMemSurfaceCacheMinExpirationMS();
// What fraction of the memory used by the surface cache we should discard
// when we get a memory pressure notification. This value is interpreted as
// 1/N, so 1 means to discard everything, 2 means to discard about half of the
// memory we're using, and so forth. We clamp it to avoid division by zero.
uint32_t surfaceCacheDiscardFactor =
max(gfxPrefs::ImageMemSurfaceCacheDiscardFactor(), 1u);
// Maximum size of the surface cache, in kilobytes.
uint64_t surfaceCacheMaxSizeKB = gfxPrefs::ImageMemSurfaceCacheMaxSizeKB();
@ -649,10 +685,9 @@ SurfaceCache::Initialize()
// For example, a value of 4 would yield a 256MB cache on a 1GB machine.
// The smallest machines we are likely to run this code on have 256MB
// of memory, which would yield a 64MB cache on this setting.
uint32_t surfaceCacheSizeFactor = gfxPrefs::ImageMemSurfaceCacheSizeFactor();
// Clamp to avoid division by zero below.
surfaceCacheSizeFactor = max(surfaceCacheSizeFactor, 1u);
// We clamp this value to avoid division by zero.
uint32_t surfaceCacheSizeFactor =
max(gfxPrefs::ImageMemSurfaceCacheSizeFactor(), 1u);
// Compute the size of the surface cache.
uint64_t proposedSize = PR_GetPhysicalMemorySize() / surfaceCacheSizeFactor;
@ -660,10 +695,11 @@ SurfaceCache::Initialize()
uint32_t finalSurfaceCacheSizeBytes =
min(surfaceCacheSizeBytes, uint64_t(UINT32_MAX));
// Create the surface cache singleton with the requested expiration time and
// size. Note that the size is a limit that the cache may not grow beyond, but
// we do not actually allocate any storage for surfaces at this time.
// Create the surface cache singleton with the requested settings. Note that
// the size is a limit that the cache may not grow beyond, but we do not
// actually allocate any storage for surfaces at this time.
sInstance = new SurfaceCacheImpl(surfaceCacheExpirationTimeMS,
surfaceCacheDiscardFactor,
finalSurfaceCacheSizeBytes);
sInstance->InitMemoryReporter();
}

View File

@ -3808,6 +3808,14 @@ pref("image.mem.surfacecache.max_size_kb", 102400); // 100MB
// systems.
pref("image.mem.surfacecache.size_factor", 64);
// How much of the data in the surface cache is discarded when we get a memory
// pressure notification, as a fraction. The discard factor is interpreted as a
// reciprocal, so a discard factor of 1 means to discard everything in the
// surface cache on memory pressure, a discard factor of 2 means to discard half
// of the data, and so forth. The default should be a good balance for desktop
// and laptop systems, where we never discard visible images.
pref("image.mem.surfacecache.discard_factor", 1);
// Whether we decode images on multiple background threads rather than the
// foreground thread.
pref("image.multithreaded_decoding.enabled", true);