Title: [136616] trunk/Source
Revision
136616
Author
[email protected]
Date
2012-12-04 20:15:31 -0800 (Tue, 04 Dec 2012)

Log Message

[chromium] Implement cache eviction logic in ImageDecodingStore
https://bugs.webkit.org/show_bug.cgi?id=103480

Reviewed by James Robinson.

Source/WebCore:

This patch implements these features in the new image cache:
- Least-recently-used eviction logic
- Setting cache limit (default is 32MB)
- Remove cache entries when ImageFrameGenerator is removed

New tests in ImageDecodingTestTest. Also covered by platform/chromium/virtual/deferred.

* platform/graphics/chromium/ImageDecodingStore.cpp:
(WebCore::ImageDecodingStore::ImageDecodingStore):
(WebCore::ImageDecodingStore::~ImageDecodingStore):
Code to assert that everything is cleaned up.
(WebCore::ImageDecodingStore::lockCompleteCache):
(WebCore::ImageDecodingStore::unlockCache):
(WebCore::ImageDecodingStore::insertAndLockCache):
(WebCore::ImageDecodingStore::removeCacheIndexedByGenerator):
New method to remove all cache entries index by ImageFrameGenerator.
(WebCore):
(WebCore::ImageDecodingStore::setCacheLimitInBytes):
(WebCore::ImageDecodingStore::memoryUsageInBytes):
(WebCore::ImageDecodingStore::cacheEntries):
(WebCore::ImageDecodingStore::prune):
New code to implement LRU eviction logic.
(WebCore::ImageDecodingStore::insertCacheInternal):
New method to help new cache insertion. This is separately because it touches 3 containers.
(WebCore::ImageDecodingStore::removeFromCacheInternal):
New method to help remove a cache entry from containers.
(WebCore::ImageDecodingStore::removeFromCacheListInternal):
New helper method to help remove cache entry from doubly linked list.
* platform/graphics/chromium/ImageDecodingStore.h:
(ImageDecodingStore):
(CacheEntry):
(WebCore::ImageDecodingStore::CacheEntry::createAndUse):
(WebCore::ImageDecodingStore::CacheEntry::CacheEntry):
(WebCore::ImageDecodingStore::CacheEntry::~CacheEntry):
(WebCore::ImageDecodingStore::CacheEntry::cacheKey):
(WebCore::ImageDecodingStore::CacheEntry::cachedImage):
(WebCore::ImageDecodingStore::CacheEntry::useCount):
(WebCore::ImageDecodingStore::CacheEntry::incrementUseCount):
(WebCore::ImageDecodingStore::CacheEntry::decrementUseCount):
(WebCore::ImageDecodingStore::CacheEntry::memoryUsageInBytes):
(WebCore::ImageDecodingStore::incrementMemoryUsage):
(WebCore::ImageDecodingStore::decrementMemoryUsage):
New helper methods.
* platform/graphics/chromium/ImageFrameGenerator.cpp:
(WebCore::ImageFrameGenerator::~ImageFrameGenerator):

Source/WebKit/chromium:

Added ImageDecodingStoreTest for unit testing new image cache.

New tests:

ImageDecodingStoreTest.evictOneCache

Test eviction of one cache entry by setting a smaller cache limit.

ImageDecodingStoreTest.pruneOrderIsLeastRecentlyUsed

Test eviction order is least recently used.

ImageDecodingStoreTest.pruneCausedByInsertion

Test that eviction is triggered also by cache insertion.

ImageDecodingStoreTest.cacheInUseNotEvicted

Test that cache in use is not evicted.

ImageDecodingStoreTest.destroyImageFrameGenerator

Test that deleting a ImageFrameGenerator causes associated cache
entries to be removed.

* WebKit.gypi:
* tests/ImageDecodingStoreTest.cpp: Added.
(WebCore):
(ImageDecodingStoreTest):
(WebCore::ImageDecodingStoreTest::SetUp):
(WebCore::ImageDecodingStoreTest::TearDown):
(WebCore::ImageDecodingStoreTest::createCompleteImage):
(WebCore::ImageDecodingStoreTest::insertCache):
(WebCore::ImageDecodingStoreTest::lockCache):
(WebCore::ImageDecodingStoreTest::unlockCache):
(WebCore::ImageDecodingStoreTest::evictOneCache):
(WebCore::ImageDecodingStoreTest::isCacheAlive):
(WebCore::TEST_F):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (136615 => 136616)


