Bug 1651587 - Make image::Image released efficient on main thread r=aosmond

Current gecko dispatches tasks of releasing image::Image to main thread at some places. The task was dispatched individually. Then there were cases that the releasing took long time. It increased peak memory usage and caused a problem like Bug 1639280. When main thread is very busy like WebGL, it takes longer time until the Image is released on main thread. If Images are released quickly on main thread, we could reduce peak memory usage.

When SurfaceCache::ReleaseImageOnMainThread() is called, there could be already an ongoing task for releasing Image. It could reduce a duration until release on main thread.

Differential Revision: https://phabricator.services.mozilla.com/D82864
This commit is contained in:
sotaro 2020-07-10 14:34:01 +00:00
parent 2020afa997
commit 80f17b60f8
6 changed files with 74 additions and 4 deletions

View file

@ -58,7 +58,7 @@ void AnimationSurfaceProvider::DropImageReference() {
}
// RasterImage objects need to be destroyed on the main thread.
NS_ReleaseOnMainThread("AnimationSurfaceProvider::mImage", mImage.forget());
SurfaceCache::ReleaseImageOnMainThread(mImage.forget());
}
void AnimationSurfaceProvider::Reset() {

View file

@ -43,7 +43,8 @@ void DecodedSurfaceProvider::DropImageReference() {
// get evicted is holding the surface cache lock, causing deadlock.
RefPtr<RasterImage> image = mImage;
mImage = nullptr;
NS_ReleaseOnMainThread(image.forget(), /* aAlwaysProxy = */ true);
SurfaceCache::ReleaseImageOnMainThread(image.forget(),
/* aAlwaysProxy = */ true);
}
DrawableFrameRef DecodedSurfaceProvider::DrawableRef(size_t aFrame) {

View file

@ -86,7 +86,7 @@ Decoder::~Decoder() {
if (mImage && !NS_IsMainThread()) {
// Dispatch mImage to main thread to prevent it from being destructed by the
// decode thread.
NS_ReleaseOnMainThread(mImage.forget());
SurfaceCache::ReleaseImageOnMainThread(mImage.forget());
}
}

View file

@ -1324,6 +1324,32 @@ class SurfaceCacheImpl final : public nsIMemoryReporter {
MaybeRemoveEmptyCache(aImageKey, cache);
}
void ReleaseImageOnMainThread(already_AddRefed<image::Image>&& aImage,
const StaticMutexAutoLock& aAutoLock) {
RefPtr<image::Image> image = aImage;
if (!image) {
return;
}
bool needsDispatch = mReleasingImagesOnMainThread.IsEmpty();
mReleasingImagesOnMainThread.AppendElement(image);
if (!needsDispatch) {
// There is already a ongoing task for ClearReleasingImages().
return;
}
NS_DispatchToMainThread(NS_NewRunnableFunction(
"SurfaceCacheImpl::ReleaseImageOnMainThread",
[]() -> void { SurfaceCache::ClearReleasingImages(); }));
}
void TakeReleasingImages(nsTArray<RefPtr<image::Image>>& aImage,
const StaticMutexAutoLock& aAutoLock) {
MOZ_ASSERT(NS_IsMainThread());
aImage.SwapElements(mReleasingImagesOnMainThread);
}
private:
already_AddRefed<ImageSurfaceCache> GetImageCache(const ImageKey aImageKey) {
RefPtr<ImageSurfaceCache> imageCache;
@ -1468,6 +1494,7 @@ class SurfaceCacheImpl final : public nsIMemoryReporter {
nsTArray<RefPtr<CachedSurface>> mCachedSurfacesDiscard;
SurfaceTracker mExpirationTracker;
RefPtr<MemoryPressureObserver> mMemoryPressureObserver;
nsTArray<RefPtr<image::Image>> mReleasingImagesOnMainThread;
const uint32_t mDiscardFactor;
const Cost mMaxCost;
Cost mAvailableCost;
@ -1784,5 +1811,35 @@ IntSize SurfaceCache::ClampSize(ImageKey aImageKey, const IntSize& aSize) {
return ClampVectorSize(aSize);
}
/* static */
void SurfaceCache::ReleaseImageOnMainThread(
already_AddRefed<image::Image> aImage, bool aAlwaysProxy) {
if (NS_IsMainThread() && !aAlwaysProxy) {
RefPtr<image::Image> image = std::move(aImage);
return;
}
StaticMutexAutoLock lock(sInstanceMutex);
if (sInstance) {
sInstance->ReleaseImageOnMainThread(std::move(aImage), lock);
} else {
NS_ReleaseOnMainThread("SurfaceCache::ReleaseImageOnMainThread",
std::move(aImage), /* aAlwaysProxy */ true);
}
}
/* static */
void SurfaceCache::ClearReleasingImages() {
MOZ_ASSERT(NS_IsMainThread());
nsTArray<RefPtr<image::Image>> images;
{
StaticMutexAutoLock lock(sInstanceMutex);
if (sInstance) {
sInstance->TakeReleasingImages(images, lock);
}
}
}
} // namespace image
} // namespace mozilla

View file

@ -444,6 +444,18 @@ struct SurfaceCache {
*/
static IntSize ClampSize(const ImageKey aImageKey, const IntSize& aSize);
/**
* Release image on main thread.
* The function uses SurfaceCache to release pending releasing images quickly.
*/
static void ReleaseImageOnMainThread(already_AddRefed<image::Image> aImage,
bool aAlwaysProxy = false);
/**
* Clear all pending releasing images.
*/
static void ClearReleasingImages();
private:
virtual ~SurfaceCache() = 0; // Forbid instantiation.
};

View file

@ -259,7 +259,7 @@ class ImageDecoderHelper final : public Runnable,
private:
~ImageDecoderHelper() {
NS_ReleaseOnMainThread("ImageDecoderHelper::mImage", mImage.forget());
SurfaceCache::ReleaseImageOnMainThread(mImage.forget());
NS_ReleaseOnMainThread("ImageDecoderHelper::mCallback", mCallback.forget());
}