1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/gpu/GrResourceCache.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,386 @@ 1.4 + 1.5 +/* 1.6 + * Copyright 2011 Google Inc. 1.7 + * 1.8 + * Use of this source code is governed by a BSD-style license that can be 1.9 + * found in the LICENSE file. 1.10 + */ 1.11 + 1.12 + 1.13 + 1.14 +#ifndef GrResourceCache_DEFINED 1.15 +#define GrResourceCache_DEFINED 1.16 + 1.17 +#include "GrConfig.h" 1.18 +#include "GrTypes.h" 1.19 +#include "GrTMultiMap.h" 1.20 +#include "GrBinHashKey.h" 1.21 +#include "SkMessageBus.h" 1.22 +#include "SkTInternalLList.h" 1.23 + 1.24 +class GrResource; 1.25 +class GrResourceEntry; 1.26 + 1.27 +class GrResourceKey { 1.28 +public: 1.29 + static GrCacheID::Domain ScratchDomain() { 1.30 + static const GrCacheID::Domain gDomain = GrCacheID::GenerateDomain(); 1.31 + return gDomain; 1.32 + } 1.33 + 1.34 + /** Uniquely identifies the GrResource subclass in the key to avoid collisions 1.35 + across resource types. */ 1.36 + typedef uint8_t ResourceType; 1.37 + 1.38 + /** Flags set by the GrResource subclass. */ 1.39 + typedef uint8_t ResourceFlags; 1.40 + 1.41 + /** Generate a unique ResourceType */ 1.42 + static ResourceType GenerateResourceType(); 1.43 + 1.44 + /** Creates a key for resource */ 1.45 + GrResourceKey(const GrCacheID& id, ResourceType type, ResourceFlags flags) { 1.46 + this->init(id.getDomain(), id.getKey(), type, flags); 1.47 + }; 1.48 + 1.49 + GrResourceKey(const GrResourceKey& src) { 1.50 + fKey = src.fKey; 1.51 + } 1.52 + 1.53 + GrResourceKey() { 1.54 + fKey.reset(); 1.55 + } 1.56 + 1.57 + void reset(const GrCacheID& id, ResourceType type, ResourceFlags flags) { 1.58 + this->init(id.getDomain(), id.getKey(), type, flags); 1.59 + } 1.60 + 1.61 + uint32_t getHash() const { 1.62 + return fKey.getHash(); 1.63 + } 1.64 + 1.65 + bool isScratch() const { 1.66 + return ScratchDomain() == 1.67 + *reinterpret_cast<const GrCacheID::Domain*>(fKey.getData() + 1.68 + kCacheIDDomainOffset); 1.69 + } 1.70 + 1.71 + ResourceType getResourceType() const { 1.72 + return *reinterpret_cast<const ResourceType*>(fKey.getData() + 1.73 + kResourceTypeOffset); 1.74 + } 1.75 + 1.76 + ResourceFlags getResourceFlags() const { 1.77 + return *reinterpret_cast<const ResourceFlags*>(fKey.getData() + 1.78 + kResourceFlagsOffset); 1.79 + } 1.80 + 1.81 + bool operator==(const GrResourceKey& other) const { return fKey == other.fKey; } 1.82 + 1.83 +private: 1.84 + enum { 1.85 + kCacheIDKeyOffset = 0, 1.86 + kCacheIDDomainOffset = kCacheIDKeyOffset + sizeof(GrCacheID::Key), 1.87 + kResourceTypeOffset = kCacheIDDomainOffset + sizeof(GrCacheID::Domain), 1.88 + kResourceFlagsOffset = kResourceTypeOffset + sizeof(ResourceType), 1.89 + kPadOffset = kResourceFlagsOffset + sizeof(ResourceFlags), 1.90 + kKeySize = SkAlign4(kPadOffset), 1.91 + kPadSize = kKeySize - kPadOffset 1.92 + }; 1.93 + 1.94 + void init(const GrCacheID::Domain domain, 1.95 + const GrCacheID::Key& key, 1.96 + ResourceType type, 1.97 + ResourceFlags flags) { 1.98 + union { 1.99 + uint8_t fKey8[kKeySize]; 1.100 + uint32_t fKey32[kKeySize / 4]; 1.101 + } keyData; 1.102 + 1.103 + uint8_t* k = keyData.fKey8; 1.104 + memcpy(k + kCacheIDKeyOffset, key.fData8, sizeof(GrCacheID::Key)); 1.105 + memcpy(k + kCacheIDDomainOffset, &domain, sizeof(GrCacheID::Domain)); 1.106 + memcpy(k + kResourceTypeOffset, &type, sizeof(ResourceType)); 1.107 + memcpy(k + kResourceFlagsOffset, &flags, sizeof(ResourceFlags)); 1.108 + memset(k + kPadOffset, 0, kPadSize); 1.109 + fKey.setKeyData(keyData.fKey32); 1.110 + } 1.111 + GrBinHashKey<kKeySize> fKey; 1.112 +}; 1.113 + 1.114 +// The cache listens for these messages to purge junk resources proactively. 1.115 +struct GrResourceInvalidatedMessage { 1.116 + GrResourceKey key; 1.117 +}; 1.118 + 1.119 +/////////////////////////////////////////////////////////////////////////////// 1.120 + 1.121 +class GrResourceEntry { 1.122 +public: 1.123 + GrResource* resource() const { return fResource; } 1.124 + const GrResourceKey& key() const { return fKey; } 1.125 + 1.126 + static const GrResourceKey& GetKey(const GrResourceEntry& e) { return e.key(); } 1.127 + static uint32_t Hash(const GrResourceKey& key) { return key.getHash(); } 1.128 + static bool Equal(const GrResourceEntry& a, const GrResourceKey& b) { 1.129 + return a.key() == b; 1.130 + } 1.131 +#ifdef SK_DEBUG 1.132 + void validate() const; 1.133 +#else 1.134 + void validate() const {} 1.135 +#endif 1.136 + 1.137 +private: 1.138 + GrResourceEntry(const GrResourceKey& key, GrResource* resource); 1.139 + ~GrResourceEntry(); 1.140 + 1.141 + GrResourceKey fKey; 1.142 + GrResource* fResource; 1.143 + 1.144 + // Linked list for the LRU ordering. 1.145 + SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrResourceEntry); 1.146 + 1.147 + friend class GrResourceCache; 1.148 +}; 1.149 + 1.150 +/////////////////////////////////////////////////////////////////////////////// 1.151 + 1.152 +/** 1.153 + * Cache of GrResource objects. 1.154 + * 1.155 + * These have a corresponding GrResourceKey, built from 128bits identifying the 1.156 + * resource. Multiple resources can map to same GrResourceKey. 1.157 + * 1.158 + * The cache stores the entries in a double-linked list, which is its LRU. 1.159 + * When an entry is "locked" (i.e. given to the caller), it is moved to the 1.160 + * head of the list. If/when we must purge some of the entries, we walk the 1.161 + * list backwards from the tail, since those are the least recently used. 1.162 + * 1.163 + * For fast searches, we maintain a hash map based on the GrResourceKey. 1.164 + * 1.165 + * It is a goal to make the GrResourceCache the central repository and bookkeeper 1.166 + * of all resources. It should replace the linked list of GrResources that 1.167 + * GrGpu uses to call abandon/release. 1.168 + */ 1.169 +class GrResourceCache { 1.170 +public: 1.171 + GrResourceCache(int maxCount, size_t maxBytes); 1.172 + ~GrResourceCache(); 1.173 + 1.174 + /** 1.175 + * Return the current resource cache limits. 1.176 + * 1.177 + * @param maxResource If non-null, returns maximum number of resources 1.178 + * that can be held in the cache. 1.179 + * @param maxBytes If non-null, returns maximum number of bytes of 1.180 + * gpu memory that can be held in the cache. 1.181 + */ 1.182 + void getLimits(int* maxResources, size_t* maxBytes) const; 1.183 + 1.184 + /** 1.185 + * Specify the resource cache limits. If the current cache exceeds either 1.186 + * of these, it will be purged (LRU) to keep the cache within these limits. 1.187 + * 1.188 + * @param maxResources The maximum number of resources that can be held in 1.189 + * the cache. 1.190 + * @param maxBytes The maximum number of bytes of resource memory that 1.191 + * can be held in the cache. 1.192 + */ 1.193 + void setLimits(int maxResources, size_t maxResourceBytes); 1.194 + 1.195 + /** 1.196 + * The callback function used by the cache when it is still over budget 1.197 + * after a purge. The passed in 'data' is the same 'data' handed to 1.198 + * setOverbudgetCallback. The callback returns true if some resources 1.199 + * have been freed. 1.200 + */ 1.201 + typedef bool (*PFOverbudgetCB)(void* data); 1.202 + 1.203 + /** 1.204 + * Set the callback the cache should use when it is still over budget 1.205 + * after a purge. The 'data' provided here will be passed back to the 1.206 + * callback. Note that the cache will attempt to purge any resources newly 1.207 + * freed by the callback. 1.208 + */ 1.209 + void setOverbudgetCallback(PFOverbudgetCB overbudgetCB, void* data) { 1.210 + fOverbudgetCB = overbudgetCB; 1.211 + fOverbudgetData = data; 1.212 + } 1.213 + 1.214 + /** 1.215 + * Returns the number of bytes consumed by cached resources. 1.216 + */ 1.217 + size_t getCachedResourceBytes() const { return fEntryBytes; } 1.218 + 1.219 + // For a found or added resource to be completely exclusive to the caller 1.220 + // both the kNoOtherOwners and kHide flags need to be specified 1.221 + enum OwnershipFlags { 1.222 + kNoOtherOwners_OwnershipFlag = 0x1, // found/added resource has no other owners 1.223 + kHide_OwnershipFlag = 0x2 // found/added resource is hidden from future 'find's 1.224 + }; 1.225 + 1.226 + /** 1.227 + * Search for an entry with the same Key. If found, return it. 1.228 + * If not found, return null. 1.229 + * If ownershipFlags includes kNoOtherOwners and a resource is returned 1.230 + * then that resource has no other refs to it. 1.231 + * If ownershipFlags includes kHide and a resource is returned then that 1.232 + * resource will not be returned from future 'find' calls until it is 1.233 + * 'freed' (and recycled) or makeNonExclusive is called. 1.234 + * For a resource to be completely exclusive to a caller both kNoOtherOwners 1.235 + * and kHide must be specified. 1.236 + */ 1.237 + GrResource* find(const GrResourceKey& key, 1.238 + uint32_t ownershipFlags = 0); 1.239 + 1.240 + /** 1.241 + * Add the new resource to the cache (by creating a new cache entry based 1.242 + * on the provided key and resource). 1.243 + * 1.244 + * Ownership of the resource is transferred to the resource cache, 1.245 + * which will unref() it when it is purged or deleted. 1.246 + * 1.247 + * If ownershipFlags includes kHide, subsequent calls to 'find' will not 1.248 + * return 'resource' until it is 'freed' (and recycled) or makeNonExclusive 1.249 + * is called. 1.250 + */ 1.251 + void addResource(const GrResourceKey& key, 1.252 + GrResource* resource, 1.253 + uint32_t ownershipFlags = 0); 1.254 + 1.255 + /** 1.256 + * Determines if the cache contains an entry matching a key. If a matching 1.257 + * entry exists but was detached then it will not be found. 1.258 + */ 1.259 + bool hasKey(const GrResourceKey& key) const { return NULL != fCache.find(key); } 1.260 + 1.261 + /** 1.262 + * Hide 'entry' so that future searches will not find it. Such 1.263 + * hidden entries will not be purged. The entry still counts against 1.264 + * the cache's budget and should be made non-exclusive when exclusive access 1.265 + * is no longer needed. 1.266 + */ 1.267 + void makeExclusive(GrResourceEntry* entry); 1.268 + 1.269 + /** 1.270 + * Restore 'entry' so that it can be found by future searches. 'entry' 1.271 + * will also be purgeable (provided its lock count is now 0.) 1.272 + */ 1.273 + void makeNonExclusive(GrResourceEntry* entry); 1.274 + 1.275 + /** 1.276 + * Remove a resource from the cache and delete it! 1.277 + */ 1.278 + void deleteResource(GrResourceEntry* entry); 1.279 + 1.280 + /** 1.281 + * Removes every resource in the cache that isn't locked. 1.282 + */ 1.283 + void purgeAllUnlocked(); 1.284 + 1.285 + /** 1.286 + * Allow cache to purge unused resources to obey resource limitations 1.287 + * Note: this entry point will be hidden (again) once totally ref-driven 1.288 + * cache maintenance is implemented. Note that the overbudget callback 1.289 + * will be called if the initial purge doesn't get the cache under 1.290 + * its budget. 1.291 + * 1.292 + * extraCount and extraBytes are added to the current resource allocation 1.293 + * to make sure enough room is available for future additions (e.g, 1.294 + * 10MB across 10 textures is about to be added). 1.295 + */ 1.296 + void purgeAsNeeded(int extraCount = 0, size_t extraBytes = 0); 1.297 + 1.298 +#ifdef SK_DEBUG 1.299 + void validate() const; 1.300 +#else 1.301 + void validate() const {} 1.302 +#endif 1.303 + 1.304 +#if GR_CACHE_STATS 1.305 + void printStats(); 1.306 +#endif 1.307 + 1.308 +private: 1.309 + enum BudgetBehaviors { 1.310 + kAccountFor_BudgetBehavior, 1.311 + kIgnore_BudgetBehavior 1.312 + }; 1.313 + 1.314 + void internalDetach(GrResourceEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior); 1.315 + void attachToHead(GrResourceEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior); 1.316 + 1.317 + void removeInvalidResource(GrResourceEntry* entry); 1.318 + 1.319 + GrTMultiMap<GrResourceEntry, 1.320 + GrResourceKey, 1.321 + GrResourceEntry::GetKey, 1.322 + GrResourceEntry::Hash, 1.323 + GrResourceEntry::Equal> fCache; 1.324 + 1.325 + // We're an internal doubly linked list 1.326 + typedef SkTInternalLList<GrResourceEntry> EntryList; 1.327 + EntryList fList; 1.328 + 1.329 +#ifdef SK_DEBUG 1.330 + // These objects cannot be returned by a search 1.331 + EntryList fExclusiveList; 1.332 +#endif 1.333 + 1.334 + // our budget, used in purgeAsNeeded() 1.335 + int fMaxCount; 1.336 + size_t fMaxBytes; 1.337 + 1.338 + // our current stats, related to our budget 1.339 +#if GR_CACHE_STATS 1.340 + int fHighWaterEntryCount; 1.341 + size_t fHighWaterEntryBytes; 1.342 + int fHighWaterClientDetachedCount; 1.343 + size_t fHighWaterClientDetachedBytes; 1.344 +#endif 1.345 + 1.346 + int fEntryCount; 1.347 + size_t fEntryBytes; 1.348 + int fClientDetachedCount; 1.349 + size_t fClientDetachedBytes; 1.350 + 1.351 + // prevents recursive purging 1.352 + bool fPurging; 1.353 + 1.354 + PFOverbudgetCB fOverbudgetCB; 1.355 + void* fOverbudgetData; 1.356 + 1.357 + void internalPurge(int extraCount, size_t extraBytes); 1.358 + 1.359 + // Listen for messages that a resource has been invalidated and purge cached junk proactively. 1.360 + SkMessageBus<GrResourceInvalidatedMessage>::Inbox fInvalidationInbox; 1.361 + void purgeInvalidated(); 1.362 + 1.363 +#ifdef SK_DEBUG 1.364 + static size_t countBytes(const SkTInternalLList<GrResourceEntry>& list); 1.365 +#endif 1.366 +}; 1.367 + 1.368 +/////////////////////////////////////////////////////////////////////////////// 1.369 + 1.370 +#ifdef SK_DEBUG 1.371 + class GrAutoResourceCacheValidate { 1.372 + public: 1.373 + GrAutoResourceCacheValidate(GrResourceCache* cache) : fCache(cache) { 1.374 + cache->validate(); 1.375 + } 1.376 + ~GrAutoResourceCacheValidate() { 1.377 + fCache->validate(); 1.378 + } 1.379 + private: 1.380 + GrResourceCache* fCache; 1.381 + }; 1.382 +#else 1.383 + class GrAutoResourceCacheValidate { 1.384 + public: 1.385 + GrAutoResourceCacheValidate(GrResourceCache*) {} 1.386 + }; 1.387 +#endif 1.388 + 1.389 +#endif