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 #ifndef SkResourceCache_DEFINED 9 #define SkResourceCache_DEFINED 10 11 #include "include/private/base/SkDebug.h" 12 #include "src/core/SkMessageBus.h" 13 14 #include <cstddef> 15 #include <cstdint> 16 17 class SkCachedData; 18 class SkDiscardableMemory; 19 class SkTraceMemoryDump; 20 21 /** 22 * Cache object for bitmaps (with possible scale in X Y as part of the key). 23 * 24 * Multiple caches can be instantiated, but each instance is not implicitly 25 * thread-safe, so if a given instance is to be shared across threads, the 26 * caller must manage the access itself (e.g. via a mutex). 27 * 28 * As a convenience, a global instance is also defined, which can be safely 29 * access across threads via the static methods (e.g. FindAndLock, etc.). 30 */ 31 class SkResourceCache { 32 public: 33 struct Key { 34 /** Key subclasses must call this after their own fields and data are initialized. 35 * All fields and data must be tightly packed. 36 * @param nameSpace must be unique per Key subclass. 37 * @param sharedID == 0 means ignore this field, does not support group purging. 38 * @param dataSize is size of fields and data of the subclass, must be a multiple of 4. 39 */ 40 void init(void* nameSpace, uint64_t sharedID, size_t dataSize); 41 42 /** Returns the size of this key. */ sizeKey43 size_t size() const { 44 return fCount32 << 2; 45 } 46 getNamespaceKey47 void* getNamespace() const { return fNamespace; } getSharedIDKey48 uint64_t getSharedID() const { return ((uint64_t)fSharedID_hi << 32) | fSharedID_lo; } 49 50 // This is only valid after having called init(). hashKey51 uint32_t hash() const { return fHash; } 52 53 bool operator==(const Key& other) const { 54 const uint32_t* a = this->as32(); 55 const uint32_t* b = other.as32(); 56 for (int i = 0; i < fCount32; ++i) { // (This checks fCount == other.fCount first.) 57 if (a[i] != b[i]) { 58 return false; 59 } 60 } 61 return true; 62 } 63 64 private: 65 int32_t fCount32; // local + user contents count32 66 uint32_t fHash; 67 // split uint64_t into hi and lo so we don't force ourselves to pad on 32bit machines. 68 uint32_t fSharedID_lo; 69 uint32_t fSharedID_hi; 70 void* fNamespace; // A unique namespace tag. This is hashed. 71 /* uint32_t fContents32[] */ 72 as32Key73 const uint32_t* as32() const { return (const uint32_t*)this; } 74 }; 75 76 struct Rec { 77 typedef SkResourceCache::Key Key; 78 RecRec79 Rec() {} ~RecRec80 virtual ~Rec() {} 81 getHashRec82 uint32_t getHash() const { return this->getKey().hash(); } 83 84 virtual const Key& getKey() const = 0; 85 virtual size_t bytesUsed() const = 0; 86 87 // Called if the cache needs to purge/remove/delete the Rec. Default returns true. 88 // Subclass may return false if there are outstanding references to it (e.g. bitmaps). 89 // Will only be deleted/removed-from-the-cache when this returns true. canBePurgedRec90 virtual bool canBePurged() { return true; } 91 92 // A rec is first created/initialized, and then added to the cache. As part of the add(), 93 // the cache will callback into the rec with postAddInstall, passing in whatever payload 94 // was passed to add/Add. 95 // 96 // This late-install callback exists because the process of add-ing might end up deleting 97 // the new rec (if an existing rec in the cache has the same key and cannot be purged). 98 // If the new rec will be deleted during add, the pre-existing one (with the same key) 99 // will have postAddInstall() called on it instead, so that either way an "install" will 100 // happen during the add. postAddInstallRec101 virtual void postAddInstall(void*) {} 102 103 // for memory usage diagnostics 104 virtual const char* getCategory() const = 0; diagnostic_only_getDiscardableRec105 virtual SkDiscardableMemory* diagnostic_only_getDiscardable() const { return nullptr; } 106 107 private: 108 Rec* fNext; 109 Rec* fPrev; 110 111 friend class SkResourceCache; 112 }; 113 114 // Used with SkMessageBus 115 struct PurgeSharedIDMessage { PurgeSharedIDMessagePurgeSharedIDMessage116 PurgeSharedIDMessage(uint64_t sharedID) : fSharedID(sharedID) {} 117 uint64_t fSharedID; 118 }; 119 120 typedef const Rec* ID; 121 122 /** 123 * Callback function for find(). If called, the cache will have found a match for the 124 * specified Key, and will pass in the corresponding Rec, along with a caller-specified 125 * context. The function can read the data in Rec, and copy whatever it likes into context 126 * (casting context to whatever it really is). 127 * 128 * The return value determines what the cache will do with the Rec. If the function returns 129 * true, then the Rec is considered "valid". If false is returned, the Rec will be considered 130 * "stale" and will be purged from the cache. 131 */ 132 typedef bool (*FindVisitor)(const Rec&, void* context); 133 134 /** 135 * Returns a locked/pinned SkDiscardableMemory instance for the specified 136 * number of bytes, or nullptr on failure. 137 */ 138 typedef SkDiscardableMemory* (*DiscardableFactory)(size_t bytes); 139 140 /* 141 * The following static methods are thread-safe wrappers around a global 142 * instance of this cache. 143 */ 144 145 /** 146 * Returns true if the visitor was called on a matching Key, and the visitor returned true. 147 * 148 * Find() will search the cache for the specified Key. If no match is found, return false and 149 * do not call the FindVisitor. If a match is found, return whatever the visitor returns. 150 * Its return value is interpreted to mean: 151 * true : Rec is valid 152 * false : Rec is "stale" -- the cache will purge it. 153 */ 154 static bool Find(const Key& key, FindVisitor, void* context); 155 static void Add(Rec*, void* payload = nullptr); 156 157 typedef void (*Visitor)(const Rec&, void* context); 158 // Call the visitor for every Rec in the cache. 159 static void VisitAll(Visitor, void* context); 160 161 static size_t GetTotalBytesUsed(); 162 static size_t GetTotalByteLimit(); 163 static size_t SetTotalByteLimit(size_t newLimit); 164 165 static size_t SetSingleAllocationByteLimit(size_t); 166 static size_t GetSingleAllocationByteLimit(); 167 static size_t GetEffectiveSingleAllocationByteLimit(); 168 169 static void PurgeAll(); 170 static void CheckMessages(); 171 172 static void TestDumpMemoryStatistics(); 173 174 /** Dump memory usage statistics of every Rec in the cache using the 175 SkTraceMemoryDump interface. 176 */ 177 static void DumpMemoryStatistics(SkTraceMemoryDump* dump); 178 179 /** 180 * Returns the DiscardableFactory used by the global cache, or nullptr. 181 */ 182 static DiscardableFactory GetDiscardableFactory(); 183 184 static SkCachedData* NewCachedData(size_t bytes); 185 186 static void PostPurgeSharedID(uint64_t sharedID); 187 188 /** 189 * Call SkDebugf() with diagnostic information about the state of the cache 190 */ 191 static void Dump(); 192 193 /////////////////////////////////////////////////////////////////////////// 194 195 /** 196 * Construct the cache to call DiscardableFactory when it 197 * allocates memory for the pixels. In this mode, the cache has 198 * not explicit budget, and so methods like getTotalBytesUsed() 199 * and getTotalByteLimit() will return 0, and setTotalByteLimit 200 * will ignore its argument and return 0. 201 */ 202 SkResourceCache(DiscardableFactory); 203 204 /** 205 * Construct the cache, allocating memory with malloc, and respect the 206 * byteLimit, purging automatically when a new image is added to the cache 207 * that pushes the total bytesUsed over the limit. Note: The limit can be 208 * changed at runtime with setTotalByteLimit. 209 */ 210 explicit SkResourceCache(size_t byteLimit); 211 ~SkResourceCache(); 212 213 /** 214 * Returns true if the visitor was called on a matching Key, and the visitor returned true. 215 * 216 * find() will search the cache for the specified Key. If no match is found, return false and 217 * do not call the FindVisitor. If a match is found, return whatever the visitor returns. 218 * Its return value is interpreted to mean: 219 * true : Rec is valid 220 * false : Rec is "stale" -- the cache will purge it. 221 */ 222 bool find(const Key&, FindVisitor, void* context); 223 void add(Rec*, void* payload = nullptr); 224 void visitAll(Visitor, void* context); 225 getTotalBytesUsed()226 size_t getTotalBytesUsed() const { return fTotalBytesUsed; } getTotalByteLimit()227 size_t getTotalByteLimit() const { return fTotalByteLimit; } 228 229 /** 230 * This is respected by SkBitmapProcState::possiblyScaleImage. 231 * 0 is no maximum at all; this is the default. 232 * setSingleAllocationByteLimit() returns the previous value. 233 */ 234 size_t setSingleAllocationByteLimit(size_t maximumAllocationSize); 235 size_t getSingleAllocationByteLimit() const; 236 // returns the logical single allocation size (pinning against the budget when the cache 237 // is not backed by discardable memory. 238 size_t getEffectiveSingleAllocationByteLimit() const; 239 240 /** 241 * Set the maximum number of bytes available to this cache. If the current 242 * cache exceeds this new value, it will be purged to try to fit within 243 * this new limit. 244 */ 245 size_t setTotalByteLimit(size_t newLimit); 246 247 void purgeSharedID(uint64_t sharedID); 248 purgeAll()249 void purgeAll() { 250 this->purgeAsNeeded(true); 251 } 252 discardableFactory()253 DiscardableFactory discardableFactory() const { return fDiscardableFactory; } 254 255 SkCachedData* newCachedData(size_t bytes); 256 257 /** 258 * Call SkDebugf() with diagnostic information about the state of the cache 259 */ 260 void dump() const; 261 262 private: 263 Rec* fHead; 264 Rec* fTail; 265 266 class Hash; 267 Hash* fHash; 268 269 DiscardableFactory fDiscardableFactory; 270 271 size_t fTotalBytesUsed; 272 size_t fTotalByteLimit; 273 size_t fSingleAllocationByteLimit; 274 int fCount; 275 276 SkMessageBus<PurgeSharedIDMessage, uint32_t>::Inbox fPurgeSharedIDInbox; 277 278 void checkMessages(); 279 void purgeAsNeeded(bool forcePurge = false); 280 281 // linklist management 282 void moveToHead(Rec*); 283 void addToHead(Rec*); 284 void release(Rec*); 285 void remove(Rec*); 286 287 void init(); // called by constructors 288 289 #ifdef SK_DEBUG 290 void validate() const; 291 #else validate()292 void validate() const {} 293 #endif 294 }; 295 #endif 296