Home | History | Annotate | Download | only in chromium
      1 /*
      2  * Copyright (C) 2012 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "core/platform/graphics/chromium/ImageDecodingStore.h"
     28 
     29 #include "core/platform/chromium/TraceEvent.h"
     30 #include "core/platform/graphics/chromium/ScaledImageFragment.h"
     31 
     32 namespace WebCore {
     33 
     34 namespace {
     35 
     36 // 32MB memory limit for cache.
     37 static const size_t defaultCacheLimitInBytes = 32768 * 1024;
     38 static ImageDecodingStore* s_instance = 0;
     39 
     40 static void setInstance(ImageDecodingStore* imageDecodingStore)
     41 {
     42     delete s_instance;
     43     s_instance = imageDecodingStore;
     44 }
     45 
     46 } // namespace
     47 
     48 ImageDecodingStore::ImageDecodingStore()
     49     : m_cacheLimitInBytes(defaultCacheLimitInBytes)
     50     , m_memoryUsageInBytes(0)
     51 {
     52 }
     53 
     54 ImageDecodingStore::~ImageDecodingStore()
     55 {
     56 #ifndef NDEBUG
     57     setCacheLimitInBytes(0);
     58     ASSERT(!m_imageCacheMap.size());
     59     ASSERT(!m_decoderCacheMap.size());
     60     ASSERT(!m_orderedCacheList.size());
     61     ASSERT(!m_imageCacheKeyMap.size());
     62     ASSERT(!m_decoderCacheKeyMap.size());
     63 #endif
     64 }
     65 
     66 ImageDecodingStore* ImageDecodingStore::instance()
     67 {
     68     return s_instance;
     69 }
     70 
     71 void ImageDecodingStore::initializeOnce()
     72 {
     73     setInstance(ImageDecodingStore::create().leakPtr());
     74 }
     75 
     76 void ImageDecodingStore::shutdown()
     77 {
     78     setInstance(0);
     79 }
     80 
     81 bool ImageDecodingStore::lockCache(const ImageFrameGenerator* generator, const SkISize& scaledSize, size_t index, const ScaledImageFragment** cachedImage)
     82 {
     83     ASSERT(cachedImage);
     84 
     85     Vector<OwnPtr<CacheEntry> > cacheEntriesToDelete;
     86     {
     87         MutexLocker lock(m_mutex);
     88         // Public access is restricted to complete images only.
     89         ImageCacheMap::iterator iter = m_imageCacheMap.find(ImageCacheEntry::makeCacheKey(generator, scaledSize, index, ScaledImageFragment::CompleteImage));
     90         if (iter == m_imageCacheMap.end())
     91             return false;
     92         return lockCacheEntryInternal(iter->value.get(), cachedImage, &cacheEntriesToDelete);
     93     }
     94 }
     95 
     96 void ImageDecodingStore::unlockCache(const ImageFrameGenerator* generator, const ScaledImageFragment* cachedImage)
     97 {
     98     MutexLocker lock(m_mutex);
     99     cachedImage->bitmap().unlockPixels();
    100     ImageCacheMap::iterator iter = m_imageCacheMap.find(ImageCacheEntry::makeCacheKey(generator, cachedImage->scaledSize(), cachedImage->index(), cachedImage->generation()));
    101     ASSERT(iter != m_imageCacheMap.end());
    102 
    103     CacheEntry* cacheEntry = iter->value.get();
    104     cacheEntry->decrementUseCount();
    105 
    106     // Put the entry to the end of list.
    107     m_orderedCacheList.remove(cacheEntry);
    108     m_orderedCacheList.append(cacheEntry);
    109 }
    110 
    111 const ScaledImageFragment* ImageDecodingStore::insertAndLockCache(const ImageFrameGenerator* generator, PassOwnPtr<ScaledImageFragment> image)
    112 {
    113     // Prune old cache entries to give space for the new one.
    114     prune();
    115 
    116     ScaledImageFragment* newImage = image.get();
    117     OwnPtr<ImageCacheEntry> newCacheEntry = ImageCacheEntry::createAndUse(generator, image);
    118     Vector<OwnPtr<CacheEntry> > cacheEntriesToDelete;
    119     {
    120         MutexLocker lock(m_mutex);
    121 
    122         ImageCacheMap::iterator iter = m_imageCacheMap.find(newCacheEntry->cacheKey());
    123 
    124         // It is rare but possible that the key of a new cache entry is found.
    125         // This happens if the generation ID of the image object wraps around.
    126         // In this case we will try to return the existing cached object and
    127         // discard the new cache object.
    128         if (iter != m_imageCacheMap.end()) {
    129             const ScaledImageFragment* oldImage;
    130             if (lockCacheEntryInternal(iter->value.get(), &oldImage, &cacheEntriesToDelete)) {
    131                 newCacheEntry->decrementUseCount();
    132                 return oldImage;
    133             }
    134         }
    135 
    136         // The new image is not locked yet so do it here.
    137         newImage->bitmap().lockPixels();
    138         insertCacheInternal(newCacheEntry.release(), &m_imageCacheMap, &m_imageCacheKeyMap);
    139     }
    140     return newImage;
    141 }
    142 
    143 bool ImageDecodingStore::lockDecoder(const ImageFrameGenerator* generator, const SkISize& scaledSize, ImageDecoder** decoder)
    144 {
    145     ASSERT(decoder);
    146 
    147     MutexLocker lock(m_mutex);
    148     DecoderCacheMap::iterator iter = m_decoderCacheMap.find(DecoderCacheEntry::makeCacheKey(generator, scaledSize));
    149     if (iter == m_decoderCacheMap.end())
    150         return false;
    151 
    152     DecoderCacheEntry* cacheEntry = iter->value.get();
    153 
    154     // There can only be one user of a decoder at a time.
    155     ASSERT(!cacheEntry->useCount());
    156     cacheEntry->incrementUseCount();
    157     *decoder = cacheEntry->cachedDecoder();
    158     return true;
    159 }
    160 
    161 void ImageDecodingStore::unlockDecoder(const ImageFrameGenerator* generator, const ImageDecoder* decoder)
    162 {
    163     MutexLocker lock(m_mutex);
    164     DecoderCacheMap::iterator iter = m_decoderCacheMap.find(DecoderCacheEntry::makeCacheKey(generator, decoder));
    165     ASSERT(iter != m_decoderCacheMap.end());
    166 
    167     CacheEntry* cacheEntry = iter->value.get();
    168     cacheEntry->decrementUseCount();
    169 
    170     // Put the entry to the end of list.
    171     m_orderedCacheList.remove(cacheEntry);
    172     m_orderedCacheList.append(cacheEntry);
    173 }
    174 
    175 void ImageDecodingStore::insertDecoder(const ImageFrameGenerator* generator, PassOwnPtr<ImageDecoder> decoder, bool isDiscardable)
    176 {
    177     // Prune old cache entries to give space for the new one.
    178     prune();
    179 
    180     OwnPtr<DecoderCacheEntry> newCacheEntry = DecoderCacheEntry::create(generator, decoder, isDiscardable);
    181 
    182     MutexLocker lock(m_mutex);
    183     ASSERT(!m_decoderCacheMap.contains(newCacheEntry->cacheKey()));
    184     insertCacheInternal(newCacheEntry.release(), &m_decoderCacheMap, &m_decoderCacheKeyMap);
    185 }
    186 
    187 void ImageDecodingStore::removeDecoder(const ImageFrameGenerator* generator, const ImageDecoder* decoder)
    188 {
    189     Vector<OwnPtr<CacheEntry> > cacheEntriesToDelete;
    190     {
    191         MutexLocker lock(m_mutex);
    192         DecoderCacheMap::iterator iter = m_decoderCacheMap.find(DecoderCacheEntry::makeCacheKey(generator, decoder));
    193         ASSERT(iter != m_decoderCacheMap.end());
    194 
    195         CacheEntry* cacheEntry = iter->value.get();
    196         ASSERT(cacheEntry->useCount());
    197         cacheEntry->decrementUseCount();
    198 
    199         // Delete only one decoder cache entry. Ownership of the cache entry
    200         // is transfered to cacheEntriesToDelete such that object can be deleted
    201         // outside of the lock.
    202         removeFromCacheInternal(cacheEntry, &cacheEntriesToDelete);
    203 
    204         // Remove from LRU list.
    205         removeFromCacheListInternal(cacheEntriesToDelete);
    206     }
    207 }
    208 
    209 bool ImageDecodingStore::isCached(const ImageFrameGenerator* generator, const SkISize& scaledSize, size_t index)
    210 {
    211     MutexLocker lock(m_mutex);
    212     ImageCacheMap::iterator iter = m_imageCacheMap.find(ImageCacheEntry::makeCacheKey(generator, scaledSize, index, ScaledImageFragment::CompleteImage));
    213     if (iter == m_imageCacheMap.end())
    214         return false;
    215     return true;
    216 }
    217 
    218 void ImageDecodingStore::removeCacheIndexedByGenerator(const ImageFrameGenerator* generator)
    219 {
    220     Vector<OwnPtr<CacheEntry> > cacheEntriesToDelete;
    221     {
    222         MutexLocker lock(m_mutex);
    223 
    224         // Remove image cache objects and decoder cache objects associated
    225         // with a ImageFrameGenerator.
    226         removeCacheIndexedByGeneratorInternal(&m_imageCacheMap, &m_imageCacheKeyMap, generator, &cacheEntriesToDelete);
    227         removeCacheIndexedByGeneratorInternal(&m_decoderCacheMap, &m_decoderCacheKeyMap, generator, &cacheEntriesToDelete);
    228 
    229         // Remove from LRU list as well.
    230         removeFromCacheListInternal(cacheEntriesToDelete);
    231     }
    232 }
    233 
    234 void ImageDecodingStore::clear()
    235 {
    236     size_t cacheLimitInBytes;
    237     {
    238         MutexLocker lock(m_mutex);
    239         cacheLimitInBytes = m_cacheLimitInBytes;
    240         m_cacheLimitInBytes = 0;
    241     }
    242 
    243     prune();
    244 
    245     {
    246         MutexLocker lock(m_mutex);
    247         m_cacheLimitInBytes = cacheLimitInBytes;
    248     }
    249 }
    250 
    251 void ImageDecodingStore::setCacheLimitInBytes(size_t cacheLimit)
    252 {
    253     {
    254         MutexLocker lock(m_mutex);
    255         m_cacheLimitInBytes = cacheLimit;
    256     }
    257     prune();
    258 }
    259 
    260 size_t ImageDecodingStore::memoryUsageInBytes()
    261 {
    262     MutexLocker lock(m_mutex);
    263     return m_memoryUsageInBytes;
    264 }
    265 
    266 unsigned ImageDecodingStore::cacheEntries()
    267 {
    268     MutexLocker lock(m_mutex);
    269     return m_imageCacheMap.size() + m_decoderCacheMap.size();
    270 }
    271 
    272 unsigned ImageDecodingStore::imageCacheEntries()
    273 {
    274     MutexLocker lock(m_mutex);
    275     return m_imageCacheMap.size();
    276 }
    277 
    278 unsigned ImageDecodingStore::decoderCacheEntries()
    279 {
    280     MutexLocker lock(m_mutex);
    281     return m_decoderCacheMap.size();
    282 }
    283 
    284 void ImageDecodingStore::prune()
    285 {
    286     TRACE_EVENT0("webkit", "ImageDecodingStore::prune");
    287 
    288     Vector<OwnPtr<CacheEntry> > cacheEntriesToDelete;
    289     {
    290         MutexLocker lock(m_mutex);
    291 
    292         // Head of the list is the least recently used entry.
    293         const CacheEntry* cacheEntry = m_orderedCacheList.head();
    294 
    295         // Walk the list of cache entries starting from the least recently used
    296         // and then keep them for deletion later.
    297         while (cacheEntry && (m_memoryUsageInBytes > m_cacheLimitInBytes || !m_cacheLimitInBytes)) {
    298             // Cache is not used; Remove it.
    299             if (!cacheEntry->useCount())
    300                 removeFromCacheInternal(cacheEntry, &cacheEntriesToDelete);
    301             cacheEntry = cacheEntry->next();
    302         }
    303 
    304         // Remove from cache list as well.
    305         removeFromCacheListInternal(cacheEntriesToDelete);
    306     }
    307 }
    308 
    309 bool ImageDecodingStore::lockCacheEntryInternal(ImageCacheEntry* cacheEntry, const ScaledImageFragment** cachedImage, Vector<OwnPtr<CacheEntry> >* deletionList)
    310 {
    311     ScaledImageFragment* image = cacheEntry->cachedImage();
    312 
    313     image->bitmap().lockPixels();
    314 
    315     // Memory for this image entry might be discarded already.
    316     // In this case remove the entry.
    317     if (!image->bitmap().getPixels()) {
    318         image->bitmap().unlockPixels();
    319         removeFromCacheInternal(cacheEntry, &m_imageCacheMap, &m_imageCacheKeyMap, deletionList);
    320         removeFromCacheListInternal(*deletionList);
    321         return false;
    322     }
    323     cacheEntry->incrementUseCount();
    324     *cachedImage = image;
    325     return true;
    326 }
    327 
    328 template<class T, class U, class V>
    329 void ImageDecodingStore::insertCacheInternal(PassOwnPtr<T> cacheEntry, U* cacheMap, V* identifierMap)
    330 {
    331     // Usage of discardable memory is not counted because we want to use more
    332     // than the cache limit allows. Cache limit only applies to non-discardable
    333     // objects.
    334     if (!cacheEntry->isDiscardable())
    335         incrementMemoryUsage(cacheEntry->memoryUsageInBytes());
    336 
    337     // m_orderedCacheList is used to support LRU operations to reorder cache
    338     // entries quickly.
    339     m_orderedCacheList.append(cacheEntry.get());
    340 
    341     typename U::KeyType key = cacheEntry->cacheKey();
    342     typename V::AddResult result = identifierMap->add(cacheEntry->generator(), typename V::MappedType());
    343     result.iterator->value.add(key);
    344     cacheMap->add(key, cacheEntry);
    345 
    346     TRACE_COUNTER1("webkit", "ImageDecodingStoreMemoryUsageBytes", m_memoryUsageInBytes);
    347     TRACE_COUNTER1("webkit", "ImageDecodingStoreNumOfImages", m_imageCacheMap.size());
    348     TRACE_COUNTER1("webkit", "ImageDecodingStoreNumOfDecoders", m_decoderCacheMap.size());
    349 }
    350 
    351 template<class T, class U, class V>
    352 void ImageDecodingStore::removeFromCacheInternal(const T* cacheEntry, U* cacheMap, V* identifierMap, Vector<OwnPtr<CacheEntry> >* deletionList)
    353 {
    354     if (!cacheEntry->isDiscardable())
    355         decrementMemoryUsage(cacheEntry->memoryUsageInBytes());
    356 
    357     // Remove entry from identifier map.
    358     typename V::iterator iter = identifierMap->find(cacheEntry->generator());
    359     ASSERT(iter != identifierMap->end());
    360     iter->value.remove(cacheEntry->cacheKey());
    361     if (!iter->value.size())
    362         identifierMap->remove(iter);
    363 
    364     // Remove entry from cache map.
    365     deletionList->append(cacheMap->take(cacheEntry->cacheKey()));
    366 
    367     TRACE_COUNTER1("webkit", "ImageDecodingStoreMemoryUsageBytes", m_memoryUsageInBytes);
    368     TRACE_COUNTER1("webkit", "ImageDecodingStoreNumOfImages", m_imageCacheMap.size());
    369     TRACE_COUNTER1("webkit", "ImageDecodingStoreNumOfDecoders", m_decoderCacheMap.size());
    370 }
    371 
    372 void ImageDecodingStore::removeFromCacheInternal(const CacheEntry* cacheEntry, Vector<OwnPtr<CacheEntry> >* deletionList)
    373 {
    374     if (cacheEntry->type() == CacheEntry::TypeImage) {
    375         removeFromCacheInternal(static_cast<const ImageCacheEntry*>(cacheEntry), &m_imageCacheMap, &m_imageCacheKeyMap, deletionList);
    376     } else if (cacheEntry->type() == CacheEntry::TypeDecoder) {
    377         removeFromCacheInternal(static_cast<const DecoderCacheEntry*>(cacheEntry), &m_decoderCacheMap, &m_decoderCacheKeyMap, deletionList);
    378     } else {
    379         ASSERT(false);
    380     }
    381 }
    382 
    383 template<class U, class V>
    384 void ImageDecodingStore::removeCacheIndexedByGeneratorInternal(U* cacheMap, V* identifierMap, const ImageFrameGenerator* generator, Vector<OwnPtr<CacheEntry> >* deletionList)
    385 {
    386     typename V::iterator iter = identifierMap->find(generator);
    387     if (iter == identifierMap->end())
    388         return;
    389 
    390     // Get all cache identifiers associated with generator.
    391     Vector<typename U::KeyType> cacheIdentifierList;
    392     copyToVector(iter->value, cacheIdentifierList);
    393 
    394     // For each cache identifier find the corresponding CacheEntry and remove it.
    395     for (size_t i = 0; i < cacheIdentifierList.size(); ++i) {
    396         ASSERT(cacheMap->contains(cacheIdentifierList[i]));
    397         const typename U::MappedType::PtrType cacheEntry = cacheMap->get(cacheIdentifierList[i]);
    398         ASSERT(!cacheEntry->useCount());
    399         removeFromCacheInternal(cacheEntry, cacheMap, identifierMap, deletionList);
    400     }
    401 }
    402 
    403 void ImageDecodingStore::removeFromCacheListInternal(const Vector<OwnPtr<CacheEntry> >& deletionList)
    404 {
    405     for (size_t i = 0; i < deletionList.size(); ++i)
    406         m_orderedCacheList.remove(deletionList[i].get());
    407 }
    408 
    409 } // namespace WebCore
    410