/* * 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 "sky/engine/platform/graphics/ImageDecodingStore.h" #include "sky/engine/platform/TraceEvent.h" #include "sky/engine/wtf/Threading.h" namespace blink { namespace { static const size_t defaultMaxTotalSizeOfHeapEntries = 32 * 1024 * 1024; } // namespace ImageDecodingStore::ImageDecodingStore() : m_heapLimitInBytes(defaultMaxTotalSizeOfHeapEntries) , m_heapMemoryUsageInBytes(0) { } ImageDecodingStore::~ImageDecodingStore() { #if ENABLE(ASSERT) setCacheLimitInBytes(0); ASSERT(!m_decoderCacheMap.size()); ASSERT(!m_orderedCacheList.size()); ASSERT(!m_decoderCacheKeyMap.size()); #endif } ImageDecodingStore* ImageDecodingStore::instance() { AtomicallyInitializedStatic(ImageDecodingStore*, store = ImageDecodingStore::create().leakPtr()); 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)); ASSERT_WITH_SECURITY_IMPLICATION(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, PassOwnPtr decoder) { // Prune old cache entries to give space for the new one. prune(); OwnPtr newCacheEntry = DecoderCacheEntry::create(generator, decoder); MutexLocker lock(m_mutex); ASSERT(!m_decoderCacheMap.contains(newCacheEntry->cacheKey())); insertCacheInternal(newCacheEntry.release(), &m_decoderCacheMap, &m_decoderCacheKeyMap); } void ImageDecodingStore::removeDecoder(const ImageFrameGenerator* generator, const ImageDecoder* decoder) { Vector > cacheEntriesToDelete; { MutexLocker lock(m_mutex); DecoderCacheMap::iterator iter = m_decoderCacheMap.find(DecoderCacheEntry::makeCacheKey(generator, decoder)); ASSERT_WITH_SECURITY_IMPLICATION(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 > 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(); } int ImageDecodingStore::decoderCacheEntries() { MutexLocker lock(m_mutex); return m_decoderCacheMap.size(); } void ImageDecodingStore::prune() { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"), "ImageDecodingStore::prune"); Vector > 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 void ImageDecodingStore::insertCacheInternal(PassOwnPtr 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, 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 void ImageDecodingStore::removeFromCacheInternal(const T* cacheEntry, U* cacheMap, V* identifierMap, Vector >* 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->append(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 >* deletionList) { if (cacheEntry->type() == CacheEntry::TypeDecoder) { removeFromCacheInternal(static_cast(cacheEntry), &m_decoderCacheMap, &m_decoderCacheKeyMap, deletionList); } else { ASSERT(false); } } template void ImageDecodingStore::removeCacheIndexedByGeneratorInternal(U* cacheMap, V* identifierMap, const ImageFrameGenerator* generator, Vector >* deletionList) { typename V::iterator iter = identifierMap->find(generator); if (iter == identifierMap->end()) return; // Get all cache identifiers associated with generator. Vector 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 typename U::MappedType::PtrType cacheEntry = cacheMap->get(cacheIdentifierList[i]); ASSERT(!cacheEntry->useCount()); removeFromCacheInternal(cacheEntry, cacheMap, identifierMap, deletionList); } } void ImageDecodingStore::removeFromCacheListInternal(const Vector >& deletionList) { for (size_t i = 0; i < deletionList.size(); ++i) m_orderedCacheList.remove(deletionList[i].get()); } } // namespace blink