--- trunk/Source/WebCore/ChangeLog	2012-12-05 04:05:33 UTC (rev 136615)
+++ trunk/Source/WebCore/ChangeLog	2012-12-05 04:15:31 UTC (rev 136616)
@@ -1,3 +1,56 @@
+2012-12-04  Alpha Lam  <[email protected]>
+
+        [chromium] Implement cache eviction logic in ImageDecodingStore
+        https://bugs.webkit.org/show_bug.cgi?id=103480
+
+        Reviewed by James Robinson.
+
+        This patch implements these features in the new image cache:
+        - Least-recently-used eviction logic
+        - Setting cache limit (default is 32MB)
+        - Remove cache entries when ImageFrameGenerator is removed
+
+        New tests in ImageDecodingTestTest. Also covered by platform/chromium/virtual/deferred.
+
+        * platform/graphics/chromium/ImageDecodingStore.cpp:
+        (WebCore::ImageDecodingStore::ImageDecodingStore):
+        (WebCore::ImageDecodingStore::~ImageDecodingStore):
+        Code to assert that everything is cleaned up.
+        (WebCore::ImageDecodingStore::lockCompleteCache):
+        (WebCore::ImageDecodingStore::unlockCache):
+        (WebCore::ImageDecodingStore::insertAndLockCache):
+        (WebCore::ImageDecodingStore::removeCacheIndexedByGenerator):
+        New method to remove all cache entries index by ImageFrameGenerator.
+        (WebCore):
+        (WebCore::ImageDecodingStore::setCacheLimitInBytes):
+        (WebCore::ImageDecodingStore::memoryUsageInBytes):
+        (WebCore::ImageDecodingStore::cacheEntries):
+        (WebCore::ImageDecodingStore::prune):
+        New code to implement LRU eviction logic.
+        (WebCore::ImageDecodingStore::insertCacheInternal):
+        New method to help new cache insertion. This is separately because it touches 3 containers.
+        (WebCore::ImageDecodingStore::removeFromCacheInternal):
+        New method to help remove a cache entry from containers.
+        (WebCore::ImageDecodingStore::removeFromCacheListInternal):
+        New helper method to help remove cache entry from doubly linked list.
+        * platform/graphics/chromium/ImageDecodingStore.h:
+        (ImageDecodingStore):
+        (CacheEntry):
+        (WebCore::ImageDecodingStore::CacheEntry::createAndUse):
+        (WebCore::ImageDecodingStore::CacheEntry::CacheEntry):
+        (WebCore::ImageDecodingStore::CacheEntry::~CacheEntry):
+        (WebCore::ImageDecodingStore::CacheEntry::cacheKey):
+        (WebCore::ImageDecodingStore::CacheEntry::cachedImage):
+        (WebCore::ImageDecodingStore::CacheEntry::useCount):
+        (WebCore::ImageDecodingStore::CacheEntry::incrementUseCount):
+        (WebCore::ImageDecodingStore::CacheEntry::decrementUseCount):
+        (WebCore::ImageDecodingStore::CacheEntry::memoryUsageInBytes):
+        (WebCore::ImageDecodingStore::incrementMemoryUsage):
+        (WebCore::ImageDecodingStore::decrementMemoryUsage):
+        New helper methods.
+        * platform/graphics/chromium/ImageFrameGenerator.cpp:
+        (WebCore::ImageFrameGenerator::~ImageFrameGenerator):
+
 2012-12-04  Andreas Kling  <[email protected]>
 
         Style sharing: Allow sharing between elements with classes not referenced by any selectors.

Modified: trunk/Source/WebCore/platform/graphics/chromium/ImageDecodingStore.cpp (136615 => 136616)


