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 // for memory usage diagnostics 86 virtual const char* getCategory() const = 0; diagnostic_only_getDiscardableRec87 virtual SkDiscardableMemory* diagnostic_only_getDiscardable() const { return nullptr; } 88 89 private: 90 Rec* fNext; 91 Rec* fPrev; 92 93 friend class SkResourceCache; 94 }; 95 96 // Used with SkMessageBus 97 struct PurgeSharedIDMessage { PurgeSharedIDMessagePurgeSharedIDMessage98 PurgeSharedIDMessage(uint64_t sharedID) : fSharedID(sharedID) {} 99 100 uint64_t fSharedID; 101 }; 102 103 typedef const Rec* ID; 104 105 /** 106 * Callback function for find(). If called, the cache will have found a match for the 107 * specified Key, and will pass in the corresponding Rec, along with a caller-specified 108 * context. The function can read the data in Rec, and copy whatever it likes into context 109 * (casting context to whatever it really is). 110 * 111 * The return value determines what the cache will do with the Rec. If the function returns 112 * true, then the Rec is considered "valid". If false is returned, the Rec will be considered 113 * "stale" and will be purged from the cache. 114 */ 115 typedef bool (*FindVisitor)(const Rec&, void* context); 116 117 /** 118 * Returns a locked/pinned SkDiscardableMemory instance for the specified 119 * number of bytes, or nullptr on failure. 120 */ 121 typedef SkDiscardableMemory* (*DiscardableFactory)(size_t bytes); 122 123 /* 124 * The following static methods are thread-safe wrappers around a global 125 * instance of this cache. 126 */ 127 128 /** 129 * Returns true if the visitor was called on a matching Key, and the visitor returned true. 130 * 131 * Find() will search the cache for the specified Key. If no match is found, return false and 132 * do not call the FindVisitor. If a match is found, return whatever the visitor returns. 133 * Its return value is interpreted to mean: 134 * true : Rec is valid 135 * false : Rec is "stale" -- the cache will purge it. 136 */ 137 static bool Find(const Key& key, FindVisitor, void* context); 138 static void Add(Rec*); 139 140 typedef void (*Visitor)(const Rec&, void* context); 141 // Call the visitor for every Rec in the cache. 142 static void VisitAll(Visitor, void* context); 143 144 static size_t GetTotalBytesUsed(); 145 static size_t GetTotalByteLimit(); 146 static size_t SetTotalByteLimit(size_t newLimit); 147 148 static size_t SetSingleAllocationByteLimit(size_t); 149 static size_t GetSingleAllocationByteLimit(); 150 static size_t GetEffectiveSingleAllocationByteLimit(); 151 152 static void PurgeAll(); 153 154 static void TestDumpMemoryStatistics(); 155 156 /** Dump memory usage statistics of every Rec in the cache using the 157 SkTraceMemoryDump interface. 158 */ 159 static void DumpMemoryStatistics(SkTraceMemoryDump* dump); 160 161 /** 162 * Returns the DiscardableFactory used by the global cache, or nullptr. 163 */ 164 static DiscardableFactory GetDiscardableFactory(); 165 166 /** 167 * Use this allocator for bitmaps, so they can use ashmem when available. 168 * Returns nullptr if the ResourceCache has not been initialized with a DiscardableFactory. 169 */ 170 static SkBitmap::Allocator* GetAllocator(); 171 172 static SkCachedData* NewCachedData(size_t bytes); 173 174 static void PostPurgeSharedID(uint64_t sharedID); 175 176 /** 177 * Call SkDebugf() with diagnostic information about the state of the cache 178 */ 179 static void Dump(); 180 181 /////////////////////////////////////////////////////////////////////////// 182 183 /** 184 * Construct the cache to call DiscardableFactory when it 185 * allocates memory for the pixels. In this mode, the cache has 186 * not explicit budget, and so methods like getTotalBytesUsed() 187 * and getTotalByteLimit() will return 0, and setTotalByteLimit 188 * will ignore its argument and return 0. 189 */ 190 SkResourceCache(DiscardableFactory); 191 192 /** 193 * Construct the cache, allocating memory with malloc, and respect the 194 * byteLimit, purging automatically when a new image is added to the cache 195 * that pushes the total bytesUsed over the limit. Note: The limit can be 196 * changed at runtime with setTotalByteLimit. 197 */ 198 explicit SkResourceCache(size_t byteLimit); 199 ~SkResourceCache(); 200 201 /** 202 * Returns true if the visitor was called on a matching Key, and the visitor returned true. 203 * 204 * find() will search the cache for the specified Key. If no match is found, return false and 205 * do not call the FindVisitor. If a match is found, return whatever the visitor returns. 206 * Its return value is interpreted to mean: 207 * true : Rec is valid 208 * false : Rec is "stale" -- the cache will purge it. 209 */ 210 bool find(const Key&, FindVisitor, void* context); 211 void add(Rec*); 212 void visitAll(Visitor, void* context); 213 getTotalBytesUsed()214 size_t getTotalBytesUsed() const { return fTotalBytesUsed; } getTotalByteLimit()215 size_t getTotalByteLimit() const { return fTotalByteLimit; } 216 217 /** 218 * This is respected by SkBitmapProcState::possiblyScaleImage. 219 * 0 is no maximum at all; this is the default. 220 * setSingleAllocationByteLimit() returns the previous value. 221 */ 222 size_t setSingleAllocationByteLimit(size_t maximumAllocationSize); 223 size_t getSingleAllocationByteLimit() const; 224 // returns the logical single allocation size (pinning against the budget when the cache 225 // is not backed by discardable memory. 226 size_t getEffectiveSingleAllocationByteLimit() const; 227 228 /** 229 * Set the maximum number of bytes available to this cache. If the current 230 * cache exceeds this new value, it will be purged to try to fit within 231 * this new limit. 232 */ 233 size_t setTotalByteLimit(size_t newLimit); 234 235 void purgeSharedID(uint64_t sharedID); 236 purgeAll()237 void purgeAll() { 238 this->purgeAsNeeded(true); 239 } 240 discardableFactory()241 DiscardableFactory discardableFactory() const { return fDiscardableFactory; } allocator()242 SkBitmap::Allocator* allocator() const { return fAllocator; } 243 244 SkCachedData* newCachedData(size_t bytes); 245 246 /** 247 * Call SkDebugf() with diagnostic information about the state of the cache 248 */ 249 void dump() const; 250 251 private: 252 Rec* fHead; 253 Rec* fTail; 254 255 class Hash; 256 Hash* fHash; 257 258 DiscardableFactory fDiscardableFactory; 259 // the allocator is nullptr or one that matches discardables 260 SkBitmap::Allocator* fAllocator; 261 262 size_t fTotalBytesUsed; 263 size_t fTotalByteLimit; 264 size_t fSingleAllocationByteLimit; 265 int fCount; 266 267 SkMessageBus<PurgeSharedIDMessage>::Inbox fPurgeSharedIDInbox; 268 269 void checkMessages(); 270 void purgeAsNeeded(bool forcePurge = false); 271 272 // linklist management 273 void moveToHead(Rec*); 274 void addToHead(Rec*); 275 void release(Rec*); 276 void remove(Rec*); 277 278 void init(); // called by constructors 279 280 #ifdef SK_DEBUG 281 void validate() const; 282 #else validate()283 void validate() const {} 284 #endif 285 }; 286 #endif 287