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 "third_party/blink/renderer/platform/graphics/image_decoding_store.h"
27 
28 #include <memory>
29 #include "base/bind.h"
30 #include "third_party/blink/renderer/platform/graphics/image_frame_generator.h"
31 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
32 #include "third_party/blink/renderer/platform/wtf/threading.h"
33 
34 namespace blink {
35 
36 namespace {
37 
38 static const size_t kDefaultMaxTotalSizeOfHeapEntries = 32 * 1024 * 1024;
39 
40 }  // namespace
41 
ImageDecodingStore()42 ImageDecodingStore::ImageDecodingStore()
43     : heap_limit_in_bytes_(kDefaultMaxTotalSizeOfHeapEntries),
44       heap_memory_usage_in_bytes_(0),
45       memory_pressure_listener_(
46           base::BindRepeating(&ImageDecodingStore::OnMemoryPressure,
47                               base::Unretained(this))) {}
48 
~ImageDecodingStore()49 ImageDecodingStore::~ImageDecodingStore() {
50 #if DCHECK_IS_ON()
51   SetCacheLimitInBytes(0);
52   DCHECK(!decoder_cache_map_.size());
53   DCHECK(!ordered_cache_list_.size());
54   DCHECK(!decoder_cache_key_map_.size());
55 #endif
56 }
57 
Instance()58 ImageDecodingStore& ImageDecodingStore::Instance() {
59   DEFINE_THREAD_SAFE_STATIC_LOCAL(ImageDecodingStore, store, ());
60   return store;
61 }
62 
LockDecoder(const ImageFrameGenerator * generator,const SkISize & scaled_size,ImageDecoder::AlphaOption alpha_option,cc::PaintImage::GeneratorClientId client_id,ImageDecoder ** decoder)63 bool ImageDecodingStore::LockDecoder(
64     const ImageFrameGenerator* generator,
65     const SkISize& scaled_size,
66     ImageDecoder::AlphaOption alpha_option,
67     cc::PaintImage::GeneratorClientId client_id,
68     ImageDecoder** decoder) {
69   DCHECK(decoder);
70 
71   MutexLocker lock(mutex_);
72   DecoderCacheMap::iterator iter =
73       decoder_cache_map_.find(DecoderCacheEntry::MakeCacheKey(
74           generator, scaled_size, alpha_option, client_id));
75   if (iter == decoder_cache_map_.end())
76     return false;
77 
78   DecoderCacheEntry* cache_entry = iter->value.get();
79 
80   // There can only be one user of a decoder at a time.
81   DCHECK(!cache_entry->UseCount());
82   cache_entry->IncrementUseCount();
83   *decoder = cache_entry->CachedDecoder();
84   return true;
85 }
86 
UnlockDecoder(const ImageFrameGenerator * generator,cc::PaintImage::GeneratorClientId client_id,const ImageDecoder * decoder)87 void ImageDecodingStore::UnlockDecoder(
88     const ImageFrameGenerator* generator,
89     cc::PaintImage::GeneratorClientId client_id,
90     const ImageDecoder* decoder) {
91   MutexLocker lock(mutex_);
92   DecoderCacheMap::iterator iter = decoder_cache_map_.find(
93       DecoderCacheEntry::MakeCacheKey(generator, decoder, client_id));
94   SECURITY_DCHECK(iter != decoder_cache_map_.end());
95 
96   CacheEntry* cache_entry = iter->value.get();
97   cache_entry->DecrementUseCount();
98 
99   // Put the entry to the end of list.
100   ordered_cache_list_.Remove(cache_entry);
101   ordered_cache_list_.Append(cache_entry);
102 }
103 
InsertDecoder(const ImageFrameGenerator * generator,cc::PaintImage::GeneratorClientId client_id,std::unique_ptr<ImageDecoder> decoder)104 void ImageDecodingStore::InsertDecoder(
105     const ImageFrameGenerator* generator,
106     cc::PaintImage::GeneratorClientId client_id,
107     std::unique_ptr<ImageDecoder> decoder) {
108   // Prune old cache entries to give space for the new one.
109   Prune();
110 
111   auto new_cache_entry = std::make_unique<DecoderCacheEntry>(
112       generator, 0, std::move(decoder), client_id);
113 
114   MutexLocker lock(mutex_);
115   DCHECK(!decoder_cache_map_.Contains(new_cache_entry->CacheKey()));
116   InsertCacheInternal(std::move(new_cache_entry), &decoder_cache_map_,
117                       &decoder_cache_key_map_);
118 }
119 
RemoveDecoder(const ImageFrameGenerator * generator,cc::PaintImage::GeneratorClientId client_id,const ImageDecoder * decoder)120 void ImageDecodingStore::RemoveDecoder(
121     const ImageFrameGenerator* generator,
122     cc::PaintImage::GeneratorClientId client_id,
123     const ImageDecoder* decoder) {
124   Vector<std::unique_ptr<CacheEntry>> cache_entries_to_delete;
125   {
126     MutexLocker lock(mutex_);
127     DecoderCacheMap::iterator iter = decoder_cache_map_.find(
128         DecoderCacheEntry::MakeCacheKey(generator, decoder, client_id));
129     SECURITY_DCHECK(iter != decoder_cache_map_.end());
130 
131     CacheEntry* cache_entry = iter->value.get();
132     DCHECK(cache_entry->UseCount());
133     cache_entry->DecrementUseCount();
134 
135     // Delete only one decoder cache entry. Ownership of the cache entry
136     // is transfered to cacheEntriesToDelete such that object can be deleted
137     // outside of the lock.
138     RemoveFromCacheInternal(cache_entry, &cache_entries_to_delete);
139 
140     // Remove from LRU list.
141     RemoveFromCacheListInternal(cache_entries_to_delete);
142   }
143 }
144 
RemoveCacheIndexedByGenerator(const ImageFrameGenerator * generator)145 void ImageDecodingStore::RemoveCacheIndexedByGenerator(
146     const ImageFrameGenerator* generator) {
147   Vector<std::unique_ptr<CacheEntry>> cache_entries_to_delete;
148   {
149     MutexLocker lock(mutex_);
150 
151     // Remove image cache objects and decoder cache objects associated
152     // with a ImageFrameGenerator.
153     RemoveCacheIndexedByGeneratorInternal(&decoder_cache_map_,
154                                           &decoder_cache_key_map_, generator,
155                                           &cache_entries_to_delete);
156 
157     // Remove from LRU list as well.
158     RemoveFromCacheListInternal(cache_entries_to_delete);
159   }
160 }
161 
Clear()162 void ImageDecodingStore::Clear() {
163   size_t cache_limit_in_bytes;
164   {
165     MutexLocker lock(mutex_);
166     cache_limit_in_bytes = heap_limit_in_bytes_;
167     heap_limit_in_bytes_ = 0;
168   }
169 
170   Prune();
171 
172   {
173     MutexLocker lock(mutex_);
174     heap_limit_in_bytes_ = cache_limit_in_bytes;
175   }
176 }
177 
SetCacheLimitInBytes(size_t cache_limit)178 void ImageDecodingStore::SetCacheLimitInBytes(size_t cache_limit) {
179   {
180     MutexLocker lock(mutex_);
181     heap_limit_in_bytes_ = cache_limit;
182   }
183   Prune();
184 }
185 
MemoryUsageInBytes()186 size_t ImageDecodingStore::MemoryUsageInBytes() {
187   MutexLocker lock(mutex_);
188   return heap_memory_usage_in_bytes_;
189 }
190 
CacheEntries()191 int ImageDecodingStore::CacheEntries() {
192   MutexLocker lock(mutex_);
193   return decoder_cache_map_.size();
194 }
195 
Prune()196 void ImageDecodingStore::Prune() {
197   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"),
198                "ImageDecodingStore::prune");
199 
200   Vector<std::unique_ptr<CacheEntry>> cache_entries_to_delete;
201   {
202     MutexLocker lock(mutex_);
203 
204     // Head of the list is the least recently used entry.
205     const CacheEntry* cache_entry = ordered_cache_list_.Head();
206 
207     // Walk the list of cache entries starting from the least recently used
208     // and then keep them for deletion later.
209     while (cache_entry) {
210       const bool is_prune_needed =
211           heap_memory_usage_in_bytes_ > heap_limit_in_bytes_ ||
212           !heap_limit_in_bytes_;
213       if (!is_prune_needed)
214         break;
215 
216       // Cache is not used; Remove it.
217       if (!cache_entry->UseCount())
218         RemoveFromCacheInternal(cache_entry, &cache_entries_to_delete);
219       cache_entry = cache_entry->Next();
220     }
221 
222     // Remove from cache list as well.
223     RemoveFromCacheListInternal(cache_entries_to_delete);
224   }
225 }
226 
OnMemoryPressure(base::MemoryPressureListener::MemoryPressureLevel level)227 void ImageDecodingStore::OnMemoryPressure(
228     base::MemoryPressureListener::MemoryPressureLevel level) {
229   switch (level) {
230     case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
231     case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
232       break;
233     case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
234       Clear();
235       break;
236   }
237 }
238 
239 template <class T, class U, class V>
InsertCacheInternal(std::unique_ptr<T> cache_entry,U * cache_map,V * identifier_map)240 void ImageDecodingStore::InsertCacheInternal(std::unique_ptr<T> cache_entry,
241                                              U* cache_map,
242                                              V* identifier_map) {
243   mutex_.AssertAcquired();
244   const size_t cache_entry_bytes = cache_entry->MemoryUsageInBytes();
245   heap_memory_usage_in_bytes_ += cache_entry_bytes;
246 
247   // m_orderedCacheList is used to support LRU operations to reorder cache
248   // entries quickly.
249   ordered_cache_list_.Append(cache_entry.get());
250 
251   typename U::KeyType key = cache_entry->CacheKey();
252   typename V::AddResult result = identifier_map->insert(
253       cache_entry->Generator(), typename V::MappedType());
254   result.stored_value->value.insert(key);
255   cache_map->insert(key, std::move(cache_entry));
256 
257   TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"),
258                  "ImageDecodingStoreHeapMemoryUsageBytes",
259                  heap_memory_usage_in_bytes_);
260   TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"),
261                  "ImageDecodingStoreNumOfDecoders", decoder_cache_map_.size());
262 }
263 
264 template <class T, class U, class V>
RemoveFromCacheInternal(const T * cache_entry,U * cache_map,V * identifier_map,Vector<std::unique_ptr<CacheEntry>> * deletion_list)265 void ImageDecodingStore::RemoveFromCacheInternal(
266     const T* cache_entry,
267     U* cache_map,
268     V* identifier_map,
269     Vector<std::unique_ptr<CacheEntry>>* deletion_list) {
270   mutex_.AssertAcquired();
271   DCHECK_EQ(cache_entry->UseCount(), 0);
272 
273   const size_t cache_entry_bytes = cache_entry->MemoryUsageInBytes();
274   DCHECK_GE(heap_memory_usage_in_bytes_, cache_entry_bytes);
275   heap_memory_usage_in_bytes_ -= cache_entry_bytes;
276 
277   // Remove entry from identifier map.
278   typename V::iterator iter = identifier_map->find(cache_entry->Generator());
279   DCHECK(iter != identifier_map->end());
280   iter->value.erase(cache_entry->CacheKey());
281   if (!iter->value.size())
282     identifier_map->erase(iter);
283 
284   // Remove entry from cache map.
285   deletion_list->push_back(cache_map->Take(cache_entry->CacheKey()));
286 
287   TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"),
288                  "ImageDecodingStoreHeapMemoryUsageBytes",
289                  heap_memory_usage_in_bytes_);
290   TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"),
291                  "ImageDecodingStoreNumOfDecoders", decoder_cache_map_.size());
292 }
293 
RemoveFromCacheInternal(const CacheEntry * cache_entry,Vector<std::unique_ptr<CacheEntry>> * deletion_list)294 void ImageDecodingStore::RemoveFromCacheInternal(
295     const CacheEntry* cache_entry,
296     Vector<std::unique_ptr<CacheEntry>>* deletion_list) {
297   if (cache_entry->GetType() == CacheEntry::kTypeDecoder) {
298     RemoveFromCacheInternal(static_cast<const DecoderCacheEntry*>(cache_entry),
299                             &decoder_cache_map_, &decoder_cache_key_map_,
300                             deletion_list);
301   } else {
302     DCHECK(false);
303   }
304 }
305 
306 template <class U, class V>
RemoveCacheIndexedByGeneratorInternal(U * cache_map,V * identifier_map,const ImageFrameGenerator * generator,Vector<std::unique_ptr<CacheEntry>> * deletion_list)307 void ImageDecodingStore::RemoveCacheIndexedByGeneratorInternal(
308     U* cache_map,
309     V* identifier_map,
310     const ImageFrameGenerator* generator,
311     Vector<std::unique_ptr<CacheEntry>>* deletion_list) {
312   mutex_.AssertAcquired();
313   typename V::iterator iter = identifier_map->find(generator);
314   if (iter == identifier_map->end())
315     return;
316 
317   // Get all cache identifiers associated with generator.
318   Vector<typename U::KeyType> cache_identifier_list;
319   CopyToVector(iter->value, cache_identifier_list);
320 
321   // For each cache identifier find the corresponding CacheEntry and remove it.
322   for (size_t i = 0; i < cache_identifier_list.size(); ++i) {
323     DCHECK(cache_map->Contains(cache_identifier_list[i]));
324     const auto& cache_entry = cache_map->at(cache_identifier_list[i]);
325     DCHECK(!cache_entry->UseCount());
326     RemoveFromCacheInternal(cache_entry, cache_map, identifier_map,
327                             deletion_list);
328   }
329 }
330 
RemoveFromCacheListInternal(const Vector<std::unique_ptr<CacheEntry>> & deletion_list)331 void ImageDecodingStore::RemoveFromCacheListInternal(
332     const Vector<std::unique_ptr<CacheEntry>>& deletion_list) {
333   mutex_.AssertAcquired();
334   for (size_t i = 0; i < deletion_list.size(); ++i)
335     ordered_cache_list_.Remove(deletion_list[i].get());
336 }
337 
338 }  // namespace blink
339