| OLD | NEW |
| (Empty) |
| 1 | |
| 2 /* | |
| 3 * Copyright 2010 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 #include "GrResourceCache.h" | |
| 10 #include "GrGpuResource.h" | |
| 11 #include "GrGpuResourceCacheAccess.h" | |
| 12 #include "GrTexturePriv.h" | |
| 13 | |
| 14 DECLARE_SKMESSAGEBUS_MESSAGE(GrResourceInvalidatedMessage); | |
| 15 | |
| 16 /////////////////////////////////////////////////////////////////////////////// | |
| 17 | |
| 18 void GrGpuResource::didChangeGpuMemorySize() const { | |
| 19 fGpuMemorySize = kInvalidGpuMemorySize; | |
| 20 if (this->cacheAccess().isInCache()) { | |
| 21 fCacheEntry->didChangeResourceSize(); | |
| 22 } | |
| 23 } | |
| 24 | |
| 25 /////////////////////////////////////////////////////////////////////////////// | |
| 26 | |
| 27 GrResourceKey::ResourceType GrResourceKey::GenerateResourceType() { | |
| 28 static int32_t gNextType = 0; | |
| 29 | |
| 30 int32_t type = sk_atomic_inc(&gNextType); | |
| 31 if (type >= (1 << 8 * sizeof(ResourceType))) { | |
| 32 SkFAIL("Too many Resource Types"); | |
| 33 } | |
| 34 | |
| 35 return static_cast<ResourceType>(type); | |
| 36 } | |
| 37 | |
| 38 /////////////////////////////////////////////////////////////////////////////// | |
| 39 | |
| 40 GrResourceCacheEntry::GrResourceCacheEntry(GrResourceCache* resourceCache, GrGpu
Resource* resource) | |
| 41 : fResourceCache(resourceCache), | |
| 42 fResource(resource), | |
| 43 fCachedSize(resource->gpuMemorySize()) { | |
| 44 // we assume ownership of the resource, and will unref it when we die | |
| 45 SkASSERT(resource); | |
| 46 resource->ref(); | |
| 47 } | |
| 48 | |
| 49 GrResourceCacheEntry::~GrResourceCacheEntry() { | |
| 50 // We're relying on having the cache entry to remove this from GrResourceCac
he2's content hash. | |
| 51 // fResource->setCacheEntry(NULL); | |
| 52 fResource->unref(); | |
| 53 } | |
| 54 | |
| 55 #ifdef SK_DEBUG | |
| 56 void GrResourceCacheEntry::validate() const { | |
| 57 SkASSERT(fResourceCache); | |
| 58 SkASSERT(fResource); | |
| 59 SkASSERT(fResource->cacheAccess().getCacheEntry() == this); | |
| 60 SkASSERT(fResource->gpuMemorySize() == fCachedSize); | |
| 61 fResource->validate(); | |
| 62 } | |
| 63 #endif | |
| 64 | |
| 65 void GrResourceCacheEntry::didChangeResourceSize() { | |
| 66 size_t oldSize = fCachedSize; | |
| 67 fCachedSize = fResource->gpuMemorySize(); | |
| 68 if (fCachedSize > oldSize) { | |
| 69 fResourceCache->didIncreaseResourceSize(this, fCachedSize - oldSize); | |
| 70 } else if (fCachedSize < oldSize) { | |
| 71 fResourceCache->didDecreaseResourceSize(this, oldSize - fCachedSize); | |
| 72 } | |
| 73 } | |
| 74 | |
| 75 /////////////////////////////////////////////////////////////////////////////// | |
| 76 | |
| 77 GrResourceCache::GrResourceCache(const GrDrawTargetCaps* caps, int maxCount, siz
e_t maxBytes) | |
| 78 : fMaxCount(maxCount) | |
| 79 , fMaxBytes(maxBytes) | |
| 80 , fCaps(SkRef(caps)) { | |
| 81 #if GR_CACHE_STATS | |
| 82 fHighWaterEntryCount = 0; | |
| 83 fHighWaterEntryBytes = 0; | |
| 84 #endif | |
| 85 | |
| 86 fEntryCount = 0; | |
| 87 fEntryBytes = 0; | |
| 88 | |
| 89 fPurging = false; | |
| 90 | |
| 91 fOverbudgetCB = NULL; | |
| 92 fOverbudgetData = NULL; | |
| 93 } | |
| 94 | |
| 95 GrResourceCache::~GrResourceCache() { | |
| 96 GrAutoResourceCacheValidate atcv(this); | |
| 97 | |
| 98 EntryList::Iter iter; | |
| 99 | |
| 100 // Unlike the removeAll, here we really remove everything, including locked
resources. | |
| 101 while (GrResourceCacheEntry* entry = fList.head()) { | |
| 102 GrAutoResourceCacheValidate atcv(this); | |
| 103 | |
| 104 // remove from our llist | |
| 105 this->internalDetach(entry); | |
| 106 | |
| 107 delete entry; | |
| 108 } | |
| 109 } | |
| 110 | |
| 111 void GrResourceCache::getLimits(int* maxResources, size_t* maxResourceBytes) con
st{ | |
| 112 if (maxResources) { | |
| 113 *maxResources = fMaxCount; | |
| 114 } | |
| 115 if (maxResourceBytes) { | |
| 116 *maxResourceBytes = fMaxBytes; | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 void GrResourceCache::setLimits(int maxResources, size_t maxResourceBytes) { | |
| 121 bool smaller = (maxResources < fMaxCount) || (maxResourceBytes < fMaxBytes); | |
| 122 | |
| 123 fMaxCount = maxResources; | |
| 124 fMaxBytes = maxResourceBytes; | |
| 125 | |
| 126 if (smaller) { | |
| 127 this->purgeAsNeeded(); | |
| 128 } | |
| 129 } | |
| 130 | |
| 131 void GrResourceCache::internalDetach(GrResourceCacheEntry* entry) { | |
| 132 fList.remove(entry); | |
| 133 fEntryCount -= 1; | |
| 134 fEntryBytes -= entry->fCachedSize; | |
| 135 } | |
| 136 | |
| 137 void GrResourceCache::attachToHead(GrResourceCacheEntry* entry) { | |
| 138 fList.addToHead(entry); | |
| 139 | |
| 140 fEntryCount += 1; | |
| 141 fEntryBytes += entry->fCachedSize; | |
| 142 | |
| 143 #if GR_CACHE_STATS | |
| 144 if (fHighWaterEntryCount < fEntryCount) { | |
| 145 fHighWaterEntryCount = fEntryCount; | |
| 146 } | |
| 147 if (fHighWaterEntryBytes < fEntryBytes) { | |
| 148 fHighWaterEntryBytes = fEntryBytes; | |
| 149 } | |
| 150 #endif | |
| 151 } | |
| 152 | |
| 153 | |
| 154 void GrResourceCache::makeResourceMRU(GrGpuResource* resource) { | |
| 155 GrResourceCacheEntry* entry = resource->cacheAccess().getCacheEntry(); | |
| 156 if (entry) { | |
| 157 this->internalDetach(entry); | |
| 158 this->attachToHead(entry); | |
| 159 } | |
| 160 } | |
| 161 | |
| 162 void GrResourceCache::notifyPurgable(const GrGpuResource* resource) { | |
| 163 // Remove scratch textures from the cache the moment they become purgeable i
f | |
| 164 // scratch texture reuse is turned off. | |
| 165 SkASSERT(resource->cacheAccess().getCacheEntry()); | |
| 166 if (resource->cacheAccess().isScratch()) { | |
| 167 const GrResourceKey& key = resource->cacheAccess().getScratchKey(); | |
| 168 if (key.getResourceType() == GrTexturePriv::ResourceType() && | |
| 169 !fCaps->reuseScratchTextures() && | |
| 170 !(static_cast<const GrSurface*>(resource)->desc().fFlags & kRenderTa
rget_GrSurfaceFlag)) { | |
| 171 this->deleteResource(resource->cacheAccess().getCacheEntry()); | |
| 172 } | |
| 173 } | |
| 174 } | |
| 175 | |
| 176 bool GrResourceCache::addResource(const GrResourceKey& key, GrGpuResource* resou
rce) { | |
| 177 if (NULL != resource->cacheAccess().getCacheEntry()) { | |
| 178 return false; | |
| 179 } | |
| 180 | |
| 181 if (key.isScratch()) { | |
| 182 SkASSERT(resource->cacheAccess().isScratch()); | |
| 183 SkASSERT(key == resource->cacheAccess().getScratchKey()); | |
| 184 } else { | |
| 185 if (!resource->cacheAccess().setContentKey(key)) { | |
| 186 return false; | |
| 187 } | |
| 188 } | |
| 189 | |
| 190 // we don't expect to create new resources during a purge. In theory | |
| 191 // this could cause purgeAsNeeded() into an infinite loop (e.g. | |
| 192 // each resource destroyed creates and locks 2 resources and | |
| 193 // unlocks 1 thereby causing a new purge). | |
| 194 SkASSERT(!fPurging); | |
| 195 GrAutoResourceCacheValidate atcv(this); | |
| 196 | |
| 197 GrResourceCacheEntry* entry = SkNEW_ARGS(GrResourceCacheEntry, (this, resour
ce)); | |
| 198 resource->cacheAccess().setCacheEntry(entry); | |
| 199 | |
| 200 this->attachToHead(entry); | |
| 201 this->purgeAsNeeded(); | |
| 202 return true; | |
| 203 } | |
| 204 | |
| 205 void GrResourceCache::didIncreaseResourceSize(const GrResourceCacheEntry* entry,
size_t amountInc) { | |
| 206 fEntryBytes += amountInc; | |
| 207 this->purgeAsNeeded(); | |
| 208 } | |
| 209 | |
| 210 void GrResourceCache::didDecreaseResourceSize(const GrResourceCacheEntry* entry,
size_t amountDec) { | |
| 211 fEntryBytes -= amountDec; | |
| 212 #ifdef SK_DEBUG | |
| 213 this->validate(); | |
| 214 #endif | |
| 215 } | |
| 216 | |
| 217 /** | |
| 218 * Destroying a resource may potentially trigger the unlock of additional | |
| 219 * resources which in turn will trigger a nested purge. We block the nested | |
| 220 * purge using the fPurging variable. However, the initial purge will keep | |
| 221 * looping until either all resources in the cache are unlocked or we've met | |
| 222 * the budget. There is an assertion in createAndLock to check against a | |
| 223 * resource's destructor inserting new resources into the cache. If these | |
| 224 * new resources were unlocked before purgeAsNeeded completed it could | |
| 225 * potentially make purgeAsNeeded loop infinitely. | |
| 226 * | |
| 227 * extraCount and extraBytes are added to the current resource totals to account | |
| 228 * for incoming resources (e.g., GrContext is about to add 10MB split between | |
| 229 * 10 textures). | |
| 230 */ | |
| 231 void GrResourceCache::purgeAsNeeded(int extraCount, size_t extraBytes) { | |
| 232 if (fPurging) { | |
| 233 return; | |
| 234 } | |
| 235 | |
| 236 fPurging = true; | |
| 237 | |
| 238 this->internalPurge(extraCount, extraBytes); | |
| 239 if (((fEntryCount+extraCount) > fMaxCount || | |
| 240 (fEntryBytes+extraBytes) > fMaxBytes) && | |
| 241 fOverbudgetCB) { | |
| 242 // Despite the purge we're still over budget. See if Ganesh can | |
| 243 // release some resources and purge again. | |
| 244 if ((*fOverbudgetCB)(fOverbudgetData)) { | |
| 245 this->internalPurge(extraCount, extraBytes); | |
| 246 } | |
| 247 } | |
| 248 | |
| 249 fPurging = false; | |
| 250 } | |
| 251 | |
| 252 void GrResourceCache::purgeInvalidated() { | |
| 253 // TODO: Implement this in GrResourceCache2. | |
| 254 } | |
| 255 | |
| 256 void GrResourceCache::deleteResource(GrResourceCacheEntry* entry) { | |
| 257 SkASSERT(entry->fResource->isPurgable()); | |
| 258 // remove from our llist | |
| 259 this->internalDetach(entry); | |
| 260 delete entry; | |
| 261 } | |
| 262 | |
| 263 void GrResourceCache::internalPurge(int extraCount, size_t extraBytes) { | |
| 264 SkASSERT(fPurging); | |
| 265 | |
| 266 bool withinBudget = false; | |
| 267 bool changed = false; | |
| 268 | |
| 269 // The purging process is repeated several times since one pass | |
| 270 // may free up other resources | |
| 271 do { | |
| 272 EntryList::Iter iter; | |
| 273 | |
| 274 changed = false; | |
| 275 | |
| 276 // Note: the following code relies on the fact that the | |
| 277 // doubly linked list doesn't invalidate its data/pointers | |
| 278 // outside of the specific area where a deletion occurs (e.g., | |
| 279 // in internalDetach) | |
| 280 GrResourceCacheEntry* entry = iter.init(fList, EntryList::Iter::kTail_It
erStart); | |
| 281 | |
| 282 while (entry) { | |
| 283 GrAutoResourceCacheValidate atcv(this); | |
| 284 | |
| 285 if ((fEntryCount+extraCount) <= fMaxCount && | |
| 286 (fEntryBytes+extraBytes) <= fMaxBytes) { | |
| 287 withinBudget = true; | |
| 288 break; | |
| 289 } | |
| 290 | |
| 291 GrResourceCacheEntry* prev = iter.prev(); | |
| 292 if (entry->fResource->isPurgable()) { | |
| 293 changed = true; | |
| 294 this->deleteResource(entry); | |
| 295 } | |
| 296 entry = prev; | |
| 297 } | |
| 298 } while (!withinBudget && changed); | |
| 299 } | |
| 300 | |
| 301 void GrResourceCache::purgeAllUnlocked() { | |
| 302 GrAutoResourceCacheValidate atcv(this); | |
| 303 | |
| 304 // we can have one GrCacheable holding a lock on another | |
| 305 // so we don't want to just do a simple loop kicking each | |
| 306 // entry out. Instead change the budget and purge. | |
| 307 | |
| 308 size_t savedMaxBytes = fMaxBytes; | |
| 309 int savedMaxCount = fMaxCount; | |
| 310 fMaxBytes = (size_t) -1; | |
| 311 fMaxCount = 0; | |
| 312 this->purgeAsNeeded(); | |
| 313 | |
| 314 fMaxBytes = savedMaxBytes; | |
| 315 fMaxCount = savedMaxCount; | |
| 316 } | |
| 317 | |
| 318 /////////////////////////////////////////////////////////////////////////////// | |
| 319 | |
| 320 #ifdef SK_DEBUG | |
| 321 size_t GrResourceCache::countBytes(const EntryList& list) { | |
| 322 size_t bytes = 0; | |
| 323 | |
| 324 EntryList::Iter iter; | |
| 325 | |
| 326 const GrResourceCacheEntry* entry = iter.init(const_cast<EntryList&>(list), | |
| 327 EntryList::Iter::kTail_IterSta
rt); | |
| 328 | |
| 329 for ( ; entry; entry = iter.prev()) { | |
| 330 bytes += entry->resource()->gpuMemorySize(); | |
| 331 } | |
| 332 return bytes; | |
| 333 } | |
| 334 | |
| 335 static bool both_zero_or_nonzero(int count, size_t bytes) { | |
| 336 return (count == 0 && bytes == 0) || (count > 0 && bytes > 0); | |
| 337 } | |
| 338 | |
| 339 void GrResourceCache::validate() const { | |
| 340 fList.validate(); | |
| 341 SkASSERT(both_zero_or_nonzero(fEntryCount, fEntryBytes)); | |
| 342 | |
| 343 EntryList::Iter iter; | |
| 344 | |
| 345 // check that the shareable entries are okay | |
| 346 const GrResourceCacheEntry* entry = iter.init(const_cast<EntryList&>(fList), | |
| 347 EntryList::Iter::kHead_IterSta
rt); | |
| 348 | |
| 349 int count = 0; | |
| 350 for ( ; entry; entry = iter.next()) { | |
| 351 entry->validate(); | |
| 352 count += 1; | |
| 353 } | |
| 354 SkASSERT(count == fEntryCount); | |
| 355 | |
| 356 size_t bytes = this->countBytes(fList); | |
| 357 SkASSERT(bytes == fEntryBytes); | |
| 358 SkASSERT(fList.countEntries() == fEntryCount); | |
| 359 } | |
| 360 #endif // SK_DEBUG | |
| 361 | |
| 362 #if GR_CACHE_STATS | |
| 363 | |
| 364 void GrResourceCache::printStats() { | |
| 365 int locked = 0; | |
| 366 int scratch = 0; | |
| 367 | |
| 368 EntryList::Iter iter; | |
| 369 | |
| 370 GrResourceCacheEntry* entry = iter.init(fList, EntryList::Iter::kTail_IterSt
art); | |
| 371 | |
| 372 for ( ; entry; entry = iter.prev()) { | |
| 373 if (!entry->fResource->isPurgable()) { | |
| 374 ++locked; | |
| 375 } | |
| 376 if (entry->fResource->cacheAccess().isScratch()) { | |
| 377 ++scratch; | |
| 378 } | |
| 379 } | |
| 380 | |
| 381 float countUtilization = (100.f * fEntryCount) / fMaxCount; | |
| 382 float byteUtilization = (100.f * fEntryBytes) / fMaxBytes; | |
| 383 | |
| 384 SkDebugf("Budget: %d items %d bytes\n", fMaxCount, fMaxBytes); | |
| 385 SkDebugf("\t\tEntry Count: current %d (%d locked, %d scratch %.2g%% full), h
igh %d\n", | |
| 386 fEntryCount, locked, scratch, countUtilization, fHighWaterEntryC
ount); | |
| 387 SkDebugf("\t\tEntry Bytes: current %d (%.2g%% full) high %d\n", | |
| 388 fEntryBytes, byteUtilization, fHighWaterEntryBytes); | |
| 389 } | |
| 390 | |
| 391 #endif | |
| 392 | |
| 393 /////////////////////////////////////////////////////////////////////////////// | |
| OLD | NEW |