Issue #3030 - Improve image cache entry handling.

This:
- increases max image cache entry size and adjusts related parameters.
- makes the cache entry timeout configurable for advanced tweaking.
- adds a mechanism to keep commonly-used, small image sizes in cache
  longer.
This commit is contained in:
Moonchild
2026-03-27 15:23:58 +00:00
committed by roytam1
parent 4e37362c89
commit 335ddc41b9
5 changed files with 57 additions and 8 deletions
+1
View File
@@ -436,6 +436,7 @@ private:
DECL_GFX_PREF(Live, "image.animated.resume-from-last-displayed", ImageAnimatedResumeFromLastDisplayed, 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(Once, "image.cache.entry_timeout", ImageCacheEntryTimeout, int32_t, 15000);
DECL_GFX_PREF(Live, "image.decode-immediately.enabled", ImageDecodeImmediatelyEnabled, bool, false);
DECL_GFX_PREF(Live, "image.downscale-during-decode.enabled", ImageDownscaleDuringDecodeEnabled, bool, true);
DECL_GFX_PREF(Live, "image.infer-src-animation.threshold-ms", ImageInferSrcAnimationThresholdMS, uint32_t, 2000);
+2 -2
View File
@@ -936,9 +936,9 @@ VectorImage::CreateSurfaceAndShow(const SVGDrawingParameters& aParams, BackendTy
// are scaled repeatedly (a rather common scenario) that can quickly exhaust
// the cache.
// Similar to max image size calculations, this has a max cap and size check.
// max cap = 8000 (pixels); size check = 5% of cache
// max cap = 8000 (pixels); size check = 10% of cache
int32_t maxDimension = 8000;
int32_t maxCacheElemSize = (gfxPrefs::ImageMemSurfaceCacheMaxSizeKB() * 1024) / 20;
int32_t maxCacheElemSize = (gfxPrefs::ImageMemSurfaceCacheMaxSizeKB() * 1024) / 10;
bool bypassCache = bool(aParams.flags & FLAG_BYPASS_SURFACE_CACHE) ||
// Refuse to cache animated images:
+43 -2
View File
@@ -1087,7 +1087,6 @@ imgLoader::CreateNewProxyForRequest(imgRequest* aRequest,
class imgCacheExpirationTracker final
: public nsExpirationTracker<imgCacheEntry, 3>
{
enum { TIMEOUT_SECONDS = 10 };
public:
imgCacheExpirationTracker();
@@ -1096,10 +1095,46 @@ protected:
};
imgCacheExpirationTracker::imgCacheExpirationTracker()
: nsExpirationTracker<imgCacheEntry, 3>(TIMEOUT_SECONDS * 1000,
: nsExpirationTracker<imgCacheEntry, 3>(gfxPrefs::ImageCacheEntryTimeout(),
"imgCacheExpirationTracker")
{ }
static bool
ShouldKeepRecentlyUsedAssetInCache(imgCacheEntry* aEntry)
{
RefPtr<imgRequest> request = aEntry->GetRequest();
if (!request) {
return false;
}
const char* mimeType = request->GetMimeType();
if (!mimeType) {
return false;
}
// Keep small, frequently reused static image assets warm a bit longer.
if (!nsCRT::strcmp(mimeType, IMAGE_SVG_XML) ||
!nsCRT::strcmp(mimeType, IMAGE_JPG) ||
!nsCRT::strcmp(mimeType, IMAGE_JPEG) ||
!nsCRT::strcmp(mimeType, IMAGE_ICO) ||
!nsCRT::strcmp(mimeType, IMAGE_PNG) ||
!nsCRT::strcmp(mimeType, IMAGE_WEBP)) {
// XXX: Should we make these configurable too?
const uint32_t kMaxWarmAssetBytes = 128 * 1024; // <= 128 kiB entries only
const uint32_t kRecentUseGraceSeconds = 60; // keep warm for 60 s
if (aEntry->GetDataSize() <= kMaxWarmAssetBytes) {
uint32_t now = SecondsFromPRTime(PR_Now());
uint32_t touched = aEntry->GetTouchedTime();
if (now >= touched && (now - touched) <= kRecentUseGraceSeconds) {
return true;
}
}
}
return false;
}
void
imgCacheExpirationTracker::NotifyExpired(imgCacheEntry* entry)
{
@@ -1107,6 +1142,12 @@ imgCacheExpirationTracker::NotifyExpired(imgCacheEntry* entry)
// mechanism doesn't.
RefPtr<imgCacheEntry> kungFuDeathGrip(entry);
if (ShouldKeepRecentlyUsedAssetInCache(entry)) {
entry->Touch();
entry->Loader()->VerifyCacheSizes();
return;
}
if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
RefPtr<imgRequest> req = entry->GetRequest();
if (req) {
+1
View File
@@ -152,6 +152,7 @@ public:
private: // methods
friend class imgLoader;
friend class imgCacheQueue;
friend class imgCacheExpirationTracker;
void Touch(bool updateTime = true);
void UpdateCache(int32_t diff = 0);
void SetEvicted(bool evict)
+10 -4
View File
@@ -4262,12 +4262,18 @@ pref("image.animated.decode-on-demand.batch-size", 6);
// advancing when out of view.
pref("image.animated.resume-from-last-displayed", true);
// Image cache prefs: Restart required for changes.
// The maximum size, in bytes, of the decoded images we cache
pref("image.cache.size", 5242880);
pref("image.cache.size", 26214400);
// A weight, from 0-1000, to place on time when comparing to size.
// Size is given a weight of 1000 - timeweight.
pref("image.cache.timeweight", 500);
pref("image.cache.timeweight", 650);
// Time in ms before unproxied entries in the in-memory image cache are
// considered for eviction by the expiration tracker.
pref("image.cache.entry_timeout", 15000);
// Decode all images automatically on load, ignoring our normal heuristics.
pref("image.decode-immediately.enabled", false);
@@ -4314,7 +4320,7 @@ pref("image.mem.decode_bytes_at_a_time", 16384);
// Minimum timeout for expiring unused images from the surface cache, in
// milliseconds. This controls how long we store cached temporary surfaces.
pref("image.mem.surfacecache.min_expiration_ms", 60000); // 60s
pref("image.mem.surfacecache.min_expiration_ms", 180000); // 180s
// Maximum size for the surface cache, in kilobytes.
pref("image.mem.surfacecache.max_size_kb", 1048576); // 1GB
@@ -4331,7 +4337,7 @@ pref("image.mem.surfacecache.size_factor", 4);
// 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);
pref("image.mem.surfacecache.discard_factor", 2);
// How many threads we'll use for multithreaded decoding. If < 0, will be
// automatically determined based on the system's number of cores.