--- trunk/Source/WebCore/platform/graphics/chromium/ImageDecodingStore.cpp	2012-12-05 04:05:33 UTC (rev 136615)
+++ trunk/Source/WebCore/platform/graphics/chromium/ImageDecodingStore.cpp	2012-12-05 04:15:31 UTC (rev 136616)
@@ -34,7 +34,9 @@
 
 namespace {
 
-ImageDecodingStore* s_instance = 0;
+// 32MB memory limit for cache.
+static const size_t defaultCacheLimitInBytes = 32768 * 1024;
+static ImageDecodingStore* s_instance = 0;
 
 static void setInstance(ImageDecodingStore* imageDecodingStore)
 {
@@ -45,11 +47,19 @@
 } // namespace
 
 ImageDecodingStore::ImageDecodingStore()
+    : m_cacheLimitInBytes(defaultCacheLimitInBytes)
+    , m_memoryUsageInBytes(0)
 {
 }
 
 ImageDecodingStore::~ImageDecodingStore()
 {
+#ifndef NDEBUG
+    setCacheLimitInBytes(0);
+    ASSERT(!m_cacheMap.size());
+    ASSERT(!m_orderedCacheList.size());
+    ASSERT(!m_cachedSizeMap.size());
+#endif
 }
 
 ImageDecodingStore* ImageDecodingStore::instance()
@@ -75,15 +85,15 @@
         CacheMap::iterator iter = m_cacheMap.find(std::make_pair(generator, scaledSize));
         if (iter == m_cacheMap.end())
             return 0;
-        cacheEntry = iter->value;
-        if (!cacheEntry->cachedImage->isComplete())
+        cacheEntry = iter->value.get();
+        if (!cacheEntry->cachedImage()->isComplete())
             return 0;
 
         // Increment use count such that it doesn't get evicted.
-        ++cacheEntry->useCount;
+        cacheEntry->incrementUseCount();
     }
-    cacheEntry->cachedImage->bitmap().lockPixels();
-    return cacheEntry->cachedImage.get();
+    cacheEntry->cachedImage()->bitmap().lockPixels();
+    return cacheEntry->cachedImage();
 }
 
 const ScaledImageFragment* ImageDecodingStore::lockIncompleteCache(const ImageFrameGenerator* generator, const SkISize& scaledSize)
@@ -105,9 +115,12 @@
     CacheMap::iterator iter = m_cacheMap.find(std::make_pair(generator, cachedImage->scaledSize()));
     ASSERT(iter != m_cacheMap.end());
 
-    CacheEntry* cacheEntry = iter->value;
-    --cacheEntry->useCount;
-    ASSERT(cacheEntry->useCount >= 0);
+    CacheEntry* cacheEntry = iter->value.get();
+    cacheEntry->decrementUseCount();
+
+    // Put the entry to the end of list.
+    m_orderedCacheList.remove(cacheEntry);
+    m_orderedCacheList.append(cacheEntry);
 }
 
 const ScaledImageFragment* ImageDecodingStore::insertAndLockCache(const ImageFrameGenerator* generator, PassOwnPtr<ScaledImageFragment> image)
@@ -127,23 +140,125 @@
     }
 
     ScaledImageFragment* cachedImage = image.get();
-    OwnPtr<CacheEntry> newCacheEntry = CacheEntry::createAndUse(image);
+    OwnPtr<CacheEntry> newCacheEntry = CacheEntry::createAndUse(generator, image);
 
-    CacheIdentifier key = std::make_pair(generator, cachedImage->scaledSize());
     MutexLocker lock(m_mutex);
-    ASSERT(!m_cacheMap.contains(key));
-
-    // m_cacheMap is used for indexing and quick lookup of a cached image.
-    // m_cacheEntries is used to support LRU operations to reorder cache
-    // entries quickly. It also owns cached images.
-    m_cacheMap.add(key, newCacheEntry.get());
-    m_cacheEntries.append(newCacheEntry.release());
+    ASSERT(!m_cacheMap.contains(newCacheEntry->cacheKey()));
+    insertCacheInternal(newCacheEntry.release());
     return cachedImage;
 }
 
