Title: [258908] trunk/Source/WebCore
- Revision
- 258908
- Author
- [email protected]
- Date
- 2020-03-23 22:05:50 -0700 (Mon, 23 Mar 2020)
Log Message
Safari jetsams with repeated drawImage/getImageData
https://bugs.webkit.org/show_bug.cgi?id=207957
Reviewed by Tim Horton.
SubimageCacheWithTimer used a DeferrableOneShotTimer to clear itself, but if content
adds an entry to the cache on every frame (as might content drawing video frames into a canvas)
then the cache was never cleared. Nor was it cleared via a memory warning.
Fix by tracking cache entries by age, and using a repeating timer to prune old images
from the cache. Also hook up the cache to the memory pressure handler, which clears it.
Reduce the timer frequency from 1s to 500ms, since that was observed to reduce the memory use
on the provided testcase from ~600M to ~350M, making jetsam less likely.
Rename m_images to m_imageCounts to make its role clearer.
* page/cocoa/MemoryReleaseCocoa.mm:
(WebCore::platformReleaseMemory):
* platform/graphics/cg/SubimageCacheWithTimer.cpp:
(WebCore::SubimageCacheWithTimer::clear):
(WebCore::SubimageCacheAdder::translate):
(WebCore::SubimageCacheWithTimer::SubimageCacheWithTimer):
(WebCore::SubimageCacheWithTimer::pruneCacheTimerFired):
(WebCore::SubimageCacheWithTimer::prune):
(WebCore::SubimageCacheWithTimer::subimage):
(WebCore::SubimageCacheWithTimer::clearImageAndSubimages):
(WebCore::SubimageCacheWithTimer::clearAll):
(WebCore::SubimageCacheWithTimer::invalidateCacheTimerFired): Deleted.
* platform/graphics/cg/SubimageCacheWithTimer.h:
Modified Paths
Diff
Modified: trunk/Source/WebCore/ChangeLog (258907 => 258908)
--- trunk/Source/WebCore/ChangeLog 2020-03-24 04:51:34 UTC (rev 258907)
+++ trunk/Source/WebCore/ChangeLog 2020-03-24 05:05:50 UTC (rev 258908)
@@ -1,3 +1,36 @@
+2020-03-23 Simon Fraser <[email protected]>
+
+ Safari jetsams with repeated drawImage/getImageData
+ https://bugs.webkit.org/show_bug.cgi?id=207957
+
+ Reviewed by Tim Horton.
+
+ SubimageCacheWithTimer used a DeferrableOneShotTimer to clear itself, but if content
+ adds an entry to the cache on every frame (as might content drawing video frames into a canvas)
+ then the cache was never cleared. Nor was it cleared via a memory warning.
+
+ Fix by tracking cache entries by age, and using a repeating timer to prune old images
+ from the cache. Also hook up the cache to the memory pressure handler, which clears it.
+
+ Reduce the timer frequency from 1s to 500ms, since that was observed to reduce the memory use
+ on the provided testcase from ~600M to ~350M, making jetsam less likely.
+
+ Rename m_images to m_imageCounts to make its role clearer.
+
+ * page/cocoa/MemoryReleaseCocoa.mm:
+ (WebCore::platformReleaseMemory):
+ * platform/graphics/cg/SubimageCacheWithTimer.cpp:
+ (WebCore::SubimageCacheWithTimer::clear):
+ (WebCore::SubimageCacheAdder::translate):
+ (WebCore::SubimageCacheWithTimer::SubimageCacheWithTimer):
+ (WebCore::SubimageCacheWithTimer::pruneCacheTimerFired):
+ (WebCore::SubimageCacheWithTimer::prune):
+ (WebCore::SubimageCacheWithTimer::subimage):
+ (WebCore::SubimageCacheWithTimer::clearImageAndSubimages):
+ (WebCore::SubimageCacheWithTimer::clearAll):
+ (WebCore::SubimageCacheWithTimer::invalidateCacheTimerFired): Deleted.
+ * platform/graphics/cg/SubimageCacheWithTimer.h:
+
2020-03-23 Stephan Szabo <[email protected]>
[WinCairo][PlayStation] Failure to build with ENABLE_XSLT=OFF
Modified: trunk/Source/WebCore/page/cocoa/MemoryReleaseCocoa.mm (258907 => 258908)
--- trunk/Source/WebCore/page/cocoa/MemoryReleaseCocoa.mm 2020-03-24 04:51:34 UTC (rev 258907)
+++ trunk/Source/WebCore/page/cocoa/MemoryReleaseCocoa.mm 2020-03-24 05:05:50 UTC (rev 258908)
@@ -31,6 +31,7 @@
#import "IOSurfacePool.h"
#import "LayerPool.h"
#import "LocaleCocoa.h"
+#import "SubimageCacheWithTimer.h"
#import "SystemFontDatabaseCoreText.h"
#import <notify.h>
#import <pal/spi/ios/GraphicsServicesSPI.h>
@@ -69,6 +70,10 @@
#if HAVE(IOSURFACE)
IOSurfacePool::sharedPool().discardAllSurfaces();
#endif
+
+#if CACHE_SUBIMAGES
+ SubimageCacheWithTimer::clear();
+#endif
}
void jettisonExpensiveObjectsOnTopLevelNavigation()
Modified: trunk/Source/WebCore/platform/graphics/cg/SubimageCacheWithTimer.cpp (258907 => 258908)
--- trunk/Source/WebCore/platform/graphics/cg/SubimageCacheWithTimer.cpp 2020-03-24 04:51:34 UTC (rev 258907)
+++ trunk/Source/WebCore/platform/graphics/cg/SubimageCacheWithTimer.cpp 2020-03-24 05:05:50 UTC (rev 258908)
@@ -36,7 +36,8 @@
SubimageCacheWithTimer* SubimageCacheWithTimer::s_cache;
-static const Seconds subimageCacheClearDelay { 1_s };
+static const Seconds subimageCachePruneDelay { 500_ms };
+static const Seconds subimageCacheEntryLifetime { 500_ms };
static const int maxSubimageCacheSize = 300;
RetainPtr<CGImageRef> SubimageCacheWithTimer::getSubimage(CGImageRef image, const FloatRect& rect)
@@ -50,6 +51,12 @@
subimageCache().clearImageAndSubimages(image);
}
+void SubimageCacheWithTimer::clear()
+{
+ if (subimageCacheExists())
+ subimageCache().clearAll();
+}
+
struct SubimageRequest {
CGImageRef image;
const FloatRect& rect;
@@ -70,28 +77,50 @@
static void translate(SubimageCacheWithTimer::SubimageCacheEntry& entry, const SubimageRequest& request, unsigned /*hashCode*/)
{
entry.image = request.image;
+ entry.subimage = adoptCF(CGImageCreateWithImageInRect(request.image, request.rect));
entry.rect = request.rect;
- entry.subimage = adoptCF(CGImageCreateWithImageInRect(request.image, request.rect));
}
};
SubimageCacheWithTimer::SubimageCacheWithTimer()
- : m_timer(*this, &SubimageCacheWithTimer::invalidateCacheTimerFired, subimageCacheClearDelay)
+ : m_timer(*this, &SubimageCacheWithTimer::pruneCacheTimerFired)
{
}
-void SubimageCacheWithTimer::invalidateCacheTimerFired()
+void SubimageCacheWithTimer::pruneCacheTimerFired()
{
- m_images.clear();
- m_cache.clear();
+ prune();
+ if (m_cache.isEmpty()) {
+ ASSERT(m_imageCounts.isEmpty());
+ m_timer.stop();
+ }
}
+void SubimageCacheWithTimer::prune()
+{
+ auto now = MonotonicTime::now();
+
+ Vector<SubimageCacheEntry> toBeRemoved;
+
+ for (const auto& entry : m_cache) {
+ if ((now - entry.lastAccessTime) > subimageCacheEntryLifetime)
+ toBeRemoved.append(entry);
+ }
+
+ for (auto& entry : toBeRemoved) {
+ m_imageCounts.remove(entry.image.get());
+ m_cache.remove(entry);
+ }
+}
+
RetainPtr<CGImageRef> SubimageCacheWithTimer::subimage(CGImageRef image, const FloatRect& rect)
{
- m_timer.restart();
+ if (!m_timer.isActive())
+ m_timer.startRepeating(subimageCachePruneDelay);
+
if (m_cache.size() == maxSubimageCacheSize) {
SubimageCacheEntry entry = *m_cache.begin();
- m_images.remove(entry.image.get());
+ m_imageCounts.remove(entry.image.get());
m_cache.remove(entry);
}
@@ -98,14 +127,15 @@
ASSERT(m_cache.size() < maxSubimageCacheSize);
auto result = m_cache.add<SubimageCacheAdder>(SubimageRequest(image, rect));
if (result.isNewEntry)
- m_images.add(image);
+ m_imageCounts.add(image);
+ result.iterator->lastAccessTime = MonotonicTime::now();
return result.iterator->subimage;
}
void SubimageCacheWithTimer::clearImageAndSubimages(CGImageRef image)
{
- if (m_images.contains(image)) {
+ if (m_imageCounts.contains(image)) {
Vector<SubimageCacheEntry> toBeRemoved;
for (const auto& entry : m_cache) {
if (entry.image.get() == image)
@@ -115,10 +145,16 @@
for (auto& entry : toBeRemoved)
m_cache.remove(entry);
- m_images.removeAll(image);
+ m_imageCounts.removeAll(image);
}
}
+void SubimageCacheWithTimer::clearAll()
+{
+ m_imageCounts.clear();
+ m_cache.clear();
+}
+
SubimageCacheWithTimer& SubimageCacheWithTimer::subimageCache()
{
if (!s_cache)
Modified: trunk/Source/WebCore/platform/graphics/cg/SubimageCacheWithTimer.h (258907 => 258908)
--- trunk/Source/WebCore/platform/graphics/cg/SubimageCacheWithTimer.h 2020-03-24 04:51:34 UTC (rev 258907)
+++ trunk/Source/WebCore/platform/graphics/cg/SubimageCacheWithTimer.h 2020-03-24 05:05:50 UTC (rev 258908)
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SubimageCacheWithTimer_h
-#define SubimageCacheWithTimer_h
+#pragma once
#include "FloatRect.h"
#include "Timer.h"
@@ -47,8 +46,9 @@
public:
struct SubimageCacheEntry {
RetainPtr<CGImageRef> image;
+ RetainPtr<CGImageRef> subimage;
FloatRect rect;
- RetainPtr<CGImageRef> subimage;
+ MonotonicTime lastAccessTime;
};
struct SubimageCacheEntryTraits : WTF::GenericHashTraits<SubimageCacheEntry> {
@@ -82,19 +82,22 @@
static RetainPtr<CGImageRef> getSubimage(CGImageRef, const FloatRect&);
static void clearImage(CGImageRef);
+ static void clear();
private:
typedef HashSet<SubimageCacheEntry, SubimageCacheHash, SubimageCacheEntryTraits> SubimageCacheHashSet;
SubimageCacheWithTimer();
- void invalidateCacheTimerFired();
+ void pruneCacheTimerFired();
RetainPtr<CGImageRef> subimage(CGImageRef, const FloatRect&);
void clearImageAndSubimages(CGImageRef);
+ void prune();
+ void clearAll();
- HashCountedSet<CGImageRef> m_images;
+ HashCountedSet<CGImageRef> m_imageCounts;
SubimageCacheHashSet m_cache;
- DeferrableOneShotTimer m_timer;
+ Timer m_timer;
static SubimageCacheWithTimer& subimageCache();
static bool subimageCacheExists();
@@ -104,5 +107,3 @@
#endif // CACHE_SUBIMAGES
}
-
-#endif // SubimageCacheWithTimer_h
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes