27#if defined(SK_USE_DISCARDABLE_SCALEDIMAGECACHE)
47#ifndef SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT
48# define SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT 1024
51#ifndef SK_DEFAULT_IMAGE_CACHE_LIMIT
52 #define SK_DEFAULT_IMAGE_CACHE_LIMIT (32 * 1024 * 1024)
59 static const int kUnhashedLocal32s = 2;
60 static const int kSharedIDLocal32s = 2;
61 static const int kHashedLocal32s = kSharedIDLocal32s + (
sizeof(fNamespace) >> 2);
62 static const int kLocal32s = kUnhashedLocal32s + kHashedLocal32s;
64 static_assert(
sizeof(
Key) == (kLocal32s << 2),
"unaccounted_key_locals");
65 static_assert(
sizeof(
Key) == offsetof(
Key, fNamespace) +
sizeof(fNamespace),
66 "namespace_field_must_be_last");
68 fCount32 =
SkToS32(kLocal32s + (dataSize >> 2));
69 fSharedID_lo = (uint32_t)(sharedID & 0xFFFFFFFF);
70 fSharedID_hi = (uint32_t)(sharedID >> 32);
71 fNamespace = nameSpace;
74 (fCount32 - kUnhashedLocal32s) << 2);
87 public THashTable<SkResourceCache::Rec*, SkResourceCache::Key, HashTraits> {};
92void SkResourceCache::init() {
98 fSingleAllocationByteLimit = 0;
102 fDiscardableFactory =
nullptr;
108 fDiscardableFactory = factory;
114 fTotalByteLimit = byteLimit;
130 this->checkMessages();
132 if (
auto found = fHash->
find(
key)) {
134 if (visitor(*rec, context)) {
135 this->moveToHead(rec);
146 const char suffix[] = {
'b',
'k',
'm',
'g',
't', 0 };
148 while (suffix[i] && (size > 1024)) {
152 str->
printf(
"%zu%c", size, suffix[i]);
158 this->checkMessages();
164 if (
prev->canBePurged()) {
169 prev->postAddInstall(payload);
175 this->addToHead(rec);
183 SkDebugf(
"RC: add %5s %12p key %08x -- total %5s, count %d\n",
188 this->purgeAsNeeded();
191void SkResourceCache::remove(
Rec* rec) {
193 size_t used = rec->bytesUsed();
197 fHash->
remove(rec->getKey());
199 fTotalBytesUsed -= used;
208 SkDebugf(
"RC: remove %5s %12p key %08x -- total %5s, count %d\n",
209 bytesStr.
c_str(), rec, rec->getHash(), totalStr.
c_str(), fCount);
215void SkResourceCache::purgeAsNeeded(
bool forcePurge) {
219 if (fDiscardableFactory) {
221 byteLimit = UINT32_MAX;
224 byteLimit = fTotalByteLimit;
229 if (!forcePurge && fTotalBytesUsed < byteLimit && fCount < countLimit) {
234 if (rec->canBePurged()) {
243#ifdef SK_TRACK_PURGE_SHAREDID_HITRATE
244static int gPurgeCallCounter;
245static int gPurgeHitCounter;
253#ifdef SK_TRACK_PURGE_SHAREDID_HITRATE
254 gPurgeCallCounter += 1;
268#ifdef SK_TRACK_PURGE_SHAREDID_HITRATE
275#ifdef SK_TRACK_PURGE_SHAREDID_HITRATE
277 gPurgeHitCounter += 1;
280 SkDebugf(
"PurgeShared calls=%d hits=%d rate=%g\n", gPurgeCallCounter, gPurgeHitCounter,
281 gPurgeHitCounter * 100.0 / gPurgeCallCounter);
290 visitor(*rec, context);
298 size_t prevLimit = fTotalByteLimit;
299 fTotalByteLimit = newLimit;
300 if (newLimit < prevLimit) {
301 this->purgeAsNeeded();
307 this->checkMessages();
309 if (fDiscardableFactory) {
319void SkResourceCache::release(
Rec* rec) {
336 rec->fNext = rec->fPrev =
nullptr;
339void SkResourceCache::moveToHead(
Rec* rec) {
358void SkResourceCache::addToHead(
Rec* rec) {
361 rec->fPrev =
nullptr;
370 fTotalBytesUsed += rec->bytesUsed();
379void SkResourceCache::validate()
const {
380 if (
nullptr == fHead) {
386 if (fHead == fTail) {
400 const Rec* rec = fHead;
403 used += rec->bytesUsed();
414 used -= rec->bytesUsed();
426 SkDebugf(
"SkResourceCache: count=%d bytes=%zu %s\n",
427 fCount, fTotalBytesUsed, fDiscardableFactory ?
"discardable" :
"malloc");
431 size_t oldLimit = fSingleAllocationByteLimit;
432 fSingleAllocationByteLimit = newLimit;
437 return fSingleAllocationByteLimit;
442 size_t limit = fSingleAllocationByteLimit;
446 if (
nullptr == fDiscardableFactory) {
448 limit = fTotalByteLimit;
450 limit = std::min(limit, fTotalByteLimit);
456void SkResourceCache::checkMessages() {
458 fPurgeSharedIDInbox.poll(&msgs);
459 for (
int i = 0; i < msgs.
size(); ++i) {
468 static SkMutex& mutex = *(
new SkMutex);
477#ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE
585 SkImageFilter_Base::PurgeCache();
592 SkDebugf(
"RC: %12s bytes %9zu discardable %p\n",
605 dump->setDiscardableMemoryBacking(dumpName.
c_str(), *discardable);
609 dump->dumpNumericValue(dumpName.
c_str(),
"discardable_size",
"bytes", rec.
bytesUsed());
612 dump->setMemoryBacking(dumpName.
c_str(),
"malloc",
nullptr);
static float next(float f)
static float prev(float f)
static constexpr T SkAlign4(T x)
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static void * sk_malloc_throw(size_t size)
static constexpr int32_t SK_MaxS32
#define DECLARE_SKMESSAGEBUS_MESSAGE(Message, IDType, AllowCopyableMessage)
static void make_size_str(size_t size, SkString *str)
static SkMutex & resource_cache_mutex()
static void dump_visitor(const SkResourceCache::Rec &rec, void *)
static SkResourceCache * get_cache()
static bool SkShouldPostMessageToBus(const SkResourceCache::PurgeSharedIDMessage &, uint32_t)
static void sk_trace_dump_visitor(const SkResourceCache::Rec &rec, void *context)
static SkResourceCache * gResourceCache
#define SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT
static bool gDumpCacheTransactions
#define SK_DEFAULT_IMAGE_CACHE_LIMIT
SK_API SkString static SkString SkStringPrintf()
constexpr int32_t SkToS32(S x)
static constexpr uint32_t SK_InvalidUniqueID
static void dump(const float m[20], SkYUVColorSpace cs, bool rgb2yuv)
static SkDiscardableMemory * Create(size_t bytes)
static size_t GetResourceCacheTotalByteLimit()
static size_t GetResourceCacheSingleAllocationByteLimit()
static size_t SetResourceCacheSingleAllocationByteLimit(size_t newLimit)
static size_t SetResourceCacheTotalByteLimit(size_t newLimit)
static void PurgeResourceCache()
static size_t GetResourceCacheTotalBytesUsed()
static void Post(Message m)
SkResourceCache(DiscardableFactory)
static size_t SetSingleAllocationByteLimit(size_t)
static SkCachedData * NewCachedData(size_t bytes)
static void Add(Rec *, void *payload=nullptr)
size_t getSingleAllocationByteLimit() const
size_t getTotalByteLimit() const
static size_t GetEffectiveSingleAllocationByteLimit()
DiscardableFactory discardableFactory() const
size_t getTotalBytesUsed() const
void visitAll(Visitor, void *context)
static void PostPurgeSharedID(uint64_t sharedID)
static size_t GetTotalBytesUsed()
void purgeSharedID(uint64_t sharedID)
static void CheckMessages()
static DiscardableFactory GetDiscardableFactory()
bool find(const Key &, FindVisitor, void *context)
static void DumpMemoryStatistics(SkTraceMemoryDump *dump)
static size_t GetSingleAllocationByteLimit()
static void VisitAll(Visitor, void *context)
static size_t GetTotalByteLimit()
static bool Find(const Key &key, FindVisitor, void *context)
static void TestDumpMemoryStatistics()
size_t setTotalByteLimit(size_t newLimit)
SkCachedData * newCachedData(size_t bytes)
SkDiscardableMemory *(* DiscardableFactory)(size_t bytes)
size_t setSingleAllocationByteLimit(size_t maximumAllocationSize)
size_t getEffectiveSingleAllocationByteLimit() const
static size_t SetTotalByteLimit(size_t newLimit)
void add(Rec *, void *payload=nullptr)
void printf(const char format[],...) SK_PRINTF_LIKE(2
const char * c_str() const
void remove(const K &key)
T * find(const K &key) const
uint32_t Hash32(const void *data, size_t bytes, uint32_t seed)
uint64_t getSharedID() const
void init(void *nameSpace, uint64_t sharedID, size_t dataSize)
virtual void postAddInstall(void *)
virtual const char * getCategory() const =0
virtual SkDiscardableMemory * diagnostic_only_getDiscardable() const
virtual size_t bytesUsed() const =0
virtual const Key & getKey() const =0
virtual bool canBePurged()