Changeset 258908 in webkit


Ignore:
Timestamp:
Mar 23, 2020 10:05:50 PM (4 years ago)
Author:
Simon Fraser
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:
Location:
trunk/Source/WebCore
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r258907 r258908  
     12020-03-23  Simon Fraser  <simon.fraser@apple.com>
     2
     3        Safari jetsams with repeated drawImage/getImageData
     4        https://bugs.webkit.org/show_bug.cgi?id=207957
     5
     6        Reviewed by Tim Horton.
     7
     8        SubimageCacheWithTimer used a DeferrableOneShotTimer to clear itself, but if content
     9        adds an entry to the cache on every frame (as might content drawing video frames into a canvas)
     10        then the cache was never cleared. Nor was it cleared via a memory warning.
     11
     12        Fix by tracking cache entries by age, and using a repeating timer to prune old images
     13        from the cache. Also hook up the cache to the memory pressure handler, which clears it.
     14
     15        Reduce the timer frequency from 1s to 500ms, since that was observed to reduce the memory use
     16        on the provided testcase from ~600M to ~350M, making jetsam less likely.
     17
     18        Rename m_images to m_imageCounts to make its role clearer.
     19
     20        * page/cocoa/MemoryReleaseCocoa.mm:
     21        (WebCore::platformReleaseMemory):
     22        * platform/graphics/cg/SubimageCacheWithTimer.cpp:
     23        (WebCore::SubimageCacheWithTimer::clear):
     24        (WebCore::SubimageCacheAdder::translate):
     25        (WebCore::SubimageCacheWithTimer::SubimageCacheWithTimer):
     26        (WebCore::SubimageCacheWithTimer::pruneCacheTimerFired):
     27        (WebCore::SubimageCacheWithTimer::prune):
     28        (WebCore::SubimageCacheWithTimer::subimage):
     29        (WebCore::SubimageCacheWithTimer::clearImageAndSubimages):
     30        (WebCore::SubimageCacheWithTimer::clearAll):
     31        (WebCore::SubimageCacheWithTimer::invalidateCacheTimerFired): Deleted.
     32        * platform/graphics/cg/SubimageCacheWithTimer.h:
     33
    1342020-03-23  Stephan Szabo  <stephan.szabo@sony.com>
    235
  • trunk/Source/WebCore/page/cocoa/MemoryReleaseCocoa.mm

    r256007 r258908  
    3232#import "LayerPool.h"
    3333#import "LocaleCocoa.h"
     34#import "SubimageCacheWithTimer.h"
    3435#import "SystemFontDatabaseCoreText.h"
    3536#import <notify.h>
     
    6970#if HAVE(IOSURFACE)
    7071    IOSurfacePool::sharedPool().discardAllSurfaces();
     72#endif
     73
     74#if CACHE_SUBIMAGES
     75    SubimageCacheWithTimer::clear();
    7176#endif
    7277}
  • trunk/Source/WebCore/platform/graphics/cg/SubimageCacheWithTimer.cpp

    r232139 r258908  
    3737SubimageCacheWithTimer* SubimageCacheWithTimer::s_cache;
    3838
    39 static const Seconds subimageCacheClearDelay { 1_s };
     39static const Seconds subimageCachePruneDelay { 500_ms };
     40static const Seconds subimageCacheEntryLifetime { 500_ms };
    4041static const int maxSubimageCacheSize = 300;
    4142
     
    4950    if (subimageCacheExists())
    5051        subimageCache().clearImageAndSubimages(image);
     52}
     53
     54void SubimageCacheWithTimer::clear()
     55{
     56    if (subimageCacheExists())
     57        subimageCache().clearAll();
    5158}
    5259
     
    7178    {
    7279        entry.image = request.image;
     80        entry.subimage = adoptCF(CGImageCreateWithImageInRect(request.image, request.rect));
    7381        entry.rect = request.rect;
    74         entry.subimage = adoptCF(CGImageCreateWithImageInRect(request.image, request.rect));
    7582    }
    7683};
    7784
    7885SubimageCacheWithTimer::SubimageCacheWithTimer()
    79     : m_timer(*this, &SubimageCacheWithTimer::invalidateCacheTimerFired, subimageCacheClearDelay)
     86    : m_timer(*this, &SubimageCacheWithTimer::pruneCacheTimerFired)
    8087{
    8188}
    8289
    83 void SubimageCacheWithTimer::invalidateCacheTimerFired()
     90void SubimageCacheWithTimer::pruneCacheTimerFired()
    8491{
    85     m_images.clear();
    86     m_cache.clear();
     92    prune();
     93    if (m_cache.isEmpty()) {
     94        ASSERT(m_imageCounts.isEmpty());
     95        m_timer.stop();
     96    }
     97}
     98
     99void SubimageCacheWithTimer::prune()
     100{
     101    auto now = MonotonicTime::now();
     102
     103    Vector<SubimageCacheEntry> toBeRemoved;
     104
     105    for (const auto& entry : m_cache) {
     106        if ((now - entry.lastAccessTime) > subimageCacheEntryLifetime)
     107            toBeRemoved.append(entry);
     108    }
     109
     110    for (auto& entry : toBeRemoved) {
     111        m_imageCounts.remove(entry.image.get());
     112        m_cache.remove(entry);
     113    }
    87114}
    88115
    89116RetainPtr<CGImageRef> SubimageCacheWithTimer::subimage(CGImageRef image, const FloatRect& rect)
    90117{
    91     m_timer.restart();
     118    if (!m_timer.isActive())
     119        m_timer.startRepeating(subimageCachePruneDelay);
     120
    92121    if (m_cache.size() == maxSubimageCacheSize) {
    93122        SubimageCacheEntry entry = *m_cache.begin();
    94         m_images.remove(entry.image.get());
     123        m_imageCounts.remove(entry.image.get());
    95124        m_cache.remove(entry);
    96125    }
     
    99128    auto result = m_cache.add<SubimageCacheAdder>(SubimageRequest(image, rect));
    100129    if (result.isNewEntry)
    101         m_images.add(image);
     130        m_imageCounts.add(image);
    102131
     132    result.iterator->lastAccessTime = MonotonicTime::now();
    103133    return result.iterator->subimage;
    104134}
     
    106136void SubimageCacheWithTimer::clearImageAndSubimages(CGImageRef image)
    107137{
    108     if (m_images.contains(image)) {
     138    if (m_imageCounts.contains(image)) {
    109139        Vector<SubimageCacheEntry> toBeRemoved;
    110140        for (const auto& entry : m_cache) {
     
    116146            m_cache.remove(entry);
    117147
    118         m_images.removeAll(image);
     148        m_imageCounts.removeAll(image);
    119149    }
     150}
     151
     152void SubimageCacheWithTimer::clearAll()
     153{
     154    m_imageCounts.clear();
     155    m_cache.clear();
    120156}
    121157
  • trunk/Source/WebCore/platform/graphics/cg/SubimageCacheWithTimer.h

    r241113 r258908  
    2424 */
    2525
    26 #ifndef SubimageCacheWithTimer_h
    27 #define SubimageCacheWithTimer_h
     26#pragma once
    2827
    2928#include "FloatRect.h"
     
    4847    struct SubimageCacheEntry {
    4948        RetainPtr<CGImageRef> image;
     49        RetainPtr<CGImageRef> subimage;
    5050        FloatRect rect;
    51         RetainPtr<CGImageRef> subimage;
     51        MonotonicTime lastAccessTime;
    5252    };
    5353
     
    8383    static RetainPtr<CGImageRef> getSubimage(CGImageRef, const FloatRect&);
    8484    static void clearImage(CGImageRef);
     85    static void clear();
    8586
    8687private:
     
    8889
    8990    SubimageCacheWithTimer();
    90     void invalidateCacheTimerFired();
     91    void pruneCacheTimerFired();
    9192
    9293    RetainPtr<CGImageRef> subimage(CGImageRef, const FloatRect&);
    9394    void clearImageAndSubimages(CGImageRef);
     95    void prune();
     96    void clearAll();
    9497
    95     HashCountedSet<CGImageRef> m_images;
     98    HashCountedSet<CGImageRef> m_imageCounts;
    9699    SubimageCacheHashSet m_cache;
    97     DeferrableOneShotTimer m_timer;
     100    Timer m_timer;
    98101
    99102    static SubimageCacheWithTimer& subimageCache();
     
    105108
    106109}
    107 
    108 #endif // SubimageCacheWithTimer_h
Note: See TracChangeset for help on using the changeset viewer.