+void ImageDecodingStore::removeCacheIndexedByGenerator(const ImageFrameGenerator* generator)
+{
+    Vector<OwnPtr<CacheEntry> > cacheEntriesToDelete;
+    {
+        MutexLocker lock(m_mutex);
+        CachedSizeMap::iterator iter = m_cachedSizeMap.find(generator);
+        if (iter == m_cachedSizeMap.end())
+            return;
+
+        // Get all cached sizes indexed by generator.
+        Vector<SkISize> cachedSizeList;
+        copyToVector(iter->value, cachedSizeList);
+
+        // For each cached size find the corresponding CacheEntry and remove it from
+        // m_cacheMap.
+        for (size_t i = 0; i < cachedSizeList.size(); ++i) {
+            CacheIdentifier key = std::make_pair(generator, cachedSizeList[i]);
+            ASSERT(m_cacheMap.contains(key));
+
+            const CacheEntry* cacheEntry = m_cacheMap.get(key);
+            ASSERT(!cacheEntry->useCount());
+            removeFromCacheInternal(cacheEntry, &cacheEntriesToDelete);
+        }
+
+        // Remove from cache list as well.
+        removeFromCacheListInternal(cacheEntriesToDelete);
+    }
+}
+
+void ImageDecodingStore::setCacheLimitInBytes(size_t cacheLimit)
+{
+    {
+        MutexLocker lock(m_mutex);
+        m_cacheLimitInBytes = cacheLimit;
+    }
+    prune();
+}
+
+size_t ImageDecodingStore::memoryUsageInBytes()
+{
+    MutexLocker lock(m_mutex);
+    return m_memoryUsageInBytes;
+}
+
+unsigned ImageDecodingStore::cacheEntries()
+{
+    MutexLocker lock(m_mutex);
+    return m_cacheMap.size();
+}
+
 void ImageDecodingStore::prune()
 {
-    // TODO: Implement.
+    Vector<OwnPtr<CacheEntry> > cacheEntriesToDelete;
+    {
+        MutexLocker lock(m_mutex);
+
+        // Head of the list is the least recently used entry.
+        const CacheEntry* cacheEntry = m_orderedCacheList.head();
+
+        // Walk the list of cache entries starting from the least recently used
+        // and then keep them for deletion later.
+        while (cacheEntry && m_memoryUsageInBytes > m_cacheLimitInBytes) {
+            // Cache is not used; Remove it.
+            if (!cacheEntry->useCount())
+                removeFromCacheInternal(cacheEntry, &cacheEntriesToDelete);
+            cacheEntry = cacheEntry->next();
+        }
+
+        // Remove from cache list as well.
+        removeFromCacheListInternal(cacheEntriesToDelete);
+    }
 }
 
+void ImageDecodingStore::insertCacheInternal(PassOwnPtr<CacheEntry> cacheEntry)
+{
+    incrementMemoryUsage(cacheEntry->memoryUsageInBytes());
+
+    // m_orderedCacheList is used to support LRU operations to reorder cache
+    // entries quickly.
+    m_orderedCacheList.append(cacheEntry.get());
+
+    CacheIdentifier key = cacheEntry->cacheKey();
+    // m_cacheMap is used for indexing and quick lookup of a cached image. It owns
+    // all cache entries.
+    m_cacheMap.add(key, cacheEntry);
+
+    // m_cachedSizeMap keeps all scaled sizes associated with an ImageFrameGenerator.
+    CachedSizeMap::AddResult result = m_cachedSizeMap.add(key.first, SizeSet());
+    result.iterator->value.add(key.second);
+}
+
+void ImageDecodingStore::removeFromCacheInternal(const CacheEntry* cacheEntry, Vector<OwnPtr<CacheEntry> >* deletionList)
+{
+    decrementMemoryUsage(cacheEntry->memoryUsageInBytes());
+
+    // Remove from m_cacheMap.
+    CacheIdentifier key = cacheEntry->cacheKey();
+    deletionList->append(m_cacheMap.take(key));
+
+    // Remove from m_cachedSizeMap.
+    CachedSizeMap::iterator iter = m_cachedSizeMap.find(key.first);
+    ASSERT(iter != m_cachedSizeMap.end());
+    iter->value.remove(key.second);
+    if (!iter->value.size())
+        m_cachedSizeMap.remove(iter);
+}
+
+void ImageDecodingStore::removeFromCacheListInternal(const Vector<OwnPtr<CacheEntry> >& deletionList)
+{
+    for (size_t i = 0; i < deletionList.size(); ++i)
+        m_orderedCacheList.remove(deletionList[i].get());
+}
+
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/graphics/chromium/ImageDecodingStore.h (136615 => 136616)


--- trunk/Source/WebCore/platform/graphics/chromium/ImageDecodingStore.h	2012-12-05 04:05:33 UTC (rev 136615)
+++ trunk/Source/WebCore/platform/graphics/chromium/ImageDecodingStore.h	2012-12-05 04:15:31 UTC (rev 136616)
@@ -31,6 +31,8 @@
 #include "SkSize.h"
 #include "SkSizeHash.h"
 
