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