blob: 016c38cdc12fabbffcc0d9657aac9f8b7e6083f8 [file] [log] [blame]
reed@google.com602a1d72013-07-23 19:13:54 +00001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/core/SkResourceCache.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -04009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkTraceMemoryDump.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/private/SkMutex.h"
12#include "include/private/SkTo.h"
13#include "src/core/SkDiscardableMemory.h"
Michael Ludwig8ee6cf32019-08-02 09:57:04 -040014#include "src/core/SkImageFilter_Base.h"
Ben Wagner21bca282019-05-15 10:15:52 -040015#include "src/core/SkMessageBus.h"
Mike Reed13711eb2020-07-14 17:16:32 -040016#include "src/core/SkMipmap.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "src/core/SkOpts.h"
reed@google.com602a1d72013-07-23 19:13:54 +000018
fmalita171e5b72014-10-22 11:20:40 -070019#include <stddef.h>
bungeman60e0fee2015-08-26 05:15:46 -070020#include <stdlib.h>
fmalita171e5b72014-10-22 11:20:40 -070021
Robert Phillipse7a959d2021-03-11 14:44:42 -050022DECLARE_SKMESSAGEBUS_MESSAGE(SkResourceCache::PurgeSharedIDMessage, uint32_t, true)
reed7eeba252015-02-24 13:54:23 -080023
Chris Dalton9a986cf2018-10-18 15:27:59 -060024static inline bool SkShouldPostMessageToBus(
25 const SkResourceCache::PurgeSharedIDMessage&, uint32_t) {
26 // SkResourceCache is typically used as a singleton and we don't label Inboxes so all messages
27 // go to all inboxes.
28 return true;
29}
30
reed@google.come4eb1222013-12-09 22:29:30 +000031// This can be defined by the caller's build system
32//#define SK_USE_DISCARDABLE_SCALEDIMAGECACHE
33
34#ifndef SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT
35# define SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT 1024
36#endif
37
reed@google.com602a1d72013-07-23 19:13:54 +000038#ifndef SK_DEFAULT_IMAGE_CACHE_LIMIT
reedae488772015-09-11 08:19:23 -070039 #define SK_DEFAULT_IMAGE_CACHE_LIMIT (32 * 1024 * 1024)
reed@google.com602a1d72013-07-23 19:13:54 +000040#endif
41
bungeman70bb8082016-02-17 10:13:49 -080042void SkResourceCache::Key::init(void* nameSpace, uint64_t sharedID, size_t dataSize) {
43 SkASSERT(SkAlign4(dataSize) == dataSize);
fmalita171e5b72014-10-22 11:20:40 -070044
45 // fCount32 and fHash are not hashed
reed7eeba252015-02-24 13:54:23 -080046 static const int kUnhashedLocal32s = 2; // fCache32 + fHash
47 static const int kSharedIDLocal32s = 2; // fSharedID_lo + fSharedID_hi
48 static const int kHashedLocal32s = kSharedIDLocal32s + (sizeof(fNamespace) >> 2);
49 static const int kLocal32s = kUnhashedLocal32s + kHashedLocal32s;
fmalita171e5b72014-10-22 11:20:40 -070050
bungeman99fe8222015-08-20 07:57:51 -070051 static_assert(sizeof(Key) == (kLocal32s << 2), "unaccounted_key_locals");
52 static_assert(sizeof(Key) == offsetof(Key, fNamespace) + sizeof(fNamespace),
53 "namespace_field_must_be_last");
fmalita171e5b72014-10-22 11:20:40 -070054
bungeman70bb8082016-02-17 10:13:49 -080055 fCount32 = SkToS32(kLocal32s + (dataSize >> 2));
Brian Osman50ea3c02019-02-04 10:01:53 -050056 fSharedID_lo = (uint32_t)(sharedID & 0xFFFFFFFF);
reed7eeba252015-02-24 13:54:23 -080057 fSharedID_hi = (uint32_t)(sharedID >> 32);
fmalita171e5b72014-10-22 11:20:40 -070058 fNamespace = nameSpace;
mtklein4e976072016-08-08 09:06:27 -070059 // skip unhashed fields when computing the hash
60 fHash = SkOpts::hash(this->as32() + kUnhashedLocal32s,
61 (fCount32 - kUnhashedLocal32s) << 2);
reed4f987e92014-08-20 13:41:56 -070062}
reed@google.com602a1d72013-07-23 19:13:54 +000063
Mike Kleinc0bd9f92019-04-23 12:05:21 -050064#include "include/private/SkTHash.h"
Mike Kleine4bf1642016-12-13 16:26:32 -050065
66namespace {
67 struct HashTraits {
68 static uint32_t Hash(const SkResourceCache::Key& key) { return key.hash(); }
69 static const SkResourceCache::Key& GetKey(const SkResourceCache::Rec* rec) {
70 return rec->getKey();
71 }
72 };
John Stilesa6841be2020-08-06 14:11:56 -040073} // namespace
reed@google.com5d1e5582013-07-25 14:36:15 +000074
reed011f39a2014-08-28 13:35:23 -070075class SkResourceCache::Hash :
Mike Kleine4bf1642016-12-13 16:26:32 -050076 public SkTHashTable<SkResourceCache::Rec*, SkResourceCache::Key, HashTraits> {};
commit-bot@chromium.org158f6462014-04-02 17:03:09 +000077
reed@google.com5d1e5582013-07-25 14:36:15 +000078
79///////////////////////////////////////////////////////////////////////////////
80
reed011f39a2014-08-28 13:35:23 -070081void SkResourceCache::init() {
halcanary96fcdcc2015-08-27 07:41:13 -070082 fHead = nullptr;
83 fTail = nullptr;
reed@google.com5d1e5582013-07-25 14:36:15 +000084 fHash = new Hash;
halcanary805ef152014-07-17 06:58:01 -070085 fTotalBytesUsed = 0;
reed@google.com602a1d72013-07-23 19:13:54 +000086 fCount = 0;
halcanary805ef152014-07-17 06:58:01 -070087 fSingleAllocationByteLimit = 0;
reed@google.come4eb1222013-12-09 22:29:30 +000088
89 // One of these should be explicit set by the caller after we return.
halcanary805ef152014-07-17 06:58:01 -070090 fTotalByteLimit = 0;
halcanary96fcdcc2015-08-27 07:41:13 -070091 fDiscardableFactory = nullptr;
reed@google.come4eb1222013-12-09 22:29:30 +000092}
93
Robert Phillipse7a959d2021-03-11 14:44:42 -050094SkResourceCache::SkResourceCache(DiscardableFactory factory)
95 : fPurgeSharedIDInbox(SK_InvalidUniqueID) {
reed@google.come4eb1222013-12-09 22:29:30 +000096 this->init();
97 fDiscardableFactory = factory;
reed@google.come4eb1222013-12-09 22:29:30 +000098}
99
Robert Phillipse7a959d2021-03-11 14:44:42 -0500100SkResourceCache::SkResourceCache(size_t byteLimit)
101 : fPurgeSharedIDInbox(SK_InvalidUniqueID) {
reed@google.come4eb1222013-12-09 22:29:30 +0000102 this->init();
halcanary805ef152014-07-17 06:58:01 -0700103 fTotalByteLimit = byteLimit;
reed@google.com602a1d72013-07-23 19:13:54 +0000104}
105
reed011f39a2014-08-28 13:35:23 -0700106SkResourceCache::~SkResourceCache() {
reed@google.com602a1d72013-07-23 19:13:54 +0000107 Rec* rec = fHead;
108 while (rec) {
109 Rec* next = rec->fNext;
halcanary385fe4d2015-08-26 13:07:48 -0700110 delete rec;
reed@google.com602a1d72013-07-23 19:13:54 +0000111 rec = next;
112 }
reed@google.com5d1e5582013-07-25 14:36:15 +0000113 delete fHash;
reed@google.com602a1d72013-07-23 19:13:54 +0000114}
115
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000116////////////////////////////////////////////////////////////////////////////////
117
reed7eeba252015-02-24 13:54:23 -0800118bool SkResourceCache::find(const Key& key, FindVisitor visitor, void* context) {
119 this->checkMessages();
120
Mike Kleine4bf1642016-12-13 16:26:32 -0500121 if (auto found = fHash->find(key)) {
122 Rec* rec = *found;
reedc90e0142014-09-15 11:39:44 -0700123 if (visitor(*rec, context)) {
124 this->moveToHead(rec); // for our LRU
125 return true;
126 } else {
127 this->remove(rec); // stale
128 return false;
129 }
reed@google.com5d1e5582013-07-25 14:36:15 +0000130 }
reedc90e0142014-09-15 11:39:44 -0700131 return false;
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000132}
133
reed9d93c2e2014-10-08 05:17:12 -0700134static void make_size_str(size_t size, SkString* str) {
135 const char suffix[] = { 'b', 'k', 'm', 'g', 't', 0 };
136 int i = 0;
137 while (suffix[i] && (size > 1024)) {
138 i += 1;
139 size >>= 10;
140 }
141 str->printf("%zu%c", size, suffix[i]);
142}
143
144static bool gDumpCacheTransactions;
145
Mike Reed7a542c52017-04-11 12:03:44 -0400146void SkResourceCache::add(Rec* rec, void* payload) {
reed7eeba252015-02-24 13:54:23 -0800147 this->checkMessages();
mtklein1b249332015-07-07 12:21:21 -0700148
reed680fb9e2014-08-26 09:08:04 -0700149 SkASSERT(rec);
150 // See if we already have this key (racy inserts, etc.)
Mike Reed7a542c52017-04-11 12:03:44 -0400151 if (Rec** preexisting = fHash->find(rec->getKey())) {
152 Rec* prev = *preexisting;
153 if (prev->canBePurged()) {
154 // if it can be purged, the install may fail, so we have to remove it
155 this->remove(prev);
156 } else {
157 // if it cannot be purged, we reuse it and delete the new one
158 prev->postAddInstall(payload);
159 delete rec;
160 return;
161 }
reed680fb9e2014-08-26 09:08:04 -0700162 }
mtklein1b249332015-07-07 12:21:21 -0700163
reed680fb9e2014-08-26 09:08:04 -0700164 this->addToHead(rec);
Mike Kleine4bf1642016-12-13 16:26:32 -0500165 fHash->set(rec);
Mike Reed7a542c52017-04-11 12:03:44 -0400166 rec->postAddInstall(payload);
reed595aa052014-09-15 10:15:18 -0700167
reed9d93c2e2014-10-08 05:17:12 -0700168 if (gDumpCacheTransactions) {
169 SkString bytesStr, totalStr;
170 make_size_str(rec->bytesUsed(), &bytesStr);
171 make_size_str(fTotalBytesUsed, &totalStr);
172 SkDebugf("RC: add %5s %12p key %08x -- total %5s, count %d\n",
173 bytesStr.c_str(), rec, rec->getHash(), totalStr.c_str(), fCount);
174 }
175
reedc90e0142014-09-15 11:39:44 -0700176 // since the new rec may push us over-budget, we perform a purge check now
177 this->purgeAsNeeded();
reed@google.com602a1d72013-07-23 19:13:54 +0000178}
179
danakj790ffe32014-09-11 10:49:52 -0700180void SkResourceCache::remove(Rec* rec) {
Mike Reed7a542c52017-04-11 12:03:44 -0400181 SkASSERT(rec->canBePurged());
danakj790ffe32014-09-11 10:49:52 -0700182 size_t used = rec->bytesUsed();
183 SkASSERT(used <= fTotalBytesUsed);
184
mtklein18300a32016-03-16 13:53:35 -0700185 this->release(rec);
danakj790ffe32014-09-11 10:49:52 -0700186 fHash->remove(rec->getKey());
danakj790ffe32014-09-11 10:49:52 -0700187
danakj790ffe32014-09-11 10:49:52 -0700188 fTotalBytesUsed -= used;
189 fCount -= 1;
reed9d93c2e2014-10-08 05:17:12 -0700190
Mike Reed7a542c52017-04-11 12:03:44 -0400191 //SkDebugf("-RC count [%3d] bytes %d\n", fCount, fTotalBytesUsed);
192
reed9d93c2e2014-10-08 05:17:12 -0700193 if (gDumpCacheTransactions) {
194 SkString bytesStr, totalStr;
195 make_size_str(used, &bytesStr);
196 make_size_str(fTotalBytesUsed, &totalStr);
197 SkDebugf("RC: remove %5s %12p key %08x -- total %5s, count %d\n",
198 bytesStr.c_str(), rec, rec->getHash(), totalStr.c_str(), fCount);
199 }
200
halcanary385fe4d2015-08-26 13:07:48 -0700201 delete rec;
danakj790ffe32014-09-11 10:49:52 -0700202}
203
reed56b00d92014-09-11 12:22:34 -0700204void SkResourceCache::purgeAsNeeded(bool forcePurge) {
reed@google.come4eb1222013-12-09 22:29:30 +0000205 size_t byteLimit;
206 int countLimit;
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000207
reed@google.come4eb1222013-12-09 22:29:30 +0000208 if (fDiscardableFactory) {
209 countLimit = SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT;
Ben Wagnerb0897652018-06-15 15:37:57 +0000210 byteLimit = UINT32_MAX; // no limit based on bytes
reed@google.come4eb1222013-12-09 22:29:30 +0000211 } else {
212 countLimit = SK_MaxS32; // no limit based on count
halcanary805ef152014-07-17 06:58:01 -0700213 byteLimit = fTotalByteLimit;
reed@google.come4eb1222013-12-09 22:29:30 +0000214 }
215
reed@google.com602a1d72013-07-23 19:13:54 +0000216 Rec* rec = fTail;
217 while (rec) {
reed56b00d92014-09-11 12:22:34 -0700218 if (!forcePurge && fTotalBytesUsed < byteLimit && fCount < countLimit) {
reed@google.com602a1d72013-07-23 19:13:54 +0000219 break;
220 }
reed@google.come4eb1222013-12-09 22:29:30 +0000221
reed@google.com602a1d72013-07-23 19:13:54 +0000222 Rec* prev = rec->fPrev;
Mike Reed7a542c52017-04-11 12:03:44 -0400223 if (rec->canBePurged()) {
224 this->remove(rec);
225 }
reed@google.com602a1d72013-07-23 19:13:54 +0000226 rec = prev;
227 }
reed@google.com602a1d72013-07-23 19:13:54 +0000228}
229
reed83787d02015-02-25 07:17:11 -0800230//#define SK_TRACK_PURGE_SHAREDID_HITRATE
231
232#ifdef SK_TRACK_PURGE_SHAREDID_HITRATE
233static int gPurgeCallCounter;
234static int gPurgeHitCounter;
235#endif
236
reed7eeba252015-02-24 13:54:23 -0800237void SkResourceCache::purgeSharedID(uint64_t sharedID) {
238 if (0 == sharedID) {
239 return;
240 }
241
reed83787d02015-02-25 07:17:11 -0800242#ifdef SK_TRACK_PURGE_SHAREDID_HITRATE
243 gPurgeCallCounter += 1;
244 bool found = false;
245#endif
reed7eeba252015-02-24 13:54:23 -0800246 // go backwards, just like purgeAsNeeded, just to make the code similar.
247 // could iterate either direction and still be correct.
248 Rec* rec = fTail;
249 while (rec) {
250 Rec* prev = rec->fPrev;
251 if (rec->getKey().getSharedID() == sharedID) {
Mike Reed7a542c52017-04-11 12:03:44 -0400252 // even though the "src" is now dead, caches could still be in-flight, so
253 // we have to check if it can be removed.
254 if (rec->canBePurged()) {
255 this->remove(rec);
256 }
reed83787d02015-02-25 07:17:11 -0800257#ifdef SK_TRACK_PURGE_SHAREDID_HITRATE
258 found = true;
259#endif
reed7eeba252015-02-24 13:54:23 -0800260 }
261 rec = prev;
262 }
reed83787d02015-02-25 07:17:11 -0800263
264#ifdef SK_TRACK_PURGE_SHAREDID_HITRATE
265 if (found) {
266 gPurgeHitCounter += 1;
267 }
268
269 SkDebugf("PurgeShared calls=%d hits=%d rate=%g\n", gPurgeCallCounter, gPurgeHitCounter,
270 gPurgeHitCounter * 100.0 / gPurgeCallCounter);
271#endif
reed7eeba252015-02-24 13:54:23 -0800272}
273
reed216b6432015-08-19 12:25:40 -0700274void SkResourceCache::visitAll(Visitor visitor, void* context) {
275 // go backwards, just like purgeAsNeeded, just to make the code similar.
276 // could iterate either direction and still be correct.
277 Rec* rec = fTail;
278 while (rec) {
279 visitor(*rec, context);
280 rec = rec->fPrev;
281 }
282}
283
284///////////////////////////////////////////////////////////////////////////////////////////////////
285
reed011f39a2014-08-28 13:35:23 -0700286size_t SkResourceCache::setTotalByteLimit(size_t newLimit) {
halcanary805ef152014-07-17 06:58:01 -0700287 size_t prevLimit = fTotalByteLimit;
288 fTotalByteLimit = newLimit;
reed@google.com602a1d72013-07-23 19:13:54 +0000289 if (newLimit < prevLimit) {
290 this->purgeAsNeeded();
291 }
292 return prevLimit;
293}
294
qiankun.miaod9aac342014-10-23 07:58:17 -0700295SkCachedData* SkResourceCache::newCachedData(size_t bytes) {
reed7eeba252015-02-24 13:54:23 -0800296 this->checkMessages();
mtklein1b249332015-07-07 12:21:21 -0700297
qiankun.miaod9aac342014-10-23 07:58:17 -0700298 if (fDiscardableFactory) {
299 SkDiscardableMemory* dm = fDiscardableFactory(bytes);
halcanary96fcdcc2015-08-27 07:41:13 -0700300 return dm ? new SkCachedData(bytes, dm) : nullptr;
qiankun.miaod9aac342014-10-23 07:58:17 -0700301 } else {
halcanary385fe4d2015-08-26 13:07:48 -0700302 return new SkCachedData(sk_malloc_throw(bytes), bytes);
qiankun.miaod9aac342014-10-23 07:58:17 -0700303 }
304}
305
reed@google.com602a1d72013-07-23 19:13:54 +0000306///////////////////////////////////////////////////////////////////////////////
307
mtklein18300a32016-03-16 13:53:35 -0700308void SkResourceCache::release(Rec* rec) {
reed@google.com602a1d72013-07-23 19:13:54 +0000309 Rec* prev = rec->fPrev;
310 Rec* next = rec->fNext;
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000311
reed@google.com602a1d72013-07-23 19:13:54 +0000312 if (!prev) {
313 SkASSERT(fHead == rec);
314 fHead = next;
315 } else {
316 prev->fNext = next;
317 }
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000318
reed@google.com602a1d72013-07-23 19:13:54 +0000319 if (!next) {
320 fTail = prev;
321 } else {
322 next->fPrev = prev;
323 }
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000324
halcanary96fcdcc2015-08-27 07:41:13 -0700325 rec->fNext = rec->fPrev = nullptr;
reed@google.com602a1d72013-07-23 19:13:54 +0000326}
327
reed011f39a2014-08-28 13:35:23 -0700328void SkResourceCache::moveToHead(Rec* rec) {
reed@google.com602a1d72013-07-23 19:13:54 +0000329 if (fHead == rec) {
330 return;
331 }
332
333 SkASSERT(fHead);
334 SkASSERT(fTail);
335
336 this->validate();
337
mtklein18300a32016-03-16 13:53:35 -0700338 this->release(rec);
reed@google.com602a1d72013-07-23 19:13:54 +0000339
340 fHead->fPrev = rec;
341 rec->fNext = fHead;
342 fHead = rec;
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000343
reed@google.com602a1d72013-07-23 19:13:54 +0000344 this->validate();
345}
346
reed011f39a2014-08-28 13:35:23 -0700347void SkResourceCache::addToHead(Rec* rec) {
reed@google.com602a1d72013-07-23 19:13:54 +0000348 this->validate();
349
halcanary96fcdcc2015-08-27 07:41:13 -0700350 rec->fPrev = nullptr;
reed@google.com602a1d72013-07-23 19:13:54 +0000351 rec->fNext = fHead;
352 if (fHead) {
353 fHead->fPrev = rec;
354 }
355 fHead = rec;
356 if (!fTail) {
357 fTail = rec;
358 }
halcanary805ef152014-07-17 06:58:01 -0700359 fTotalBytesUsed += rec->bytesUsed();
reed@google.com602a1d72013-07-23 19:13:54 +0000360 fCount += 1;
361
362 this->validate();
363}
364
reed@google.comfa7fd802013-12-12 21:37:25 +0000365///////////////////////////////////////////////////////////////////////////////
366
reed@google.com602a1d72013-07-23 19:13:54 +0000367#ifdef SK_DEBUG
reed011f39a2014-08-28 13:35:23 -0700368void SkResourceCache::validate() const {
halcanary96fcdcc2015-08-27 07:41:13 -0700369 if (nullptr == fHead) {
370 SkASSERT(nullptr == fTail);
halcanary805ef152014-07-17 06:58:01 -0700371 SkASSERT(0 == fTotalBytesUsed);
reed@google.com602a1d72013-07-23 19:13:54 +0000372 return;
373 }
374
375 if (fHead == fTail) {
halcanary96fcdcc2015-08-27 07:41:13 -0700376 SkASSERT(nullptr == fHead->fPrev);
377 SkASSERT(nullptr == fHead->fNext);
halcanary805ef152014-07-17 06:58:01 -0700378 SkASSERT(fHead->bytesUsed() == fTotalBytesUsed);
reed@google.com602a1d72013-07-23 19:13:54 +0000379 return;
380 }
381
halcanary96fcdcc2015-08-27 07:41:13 -0700382 SkASSERT(nullptr == fHead->fPrev);
bsalomon49f085d2014-09-05 13:34:00 -0700383 SkASSERT(fHead->fNext);
halcanary96fcdcc2015-08-27 07:41:13 -0700384 SkASSERT(nullptr == fTail->fNext);
bsalomon49f085d2014-09-05 13:34:00 -0700385 SkASSERT(fTail->fPrev);
reed@google.com602a1d72013-07-23 19:13:54 +0000386
387 size_t used = 0;
388 int count = 0;
389 const Rec* rec = fHead;
390 while (rec) {
391 count += 1;
392 used += rec->bytesUsed();
halcanary805ef152014-07-17 06:58:01 -0700393 SkASSERT(used <= fTotalBytesUsed);
reed@google.com602a1d72013-07-23 19:13:54 +0000394 rec = rec->fNext;
395 }
396 SkASSERT(fCount == count);
397
398 rec = fTail;
399 while (rec) {
400 SkASSERT(count > 0);
401 count -= 1;
402 SkASSERT(used >= rec->bytesUsed());
403 used -= rec->bytesUsed();
404 rec = rec->fPrev;
405 }
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000406
reed@google.com602a1d72013-07-23 19:13:54 +0000407 SkASSERT(0 == count);
408 SkASSERT(0 == used);
409}
410#endif
411
reed011f39a2014-08-28 13:35:23 -0700412void SkResourceCache::dump() const {
reed@google.comfa7fd802013-12-12 21:37:25 +0000413 this->validate();
414
Tyler Denniston283dba52021-06-25 13:32:45 +0000415 SkDebugf("SkResourceCache: count=%d bytes=%d %s\n",
reedc90e0142014-09-15 11:39:44 -0700416 fCount, fTotalBytesUsed, fDiscardableFactory ? "discardable" : "malloc");
reed@google.comfa7fd802013-12-12 21:37:25 +0000417}
418
reed011f39a2014-08-28 13:35:23 -0700419size_t SkResourceCache::setSingleAllocationByteLimit(size_t newLimit) {
halcanary805ef152014-07-17 06:58:01 -0700420 size_t oldLimit = fSingleAllocationByteLimit;
421 fSingleAllocationByteLimit = newLimit;
422 return oldLimit;
423}
424
reed011f39a2014-08-28 13:35:23 -0700425size_t SkResourceCache::getSingleAllocationByteLimit() const {
halcanary805ef152014-07-17 06:58:01 -0700426 return fSingleAllocationByteLimit;
427}
428
reed1d9e80f2015-01-26 11:24:37 -0800429size_t SkResourceCache::getEffectiveSingleAllocationByteLimit() const {
430 // fSingleAllocationByteLimit == 0 means the caller is asking for our default
431 size_t limit = fSingleAllocationByteLimit;
432
433 // if we're not discardable (i.e. we are fixed-budget) then cap the single-limit
434 // to our budget.
halcanary96fcdcc2015-08-27 07:41:13 -0700435 if (nullptr == fDiscardableFactory) {
reed1d9e80f2015-01-26 11:24:37 -0800436 if (0 == limit) {
437 limit = fTotalByteLimit;
438 } else {
Brian Osman788b9162020-02-07 10:36:46 -0500439 limit = std::min(limit, fTotalByteLimit);
reed1d9e80f2015-01-26 11:24:37 -0800440 }
441 }
442 return limit;
443}
444
reed7eeba252015-02-24 13:54:23 -0800445void SkResourceCache::checkMessages() {
446 SkTArray<PurgeSharedIDMessage> msgs;
447 fPurgeSharedIDInbox.poll(&msgs);
448 for (int i = 0; i < msgs.count(); ++i) {
449 this->purgeSharedID(msgs[i].fSharedID);
450 }
451}
452
reed@google.com602a1d72013-07-23 19:13:54 +0000453///////////////////////////////////////////////////////////////////////////////
454
halcanary96fcdcc2015-08-27 07:41:13 -0700455static SkResourceCache* gResourceCache = nullptr;
Herb Derby9c71e7b2019-06-17 14:40:42 -0400456static SkMutex& resource_cache_mutex() {
457 static SkMutex& mutex = *(new SkMutex);
458 return mutex;
459}
reed@google.com602a1d72013-07-23 19:13:54 +0000460
Herb Derby9c71e7b2019-06-17 14:40:42 -0400461/** Must hold resource_cache_mutex() when calling. */
reed011f39a2014-08-28 13:35:23 -0700462static SkResourceCache* get_cache() {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400463 // resource_cache_mutex() is always held when this is called, so we don't need to be fancy in here.
464 resource_cache_mutex().assertHeld();
halcanary96fcdcc2015-08-27 07:41:13 -0700465 if (nullptr == gResourceCache) {
commit-bot@chromium.org60c8d242014-05-27 16:28:43 +0000466#ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE
halcanary385fe4d2015-08-26 13:07:48 -0700467 gResourceCache = new SkResourceCache(SkDiscardableMemory::Create);
commit-bot@chromium.org60c8d242014-05-27 16:28:43 +0000468#else
halcanary385fe4d2015-08-26 13:07:48 -0700469 gResourceCache = new SkResourceCache(SK_DEFAULT_IMAGE_CACHE_LIMIT);
commit-bot@chromium.org60c8d242014-05-27 16:28:43 +0000470#endif
commit-bot@chromium.org60c8d242014-05-27 16:28:43 +0000471 }
reed011f39a2014-08-28 13:35:23 -0700472 return gResourceCache;
reed@google.com602a1d72013-07-23 19:13:54 +0000473}
474
reed011f39a2014-08-28 13:35:23 -0700475size_t SkResourceCache::GetTotalBytesUsed() {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400476 SkAutoMutexExclusive am(resource_cache_mutex());
halcanary805ef152014-07-17 06:58:01 -0700477 return get_cache()->getTotalBytesUsed();
reed@google.com602a1d72013-07-23 19:13:54 +0000478}
479
reed011f39a2014-08-28 13:35:23 -0700480size_t SkResourceCache::GetTotalByteLimit() {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400481 SkAutoMutexExclusive am(resource_cache_mutex());
halcanary805ef152014-07-17 06:58:01 -0700482 return get_cache()->getTotalByteLimit();
reed@google.com602a1d72013-07-23 19:13:54 +0000483}
484
reed011f39a2014-08-28 13:35:23 -0700485size_t SkResourceCache::SetTotalByteLimit(size_t newLimit) {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400486 SkAutoMutexExclusive am(resource_cache_mutex());
halcanary805ef152014-07-17 06:58:01 -0700487 return get_cache()->setTotalByteLimit(newLimit);
reed@google.com602a1d72013-07-23 19:13:54 +0000488}
489
reed30ad5302014-09-16 10:39:55 -0700490SkResourceCache::DiscardableFactory SkResourceCache::GetDiscardableFactory() {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400491 SkAutoMutexExclusive am(resource_cache_mutex());
reed30ad5302014-09-16 10:39:55 -0700492 return get_cache()->discardableFactory();
493}
494
qiankun.miaod9aac342014-10-23 07:58:17 -0700495SkCachedData* SkResourceCache::NewCachedData(size_t bytes) {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400496 SkAutoMutexExclusive am(resource_cache_mutex());
qiankun.miaod9aac342014-10-23 07:58:17 -0700497 return get_cache()->newCachedData(bytes);
498}
499
reed011f39a2014-08-28 13:35:23 -0700500void SkResourceCache::Dump() {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400501 SkAutoMutexExclusive am(resource_cache_mutex());
reed@google.comfa7fd802013-12-12 21:37:25 +0000502 get_cache()->dump();
503}
504
reed011f39a2014-08-28 13:35:23 -0700505size_t SkResourceCache::SetSingleAllocationByteLimit(size_t size) {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400506 SkAutoMutexExclusive am(resource_cache_mutex());
halcanary805ef152014-07-17 06:58:01 -0700507 return get_cache()->setSingleAllocationByteLimit(size);
508}
509
reed011f39a2014-08-28 13:35:23 -0700510size_t SkResourceCache::GetSingleAllocationByteLimit() {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400511 SkAutoMutexExclusive am(resource_cache_mutex());
halcanary805ef152014-07-17 06:58:01 -0700512 return get_cache()->getSingleAllocationByteLimit();
513}
514
reed1d9e80f2015-01-26 11:24:37 -0800515size_t SkResourceCache::GetEffectiveSingleAllocationByteLimit() {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400516 SkAutoMutexExclusive am(resource_cache_mutex());
reed1d9e80f2015-01-26 11:24:37 -0800517 return get_cache()->getEffectiveSingleAllocationByteLimit();
518}
519
reed56b00d92014-09-11 12:22:34 -0700520void SkResourceCache::PurgeAll() {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400521 SkAutoMutexExclusive am(resource_cache_mutex());
reed56b00d92014-09-11 12:22:34 -0700522 return get_cache()->purgeAll();
523}
524
Mike Reedb9bd12d2021-03-04 08:13:56 -0500525void SkResourceCache::CheckMessages() {
526 SkAutoMutexExclusive am(resource_cache_mutex());
527 return get_cache()->checkMessages();
528}
529
reed7eeba252015-02-24 13:54:23 -0800530bool SkResourceCache::Find(const Key& key, FindVisitor visitor, void* context) {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400531 SkAutoMutexExclusive am(resource_cache_mutex());
reedc90e0142014-09-15 11:39:44 -0700532 return get_cache()->find(key, visitor, context);
reed680fb9e2014-08-26 09:08:04 -0700533}
534
Mike Reed7a542c52017-04-11 12:03:44 -0400535void SkResourceCache::Add(Rec* rec, void* payload) {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400536 SkAutoMutexExclusive am(resource_cache_mutex());
Mike Reed7a542c52017-04-11 12:03:44 -0400537 get_cache()->add(rec, payload);
reed680fb9e2014-08-26 09:08:04 -0700538}
539
reed216b6432015-08-19 12:25:40 -0700540void SkResourceCache::VisitAll(Visitor visitor, void* context) {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400541 SkAutoMutexExclusive am(resource_cache_mutex());
reed216b6432015-08-19 12:25:40 -0700542 get_cache()->visitAll(visitor, context);
543}
544
reed7eeba252015-02-24 13:54:23 -0800545void SkResourceCache::PostPurgeSharedID(uint64_t sharedID) {
546 if (sharedID) {
Robert Phillipse7a959d2021-03-11 14:44:42 -0500547 SkMessageBus<PurgeSharedIDMessage, uint32_t>::Post(PurgeSharedIDMessage(sharedID));
reed7eeba252015-02-24 13:54:23 -0800548 }
549}
550
reed@google.com602a1d72013-07-23 19:13:54 +0000551///////////////////////////////////////////////////////////////////////////////
552
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500553#include "include/core/SkGraphics.h"
554#include "include/core/SkImageFilter.h"
reed@google.com602a1d72013-07-23 19:13:54 +0000555
reed011f39a2014-08-28 13:35:23 -0700556size_t SkGraphics::GetResourceCacheTotalBytesUsed() {
557 return SkResourceCache::GetTotalBytesUsed();
reed@google.com602a1d72013-07-23 19:13:54 +0000558}
559
reed011f39a2014-08-28 13:35:23 -0700560size_t SkGraphics::GetResourceCacheTotalByteLimit() {
561 return SkResourceCache::GetTotalByteLimit();
reed@google.com602a1d72013-07-23 19:13:54 +0000562}
563
reed011f39a2014-08-28 13:35:23 -0700564size_t SkGraphics::SetResourceCacheTotalByteLimit(size_t newLimit) {
565 return SkResourceCache::SetTotalByteLimit(newLimit);
reed@google.com602a1d72013-07-23 19:13:54 +0000566}
halcanary805ef152014-07-17 06:58:01 -0700567
reed011f39a2014-08-28 13:35:23 -0700568size_t SkGraphics::GetResourceCacheSingleAllocationByteLimit() {
569 return SkResourceCache::GetSingleAllocationByteLimit();
halcanary805ef152014-07-17 06:58:01 -0700570}
571
reed011f39a2014-08-28 13:35:23 -0700572size_t SkGraphics::SetResourceCacheSingleAllocationByteLimit(size_t newLimit) {
573 return SkResourceCache::SetSingleAllocationByteLimit(newLimit);
halcanary805ef152014-07-17 06:58:01 -0700574}
575
reed56b00d92014-09-11 12:22:34 -0700576void SkGraphics::PurgeResourceCache() {
Michael Ludwig8ee6cf32019-08-02 09:57:04 -0400577 SkImageFilter_Base::PurgeCache();
reed56b00d92014-09-11 12:22:34 -0700578 return SkResourceCache::PurgeAll();
579}
580
reed216b6432015-08-19 12:25:40 -0700581/////////////
582
583static void dump_visitor(const SkResourceCache::Rec& rec, void*) {
Tyler Denniston283dba52021-06-25 13:32:45 +0000584 SkDebugf("RC: %12s bytes %9lu discardable %p\n",
reed216b6432015-08-19 12:25:40 -0700585 rec.getCategory(), rec.bytesUsed(), rec.diagnostic_only_getDiscardable());
586}
587
588void SkResourceCache::TestDumpMemoryStatistics() {
589 VisitAll(dump_visitor, nullptr);
590}
ssid33c594c2015-08-27 09:23:54 -0700591
592static void sk_trace_dump_visitor(const SkResourceCache::Rec& rec, void* context) {
593 SkTraceMemoryDump* dump = static_cast<SkTraceMemoryDump*>(context);
ssidf0c98652015-09-30 04:31:23 -0700594 SkString dumpName = SkStringPrintf("skia/sk_resource_cache/%s_%p", rec.getCategory(), &rec);
ssid33c594c2015-08-27 09:23:54 -0700595 SkDiscardableMemory* discardable = rec.diagnostic_only_getDiscardable();
596 if (discardable) {
ssidf0c98652015-09-30 04:31:23 -0700597 dump->setDiscardableMemoryBacking(dumpName.c_str(), *discardable);
ssid60df5422015-10-01 12:41:35 -0700598
599 // The discardable memory size will be calculated by dumper, but we also dump what we think
600 // the size of object in memory is irrespective of whether object is live or dead.
601 dump->dumpNumericValue(dumpName.c_str(), "discardable_size", "bytes", rec.bytesUsed());
ssid33c594c2015-08-27 09:23:54 -0700602 } else {
ssidf0c98652015-09-30 04:31:23 -0700603 dump->dumpNumericValue(dumpName.c_str(), "size", "bytes", rec.bytesUsed());
604 dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr);
ssid33c594c2015-08-27 09:23:54 -0700605 }
606}
607
608void SkResourceCache::DumpMemoryStatistics(SkTraceMemoryDump* dump) {
ssidf0c98652015-09-30 04:31:23 -0700609 // Since resource could be backed by malloc or discardable, the cache always dumps detailed
610 // stats to be accurate.
ssid33c594c2015-08-27 09:23:54 -0700611 VisitAll(sk_trace_dump_visitor, dump);
612}