+#include <wtf/DoublyLinkedList.h>
+#include <wtf/HashSet.h>
 #include <wtf/OwnPtr.h>
 #include <wtf/PassOwnPtr.h>
 #include <wtf/ThreadingPrimitives.h>
@@ -56,42 +58,91 @@
     void unlockCache(const ImageFrameGenerator*, const ScaledImageFragment*);
     const ScaledImageFragment* insertAndLockCache(const ImageFrameGenerator*, PassOwnPtr<ScaledImageFragment>);
 
+    // Remove all cache entries indexed by ImageFrameGenerator.
+    void removeCacheIndexedByGenerator(const ImageFrameGenerator*);
+
+    void setCacheLimitInBytes(size_t);
+    size_t memoryUsageInBytes();
+    unsigned cacheEntries();
+
 private:
-    struct CacheEntry {
-        static PassOwnPtr<CacheEntry> create() { return adoptPtr(new CacheEntry()); }
-        static PassOwnPtr<CacheEntry> createAndUse(PassOwnPtr<ScaledImageFragment> image) { return adoptPtr(new CacheEntry(image, 1)); }
+    typedef std::pair<const ImageFrameGenerator*, SkISize> CacheIdentifier;
 
-        CacheEntry()
-            : useCount(0)
+    class CacheEntry : public DoublyLinkedListNode<CacheEntry> {
+        friend class DoublyLinkedListNode<CacheEntry>;
+    public:
+        static PassOwnPtr<CacheEntry> createAndUse(const ImageFrameGenerator* generator, PassOwnPtr<ScaledImageFragment> image)
         {
+            return adoptPtr(new CacheEntry(generator, image, 1));
         }
 
-        CacheEntry(PassOwnPtr<ScaledImageFragment> image, int count)
-            : cachedImage(image)
-            , useCount(count)
+        CacheEntry(const ImageFrameGenerator* generator, PassOwnPtr<ScaledImageFragment> image, int count)
+            : m_prev(0)
+            , m_next(0)
+            , m_generator(generator)
+            , m_cachedImage(image)
+            , m_useCount(count)
         {
         }
 
         ~CacheEntry()
         {
-            ASSERT(!useCount);
+            ASSERT(!m_useCount);
         }
 
-        OwnPtr<ScaledImageFragment> cachedImage;
-        int useCount;
+        CacheIdentifier cacheKey() const { return std::make_pair(m_generator, m_cachedImage->scaledSize()); }
+        const ScaledImageFragment* cachedImage() const { return m_cachedImage.get(); }
+        int useCount() const { return m_useCount; }
+        void incrementUseCount() { ++m_useCount; }
+        void decrementUseCount() { --m_useCount; ASSERT(m_useCount >= 0); }
+
+        // FIXME: getSafeSize() returns size in bytes truncated to a 32-bits integer.
+        //        Find a way to get the size in 64-bits.
+        size_t memoryUsageInBytes() const { return cachedImage()->bitmap().getSafeSize(); }
+
+    private:
+        CacheEntry* m_prev;
+        CacheEntry* m_next;
+        const ImageFrameGenerator* m_generator;
+        OwnPtr<ScaledImageFragment> m_cachedImage;
+        int m_useCount;
     };
 
     ImageDecodingStore();
+
     void prune();
 
-    Vector<OwnPtr<CacheEntry> > m_cacheEntries;
+    // These methods are called while m_mutex is locked.
+    void insertCacheInternal(PassOwnPtr<CacheEntry>);
+    void removeFromCacheInternal(const CacheEntry*, Vector<OwnPtr<CacheEntry> >* deletionList);
+    void removeFromCacheListInternal(const Vector<OwnPtr<CacheEntry> >& deletionList);
+    void incrementMemoryUsage(size_t size) { m_memoryUsageInBytes += size; }
+    void decrementMemoryUsage(size_t size)
+    {
+        ASSERT(m_memoryUsageInBytes >= size);
+        m_memoryUsageInBytes -= size;
+    }
 
-    typedef std::pair<const ImageFrameGenerator*, SkISize> CacheIdentifier;
-    typedef HashMap<CacheIdentifier, CacheEntry*> CacheMap;
+    // Head of this list is the least recently used cache entry.
+    // Tail of this list is the most recently used cache entry.
+    DoublyLinkedList<CacheEntry> m_orderedCacheList;
+
+    typedef HashMap<CacheIdentifier, OwnPtr<CacheEntry> > CacheMap;
     CacheMap m_cacheMap;
 
-    // Protect concurrent access to all instances of CacheEntry stored in m_cacheEntries and
-    // m_cacheMap as well as the containers.
+    typedef HashSet<SkISize> SizeSet;
+    typedef HashMap<const ImageFrameGenerator*, SizeSet> CachedSizeMap;
+    CachedSizeMap m_cachedSizeMap;
+
+    size_t m_cacheLimitInBytes;
+    size_t m_memoryUsageInBytes;
+
+    // Protect concurrent access to these members:
+    //   m_orderedCacheList
+    //   m_cacheMap and all CacheEntrys stored in it
+    //   m_cachedSizeMap
+    //   m_cacheLimitInBytes
+    //   m_memoryUsageInBytes
     Mutex m_mutex;
 };
 

