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