Home | History | Annotate | Download | only in graphics
      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 "platform/graphics/ImageDecodingStore.h"
     28 
     29 #include "platform/TraceEvent.h"
     30 #include "wtf/Threading.h"
     31 
     32 namespace WebCore {
     33 
     34 namespace {
     35 
     36 // Allow up to 256 MBytes of discardable entries. The previous limit allowed up to
     37 // 128 entries (independently of their size) which caused OOM errors on websites
     38 // with a lot of (very) large images.
     39 static const size_t maxTotalSizeOfDiscardableEntries = 256 * 1024 * 1024;
     40 static const size_t defaultMaxTotalSizeOfHeapEntries = 32 * 1024 * 1024;
     41 static bool s_imageCachingEnabled = true;
     42 
     43 } // namespace
     44 
     45 ImageDecodingStore::ImageDecodingStore()
     46     : m_heapLimitInBytes(defaultMaxTotalSizeOfHeapEntries)
     47     , m_heapMemoryUsageInBytes(0)
     48     , m_discardableMemoryUsageInBytes(0)
     49 {
     50 }
     51 
     52 ImageDecodingStore::~ImageDecodingStore()
     53 {
     54 #ifndef NDEBUG
     55     setCacheLimitInBytes(0);
     56     ASSERT(!m_imageCacheMap.size());
     57     ASSERT(!m_decoderCacheMap.size());
     58     ASSERT(!m_orderedCacheList.size());
     59     ASSERT(!m_imageCacheKeyMap.size());
     60     ASSERT(!m_decoderCacheKeyMap.size());
     61 #endif
     62 }
     63 
     64 ImageDecodingStore* ImageDecodingStore::instance()
     65 {
     66     AtomicallyInitializedStatic(ImageDecodingStore*, store = ImageDecodingStore::create().leakPtr());
     67     return store;
     68 }
     69 
     70 void ImageDecodingStore::setImageCachingEnabled(bool enabled)
     71 {
     72     s_imageCachingEnabled = enabled;
     73 }
     74 
     75 bool ImageDecodingStore::lockCache(const ImageFrameGenerator* generator, const SkISize& scaledSize, size_t index, const ScaledImageFragment** cachedImage)
     76 {
     77     ASSERT(cachedImage);
     78 
     79     Vector<OwnPtr<CacheEntry> > cacheEntriesToDelete;
     80     {
     81         MutexLocker lock(m_mutex);
     82         // Public access is restricted to complete images only.
     83         ImageCacheMap::iterator iter = m_imageCacheMap.find(ImageCacheEntry::makeCacheKey(generator, scaledSize, index, ScaledImageFragment::CompleteImage));
     84         if (iter == m_imageCacheMap.end())
     85             return false;
     86         return lockCacheEntryInternal(iter->value.get(), cachedImage, &cacheEntriesToDelete);
     87     }
     88 }
     89 
     90 void ImageDecodingStore::unlockCache(const ImageFrameGenerator* generator, const ScaledImageFragment* cachedImage)
     91 {
     92     Vector<OwnPtr<CacheEntry> > cacheEntriesToDelete;
     93     {
     94         MutexLocker lock(m_mutex);
     95         cachedImage->bitmap().unlockPixels();
     96         ImageCacheMap::iterator iter = m_imageCacheMap.find(ImageCacheEntry::makeCacheKey(generator, cachedImage->scaledSize(), cachedImage->index(), cachedImage->generation()));
     97         ASSERT_WITH_SECURITY_IMPLICATION(iter != m_imageCacheMap.end());
     98 
     99         CacheEntry* cacheEntry = iter->value.get();
    100         cacheEntry->decrementUseCount();
    101 
    102         // Put the entry to the end of list.
    103         m_orderedCacheList.remove(cacheEntry);
    104         m_orderedCacheList.append(cacheEntry);
    105 
    106         // FIXME: This code is temporary such that in the new Skia
    107         // discardable memory path we do not cache images.
    108         // Once the transition is complete the logic to handle
    109         // image caching should be removed entirely.
    110         if (!s_imageCachingEnabled && !cacheEntry->useCount()) {
    111             removeFromCacheInternal(cacheEntry, &cacheEntriesToDelete);
    112             removeFromCacheListInternal(cacheEntriesToDelete);
    113         }
    114     }
    115 }
    116 
    117 const ScaledImageFragment* ImageDecodingStore::insertAndLockCache(const ImageFrameGenerator* generator, PassOwnPtr<ScaledImageFragment> image)
    118 {
    119     // Prune old cache entries to give space for the new one.
    120     prune();
    121 
    122     ScaledImageFragment* newImage = image.get();
    123     OwnPtr<ImageCacheEntry> newCacheEntry = ImageCacheEntry::createAndUse(generator, image);
    124     Vector<OwnPtr<CacheEntry> > cacheEntriesToDelete;
    125     {
    126         MutexLocker lock(m_mutex);
    127 
    128         ImageCacheMap::iterator iter = m_imageCacheMap.find(newCacheEntry->cacheKey());
    129 
    130         // It is rare but possible that the key of a new cache entry is found.
    131         // This happens if the generation ID of the image object wraps around.
    132         // In this case we will try to return the existing cached object and
    133         // discard the new cache object.
    134         if (iter != m_imageCacheMap.end()) {
    135             const ScaledImageFragment* oldImage;
    136             if (lockCacheEntryInternal(iter->value.get(), &oldImage, &cacheEntriesToDelete)) {
    137                 newCacheEntry->decrementUseCount();
    138                 return oldImage;
    139             }
    140         }
    141 
    142         // The new image is not locked yet so do it here.
    143         newImage->bitmap().lockPixels();
    144         insertCacheInternal(newCacheEntry.release(), &m_imageCacheMap, &m_imageCacheKeyMap);
    145     }
    146     return newImage;
    147 }
    148 
    149 bool ImageDecodingStore::lockDecoder(const ImageFrameGenerator* generator, const SkISize& scaledSize, ImageDecoder** decoder)
    150 {
    151     ASSERT(decoder);
    152 
    153     MutexLocker lock(m_mutex);
    154     DecoderCacheMap::iterator iter = m_decoderCacheMap.find(DecoderCacheEntry::makeCacheKey(generator, scaledSize));
    155     if (iter == m_decoderCacheMap.end())
    156         return false;
    157 
    158     DecoderCacheEntry* cacheEntry = iter->value.get();
    159 
    160     // There can only be one user of a decoder at a time.
    161     ASSERT(!cacheEntry->useCount());
    162     cacheEntry->incrementUseCount();
    163     *decoder = cacheEntry->cachedDecoder();
    164     return true;
    165 }
    166 
    167 void ImageDecodingStore::unlockDecoder(const ImageFrameGenerator* generator, const ImageDecoder* decoder)
    168 {
    169     MutexLocker lock(m_mutex);
    170     DecoderCacheMap::iterator iter = m_decoderCacheMap.find(DecoderCacheEntry::makeCacheKey(generator, decoder));
    171     ASSERT_WITH_SECURITY_IMPLICATION(iter != m_decoderCacheMap.end());
    172 
    173     CacheEntry* cacheEntry = iter->value.get();
    174     cacheEntry->decrementUseCount();
    175 
    176     // Put the entry to the end of list.
    177     m_orderedCacheList.remove(cacheEntry);
    178     m_orderedCacheList.append(cacheEntry);
    179 }
    180 
    181 void ImageDecodingStore::insertDecoder(const ImageFrameGenerator* generator, PassOwnPtr<ImageDecoder> decoder, bool isDiscardable)
    182 {
    183     // Prune old cache entries to give space for the new one.
    184     prune();
    185 
    186     OwnPtr<DecoderCacheEntry> newCacheEntry = DecoderCacheEntry::create(generator, decoder, isDiscardable);
    187 
    188     MutexLocker lock(m_mutex);
    189     ASSERT(!m_decoderCacheMap.contains(newCacheEntry->cacheKey()));
    190     insertCacheInternal(newCacheEntry.release(), &m_decoderCacheMap, &m_decoderCacheKeyMap);
    191 }
    192 
    193 void ImageDecodingStore::removeDecoder(const ImageFrameGenerator* generator, const ImageDecoder* decoder)
    194 {
    195     Vector<OwnPtr<CacheEntry> > cacheEntriesToDelete;
    196     {
    197         MutexLocker lock(m_mutex);
    198         DecoderCacheMap::iterator iter = m_decoderCacheMap.find(DecoderCacheEntry::makeCacheKey(generator, decoder));
    199         ASSERT_WITH_SECURITY_IMPLICATION(iter != m_decoderCacheMap.end());
    200 
    201         CacheEntry* cacheEntry = iter->value.get();
    202         ASSERT(cacheEntry->useCount());
    203         cacheEntry->decrementUseCount();
    204 
    205         // Delete only one decoder cache entry. Ownership of the cache entry
    206         // is transfered to cacheEntriesToDelete such that object can be deleted
    207         // outside of the lock.
    208         removeFromCacheInternal(cacheEntry, &cacheEntriesToDelete);
    209 
    210         // Remove from LRU list.
    211         removeFromCacheListInternal(cacheEntriesToDelete);
    212     }
    213 }
    214 
    215 bool ImageDecodingStore::isCached(const ImageFrameGenerator* generator, const SkISize& scaledSize, size_t index)
    216 {
    217     MutexLocker lock(m_mutex);
    218     ImageCacheMap::iterator iter = m_imageCacheMap.find(ImageCacheEntry::makeCacheKey(generator, scaledSize, index, ScaledImageFragment::CompleteImage));
    219     if (iter == m_imageCacheMap.end())
    220         return false;
    221     return true;
    222 }
    223 
    224 void ImageDecodingStore::removeCacheIndexedByGenerator(const ImageFrameGenerator* generator)
    225 {
    226     Vector<OwnPtr<CacheEntry> > cacheEntriesToDelete;
    227     {
    228         MutexLocker lock(m_mutex);
    229 
    230         // Remove image cache objects and decoder cache objects associated
    231         // with a ImageFrameGenerator.
    232         removeCacheIndexedByGeneratorInternal(&m_imageCacheMap, &m_imageCacheKeyMap, generator, &cacheEntriesToDelete);
    233         removeCacheIndexedByGeneratorInternal(&m_decoderCacheMap, &m_decoderCacheKeyMap, generator, &cacheEntriesToDelete);
    234 
    235         // Remove from LRU list as well.
    236         removeFromCacheListInternal(cacheEntriesToDelete);
    237     }
    238 }
    239 
    240 void ImageDecodingStore::clear()
    241 {
    242     size_t cacheLimitInBytes;
    243     {
    244         MutexLocker lock(m_mutex);
    245         cacheLimitInBytes = m_heapLimitInBytes;
    246         m_heapLimitInBytes = 0;
    247     }
    248 
    249     prune();
    250 
    251     {
    252         MutexLocker lock(m_mutex);
    253         m_heapLimitInBytes = cacheLimitInBytes;
    254     }
    255 }
    256 
    257 void ImageDecodingStore::setCacheLimitInBytes(size_t cacheLimit)
    258 {
    259     // Note that the discardable entries limit is constant (i.e. only the heap limit is updated).
    260     {
    261         MutexLocker lock(m_mutex);
    262         m_heapLimitInBytes = cacheLimit;
    263     }
    264     prune();
    265 }
    266 
    267 size_t ImageDecodingStore::memoryUsageInBytes()
    268 {
    269     MutexLocker lock(m_mutex);
    270     return m_heapMemoryUsageInBytes;
    271 }
    272 
    273 int ImageDecodingStore::cacheEntries()
    274 {
    275     MutexLocker lock(m_mutex);
    276     return m_imageCacheMap.size() + m_decoderCacheMap.size();
    277 }
    278 
    279 int ImageDecodingStore::imageCacheEntries()
    280 {
    281     MutexLocker lock(m_mutex);
    282     return m_imageCacheMap.size();
    283 }
    284 
    285 int ImageDecodingStore::decoderCacheEntries()
    286 {
    287     MutexLocker lock(m_mutex);
    288     return m_decoderCacheMap.size();
    289 }
    290 
    291 void ImageDecodingStore::prune()
    292 {
    293     TRACE_EVENT0("webkit", "ImageDecodingStore::prune");
    294 
    295     Vector<OwnPtr<CacheEntry> > cacheEntriesToDelete;
    296     {
    297         MutexLocker lock(m_mutex);
    298 
    299         // Head of the list is the least recently used entry.
    300         const CacheEntry* cacheEntry = m_orderedCacheList.head();
    301 
    302         // Walk the list of cache entries starting from the least recently used
    303         // and then keep them for deletion later.
    304         while (cacheEntry) {
    305             const bool isPruneNeeded = m_heapMemoryUsageInBytes > m_heapLimitInBytes || !m_heapLimitInBytes
    306                 || m_discardableMemoryUsageInBytes > maxTotalSizeOfDiscardableEntries;
    307             if (!isPruneNeeded)
    308                 break;
    309 
    310             // Cache is not used; Remove it.
    311             if (!cacheEntry->useCount())
    312                 removeFromCacheInternal(cacheEntry, &cacheEntriesToDelete);
    313             cacheEntry = cacheEntry->next();
    314         }
    315 
    316         // Remove from cache list as well.
    317         removeFromCacheListInternal(cacheEntriesToDelete);
    318     }
    319 }
    320 
    321 bool ImageDecodingStore::lockCacheEntryInternal(ImageCacheEntry* cacheEntry, const ScaledImageFragment** cachedImage, Vector<OwnPtr<CacheEntry> >* deletionList)
    322 {
    323     ScaledImageFragment* image = cacheEntry->cachedImage();
    324 
    325     image->bitmap().lockPixels();
    326 
    327     // Memory for this image entry might be discarded already.
    328     // In this case remove the entry.
    329     if (!image->bitmap().getPixels()) {
    330         image->bitmap().unlockPixels();
    331         removeFromCacheInternal(cacheEntry, &m_imageCacheMap, &m_imageCacheKeyMap, deletionList);
    332         removeFromCacheListInternal(*deletionList);
    333         return false;
    334     }
    335     cacheEntry->incrementUseCount();
    336     *cachedImage = image;
    337     return true;
    338 }
    339 
    340 template<class T, class U, class V>
    341 void ImageDecodingStore::insertCacheInternal(PassOwnPtr<T> cacheEntry, U* cacheMap, V* identifierMap)
    342 {
    343     const size_t cacheEntryBytes = cacheEntry->memoryUsageInBytes();
    344     if (cacheEntry->isDiscardable())
    345         m_discardableMemoryUsageInBytes += cacheEntryBytes;
    346     else
    347         m_heapMemoryUsageInBytes += cacheEntryBytes;
    348 
    349     // m_orderedCacheList is used to support LRU operations to reorder cache
    350     // entries quickly.
    351     m_orderedCacheList.append(cacheEntry.get());
    352 
    353     typename U::KeyType key = cacheEntry->cacheKey();
    354     typename V::AddResult result = identifierMap->add(cacheEntry->generator(), typename V::MappedType());
    355     result.storedValue->value.add(key);
    356     cacheMap->add(key, cacheEntry);
    357 
    358     TRACE_COUNTER1("webkit", "ImageDecodingStoreDiscardableMemoryUsageBytes", m_discardableMemoryUsageInBytes);
    359     TRACE_COUNTER1("webkit", "ImageDecodingStoreHeapMemoryUsageBytes", m_heapMemoryUsageInBytes);
    360     TRACE_COUNTER1("webkit", "ImageDecodingStoreNumOfImages", m_imageCacheMap.size());
    361     TRACE_COUNTER1("webkit", "ImageDecodingStoreNumOfDecoders", m_decoderCacheMap.size());
    362 }
    363 
    364 template<class T, class U, class V>
    365 void ImageDecodingStore::removeFromCacheInternal(const T* cacheEntry, U* cacheMap, V* identifierMap, Vector<OwnPtr<CacheEntry> >* deletionList)
    366 {
    367     const size_t cacheEntryBytes = cacheEntry->memoryUsageInBytes();
    368     if (cacheEntry->isDiscardable()) {
    369         ASSERT(m_discardableMemoryUsageInBytes >= cacheEntryBytes);
    370         m_discardableMemoryUsageInBytes -= cacheEntryBytes;
    371     } else {
    372         ASSERT(m_heapMemoryUsageInBytes >= cacheEntryBytes);
    373         m_heapMemoryUsageInBytes -= cacheEntryBytes;
    374 
    375     }
    376 
    377     // Remove entry from identifier map.
    378     typename V::iterator iter = identifierMap->find(cacheEntry->generator());
    379     ASSERT(iter != identifierMap->end());
    380     iter->value.remove(cacheEntry->cacheKey());
    381     if (!iter->value.size())
    382         identifierMap->remove(iter);
    383 
    384     // Remove entry from cache map.
    385     deletionList->append(cacheMap->take(cacheEntry->cacheKey()));
    386 
    387     TRACE_COUNTER1("webkit", "ImageDecodingStoreDiscardableMemoryUsageBytes", m_discardableMemoryUsageInBytes);
    388     TRACE_COUNTER1("webkit", "ImageDecodingStoreHeapMemoryUsageBytes", m_heapMemoryUsageInBytes);
    389     TRACE_COUNTER1("webkit", "ImageDecodingStoreNumOfImages", m_imageCacheMap.size());
    390     TRACE_COUNTER1("webkit", "ImageDecodingStoreNumOfDecoders", m_decoderCacheMap.size());
    391 }
    392 
    393 void ImageDecodingStore::removeFromCacheInternal(const CacheEntry* cacheEntry, Vector<OwnPtr<CacheEntry> >* deletionList)
    394 {
    395     if (cacheEntry->type() == CacheEntry::TypeImage) {
    396         removeFromCacheInternal(static_cast<const ImageCacheEntry*>(cacheEntry), &m_imageCacheMap, &m_imageCacheKeyMap, deletionList);
    397     } else if (cacheEntry->type() == CacheEntry::TypeDecoder) {
    398         removeFromCacheInternal(static_cast<const DecoderCacheEntry*>(cacheEntry), &m_decoderCacheMap, &m_decoderCacheKeyMap, deletionList);
    399     } else {
    400         ASSERT(false);
    401     }
    402 }
    403 
    404 template<class U, class V>
    405 void ImageDecodingStore::removeCacheIndexedByGeneratorInternal(U* cacheMap, V* identifierMap, const ImageFrameGenerator* generator, Vector<OwnPtr<CacheEntry> >* deletionList)
    406 {
    407     typename V::iterator iter = identifierMap->find(generator);
    408     if (iter == identifierMap->end())
    409         return;
    410 
    411     // Get all cache identifiers associated with generator.
    412     Vector<typename U::KeyType> cacheIdentifierList;
    413     copyToVector(iter->value, cacheIdentifierList);
    414 
    415     // For each cache identifier find the corresponding CacheEntry and remove it.
    416     for (size_t i = 0; i < cacheIdentifierList.size(); ++i) {
    417         ASSERT(cacheMap->contains(cacheIdentifierList[i]));
    418         const typename U::MappedType::PtrType cacheEntry = cacheMap->get(cacheIdentifierList[i]);
    419         ASSERT(!cacheEntry->useCount());
    420         removeFromCacheInternal(cacheEntry, cacheMap, identifierMap, deletionList);
    421     }
    422 }
    423 
    424 void ImageDecodingStore::removeFromCacheListInternal(const Vector<OwnPtr<CacheEntry> >& deletionList)
    425 {
    426     for (size_t i = 0; i < deletionList.size(); ++i)
    427         m_orderedCacheList.remove(deletionList[i].get());
    428 }
    429 
    430 } // namespace WebCore
    431