Issue #2073 - m-c 1651587: Make image::Image release efficient on main thread

This commit is contained in:
Martok 2023-01-03 23:30:46 +01:00 • committed by roytam1
commit 9a39001cc3
6 changed files with 75 additions and 13 deletions

View file

@ -61,14 +61,8 @@ AnimationSurfaceProvider::DropImageReference()
return; // Nothing to do. return; // Nothing to do.
} }
// RasterImage objects need to be destroyed on the main thread. We also need // RasterImage objects need to be destroyed on the main thread.
// to destroy them asynchronously, because if our surface cache entry is SurfaceCache::ReleaseImageOnMainThread(mImage.forget());
// destroyed and we were the only thing keeping |mImage| alive, RasterImage's
// destructor may call into the surface cache while whatever code caused us to
// get evicted is holding the surface cache lock, causing deadlock.
RefPtr<RasterImage> image = mImage;
mImage = nullptr;
NS_ReleaseOnMainThread(image.forget(), /* aAlwaysProxy = */ true);
} }
void void

View file

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

View file

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

View file

@ -434,8 +434,7 @@ RasterImage::OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey)
mAnimationState && aSurfaceKey.Playback() == PlaybackType::eAnimated; mAnimationState && aSurfaceKey.Playback() == PlaybackType::eAnimated;
RefPtr<RasterImage> image = this; RefPtr<RasterImage> image = this;
NS_DispatchToMainThread(NS_NewRunnableFunction( NS_DispatchToMainThread(NS_NewRunnableFunction([=]() -> void {
[=]() -> void {
image->OnSurfaceDiscardedInternal(animatedFramesDiscarded); image->OnSurfaceDiscardedInternal(animatedFramesDiscarded);
})); }));
} }

View file

@ -835,6 +835,32 @@ public:
} }
} }
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([]() -> void {
SurfaceCache::ClearReleasingImages();
}));
}
void TakeReleasingImages(nsTArray<RefPtr<image::Image>>& aImage,
const StaticMutexAutoLock& aAutoLock) {
MOZ_ASSERT(NS_IsMainThread());
aImage.SwapElements(mReleasingImagesOnMainThread);
}
private: private:
already_AddRefed<ImageSurfaceCache> GetImageCache(const ImageKey aImageKey) already_AddRefed<ImageSurfaceCache> GetImageCache(const ImageKey aImageKey)
{ {
@ -943,6 +969,7 @@ private:
ImageSurfaceCache> mImageCaches; ImageSurfaceCache> mImageCaches;
SurfaceTracker mExpirationTracker; SurfaceTracker mExpirationTracker;
RefPtr<MemoryPressureObserver> mMemoryPressureObserver; RefPtr<MemoryPressureObserver> mMemoryPressureObserver;
nsTArray<RefPtr<image::Image>> mReleasingImagesOnMainThread;
const uint32_t mDiscardFactor; const uint32_t mDiscardFactor;
const Cost mMaxCost; const Cost mMaxCost;
Cost mAvailableCost; Cost mAvailableCost;
@ -1160,5 +1187,34 @@ SurfaceCache::MaximumCapacity()
return sInstance->MaximumCapacity(); return sInstance->MaximumCapacity();
} }
/* 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(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 image
} // namespace mozilla } // namespace mozilla

View file

@ -417,6 +417,18 @@ struct SurfaceCache
*/ */
static size_t MaximumCapacity(); static size_t MaximumCapacity();
/**
* 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: private:
virtual ~SurfaceCache() = 0; // Forbid instantiation. virtual ~SurfaceCache() = 0; // Forbid instantiation.
}; };