Bug 1122446 - Give cached surfaces a chance to expire, when starting a downscale-during-decode. r=dholbert

This commit is contained in:
Seth Fowler 2015-01-20 03:06:37 -08:00
parent b19344d071
commit 65bfa3a6bb
3 changed files with 112 additions and 37 deletions

View File

@ -1415,6 +1415,17 @@ void
RasterImage::WantDecodedFrames(const nsIntSize& aSize, uint32_t aFlags,
bool aShouldSyncNotify)
{
if (mDownscaleDuringDecode) {
// We're about to decode again, which may mean that some of the previous
// sizes we've decoded at aren't useful anymore. We can allow them to
// expire from the cache by unlocking them here. When the decode finishes,
// it will send an invalidation that will cause all instances of this image
// to redraw. If this image is locked, any surfaces that are still useful
// will become locked again when LookupFrame touches them, and the remainder
// will eventually expire.
SurfaceCache::UnlockSurfaces(ImageKey(this));
}
if (aShouldSyncNotify) {
// We can sync notify, which means we can also sync decode.
if (aFlags & FLAG_SYNC_DECODE) {

View File

@ -532,7 +532,9 @@ public:
return DrawableFrameRef();
}
if (!surface->IsLocked()) {
if (cache->IsLocked()) {
LockSurface(surface);
} else {
mExpirationTracker.MarkUsed(surface);
}
@ -571,7 +573,9 @@ public:
Remove(surface);
}
if (!surface->IsLocked()) {
if (cache->IsLocked()) {
LockSurface(surface);
} else {
mExpirationTracker.MarkUsed(surface);
}
@ -607,15 +611,16 @@ public:
cache->SetLocked(true);
// Try to lock all the surfaces the per-image cache is holding.
cache->ForEach(DoLockSurface, this);
// We don't relock this image's existing surfaces right away; instead, the
// image should arrange for Lookup() to touch them if they are still useful.
}
void UnlockImage(const ImageKey aImageKey)
{
nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
if (!cache)
return; // Already unlocked and removed.
if (!cache || !cache->IsLocked()) {
return; // Already unlocked.
}
cache->SetLocked(false);
@ -623,6 +628,20 @@ public:
cache->ForEach(DoUnlockSurface, this);
}
void UnlockSurfaces(const ImageKey aImageKey)
{
nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
if (!cache || !cache->IsLocked()) {
return; // Already unlocked.
}
// (Note that we *don't* unlock the per-image cache here; that's the
// difference between this and UnlockImage.)
// Unlock all the surfaces the per-image cache is holding.
cache->ForEach(DoUnlockSurface, this);
}
void RemoveImage(const ImageKey aImageKey)
{
nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
@ -678,6 +697,20 @@ public:
}
}
void LockSurface(CachedSurface* aSurface)
{
if (aSurface->GetLifetime() == Lifetime::Transient ||
aSurface->IsLocked()) {
return;
}
StopTracking(aSurface);
// Lock the surface. This can fail.
aSurface->SetLocked(true);
StartTracking(aSurface);
}
static PLDHashOperator DoStopTracking(const SurfaceKey&,
CachedSurface* aSurface,
void* aCache)
@ -686,25 +719,6 @@ public:
return PL_DHASH_NEXT;
}
static PLDHashOperator DoLockSurface(const SurfaceKey&,
CachedSurface* aSurface,
void* aCache)
{
if (aSurface->GetLifetime() == Lifetime::Transient ||
aSurface->IsLocked()) {
return PL_DHASH_NEXT;
}
auto cache = static_cast<SurfaceCacheImpl*>(aCache);
cache->StopTracking(aSurface);
// Lock the surface. This can fail.
aSurface->SetLocked(true);
cache->StartTracking(aSurface);
return PL_DHASH_NEXT;
}
static PLDHashOperator DoUnlockSurface(const SurfaceKey&,
CachedSurface* aSurface,
void* aCache)
@ -999,6 +1013,15 @@ SurfaceCache::UnlockImage(Image* aImageKey)
}
}
/* static */ void
SurfaceCache::UnlockSurfaces(const ImageKey aImageKey)
{
if (sInstance) {
MutexAutoLock lock(sInstance->GetMutex());
return sInstance->UnlockSurfaces(aImageKey);
}
}
/* static */ void
SurfaceCache::RemoveSurface(const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey)

View File

