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