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/core/SkBitmap.h" 12 #include "include/private/SkTDArray.h" 13 #include "src/core/SkMessageBus.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 static void CheckMessages(); 169 170 static void TestDumpMemoryStatistics(); 171 172 /** Dump memory usage statistics of every Rec in the cache using the 173 SkTraceMemoryDump interface. 174 */ 175 static void DumpMemoryStatistics(SkTraceMemoryDump* dump); 176 177 /** 178 * Returns the DiscardableFactory used by the global cache, or nullptr. 179 */ 180 static DiscardableFactory GetDiscardableFactory(); 181 182 static SkCachedData* NewCachedData(size_t bytes); 183 184 static void PostPurgeSharedID(uint64_t sharedID); 185 186 /** 187 * Call SkDebugf() with diagnostic information about the state of the cache 188 */ 189 static void Dump(); 190 191 /////////////////////////////////////////////////////////////////////////// 192 193 /** 194 * Construct the cache to call DiscardableFactory when it 195 * allocates memory for the pixels. In this mode, the cache has 196 * not explicit budget, and so methods like getTotalBytesUsed() 197 * and getTotalByteLimit() will return 0, and setTotalByteLimit 198 * will ignore its argument and return 0. 199 */ 200 SkResourceCache(DiscardableFactory); 201 202 /** 203 * Construct the cache, allocating memory with malloc, and respect the 204 * byteLimit, purging automatically when a new image is added to the cache 205 * that pushes the total bytesUsed over the limit. Note: The limit can be 206 * changed at runtime with setTotalByteLimit. 207 */ 208 explicit SkResourceCache(size_t byteLimit); 209 ~SkResourceCache(); 210 211 /** 212 * Returns true if the visitor was called on a matching Key, and the visitor returned true. 213 * 214 * find() will search the cache for the specified Key. If no match is found, return false and 215 * do not call the FindVisitor. If a match is found, return whatever the visitor returns. 216 * Its return value is interpreted to mean: 217 * true : Rec is valid 218 * false : Rec is "stale" -- the cache will purge it. 219 */ 220 bool find(const Key&, FindVisitor, void* context); 221 void add(Rec*, void* payload = nullptr); 222 void visitAll(Visitor, void* context); 223 getTotalBytesUsed()224 size_t getTotalBytesUsed() const { return fTotalBytesUsed; } getTotalByteLimit()225 size_t getTotalByteLimit() const { return fTotalByteLimit; } 226 227 /** 228 * This is respected by SkBitmapProcState::possiblyScaleImage. 229 * 0 is no maximum at all; this is the default. 230 * setSingleAllocationByteLimit() returns the previous value. 231 */ 232 size_t setSingleAllocationByteLimit(size_t maximumAllocationSize); 233 size_t getSingleAllocationByteLimit() const; 234 // returns the logical single allocation size (pinning against the budget when the cache 235 // is not backed by discardable memory. 236 size_t getEffectiveSingleAllocationByteLimit() const; 237 238 /** 239 * Set the maximum number of bytes available to this cache. If the current 240 * cache exceeds this new value, it will be purged to try to fit within 241 * this new limit. 242 */ 243 size_t setTotalByteLimit(size_t newLimit); 244 245 void purgeSharedID(uint64_t sharedID); 246 purgeAll()247 void purgeAll() { 248 this->purgeAsNeeded(true); 249 } 250 discardableFactory()251 DiscardableFactory discardableFactory() const { return fDiscardableFactory; } 252 253 SkCachedData* newCachedData(size_t bytes); 254 255 /** 256 * Call SkDebugf() with diagnostic information about the state of the cache 257 */ 258 void dump() const; 259 260 private: 261 Rec* fHead; 262 Rec* fTail; 263 264 class Hash; 265 Hash* fHash; 266 267 DiscardableFactory fDiscardableFactory; 268 269 size_t fTotalBytesUsed; 270 size_t fTotalByteLimit; 271 size_t fSingleAllocationByteLimit; 272 int fCount; 273 274 SkMessageBus<PurgeSharedIDMessage, uint32_t>::Inbox fPurgeSharedIDInbox; 275 276 void checkMessages(); 277 void purgeAsNeeded(bool forcePurge = false); 278 279 // linklist management 280 void moveToHead(Rec*); 281 void addToHead(Rec*); 282 void release(Rec*); 283 void remove(Rec*); 284 285 void init(); // called by constructors 286 287 #ifdef SK_DEBUG 288 void validate() const; 289 #else validate()290 void validate() const {} 291 #endif 292 }; 293 #endif 294