blob: 6f755da76256096c92639ef48ecaca3463792b60 [file] [log] [blame]
/*
* 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/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 ENABLE(ASSERT)
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->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<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