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