Modified: trunk/Source/WebCore/platform/graphics/chromium/ImageFrameGenerator.cpp (136615 => 136616)


--- trunk/Source/WebCore/platform/graphics/chromium/ImageFrameGenerator.cpp	2012-12-05 04:05:33 UTC (rev 136615)
+++ trunk/Source/WebCore/platform/graphics/chromium/ImageFrameGenerator.cpp	2012-12-05 04:15:31 UTC (rev 136616)
@@ -54,8 +54,11 @@
 
 ImageFrameGenerator::~ImageFrameGenerator()
 {
-    // TODO: Call ImageDecodingStore to remove all cache entries indexed
-    // by this object.
+    // FIXME: This check is not really thread-safe. This should be changed to:
+    // ImageDecodingStore::removeCacheFromInstance(this);
+    // Which uses a lock internally.
+    if (ImageDecodingStore::instance())
+        ImageDecodingStore::instance()->removeCacheIndexedByGenerator(this);
 }
 
 void ImageFrameGenerator::setData(PassRefPtr<SharedBuffer> data, bool allDataReceived)

Modified: trunk/Source/WebKit/chromium/ChangeLog (136615 => 136616)


--- trunk/Source/WebKit/chromium/ChangeLog	2012-12-05 04:05:33 UTC (rev 136615)
+++ trunk/Source/WebKit/chromium/ChangeLog	2012-12-05 04:15:31 UTC (rev 136616)
@@ -1,3 +1,49 @@
+2012-12-04  Alpha Lam  <[email protected]>
+
+        [chromium] Implement cache eviction logic in ImageDecodingStore
+        https://bugs.webkit.org/show_bug.cgi?id=103480
+
+        Reviewed by James Robinson.
+
+        Added ImageDecodingStoreTest for unit testing new image cache.
+
+        New tests:
+
+        ImageDecodingStoreTest.evictOneCache
+
+        Test eviction of one cache entry by setting a smaller cache limit.
+
+        ImageDecodingStoreTest.pruneOrderIsLeastRecentlyUsed
+
+        Test eviction order is least recently used.
+
+        ImageDecodingStoreTest.pruneCausedByInsertion
+
+        Test that eviction is triggered also by cache insertion.
+
+        ImageDecodingStoreTest.cacheInUseNotEvicted
+
+        Test that cache in use is not evicted.
+
+        ImageDecodingStoreTest.destroyImageFrameGenerator
+
+        Test that deleting a ImageFrameGenerator causes associated cache
+        entries to be removed.
+
+        * WebKit.gypi:
+        * tests/ImageDecodingStoreTest.cpp: Added.
+        (WebCore):
+        (ImageDecodingStoreTest):
+        (WebCore::ImageDecodingStoreTest::SetUp):
+        (WebCore::ImageDecodingStoreTest::TearDown):
+        (WebCore::ImageDecodingStoreTest::createCompleteImage):
+        (WebCore::ImageDecodingStoreTest::insertCache):
+        (WebCore::ImageDecodingStoreTest::lockCache):
+        (WebCore::ImageDecodingStoreTest::unlockCache):
+        (WebCore::ImageDecodingStoreTest::evictOneCache):
+        (WebCore::ImageDecodingStoreTest::isCacheAlive):
+        (WebCore::TEST_F):
+
 2012-12-04  Kenneth Russell  <[email protected]>
 
         Associate URLs with GraphicsContext3D instances created for WebGL

Modified: trunk/Source/WebKit/chromium/WebKit.gypi (136615 => 136616)


