1 2/* 3 * Copyright 2011 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10 11#ifndef GrResourceCache_DEFINED 12#define GrResourceCache_DEFINED 13 14#include "GrConfig.h" 15#include "GrTypes.h" 16#include "GrTMultiMap.h" 17#include "GrBinHashKey.h" 18#include "SkMessageBus.h" 19#include "SkTInternalLList.h" 20 21class GrCacheable; 22class GrResourceCache; 23class GrResourceCacheEntry; 24 25class GrResourceKey { 26public: 27 static GrCacheID::Domain ScratchDomain() { 28 static const GrCacheID::Domain gDomain = GrCacheID::GenerateDomain(); 29 return gDomain; 30 } 31 32 /** Uniquely identifies the GrCacheable subclass in the key to avoid collisions 33 across resource types. */ 34 typedef uint8_t ResourceType; 35 36 /** Flags set by the GrCacheable subclass. */ 37 typedef uint8_t ResourceFlags; 38 39 /** Generate a unique ResourceType */ 40 static ResourceType GenerateResourceType(); 41 42 /** Creates a key for resource */ 43 GrResourceKey(const GrCacheID& id, ResourceType type, ResourceFlags flags) { 44 this->init(id.getDomain(), id.getKey(), type, flags); 45 }; 46 47 GrResourceKey(const GrResourceKey& src) { 48 fKey = src.fKey; 49 } 50 51 GrResourceKey() { 52 fKey.reset(); 53 } 54 55 void reset(const GrCacheID& id, ResourceType type, ResourceFlags flags) { 56 this->init(id.getDomain(), id.getKey(), type, flags); 57 } 58 59 uint32_t getHash() const { 60 return fKey.getHash(); 61 } 62 63 bool isScratch() const { 64 return ScratchDomain() == 65 *reinterpret_cast<const GrCacheID::Domain*>(fKey.getData() + 66 kCacheIDDomainOffset); 67 } 68 69 ResourceType getResourceType() const { 70 return *reinterpret_cast<const ResourceType*>(fKey.getData() + 71 kResourceTypeOffset); 72 } 73 74 ResourceFlags getResourceFlags() const { 75 return *reinterpret_cast<const ResourceFlags*>(fKey.getData() + 76 kResourceFlagsOffset); 77 } 78 79 bool operator==(const GrResourceKey& other) const { return fKey == other.fKey; } 80 81private: 82 enum { 83 kCacheIDKeyOffset = 0, 84 kCacheIDDomainOffset = kCacheIDKeyOffset + sizeof(GrCacheID::Key), 85 kResourceTypeOffset = kCacheIDDomainOffset + sizeof(GrCacheID::Domain), 86 kResourceFlagsOffset = kResourceTypeOffset + sizeof(ResourceType), 87 kPadOffset = kResourceFlagsOffset + sizeof(ResourceFlags), 88 kKeySize = SkAlign4(kPadOffset), 89 kPadSize = kKeySize - kPadOffset 90 }; 91 92 void init(const GrCacheID::Domain domain, 93 const GrCacheID::Key& key, 94 ResourceType type, 95 ResourceFlags flags) { 96 union { 97 uint8_t fKey8[kKeySize]; 98 uint32_t fKey32[kKeySize / 4]; 99 } keyData; 100 101 uint8_t* k = keyData.fKey8; 102 memcpy(k + kCacheIDKeyOffset, key.fData8, sizeof(GrCacheID::Key)); 103 memcpy(k + kCacheIDDomainOffset, &domain, sizeof(GrCacheID::Domain)); 104 memcpy(k + kResourceTypeOffset, &type, sizeof(ResourceType)); 105 memcpy(k + kResourceFlagsOffset, &flags, sizeof(ResourceFlags)); 106 memset(k + kPadOffset, 0, kPadSize); 107 fKey.setKeyData(keyData.fKey32); 108 } 109 GrBinHashKey<kKeySize> fKey; 110}; 111 112// The cache listens for these messages to purge junk resources proactively. 113struct GrResourceInvalidatedMessage { 114 GrResourceKey key; 115}; 116 117/////////////////////////////////////////////////////////////////////////////// 118 119class GrResourceCacheEntry { 120public: 121 GrCacheable* resource() const { return fResource; } 122 const GrResourceKey& key() const { return fKey; } 123 124 static const GrResourceKey& GetKey(const GrResourceCacheEntry& e) { return e.key(); } 125 static uint32_t Hash(const GrResourceKey& key) { return key.getHash(); } 126#ifdef SK_DEBUG 127 void validate() const; 128#else 129 void validate() const {} 130#endif 131 132 /** 133 * Update the cached size for this entry and inform the resource cache that 134 * it has changed. Usually invoked from GrCacheable::didChangeGpuMemorySize, 135 * not directly from here. 136 */ 137 void didChangeResourceSize(); 138 139private: 140 GrResourceCacheEntry(GrResourceCache* resourceCache, 141 const GrResourceKey& key, 142 GrCacheable* resource); 143 ~GrResourceCacheEntry(); 144 145 GrResourceCache* fResourceCache; 146 GrResourceKey fKey; 147 GrCacheable* fResource; 148 size_t fCachedSize; 149 bool fIsExclusive; 150 151 // Linked list for the LRU ordering. 152 SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrResourceCacheEntry); 153 154 friend class GrResourceCache; 155}; 156 157/////////////////////////////////////////////////////////////////////////////// 158 159/** 160 * Cache of GrCacheable objects. 161 * 162 * These have a corresponding GrResourceKey, built from 128bits identifying the 163 * resource. Multiple resources can map to same GrResourceKey. 164 * 165 * The cache stores the entries in a double-linked list, which is its LRU. 166 * When an entry is "locked" (i.e. given to the caller), it is moved to the 167 * head of the list. If/when we must purge some of the entries, we walk the 168 * list backwards from the tail, since those are the least recently used. 169 * 170 * For fast searches, we maintain a hash map based on the GrResourceKey. 171 * 172 * It is a goal to make the GrResourceCache the central repository and bookkeeper 173 * of all resources. It should replace the linked list of GrGpuObjects that 174 * GrGpu uses to call abandon/release. 175 */ 176class GrResourceCache { 177public: 178 GrResourceCache(int maxCount, size_t maxBytes); 179 ~GrResourceCache(); 180 181 /** 182 * Return the current resource cache limits. 183 * 184 * @param maxResource If non-null, returns maximum number of resources 185 * that can be held in the cache. 186 * @param maxBytes If non-null, returns maximum number of bytes of 187 * gpu memory that can be held in the cache. 188 */ 189 void getLimits(int* maxResources, size_t* maxBytes) const; 190 191 /** 192 * Specify the resource cache limits. If the current cache exceeds either 193 * of these, it will be purged (LRU) to keep the cache within these limits. 194 * 195 * @param maxResources The maximum number of resources that can be held in 196 * the cache. 197 * @param maxBytes The maximum number of bytes of resource memory that 198 * can be held in the cache. 199 */ 200 void setLimits(int maxResources, size_t maxResourceBytes); 201 202 /** 203 * The callback function used by the cache when it is still over budget 204 * after a purge. The passed in 'data' is the same 'data' handed to 205 * setOverbudgetCallback. The callback returns true if some resources 206 * have been freed. 207 */ 208 typedef bool (*PFOverbudgetCB)(void* data); 209 210 /** 211 * Set the callback the cache should use when it is still over budget 212 * after a purge. The 'data' provided here will be passed back to the 213 * callback. Note that the cache will attempt to purge any resources newly 214 * freed by the callback. 215 */ 216 void setOverbudgetCallback(PFOverbudgetCB overbudgetCB, void* data) { 217 fOverbudgetCB = overbudgetCB; 218 fOverbudgetData = data; 219 } 220 221 /** 222 * Returns the number of bytes consumed by cached resources. 223 */ 224 size_t getCachedResourceBytes() const { return fEntryBytes; } 225 226 /** 227 * Returns the number of cached resources. 228 */ 229 int getCachedResourceCount() const { return fEntryCount; } 230 231 // For a found or added resource to be completely exclusive to the caller 232 // both the kNoOtherOwners and kHide flags need to be specified 233 enum OwnershipFlags { 234 kNoOtherOwners_OwnershipFlag = 0x1, // found/added resource has no other owners 235 kHide_OwnershipFlag = 0x2 // found/added resource is hidden from future 'find's 236 }; 237 238 /** 239 * Search for an entry with the same Key. If found, return it. 240 * If not found, return null. 241 * If ownershipFlags includes kNoOtherOwners and a resource is returned 242 * then that resource has no other refs to it. 243 * If ownershipFlags includes kHide and a resource is returned then that 244 * resource will not be returned from future 'find' calls until it is 245 * 'freed' (and recycled) or makeNonExclusive is called. 246 * For a resource to be completely exclusive to a caller both kNoOtherOwners 247 * and kHide must be specified. 248 */ 249 GrCacheable* find(const GrResourceKey& key, 250 uint32_t ownershipFlags = 0); 251 252 /** 253 * Add the new resource to the cache (by creating a new cache entry based 254 * on the provided key and resource). 255 * 256 * Ownership of the resource is transferred to the resource cache, 257 * which will unref() it when it is purged or deleted. 258 * 259 * If ownershipFlags includes kHide, subsequent calls to 'find' will not 260 * return 'resource' until it is 'freed' (and recycled) or makeNonExclusive 261 * is called. 262 */ 263 void addResource(const GrResourceKey& key, 264 GrCacheable* resource, 265 uint32_t ownershipFlags = 0); 266 267 /** 268 * Determines if the cache contains an entry matching a key. If a matching 269 * entry exists but was detached then it will not be found. 270 */ 271 bool hasKey(const GrResourceKey& key) const { return NULL != fCache.find(key); } 272 273 /** 274 * Hide 'entry' so that future searches will not find it. Such 275 * hidden entries will not be purged. The entry still counts against 276 * the cache's budget and should be made non-exclusive when exclusive access 277 * is no longer needed. 278 */ 279 void makeExclusive(GrResourceCacheEntry* entry); 280 281 /** 282 * Restore 'entry' so that it can be found by future searches. 'entry' 283 * will also be purgeable (provided its lock count is now 0.) 284 */ 285 void makeNonExclusive(GrResourceCacheEntry* entry); 286 287 /** 288 * Notify the cache that the size of a resource has changed. 289 */ 290 void didIncreaseResourceSize(const GrResourceCacheEntry*, size_t amountInc); 291 void didDecreaseResourceSize(const GrResourceCacheEntry*, size_t amountDec); 292 293 /** 294 * Remove a resource from the cache and delete it! 295 */ 296 void deleteResource(GrResourceCacheEntry* entry); 297 298 /** 299 * Removes every resource in the cache that isn't locked. 300 */ 301 void purgeAllUnlocked(); 302 303 /** 304 * Allow cache to purge unused resources to obey resource limitations 305 * Note: this entry point will be hidden (again) once totally ref-driven 306 * cache maintenance is implemented. Note that the overbudget callback 307 * will be called if the initial purge doesn't get the cache under 308 * its budget. 309 * 310 * extraCount and extraBytes are added to the current resource allocation 311 * to make sure enough room is available for future additions (e.g, 312 * 10MB across 10 textures is about to be added). 313 */ 314 void purgeAsNeeded(int extraCount = 0, size_t extraBytes = 0); 315 316#ifdef SK_DEBUG 317 void validate() const; 318#else 319 void validate() const {} 320#endif 321 322#if GR_CACHE_STATS 323 void printStats(); 324#endif 325 326private: 327 enum BudgetBehaviors { 328 kAccountFor_BudgetBehavior, 329 kIgnore_BudgetBehavior 330 }; 331 332 void internalDetach(GrResourceCacheEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior); 333 void attachToHead(GrResourceCacheEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior); 334 335 void removeInvalidResource(GrResourceCacheEntry* entry); 336 337 GrTMultiMap<GrResourceCacheEntry, GrResourceKey> fCache; 338 339 // We're an internal doubly linked list 340 typedef SkTInternalLList<GrResourceCacheEntry> EntryList; 341 EntryList fList; 342 343#ifdef SK_DEBUG 344 // These objects cannot be returned by a search 345 EntryList fExclusiveList; 346#endif 347 348 // our budget, used in purgeAsNeeded() 349 int fMaxCount; 350 size_t fMaxBytes; 351 352 // our current stats, related to our budget 353#if GR_CACHE_STATS 354 int fHighWaterEntryCount; 355 size_t fHighWaterEntryBytes; 356 int fHighWaterClientDetachedCount; 357 size_t fHighWaterClientDetachedBytes; 358#endif 359 360 int fEntryCount; 361 size_t fEntryBytes; 362 int fClientDetachedCount; 363 size_t fClientDetachedBytes; 364 365 // prevents recursive purging 366 bool fPurging; 367 368 PFOverbudgetCB fOverbudgetCB; 369 void* fOverbudgetData; 370 371 void internalPurge(int extraCount, size_t extraBytes); 372 373 // Listen for messages that a resource has been invalidated and purge cached junk proactively. 374 SkMessageBus<GrResourceInvalidatedMessage>::Inbox fInvalidationInbox; 375 void purgeInvalidated(); 376 377#ifdef SK_DEBUG 378 static size_t countBytes(const SkTInternalLList<GrResourceCacheEntry>& list); 379#endif 380}; 381 382/////////////////////////////////////////////////////////////////////////////// 383 384#ifdef SK_DEBUG 385 class GrAutoResourceCacheValidate { 386 public: 387 GrAutoResourceCacheValidate(GrResourceCache* cache) : fCache(cache) { 388 cache->validate(); 389 } 390 ~GrAutoResourceCacheValidate() { 391 fCache->validate(); 392 } 393 private: 394 GrResourceCache* fCache; 395 }; 396#else 397 class GrAutoResourceCacheValidate { 398 public: 399 GrAutoResourceCacheValidate(GrResourceCache*) {} 400 }; 401#endif 402 403#endif 404