/src/skia/src/core/SkResourceCache.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2013 Google Inc. |
3 | | * |
4 | | * Use of this source code is governed by a BSD-style license that can be |
5 | | * found in the LICENSE file. |
6 | | */ |
7 | | |
8 | | #include "src/core/SkResourceCache.h" |
9 | | |
10 | | #include "include/core/SkGraphics.h" |
11 | | #include "include/core/SkString.h" |
12 | | #include "include/core/SkTraceMemoryDump.h" |
13 | | #include "include/core/SkTypes.h" |
14 | | #include "include/private/base/SkAlign.h" |
15 | | #include "include/private/base/SkDebug.h" |
16 | | #include "include/private/base/SkMalloc.h" |
17 | | #include "include/private/base/SkMath.h" |
18 | | #include "include/private/base/SkMutex.h" |
19 | | #include "include/private/base/SkTArray.h" |
20 | | #include "include/private/base/SkTo.h" |
21 | | #include "src/core/SkCachedData.h" |
22 | | #include "src/core/SkChecksum.h" |
23 | | #include "src/core/SkImageFilter_Base.h" |
24 | | #include "src/core/SkMessageBus.h" |
25 | | #include "src/core/SkTHash.h" |
26 | | |
27 | | #if defined(SK_USE_DISCARDABLE_SCALEDIMAGECACHE) |
28 | | #include "include/private/chromium/SkDiscardableMemory.h" |
29 | | #endif |
30 | | |
31 | | #include <algorithm> |
32 | | |
33 | | using namespace skia_private; |
34 | | |
35 | | DECLARE_SKMESSAGEBUS_MESSAGE(SkResourceCache::PurgeSharedIDMessage, uint32_t, true) |
36 | | |
37 | | static inline bool SkShouldPostMessageToBus( |
38 | 10.6k | const SkResourceCache::PurgeSharedIDMessage&, uint32_t) { |
39 | | // SkResourceCache is typically used as a singleton and we don't label Inboxes so all messages |
40 | | // go to all inboxes. |
41 | 10.6k | return true; |
42 | 10.6k | } |
43 | | |
44 | | // This can be defined by the caller's build system |
45 | | //#define SK_USE_DISCARDABLE_SCALEDIMAGECACHE |
46 | | |
47 | | #ifndef SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT |
48 | 0 | # define SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT 1024 |
49 | | #endif |
50 | | |
51 | | #ifndef SK_DEFAULT_IMAGE_CACHE_LIMIT |
52 | 7 | #define SK_DEFAULT_IMAGE_CACHE_LIMIT (32 * 1024 * 1024) |
53 | | #endif |
54 | | |
55 | 49.7k | void SkResourceCache::Key::init(void* nameSpace, uint64_t sharedID, size_t dataSize) { |
56 | 49.7k | SkASSERT(SkAlign4(dataSize) == dataSize); |
57 | | |
58 | | // fCount32 and fHash are not hashed |
59 | 49.7k | static const int kUnhashedLocal32s = 2; // fCache32 + fHash |
60 | 49.7k | static const int kSharedIDLocal32s = 2; // fSharedID_lo + fSharedID_hi |
61 | 49.7k | static const int kHashedLocal32s = kSharedIDLocal32s + (sizeof(fNamespace) >> 2); |
62 | 49.7k | static const int kLocal32s = kUnhashedLocal32s + kHashedLocal32s; |
63 | | |
64 | 49.7k | static_assert(sizeof(Key) == (kLocal32s << 2), "unaccounted_key_locals"); |
65 | 49.7k | static_assert(sizeof(Key) == offsetof(Key, fNamespace) + sizeof(fNamespace), |
66 | 49.7k | "namespace_field_must_be_last"); |
67 | | |
68 | 49.7k | fCount32 = SkToS32(kLocal32s + (dataSize >> 2)); |
69 | 49.7k | fSharedID_lo = (uint32_t)(sharedID & 0xFFFFFFFF); |
70 | 49.7k | fSharedID_hi = (uint32_t)(sharedID >> 32); |
71 | 49.7k | fNamespace = nameSpace; |
72 | | // skip unhashed fields when computing the hash |
73 | 49.7k | fHash = SkChecksum::Hash32(this->as32() + kUnhashedLocal32s, |
74 | 49.7k | (fCount32 - kUnhashedLocal32s) << 2); |
75 | 49.7k | } |
76 | | |
77 | | namespace { |
78 | | struct HashTraits { |
79 | 71.0k | static uint32_t Hash(const SkResourceCache::Key& key) { return key.hash(); } |
80 | 29.4k | static const SkResourceCache::Key& GetKey(const SkResourceCache::Rec* rec) { |
81 | 29.4k | return rec->getKey(); |
82 | 29.4k | } |
83 | | }; |
84 | | } // namespace |
85 | | |
86 | | class SkResourceCache::Hash : |
87 | | public THashTable<SkResourceCache::Rec*, SkResourceCache::Key, HashTraits> {}; |
88 | | |
89 | | |
90 | | /////////////////////////////////////////////////////////////////////////////// |
91 | | |
92 | 7 | void SkResourceCache::init() { |
93 | 7 | fHead = nullptr; |
94 | 7 | fTail = nullptr; |
95 | 7 | fHash = new Hash; |
96 | 7 | fTotalBytesUsed = 0; |
97 | 7 | fCount = 0; |
98 | 7 | fSingleAllocationByteLimit = 0; |
99 | | |
100 | | // One of these should be explicit set by the caller after we return. |
101 | 7 | fTotalByteLimit = 0; |
102 | 7 | fDiscardableFactory = nullptr; |
103 | 7 | } |
104 | | |
105 | | SkResourceCache::SkResourceCache(DiscardableFactory factory) |
106 | 0 | : fPurgeSharedIDInbox(SK_InvalidUniqueID) { |
107 | 0 | this->init(); |
108 | 0 | fDiscardableFactory = factory; |
109 | 0 | } |
110 | | |
111 | | SkResourceCache::SkResourceCache(size_t byteLimit) |
112 | 7 | : fPurgeSharedIDInbox(SK_InvalidUniqueID) { |
113 | 7 | this->init(); |
114 | 7 | fTotalByteLimit = byteLimit; |
115 | 7 | } |
116 | | |
117 | 0 | SkResourceCache::~SkResourceCache() { |
118 | 0 | Rec* rec = fHead; |
119 | 0 | while (rec) { |
120 | 0 | Rec* next = rec->fNext; |
121 | 0 | delete rec; |
122 | 0 | rec = next; |
123 | 0 | } |
124 | 0 | delete fHash; |
125 | 0 | } |
126 | | |
127 | | //////////////////////////////////////////////////////////////////////////////// |
128 | | |
129 | 36.8k | bool SkResourceCache::find(const Key& key, FindVisitor visitor, void* context) { |
130 | 36.8k | this->checkMessages(); |
131 | | |
132 | 36.8k | if (auto found = fHash->find(key)) { |
133 | 6.58k | Rec* rec = *found; |
134 | 6.58k | if (visitor(*rec, context)) { |
135 | 6.58k | this->moveToHead(rec); // for our LRU |
136 | 6.58k | return true; |
137 | 6.58k | } else { |
138 | 0 | this->remove(rec); // stale |
139 | 0 | return false; |
140 | 0 | } |
141 | 6.58k | } |
142 | 30.3k | return false; |
143 | 36.8k | } |
144 | | |
145 | 0 | static void make_size_str(size_t size, SkString* str) { |
146 | 0 | const char suffix[] = { 'b', 'k', 'm', 'g', 't', 0 }; |
147 | 0 | int i = 0; |
148 | 0 | while (suffix[i] && (size > 1024)) { |
149 | 0 | i += 1; |
150 | 0 | size >>= 10; |
151 | 0 | } |
152 | 0 | str->printf("%zu%c", size, suffix[i]); |
153 | 0 | } |
154 | | |
155 | | static bool gDumpCacheTransactions; |
156 | | |
157 | 11.2k | void SkResourceCache::add(Rec* rec, void* payload) { |
158 | 11.2k | this->checkMessages(); |
159 | | |
160 | 11.2k | SkASSERT(rec); |
161 | | // See if we already have this key (racy inserts, etc.) |
162 | 11.2k | if (Rec** preexisting = fHash->find(rec->getKey())) { |
163 | 0 | Rec* prev = *preexisting; |
164 | 0 | if (prev->canBePurged()) { |
165 | | // if it can be purged, the install may fail, so we have to remove it |
166 | 0 | this->remove(prev); |
167 | 0 | } else { |
168 | | // if it cannot be purged, we reuse it and delete the new one |
169 | 0 | prev->postAddInstall(payload); |
170 | 0 | delete rec; |
171 | 0 | return; |
172 | 0 | } |
173 | 0 | } |
174 | | |
175 | 11.2k | this->addToHead(rec); |
176 | 11.2k | fHash->set(rec); |
177 | 11.2k | rec->postAddInstall(payload); |
178 | | |
179 | 11.2k | if (gDumpCacheTransactions) { |
180 | 0 | SkString bytesStr, totalStr; |
181 | 0 | make_size_str(rec->bytesUsed(), &bytesStr); |
182 | 0 | make_size_str(fTotalBytesUsed, &totalStr); |
183 | 0 | SkDebugf("RC: add %5s %12p key %08x -- total %5s, count %d\n", |
184 | 0 | bytesStr.c_str(), rec, rec->getHash(), totalStr.c_str(), fCount); |
185 | 0 | } |
186 | | |
187 | | // since the new rec may push us over-budget, we perform a purge check now |
188 | 11.2k | this->purgeAsNeeded(); |
189 | 11.2k | } SkResourceCache::add(SkResourceCache::Rec*, void*) Line | Count | Source | 157 | 11.2k | void SkResourceCache::add(Rec* rec, void* payload) { | 158 | 11.2k | this->checkMessages(); | 159 | | | 160 | 11.2k | SkASSERT(rec); | 161 | | // See if we already have this key (racy inserts, etc.) | 162 | 11.2k | if (Rec** preexisting = fHash->find(rec->getKey())) { | 163 | 0 | Rec* prev = *preexisting; | 164 | 0 | if (prev->canBePurged()) { | 165 | | // if it can be purged, the install may fail, so we have to remove it | 166 | 0 | this->remove(prev); | 167 | 0 | } else { | 168 | | // if it cannot be purged, we reuse it and delete the new one | 169 | 0 | prev->postAddInstall(payload); | 170 | 0 | delete rec; | 171 | 0 | return; | 172 | 0 | } | 173 | 0 | } | 174 | | | 175 | 11.2k | this->addToHead(rec); | 176 | 11.2k | fHash->set(rec); | 177 | 11.2k | rec->postAddInstall(payload); | 178 | | | 179 | 11.2k | if (gDumpCacheTransactions) { | 180 | 0 | SkString bytesStr, totalStr; | 181 | 0 | make_size_str(rec->bytesUsed(), &bytesStr); | 182 | 0 | make_size_str(fTotalBytesUsed, &totalStr); | 183 | 0 | SkDebugf("RC: add %5s %12p key %08x -- total %5s, count %d\n", | 184 | 0 | bytesStr.c_str(), rec, rec->getHash(), totalStr.c_str(), fCount); | 185 | 0 | } | 186 | | | 187 | | // since the new rec may push us over-budget, we perform a purge check now | 188 | 11.2k | this->purgeAsNeeded(); | 189 | 11.2k | } |
Unexecuted instantiation: SkResourceCache::add(SkResourceCache::Rec*, void*) |
190 | | |
191 | 10.9k | void SkResourceCache::remove(Rec* rec) { |
192 | 10.9k | SkASSERT(rec->canBePurged()); |
193 | 10.9k | size_t used = rec->bytesUsed(); |
194 | 10.9k | SkASSERT(used <= fTotalBytesUsed); |
195 | | |
196 | 10.9k | this->release(rec); |
197 | 10.9k | fHash->remove(rec->getKey()); |
198 | | |
199 | 10.9k | fTotalBytesUsed -= used; |
200 | 10.9k | fCount -= 1; |
201 | | |
202 | | //SkDebugf("-RC count [%3d] bytes %d\n", fCount, fTotalBytesUsed); |
203 | | |
204 | 10.9k | if (gDumpCacheTransactions) { |
205 | 0 | SkString bytesStr, totalStr; |
206 | 0 | make_size_str(used, &bytesStr); |
207 | 0 | make_size_str(fTotalBytesUsed, &totalStr); |
208 | 0 | SkDebugf("RC: remove %5s %12p key %08x -- total %5s, count %d\n", |
209 | 0 | bytesStr.c_str(), rec, rec->getHash(), totalStr.c_str(), fCount); |
210 | 0 | } |
211 | | |
212 | 10.9k | delete rec; |
213 | 10.9k | } |
214 | | |
215 | 11.2k | void SkResourceCache::purgeAsNeeded(bool forcePurge) { |
216 | 11.2k | size_t byteLimit; |
217 | 11.2k | int countLimit; |
218 | | |
219 | 11.2k | if (fDiscardableFactory) { |
220 | 0 | countLimit = SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT; |
221 | 0 | byteLimit = UINT32_MAX; // no limit based on bytes |
222 | 11.2k | } else { |
223 | 11.2k | countLimit = SK_MaxS32; // no limit based on count |
224 | 11.2k | byteLimit = fTotalByteLimit; |
225 | 11.2k | } |
226 | | |
227 | 11.2k | Rec* rec = fTail; |
228 | 11.5k | while (rec) { |
229 | 11.5k | if (!forcePurge && fTotalBytesUsed < byteLimit && fCount < countLimit) { |
230 | 11.2k | break; |
231 | 11.2k | } |
232 | | |
233 | 318 | Rec* prev = rec->fPrev; |
234 | 318 | if (rec->canBePurged()) { |
235 | 318 | this->remove(rec); |
236 | 318 | } |
237 | 318 | rec = prev; |
238 | 318 | } |
239 | 11.2k | } |
240 | | |
241 | | //#define SK_TRACK_PURGE_SHAREDID_HITRATE |
242 | | |
243 | | #ifdef SK_TRACK_PURGE_SHAREDID_HITRATE |
244 | | static int gPurgeCallCounter; |
245 | | static int gPurgeHitCounter; |
246 | | #endif |
247 | | |
248 | 10.6k | void SkResourceCache::purgeSharedID(uint64_t sharedID) { |
249 | 10.6k | if (0 == sharedID) { |
250 | 0 | return; |
251 | 0 | } |
252 | | |
253 | | #ifdef SK_TRACK_PURGE_SHAREDID_HITRATE |
254 | | gPurgeCallCounter += 1; |
255 | | bool found = false; |
256 | | #endif |
257 | | // go backwards, just like purgeAsNeeded, just to make the code similar. |
258 | | // could iterate either direction and still be correct. |
259 | 10.6k | Rec* rec = fTail; |
260 | 307k | while (rec) { |
261 | 296k | Rec* prev = rec->fPrev; |
262 | 296k | if (rec->getKey().getSharedID() == sharedID) { |
263 | | // even though the "src" is now dead, caches could still be in-flight, so |
264 | | // we have to check if it can be removed. |
265 | 10.6k | if (rec->canBePurged()) { |
266 | 10.6k | this->remove(rec); |
267 | 10.6k | } |
268 | | #ifdef SK_TRACK_PURGE_SHAREDID_HITRATE |
269 | | found = true; |
270 | | #endif |
271 | 10.6k | } |
272 | 296k | rec = prev; |
273 | 296k | } |
274 | | |
275 | | #ifdef SK_TRACK_PURGE_SHAREDID_HITRATE |
276 | | if (found) { |
277 | | gPurgeHitCounter += 1; |
278 | | } |
279 | | |
280 | | SkDebugf("PurgeShared calls=%d hits=%d rate=%g\n", gPurgeCallCounter, gPurgeHitCounter, |
281 | | gPurgeHitCounter * 100.0 / gPurgeCallCounter); |
282 | | #endif |
283 | 10.6k | } |
284 | | |
285 | 0 | void SkResourceCache::visitAll(Visitor visitor, void* context) { |
286 | | // go backwards, just like purgeAsNeeded, just to make the code similar. |
287 | | // could iterate either direction and still be correct. |
288 | 0 | Rec* rec = fTail; |
289 | 0 | while (rec) { |
290 | 0 | visitor(*rec, context); |
291 | 0 | rec = rec->fPrev; |
292 | 0 | } |
293 | 0 | } |
294 | | |
295 | | /////////////////////////////////////////////////////////////////////////////////////////////////// |
296 | | |
297 | 0 | size_t SkResourceCache::setTotalByteLimit(size_t newLimit) { |
298 | 0 | size_t prevLimit = fTotalByteLimit; |
299 | 0 | fTotalByteLimit = newLimit; |
300 | 0 | if (newLimit < prevLimit) { |
301 | 0 | this->purgeAsNeeded(); |
302 | 0 | } |
303 | 0 | return prevLimit; |
304 | 0 | } |
305 | | |
306 | 572 | SkCachedData* SkResourceCache::newCachedData(size_t bytes) { |
307 | 572 | this->checkMessages(); |
308 | | |
309 | 572 | if (fDiscardableFactory) { |
310 | 0 | SkDiscardableMemory* dm = fDiscardableFactory(bytes); |
311 | 0 | return dm ? new SkCachedData(bytes, dm) : nullptr; |
312 | 572 | } else { |
313 | 572 | return new SkCachedData(sk_malloc_throw(bytes), bytes); |
314 | 572 | } |
315 | 572 | } |
316 | | |
317 | | /////////////////////////////////////////////////////////////////////////////// |
318 | | |
319 | 11.3k | void SkResourceCache::release(Rec* rec) { |
320 | 11.3k | Rec* prev = rec->fPrev; |
321 | 11.3k | Rec* next = rec->fNext; |
322 | | |
323 | 11.3k | if (!prev) { |
324 | 10.4k | SkASSERT(fHead == rec); |
325 | 10.4k | fHead = next; |
326 | 10.4k | } else { |
327 | 941 | prev->fNext = next; |
328 | 941 | } |
329 | | |
330 | 11.3k | if (!next) { |
331 | 7.18k | fTail = prev; |
332 | 7.18k | } else { |
333 | 4.16k | next->fPrev = prev; |
334 | 4.16k | } |
335 | | |
336 | 11.3k | rec->fNext = rec->fPrev = nullptr; |
337 | 11.3k | } SkResourceCache::release(SkResourceCache::Rec*) Line | Count | Source | 319 | 11.3k | void SkResourceCache::release(Rec* rec) { | 320 | 11.3k | Rec* prev = rec->fPrev; | 321 | 11.3k | Rec* next = rec->fNext; | 322 | | | 323 | 11.3k | if (!prev) { | 324 | 10.4k | SkASSERT(fHead == rec); | 325 | 10.4k | fHead = next; | 326 | 10.4k | } else { | 327 | 941 | prev->fNext = next; | 328 | 941 | } | 329 | | | 330 | 11.3k | if (!next) { | 331 | 7.18k | fTail = prev; | 332 | 7.18k | } else { | 333 | 4.16k | next->fPrev = prev; | 334 | 4.16k | } | 335 | | | 336 | 11.3k | rec->fNext = rec->fPrev = nullptr; | 337 | 11.3k | } |
Unexecuted instantiation: SkResourceCache::release(SkResourceCache::Rec*) |
338 | | |
339 | 6.58k | void SkResourceCache::moveToHead(Rec* rec) { |
340 | 6.58k | if (fHead == rec) { |
341 | 6.15k | return; |
342 | 6.15k | } |
343 | | |
344 | 424 | SkASSERT(fHead); |
345 | 424 | SkASSERT(fTail); |
346 | | |
347 | 424 | this->validate(); |
348 | | |
349 | 424 | this->release(rec); |
350 | | |
351 | 424 | fHead->fPrev = rec; |
352 | 424 | rec->fNext = fHead; |
353 | 424 | fHead = rec; |
354 | | |
355 | 424 | this->validate(); |
356 | 424 | } |
357 | | |
358 | 11.2k | void SkResourceCache::addToHead(Rec* rec) { |
359 | 11.2k | this->validate(); |
360 | | |
361 | 11.2k | rec->fPrev = nullptr; |
362 | 11.2k | rec->fNext = fHead; |
363 | 11.2k | if (fHead) { |
364 | 4.39k | fHead->fPrev = rec; |
365 | 4.39k | } |
366 | 11.2k | fHead = rec; |
367 | 11.2k | if (!fTail) { |
368 | 6.83k | fTail = rec; |
369 | 6.83k | } |
370 | 11.2k | fTotalBytesUsed += rec->bytesUsed(); |
371 | 11.2k | fCount += 1; |
372 | | |
373 | 11.2k | this->validate(); |
374 | 11.2k | } |
375 | | |
376 | | /////////////////////////////////////////////////////////////////////////////// |
377 | | |
378 | | #ifdef SK_DEBUG |
379 | 0 | void SkResourceCache::validate() const { |
380 | 0 | if (nullptr == fHead) { |
381 | 0 | SkASSERT(nullptr == fTail); |
382 | 0 | SkASSERT(0 == fTotalBytesUsed); |
383 | 0 | return; |
384 | 0 | } |
385 | | |
386 | 0 | if (fHead == fTail) { |
387 | 0 | SkASSERT(nullptr == fHead->fPrev); |
388 | 0 | SkASSERT(nullptr == fHead->fNext); |
389 | 0 | SkASSERT(fHead->bytesUsed() == fTotalBytesUsed); |
390 | 0 | return; |
391 | 0 | } |
392 | | |
393 | 0 | SkASSERT(nullptr == fHead->fPrev); |
394 | 0 | SkASSERT(fHead->fNext); |
395 | 0 | SkASSERT(nullptr == fTail->fNext); |
396 | 0 | SkASSERT(fTail->fPrev); |
397 | |
|
398 | 0 | size_t used = 0; |
399 | 0 | int count = 0; |
400 | 0 | const Rec* rec = fHead; |
401 | 0 | while (rec) { |
402 | 0 | count += 1; |
403 | 0 | used += rec->bytesUsed(); |
404 | 0 | SkASSERT(used <= fTotalBytesUsed); |
405 | 0 | rec = rec->fNext; |
406 | 0 | } |
407 | 0 | SkASSERT(fCount == count); |
408 | |
|
409 | 0 | rec = fTail; |
410 | 0 | while (rec) { |
411 | 0 | SkASSERT(count > 0); |
412 | 0 | count -= 1; |
413 | 0 | SkASSERT(used >= rec->bytesUsed()); |
414 | 0 | used -= rec->bytesUsed(); |
415 | 0 | rec = rec->fPrev; |
416 | 0 | } |
417 | |
|
418 | 0 | SkASSERT(0 == count); |
419 | 0 | SkASSERT(0 == used); |
420 | 0 | } |
421 | | #endif |
422 | | |
423 | 0 | void SkResourceCache::dump() const { |
424 | 0 | this->validate(); |
425 | |
|
426 | 0 | SkDebugf("SkResourceCache: count=%d bytes=%zu %s\n", |
427 | 0 | fCount, fTotalBytesUsed, fDiscardableFactory ? "discardable" : "malloc"); |
428 | 0 | } |
429 | | |
430 | 0 | size_t SkResourceCache::setSingleAllocationByteLimit(size_t newLimit) { |
431 | 0 | size_t oldLimit = fSingleAllocationByteLimit; |
432 | 0 | fSingleAllocationByteLimit = newLimit; |
433 | 0 | return oldLimit; |
434 | 0 | } |
435 | | |
436 | 0 | size_t SkResourceCache::getSingleAllocationByteLimit() const { |
437 | 0 | return fSingleAllocationByteLimit; |
438 | 0 | } |
439 | | |
440 | 0 | size_t SkResourceCache::getEffectiveSingleAllocationByteLimit() const { |
441 | | // fSingleAllocationByteLimit == 0 means the caller is asking for our default |
442 | 0 | size_t limit = fSingleAllocationByteLimit; |
443 | | |
444 | | // if we're not discardable (i.e. we are fixed-budget) then cap the single-limit |
445 | | // to our budget. |
446 | 0 | if (nullptr == fDiscardableFactory) { |
447 | 0 | if (0 == limit) { |
448 | 0 | limit = fTotalByteLimit; |
449 | 0 | } else { |
450 | 0 | limit = std::min(limit, fTotalByteLimit); |
451 | 0 | } |
452 | 0 | } |
453 | 0 | return limit; |
454 | 0 | } |
455 | | |
456 | 48.6k | void SkResourceCache::checkMessages() { |
457 | 48.6k | TArray<PurgeSharedIDMessage> msgs; |
458 | 48.6k | fPurgeSharedIDInbox.poll(&msgs); |
459 | 59.3k | for (int i = 0; i < msgs.size(); ++i) { |
460 | 10.6k | this->purgeSharedID(msgs[i].fSharedID); |
461 | 10.6k | } |
462 | 48.6k | } |
463 | | |
464 | | /////////////////////////////////////////////////////////////////////////////// |
465 | | |
466 | | static SkResourceCache* gResourceCache = nullptr; |
467 | 123k | static SkMutex& resource_cache_mutex() { |
468 | 123k | static SkMutex& mutex = *(new SkMutex); |
469 | 123k | return mutex; |
470 | 123k | } |
471 | | |
472 | | /** Must hold resource_cache_mutex() when calling. */ |
473 | 61.6k | static SkResourceCache* get_cache() { |
474 | | // resource_cache_mutex() is always held when this is called, so we don't need to be fancy in here. |
475 | 61.6k | resource_cache_mutex().assertHeld(); |
476 | 61.6k | if (nullptr == gResourceCache) { |
477 | | #ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE |
478 | | gResourceCache = new SkResourceCache(SkDiscardableMemory::Create); |
479 | | #else |
480 | 7 | gResourceCache = new SkResourceCache(SK_DEFAULT_IMAGE_CACHE_LIMIT); |
481 | 7 | #endif |
482 | 7 | } |
483 | 61.6k | return gResourceCache; |
484 | 61.6k | } |
485 | | |
486 | 0 | size_t SkResourceCache::GetTotalBytesUsed() { |
487 | 0 | SkAutoMutexExclusive am(resource_cache_mutex()); |
488 | 0 | return get_cache()->getTotalBytesUsed(); |
489 | 0 | } |
490 | | |
491 | 0 | size_t SkResourceCache::GetTotalByteLimit() { |
492 | 0 | SkAutoMutexExclusive am(resource_cache_mutex()); |
493 | 0 | return get_cache()->getTotalByteLimit(); |
494 | 0 | } |
495 | | |
496 | 0 | size_t SkResourceCache::SetTotalByteLimit(size_t newLimit) { |
497 | 0 | SkAutoMutexExclusive am(resource_cache_mutex()); |
498 | 0 | return get_cache()->setTotalByteLimit(newLimit); |
499 | 0 | } |
500 | | |
501 | 12.9k | SkResourceCache::DiscardableFactory SkResourceCache::GetDiscardableFactory() { |
502 | 12.9k | SkAutoMutexExclusive am(resource_cache_mutex()); |
503 | 12.9k | return get_cache()->discardableFactory(); |
504 | 12.9k | } |
505 | | |
506 | 572 | SkCachedData* SkResourceCache::NewCachedData(size_t bytes) { |
507 | 572 | SkAutoMutexExclusive am(resource_cache_mutex()); |
508 | 572 | return get_cache()->newCachedData(bytes); |
509 | 572 | } |
510 | | |
511 | 0 | void SkResourceCache::Dump() { |
512 | 0 | SkAutoMutexExclusive am(resource_cache_mutex()); |
513 | 0 | get_cache()->dump(); |
514 | 0 | } |
515 | | |
516 | 0 | size_t SkResourceCache::SetSingleAllocationByteLimit(size_t size) { |
517 | 0 | SkAutoMutexExclusive am(resource_cache_mutex()); |
518 | 0 | return get_cache()->setSingleAllocationByteLimit(size); |
519 | 0 | } |
520 | | |
521 | 0 | size_t SkResourceCache::GetSingleAllocationByteLimit() { |
522 | 0 | SkAutoMutexExclusive am(resource_cache_mutex()); |
523 | 0 | return get_cache()->getSingleAllocationByteLimit(); |
524 | 0 | } |
525 | | |
526 | 0 | size_t SkResourceCache::GetEffectiveSingleAllocationByteLimit() { |
527 | 0 | SkAutoMutexExclusive am(resource_cache_mutex()); |
528 | 0 | return get_cache()->getEffectiveSingleAllocationByteLimit(); |
529 | 0 | } |
530 | | |
531 | 0 | void SkResourceCache::PurgeAll() { |
532 | 0 | SkAutoMutexExclusive am(resource_cache_mutex()); |
533 | 0 | return get_cache()->purgeAll(); |
534 | 0 | } |
535 | | |
536 | 0 | void SkResourceCache::CheckMessages() { |
537 | 0 | SkAutoMutexExclusive am(resource_cache_mutex()); |
538 | 0 | return get_cache()->checkMessages(); |
539 | 0 | } |
540 | | |
541 | 36.8k | bool SkResourceCache::Find(const Key& key, FindVisitor visitor, void* context) { |
542 | 36.8k | SkAutoMutexExclusive am(resource_cache_mutex()); |
543 | 36.8k | return get_cache()->find(key, visitor, context); |
544 | 36.8k | } |
545 | | |
546 | 11.2k | void SkResourceCache::Add(Rec* rec, void* payload) { |
547 | 11.2k | SkAutoMutexExclusive am(resource_cache_mutex()); |
548 | 11.2k | get_cache()->add(rec, payload); |
549 | 11.2k | } |
550 | | |
551 | 0 | void SkResourceCache::VisitAll(Visitor visitor, void* context) { |
552 | 0 | SkAutoMutexExclusive am(resource_cache_mutex()); |
553 | 0 | get_cache()->visitAll(visitor, context); |
554 | 0 | } |
555 | | |
556 | 10.6k | void SkResourceCache::PostPurgeSharedID(uint64_t sharedID) { |
557 | 10.6k | if (sharedID) { |
558 | 10.6k | SkMessageBus<PurgeSharedIDMessage, uint32_t>::Post(PurgeSharedIDMessage(sharedID)); |
559 | 10.6k | } |
560 | 10.6k | } |
561 | | |
562 | | /////////////////////////////////////////////////////////////////////////////// |
563 | | |
564 | 0 | size_t SkGraphics::GetResourceCacheTotalBytesUsed() { |
565 | 0 | return SkResourceCache::GetTotalBytesUsed(); |
566 | 0 | } |
567 | | |
568 | 0 | size_t SkGraphics::GetResourceCacheTotalByteLimit() { |
569 | 0 | return SkResourceCache::GetTotalByteLimit(); |
570 | 0 | } |
571 | | |
572 | 0 | size_t SkGraphics::SetResourceCacheTotalByteLimit(size_t newLimit) { |
573 | 0 | return SkResourceCache::SetTotalByteLimit(newLimit); |
574 | 0 | } |
575 | | |
576 | 0 | size_t SkGraphics::GetResourceCacheSingleAllocationByteLimit() { |
577 | 0 | return SkResourceCache::GetSingleAllocationByteLimit(); |
578 | 0 | } |
579 | | |
580 | 0 | size_t SkGraphics::SetResourceCacheSingleAllocationByteLimit(size_t newLimit) { |
581 | 0 | return SkResourceCache::SetSingleAllocationByteLimit(newLimit); |
582 | 0 | } |
583 | | |
584 | 0 | void SkGraphics::PurgeResourceCache() { |
585 | 0 | SkImageFilter_Base::PurgeCache(); |
586 | 0 | return SkResourceCache::PurgeAll(); |
587 | 0 | } |
588 | | |
589 | | ///////////// |
590 | | |
591 | 0 | static void dump_visitor(const SkResourceCache::Rec& rec, void*) { |
592 | 0 | SkDebugf("RC: %12s bytes %9zu discardable %p\n", |
593 | 0 | rec.getCategory(), rec.bytesUsed(), rec.diagnostic_only_getDiscardable()); |
594 | 0 | } |
595 | | |
596 | 0 | void SkResourceCache::TestDumpMemoryStatistics() { |
597 | 0 | VisitAll(dump_visitor, nullptr); |
598 | 0 | } |
599 | | |
600 | 0 | static void sk_trace_dump_visitor(const SkResourceCache::Rec& rec, void* context) { |
601 | 0 | SkTraceMemoryDump* dump = static_cast<SkTraceMemoryDump*>(context); |
602 | 0 | SkString dumpName = SkStringPrintf("skia/sk_resource_cache/%s_%p", rec.getCategory(), &rec); |
603 | 0 | SkDiscardableMemory* discardable = rec.diagnostic_only_getDiscardable(); |
604 | 0 | if (discardable) { |
605 | 0 | dump->setDiscardableMemoryBacking(dumpName.c_str(), *discardable); |
606 | | |
607 | | // The discardable memory size will be calculated by dumper, but we also dump what we think |
608 | | // the size of object in memory is irrespective of whether object is live or dead. |
609 | 0 | dump->dumpNumericValue(dumpName.c_str(), "discardable_size", "bytes", rec.bytesUsed()); |
610 | 0 | } else { |
611 | 0 | dump->dumpNumericValue(dumpName.c_str(), "size", "bytes", rec.bytesUsed()); |
612 | 0 | dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr); |
613 | 0 | } |
614 | 0 | } |
615 | | |
616 | 0 | void SkResourceCache::DumpMemoryStatistics(SkTraceMemoryDump* dump) { |
617 | | // Since resource could be backed by malloc or discardable, the cache always dumps detailed |
618 | | // stats to be accurate. |
619 | 0 | VisitAll(sk_trace_dump_visitor, dump); |
620 | 0 | } |