| /* | |
| * 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 COMPUTER, INC. ``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 COMPUTER, INC. OR | |
| * 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 "platform/graphics/ImageDecodingStore.h" | |
| #include "platform/graphics/ImageFrameGenerator.h" | |
| #include "platform/instrumentation/tracing/TraceEvent.h" | |
| #include "wtf/Threading.h" | |
| #include <memory> | |
| namespace blink { | |
| namespace { | |
| static const size_t defaultMaxTotalSizeOfHeapEntries = 32 * 1024 * 1024; | |
| } // namespace | |
| ImageDecodingStore::ImageDecodingStore() | |
| : m_heapLimitInBytes(defaultMaxTotalSizeOfHeapEntries), | |
| m_heapMemoryUsageInBytes(0) {} | |
| ImageDecodingStore::~ImageDecodingStore() { | |
| #if DCHECK_IS_ON() | |
| setCacheLimitInBytes(0); | |
| ASSERT(!m_decoderCacheMap.size()); | |
| ASSERT(!m_orderedCacheList.size()); | |
| ASSERT(!m_decoderCacheKeyMap.size()); | |
| #endif | |
| } | |
| ImageDecodingStore& ImageDecodingStore::instance() { | |
| DEFINE_THREAD_SAFE_STATIC_LOCAL(ImageDecodingStore, store, | |
| ImageDecodingStore::create().release()); | |
| return store; | |
| } | |
| bool ImageDecodingStore::lockDecoder(const ImageFrameGenerator* generator, | |
| const SkISize& scaledSize, | |
| ImageDecoder** decoder) { | |
| ASSERT(decoder); | |
| MutexLocker lock(m_mutex); | |
| DecoderCacheMap::iterator iter = m_decoderCacheMap.find( | |
| DecoderCacheEntry::makeCacheKey(generator, scaledSize)); | |
| if (iter == m_decoderCacheMap.end()) | |
| return false; | |
| DecoderCacheEntry* cacheEntry = iter->value.get(); | |
| // There can only be one user of a decoder at a time. | |
| ASSERT(!cacheEntry->useCount()); | |
| cacheEntry->incrementUseCount(); | |
| *decoder = cacheEntry->cachedDecoder(); | |
| return true; | |
| } | |
| void ImageDecodingStore::unlockDecoder(const ImageFrameGenerator* generator, | |
| const ImageDecoder* decoder) { | |
| MutexLocker lock(m_mutex); | |
| DecoderCacheMap::iterator iter = m_decoderCacheMap.find( | |
| DecoderCacheEntry::makeCacheKey(generator, decoder)); | |
| SECURITY_DCHECK(iter != m_decoderCacheMap.end()); | |
| CacheEntry* cacheEntry = iter->value.get(); | |
| cacheEntry->decrementUseCount(); | |
| // Put the entry to the end of list. | |
| m_orderedCacheList.remove(cacheEntry); | |
| m_orderedCacheList.append(cacheEntry); | |
| } | |
| void ImageDecodingStore::insertDecoder(const ImageFrameGenerator* generator, | |
| std::unique_ptr<ImageDecoder> decoder) { | |
| // Prune old cache entries to give space for the new one. | |
| prune(); | |
| std::unique_ptr<DecoderCacheEntry> newCacheEntry = | |
| DecoderCacheEntry::create(generator, std::move(decoder)); | |
| MutexLocker lock(m_mutex); | |
| ASSERT(!m_decoderCacheMap.contains(newCacheEntry->cacheKey())); | |
| insertCacheInternal(std::move(newCacheEntry), &m_decoderCacheMap, | |
| &m_decoderCacheKeyMap); | |
| } | |
| void ImageDecodingStore::removeDecoder(const ImageFrameGenerator* generator, | |
| const ImageDecoder* decoder) { | |
| Vector<std::unique_ptr<CacheEntry>> cacheEntriesToDelete; | |
| { | |
| MutexLocker lock(m_mutex); | |
| DecoderCacheMap::iterator iter = m_decoderCacheMap.find( | |
| DecoderCacheEntry::makeCacheKey(generator, decoder)); | |
| SECURITY_DCHECK(iter != m_decoderCacheMap.end()); | |
| CacheEntry* cacheEntry = iter->value.get(); | |
| ASSERT(cacheEntry->useCount()); | |
| cacheEntry->decrementUseCount(); | |
| // Delete only one decoder cache entry. Ownership of the cache entry | |
| // is transfered to cacheEntriesToDelete such that object can be deleted | |
| // outside of the lock. | |
| removeFromCacheInternal(cacheEntry, &cacheEntriesToDelete); | |
| // Remove from LRU list. | |
| removeFromCacheListInternal(cacheEntriesToDelete); | |
| } | |
| } | |
| void ImageDecodingStore::removeCacheIndexedByGenerator( | |
| const ImageFrameGenerator* generator) { | |
| Vector<std::unique_ptr<CacheEntry>> cacheEntriesToDelete; | |
| { | |
| MutexLocker lock(m_mutex); | |
| // Remove image cache objects and decoder cache objects associated | |
| // with a ImageFrameGenerator. | |
| removeCacheIndexedByGeneratorInternal(&m_decoderCacheMap, | |
| &m_decoderCacheKeyMap, generator, | |
| &cacheEntriesToDelete); | |
| // Remove from LRU list as well. | |
| removeFromCacheListInternal(cacheEntriesToDelete); | |
| } | |
| } | |
| void ImageDecodingStore::clear() { | |
| size_t cacheLimitInBytes; | |
| { | |
| MutexLocker lock(m_mutex); | |
| cacheLimitInBytes = m_heapLimitInBytes; | |
| m_heapLimitInBytes = 0; | |
| } | |
| prune(); | |
| { | |
| MutexLocker lock(m_mutex); | |
| m_heapLimitInBytes = cacheLimitInBytes; | |
| } | |
| } | |
| void ImageDecodingStore::setCacheLimitInBytes(size_t cacheLimit) { | |
| { | |
| MutexLocker lock(m_mutex); | |
| m_heapLimitInBytes = cacheLimit; | |
| } | |
| prune(); | |
| } | |
| size_t ImageDecodingStore::memoryUsageInBytes() { | |
| MutexLocker lock(m_mutex); | |
| return m_heapMemoryUsageInBytes; | |
| } | |
| int ImageDecodingStore::cacheEntries() { | |
| MutexLocker lock(m_mutex); | |
| return m_decoderCacheMap.size(); | |
| } | |
| void ImageDecodingStore::prune() { | |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"), | |
| "ImageDecodingStore::prune"); | |
| Vector<std::unique_ptr<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) { | |
| const bool isPruneNeeded = | |
| m_heapMemoryUsageInBytes > m_heapLimitInBytes || !m_heapLimitInBytes; | |
| if (!isPruneNeeded) | |
| break; | |
| // Cache is not used; Remove it. | |
| if (!cacheEntry->useCount()) | |
| removeFromCacheInternal(cacheEntry, &cacheEntriesToDelete); | |
| cacheEntry = cacheEntry->next(); | |
| } | |
| // Remove from cache list as well. | |
| removeFromCacheListInternal(cacheEntriesToDelete); | |
| } | |
| } | |
| template <class T, class U, class V> | |
| void ImageDecodingStore::insertCacheInternal(std::unique_ptr<T> cacheEntry, | |
| U* cacheMap, | |
| V* identifierMap) { | |
| const size_t cacheEntryBytes = cacheEntry->memoryUsageInBytes(); | |
| m_heapMemoryUsageInBytes += cacheEntryBytes; | |
| // m_orderedCacheList is used to support LRU operations to reorder cache | |
| // entries quickly. | |
| m_orderedCacheList.append(cacheEntry.get()); | |
| typename U::KeyType key = cacheEntry->cacheKey(); | |
| typename V::AddResult result = | |
| identifierMap->add(cacheEntry->generator(), typename V::MappedType()); | |
| result.storedValue->value.add(key); | |
| cacheMap->add(key, std::move(cacheEntry)); | |
| TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"), | |
| "ImageDecodingStoreHeapMemoryUsageBytes", | |
| m_heapMemoryUsageInBytes); | |
| TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"), | |
| "ImageDecodingStoreNumOfDecoders", m_decoderCacheMap.size()); | |
| } | |
| template <class T, class U, class V> | |
| void ImageDecodingStore::removeFromCacheInternal( | |
| const T* cacheEntry, | |
| U* cacheMap, | |
| V* identifierMap, | |
| Vector<std::unique_ptr<CacheEntry>>* deletionList) { | |
| const size_t cacheEntryBytes = cacheEntry->memoryUsageInBytes(); | |
| ASSERT(m_heapMemoryUsageInBytes >= cacheEntryBytes); | |
| m_heapMemoryUsageInBytes -= cacheEntryBytes; | |
| // Remove entry from identifier map. | |
| typename V::iterator iter = identifierMap->find(cacheEntry->generator()); | |
| ASSERT(iter != identifierMap->end()); | |
| iter->value.remove(cacheEntry->cacheKey()); | |
| if (!iter->value.size()) | |
| identifierMap->remove(iter); | |
| // Remove entry from cache map. | |
| deletionList->push_back(cacheMap->take(cacheEntry->cacheKey())); | |
| TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"), | |
| "ImageDecodingStoreHeapMemoryUsageBytes", | |
| m_heapMemoryUsageInBytes); | |
| TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"), | |
| "ImageDecodingStoreNumOfDecoders", m_decoderCacheMap.size()); | |
| } | |
| void ImageDecodingStore::removeFromCacheInternal( | |
| const CacheEntry* cacheEntry, | |
| Vector<std::unique_ptr<CacheEntry>>* deletionList) { | |
| if (cacheEntry->type() == CacheEntry::TypeDecoder) { | |
| removeFromCacheInternal(static_cast<const DecoderCacheEntry*>(cacheEntry), | |
| &m_decoderCacheMap, &m_decoderCacheKeyMap, | |
| deletionList); | |
| } else { | |
| ASSERT(false); | |
| } | |
| } | |
| template <class U, class V> | |
| void ImageDecodingStore::removeCacheIndexedByGeneratorInternal( | |
| U* cacheMap, | |
| V* identifierMap, | |
| const ImageFrameGenerator* generator, | |
| Vector<std::unique_ptr<CacheEntry>>* deletionList) { | |
| typename V::iterator iter = identifierMap->find(generator); | |
| if (iter == identifierMap->end()) | |
| return; | |
| // Get all cache identifiers associated with generator. | |
| Vector<typename U::KeyType> cacheIdentifierList; | |
| copyToVector(iter->value, cacheIdentifierList); | |
| // For each cache identifier find the corresponding CacheEntry and remove it. | |
| for (size_t i = 0; i < cacheIdentifierList.size(); ++i) { | |
| ASSERT(cacheMap->contains(cacheIdentifierList[i])); | |
| const auto& cacheEntry = cacheMap->get(cacheIdentifierList[i]); | |
| ASSERT(!cacheEntry->useCount()); | |
| removeFromCacheInternal(cacheEntry, cacheMap, identifierMap, deletionList); | |
| } | |
| } | |
| void ImageDecodingStore::removeFromCacheListInternal( | |
| const Vector<std::unique_ptr<CacheEntry>>& deletionList) { | |
| for (size_t i = 0; i < deletionList.size(); ++i) | |
| m_orderedCacheList.remove(deletionList[i].get()); | |
| } | |
| } // namespace blink |