--- trunk/Source/WebKit/chromium/WebKit.gypi	2012-12-05 04:05:33 UTC (rev 136615)
+++ trunk/Source/WebKit/chromium/WebKit.gypi	2012-12-05 04:15:31 UTC (rev 136616)
@@ -81,6 +81,7 @@
             'tests/IDBKeyPathTest.cpp',
             'tests/IDBLevelDBCodingTest.cpp',
             'tests/IDBRequestTest.cpp',
+            'tests/ImageDecodingStoreTest.cpp',
             'tests/ImageFrameGeneratorTest.cpp',
             'tests/ImageLayerChromiumTest.cpp',
             'tests/MockImageDecoder.h',

Added: trunk/Source/WebKit/chromium/tests/ImageDecodingStoreTest.cpp (0 => 136616)


--- trunk/Source/WebKit/chromium/tests/ImageDecodingStoreTest.cpp	                        (rev 0)
+++ trunk/Source/WebKit/chromium/tests/ImageDecodingStoreTest.cpp	2012-12-05 04:15:31 UTC (rev 136616)
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "ImageDecodingStore.h"
+
+#include "ImageFrameGenerator.h"
+#include "SharedBuffer.h"
+#include <gtest/gtest.h>
+
+using namespace WebCore;
+
+namespace {
+
+class ImageDecodingStoreTest : public ::testing::Test {
+public:
+    virtual void SetUp()
+    {
+        ImageDecodingStore::initializeOnce();
+        m_data = SharedBuffer::create();
+        m_generator = ImageFrameGenerator::create(SkISize::Make(100, 100), m_data, true);
+    }
+
+    virtual void TearDown()
+    {
+        ImageDecodingStore::shutdown();
+    }
+
+protected:
+    PassOwnPtr<ScaledImageFragment> createCompleteImage(const SkISize& size)
+    {
+        SkBitmap bitmap;
+        bitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height());
+        bitmap.allocPixels();
+        return ScaledImageFragment::create(size, bitmap, true);
+    }
+
+    void insertCache(const SkISize& size)
+    {
+        const ScaledImageFragment* image = ImageDecodingStore::instance()->insertAndLockCache(
+            m_generator.get(), createCompleteImage(size));
+        unlockCache(image);
+    }
+
+    const ScaledImageFragment* lockCache(const SkISize& size)
+    {
+        return ImageDecodingStore::instance()->lockCompleteCache(m_generator.get(), size);
+    }
+
+    void unlockCache(const ScaledImageFragment* cachedImage)
+    {
+        ImageDecodingStore::instance()->unlockCache(m_generator.get(), cachedImage);
+    }
+
+    void evictOneCache()
+    {
+        size_t memoryUsageInBytes = ImageDecodingStore::instance()->memoryUsageInBytes();
+        if (memoryUsageInBytes)
+            ImageDecodingStore::instance()->setCacheLimitInBytes(memoryUsageInBytes - 1);
+        else
+            ImageDecodingStore::instance()->setCacheLimitInBytes(0);
+    }
+
+    bool isCacheAlive(const SkISize& size)
+    {
+        const ScaledImageFragment* cachedImage = lockCache(size);
+        if (!cachedImage)
+            return false;
+        ImageDecodingStore::instance()->unlockCache(m_generator.get(), cachedImage);
+        return true;
+    }
+
+    RefPtr<SharedBuffer> m_data;
+    RefPtr<ImageFrameGenerator> m_generator;
+};
+
+TEST_F(ImageDecodingStoreTest, evictOneCache)
+{
+    insertCache(SkISize::Make(1, 1));
+    insertCache(SkISize::Make(2, 2));
+    insertCache(SkISize::Make(3, 3));
+    EXPECT_EQ(3u, ImageDecodingStore::instance()->cacheEntries());
+
+    evictOneCache();
+    EXPECT_EQ(2u, ImageDecodingStore::instance()->cacheEntries());
+
+    evictOneCache();
+    EXPECT_EQ(1u, ImageDecodingStore::instance()->cacheEntries());
+}
+
+TEST_F(ImageDecodingStoreTest, pruneOrderIsLeastRecentlyUsed)
+{
+    insertCache(SkISize::Make(1, 1));
+    insertCache(SkISize::Make(2, 2));
+    insertCache(SkISize::Make(3, 3));
+    insertCache(SkISize::Make(4, 4));
+    insertCache(SkISize::Make(5, 5));
+    EXPECT_EQ(5u, ImageDecodingStore::instance()->cacheEntries());
+
+    // Use cache in the order 3, 2, 4, 1, 5.
+    EXPECT_TRUE(isCacheAlive(SkISize::Make(3, 3)));
+    EXPECT_TRUE(isCacheAlive(SkISize::Make(2, 2)));
+    EXPECT_TRUE(isCacheAlive(SkISize::Make(4, 4)));
+    EXPECT_TRUE(isCacheAlive(SkISize::Make(1, 1)));
+    EXPECT_TRUE(isCacheAlive(SkISize::Make(5, 5)));
+
+    // Evict 3.
+    evictOneCache();
+    EXPECT_FALSE(isCacheAlive(SkISize::Make(3, 3)));
+    EXPECT_EQ(4u, ImageDecodingStore::instance()->cacheEntries());
+
+    // Evict 2.
+    evictOneCache();
+    EXPECT_FALSE(isCacheAlive(SkISize::Make(2, 2)));
+    EXPECT_EQ(3u, ImageDecodingStore::instance()->cacheEntries());
+
+    // Evict 4.
+    evictOneCache();
+    EXPECT_FALSE(isCacheAlive(SkISize::Make(4, 4)));
+    EXPECT_EQ(2u, ImageDecodingStore::instance()->cacheEntries());
+
+    // Evict 1.
+    evictOneCache();
+    EXPECT_FALSE(isCacheAlive(SkISize::Make(1, 1)));
+    EXPECT_EQ(1u, ImageDecodingStore::instance()->cacheEntries());
+
+    // Evict 5.
+    evictOneCache();
+    EXPECT_FALSE(isCacheAlive(SkISize::Make(5, 5)));
+    EXPECT_EQ(0u, ImageDecodingStore::instance()->cacheEntries());
+}
+
+TEST_F(ImageDecodingStoreTest, pruneCausedByInsertion)
+{
+    ImageDecodingStore::instance()->setCacheLimitInBytes(100);
+
+    // Insert 100 entries.
+    // Cache entries stored should increase and eventually decrease to 1.
+    insertCache(SkISize::Make(1, 1));
+    insertCache(SkISize::Make(2, 2));
+    insertCache(SkISize::Make(3, 3));
+    EXPECT_EQ(3u, ImageDecodingStore::instance()->cacheEntries());
+
+    for (int i = 4; i <= 100; ++i)
+        insertCache(SkISize::Make(i, i));
+
+    EXPECT_EQ(1u, ImageDecodingStore::instance()->cacheEntries());
+    for (int i = 1; i <= 99; ++i)
+        EXPECT_FALSE(isCacheAlive(SkISize::Make(i, i)));
+    EXPECT_TRUE(isCacheAlive(SkISize::Make(100, 100)));
+}
+
+TEST_F(ImageDecodingStoreTest, cacheInUseNotEvicted)
+{
+    insertCache(SkISize::Make(1, 1));
+    insertCache(SkISize::Make(2, 2));
+    insertCache(SkISize::Make(3, 3));
+    EXPECT_EQ(3u, ImageDecodingStore::instance()->cacheEntries());
+
+    const ScaledImageFragment* cachedImage = lockCache(SkISize::Make(1, 1));
+    EXPECT_TRUE(cachedImage);
+
+    // Cache 2 is evicted because cache 1 is in use.
+    evictOneCache();
+    EXPECT_TRUE(isCacheAlive(SkISize::Make(1, 1)));
+    EXPECT_FALSE(isCacheAlive(SkISize::Make(2, 2)));
+    EXPECT_TRUE(isCacheAlive(SkISize::Make(3, 3)));
+
+    EXPECT_EQ(2u, ImageDecodingStore::instance()->cacheEntries());
+    unlockCache(cachedImage);
+}
+
+TEST_F(ImageDecodingStoreTest, destroyImageFrameGenerator)
+{
+    insertCache(SkISize::Make(1, 1));
+    insertCache(SkISize::Make(2, 2));
+    insertCache(SkISize::Make(3, 3));
+    EXPECT_EQ(3u, ImageDecodingStore::instance()->cacheEntries());
+
+    m_generator.clear();
+    EXPECT_FALSE(ImageDecodingStore::instance()->cacheEntries());
+}
+
+} // namespace
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to