@ -149,9 +149,6 @@ MOZ_END_ENUM_CLASS(InsertOutcome)
* Any image which stores surfaces in the SurfaceCache *must* ensure that it
* calls RemoveImage() before it is destroyed. See the comments for
* RemoveImage() for more details.
*
* SurfaceCache is not thread-safe; it should only be accessed from the main
* thread.
*/
struct SurfaceCache
{
@ -171,6 +168,9 @@ struct SurfaceCache
* Look up the imgFrame containing a surface in the cache and returns a
* drawable reference to that imgFrame.
*
* If the image associated with the surface is locked, then the surface will
* be locked before it is returned.
*
* If the imgFrame was found in the cache, but had stored its surface in a
* volatile buffer which was discarded by the OS, then it is automatically
* removed from the cache and an empty DrawableFrameRef is returned. Note that
@ -200,6 +200,9 @@ struct SurfaceCache
* Returned surfaces may vary from the requested surface only in terms of
* size, unless @aAlternateFlags is specified.
*
* If the image associated with the surface is locked, then the surface will
* be locked before it is returned.
*
* @param aImageKey Key data identifying which image the surface belongs to.
* @param aSurfaceKey Key data which identifies the ideal surface to return.
* @param aAlternateFlags If not Nothing(), a different set of flags than the
@ -221,12 +224,20 @@ struct SurfaceCache
*
* Each surface in the cache has a lifetime, either Transient or Persistent.
* Transient surfaces can expire from the cache at any time. Persistent
* surfaces can ordinarily also expire from the cache at any time, but if the
* image they're associated with is locked, then these surfaces will never
* expire. This means that surfaces which cannot be rematerialized should be
* inserted with a persistent lifetime *after* the image is locked with
* LockImage(); if you use the other order, the surfaces might expire before
* LockImage() gets called.
* surfaces, on the other hand, will never expire as long as they remain
* locked, but if they become unlocked, can expire just like transient
* surfaces. When it is first inserted, a persistent surface is locked if its
* associated image is locked. When that image is later unlocked, the surface
* becomes unlocked too. To become locked again at that point, two things must
* happen: the image must become locked again (via LockImage()), and the
* surface must be touched again (via one of the Lookup() functions).
*
* All of this means that a very particular procedure has to be followed for
* surfaces which cannot be rematerialized. First, they must be inserted
* with a persistent lifetime *after* the image is locked with LockImage(); if
* you use the other order, the surfaces might expire before LockImage() gets
* called or before the surface is touched again by Lookup(). Second, the
* image they are associated with must never be unlocked.
*
* If a surface cannot be rematerialized, it may be important to know whether
* it was inserted into the cache successfully. Insert() returns FAILURE if it
@ -276,8 +287,17 @@ struct SurfaceCache
static bool CanHold(size_t aSize);
/**
* Locks an image, preventing any of that image's surfaces from expiring
* unless they have a transient lifetime.
* Locks an image. Any of the image's persistent surfaces which are either
* inserted or accessed while the image is locked will not expire.
*
* Locking an image does not automatically lock that image's existing
* surfaces. A call to LockImage() guarantees that persistent surfaces which
* are inserted afterward will not expire before the next call to
* UnlockImage() or UnlockSurfaces() for that image. Surfaces that are
* accessed via Lookup() or LookupBestMatch() after a LockImage() call will
* also not expire until the next UnlockImage() or UnlockSurfaces() call for
* that image. Any other surfaces owned by the image may expire at any time,
* whether they are persistent or transient.
*
* Regardless of locking, any of an image's surfaces may be removed using
* RemoveSurface(), and all of an image's surfaces are removed by
@ -302,10 +322,31 @@ struct SurfaceCache
* It's OK to call UnlockImage() on an image that's already unlocked; this has
* no effect.
*
* @param aImageKey The image to lock.
* @param aImageKey The image to unlock.
*/
static void UnlockImage(const ImageKey aImageKey);
/**
* Unlocks the existing surfaces of an image, allowing them to expire at any
* time.
*
* This does not unlock the image itself, so accessing the surfaces via
* Lookup() or LookupBestMatch() will lock them again, and prevent them from
* expiring.
*
* This is intended to be used in situations where it's no longer clear that
* all of the persistent surfaces owned by an image are needed. Calling
* UnlockSurfaces() and then taking some action that will cause Lookup() to
* touch any surfaces that are still useful will permit the remaining surfaces
* to expire from the cache.
*
* If the image is unlocked, this has no effect.
*
* @param aImageKey The image which should have its existing surfaces
* unlocked.
*/
static void UnlockSurfaces(const ImageKey aImageKey);
/**
* Removes a surface from the cache, if it's present. If it's not present,
* RemoveSurface() has no effect.