OLD | NEW |
1 | 1 |
2 /* | 2 /* |
3 * Copyright 2014 Google Inc. | 3 * Copyright 2014 Google Inc. |
4 * | 4 * |
5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
7 */ | 7 */ |
8 | 8 |
9 | 9 |
10 #include "GrResourceCache.h" | 10 #include "GrResourceCache.h" |
11 #include "GrGpuResourceCacheAccess.h" | 11 #include "GrGpuResourceCacheAccess.h" |
12 #include "SkChecksum.h" | 12 #include "SkChecksum.h" |
13 #include "SkGr.h" | 13 #include "SkGr.h" |
14 #include "SkMessageBus.h" | 14 #include "SkMessageBus.h" |
| 15 #include "SkTSort.h" |
15 | 16 |
16 DECLARE_SKMESSAGEBUS_MESSAGE(GrUniqueKeyInvalidatedMessage); | 17 DECLARE_SKMESSAGEBUS_MESSAGE(GrUniqueKeyInvalidatedMessage); |
17 | 18 |
18 ////////////////////////////////////////////////////////////////////////////// | 19 ////////////////////////////////////////////////////////////////////////////// |
19 | 20 |
20 GrScratchKey::ResourceType GrScratchKey::GenerateResourceType() { | 21 GrScratchKey::ResourceType GrScratchKey::GenerateResourceType() { |
21 static int32_t gType = INHERITED::kInvalidDomain + 1; | 22 static int32_t gType = INHERITED::kInvalidDomain + 1; |
22 | 23 |
23 int32_t type = sk_atomic_inc(&gType); | 24 int32_t type = sk_atomic_inc(&gType); |
24 if (type > SK_MaxU16) { | 25 if (type > SK_MaxU16) { |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
83 fMaxCount = count; | 84 fMaxCount = count; |
84 fMaxBytes = bytes; | 85 fMaxBytes = bytes; |
85 this->purgeAsNeeded(); | 86 this->purgeAsNeeded(); |
86 } | 87 } |
87 | 88 |
88 void GrResourceCache::insertResource(GrGpuResource* resource) { | 89 void GrResourceCache::insertResource(GrGpuResource* resource) { |
89 SkASSERT(resource); | 90 SkASSERT(resource); |
90 SkASSERT(!this->isInCache(resource)); | 91 SkASSERT(!this->isInCache(resource)); |
91 SkASSERT(!resource->wasDestroyed()); | 92 SkASSERT(!resource->wasDestroyed()); |
92 SkASSERT(!resource->isPurgeable()); | 93 SkASSERT(!resource->isPurgeable()); |
| 94 |
| 95 // We must set the timestamp before adding to the array in case the timestam
p wraps and we wind |
| 96 // up iterating over all the resources that already have timestamps. |
| 97 resource->cacheAccess().setTimestamp(this->getNextTimestamp()); |
| 98 |
93 this->addToNonpurgeableArray(resource); | 99 this->addToNonpurgeableArray(resource); |
94 | 100 |
95 size_t size = resource->gpuMemorySize(); | 101 size_t size = resource->gpuMemorySize(); |
96 SkDEBUGCODE(++fCount;) | 102 SkDEBUGCODE(++fCount;) |
97 fBytes += size; | 103 fBytes += size; |
98 #if GR_CACHE_STATS | 104 #if GR_CACHE_STATS |
99 fHighWaterCount = SkTMax(this->getResourceCount(), fHighWaterCount); | 105 fHighWaterCount = SkTMax(this->getResourceCount(), fHighWaterCount); |
100 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes); | 106 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes); |
101 #endif | 107 #endif |
102 if (resource->resourcePriv().isBudgeted()) { | 108 if (resource->resourcePriv().isBudgeted()) { |
103 ++fBudgetedCount; | 109 ++fBudgetedCount; |
104 fBudgetedBytes += size; | 110 fBudgetedBytes += size; |
105 #if GR_CACHE_STATS | 111 #if GR_CACHE_STATS |
106 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount
); | 112 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount
); |
107 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes
); | 113 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes
); |
108 #endif | 114 #endif |
109 } | 115 } |
110 if (resource->resourcePriv().getScratchKey().isValid()) { | 116 if (resource->resourcePriv().getScratchKey().isValid()) { |
111 SkASSERT(!resource->cacheAccess().isWrapped()); | 117 SkASSERT(!resource->cacheAccess().isWrapped()); |
112 fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource); | 118 fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource); |
113 } | 119 } |
114 | 120 |
115 resource->cacheAccess().setTimestamp(fTimestamp++); | |
116 | |
117 this->purgeAsNeeded(); | 121 this->purgeAsNeeded(); |
118 } | 122 } |
119 | 123 |
120 void GrResourceCache::removeResource(GrGpuResource* resource) { | 124 void GrResourceCache::removeResource(GrGpuResource* resource) { |
121 this->validate(); | 125 this->validate(); |
122 SkASSERT(this->isInCache(resource)); | 126 SkASSERT(this->isInCache(resource)); |
123 | 127 |
124 if (resource->isPurgeable()) { | 128 if (resource->isPurgeable()) { |
125 fPurgeableQueue.remove(resource); | 129 fPurgeableQueue.remove(resource); |
126 } else { | 130 } else { |
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
279 } else { | 283 } else { |
280 resource->cacheAccess().removeUniqueKey(); | 284 resource->cacheAccess().removeUniqueKey(); |
281 } | 285 } |
282 | 286 |
283 this->validate(); | 287 this->validate(); |
284 } | 288 } |
285 | 289 |
286 void GrResourceCache::refAndMakeResourceMRU(GrGpuResource* resource) { | 290 void GrResourceCache::refAndMakeResourceMRU(GrGpuResource* resource) { |
287 SkASSERT(resource); | 291 SkASSERT(resource); |
288 SkASSERT(this->isInCache(resource)); | 292 SkASSERT(this->isInCache(resource)); |
| 293 |
289 if (resource->isPurgeable()) { | 294 if (resource->isPurgeable()) { |
290 // It's about to become unpurgeable. | 295 // It's about to become unpurgeable. |
291 fPurgeableQueue.remove(resource); | 296 fPurgeableQueue.remove(resource); |
292 this->addToNonpurgeableArray(resource); | 297 this->addToNonpurgeableArray(resource); |
293 } | 298 } |
294 resource->ref(); | 299 resource->ref(); |
295 resource->cacheAccess().setTimestamp(fTimestamp++); | 300 |
| 301 resource->cacheAccess().setTimestamp(this->getNextTimestamp()); |
296 this->validate(); | 302 this->validate(); |
297 } | 303 } |
298 | 304 |
299 void GrResourceCache::notifyPurgeable(GrGpuResource* resource) { | 305 void GrResourceCache::notifyPurgeable(GrGpuResource* resource) { |
300 SkASSERT(resource); | 306 SkASSERT(resource); |
301 SkASSERT(this->isInCache(resource)); | 307 SkASSERT(this->isInCache(resource)); |
302 SkASSERT(resource->isPurgeable()); | 308 SkASSERT(resource->isPurgeable()); |
303 | 309 |
304 this->removeFromNonpurgeableArray(resource); | 310 this->removeFromNonpurgeableArray(resource); |
305 fPurgeableQueue.insert(resource); | 311 fPurgeableQueue.insert(resource); |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
434 // Fill the whole we will create in the array with the tail object, adjust i
ts index, and | 440 // Fill the whole we will create in the array with the tail object, adjust i
ts index, and |
435 // then pop the array | 441 // then pop the array |
436 GrGpuResource* tail = *(fNonpurgeableResources.end() - 1); | 442 GrGpuResource* tail = *(fNonpurgeableResources.end() - 1); |
437 SkASSERT(fNonpurgeableResources[*index] == resource); | 443 SkASSERT(fNonpurgeableResources[*index] == resource); |
438 fNonpurgeableResources[*index] = tail; | 444 fNonpurgeableResources[*index] = tail; |
439 *tail->cacheAccess().accessCacheIndex() = *index; | 445 *tail->cacheAccess().accessCacheIndex() = *index; |
440 fNonpurgeableResources.pop(); | 446 fNonpurgeableResources.pop(); |
441 SkDEBUGCODE(*index = -1); | 447 SkDEBUGCODE(*index = -1); |
442 } | 448 } |
443 | 449 |
| 450 uint32_t GrResourceCache::getNextTimestamp() { |
| 451 // If we wrap then all the existing resources will appear older than any res
ources that get |
| 452 // a timestamp after the wrap. |
| 453 if (0 == fTimestamp) { |
| 454 int count = this->getResourceCount(); |
| 455 if (count) { |
| 456 // Reset all the timestamps. We sort the resources by timestamp and
then assign |
| 457 // sequential timestamps beginning with 0. This is O(n*lg(n)) but it
should be extremely |
| 458 // rare. |
| 459 SkTDArray<GrGpuResource*> sortedPurgeableResources; |
| 460 sortedPurgeableResources.setReserve(fPurgeableQueue.count()); |
| 461 |
| 462 while (fPurgeableQueue.count()) { |
| 463 *sortedPurgeableResources.append() = fPurgeableQueue.peek(); |
| 464 fPurgeableQueue.pop(); |
| 465 } |
| 466 |
| 467 struct Less { |
| 468 bool operator()(GrGpuResource* a, GrGpuResource* b) { |
| 469 return CompareTimestamp(a,b); |
| 470 } |
| 471 }; |
| 472 Less less; |
| 473 SkTQSort(fNonpurgeableResources.begin(), fNonpurgeableResources.end(
) - 1, less); |
| 474 |
| 475 // Pick resources out of the purgeable and non-purgeable arrays base
d on lowest |
| 476 // timestamp and assign new timestamps. |
| 477 int currP = 0; |
| 478 int currNP = 0; |
| 479 while (currP < sortedPurgeableResources.count() && |
| 480 currNP < fNonpurgeableResources.count()) { |
| 481 uint32_t tsP = sortedPurgeableResources[currP]->cacheAccess().ti
mestamp(); |
| 482 uint32_t tsNP = fNonpurgeableResources[currNP]->cacheAccess().ti
mestamp(); |
| 483 SkASSERT(tsP != tsNP); |
| 484 if (tsP < tsNP) { |
| 485 sortedPurgeableResources[currP++]->cacheAccess().setTimestam
p(fTimestamp++); |
| 486 } else { |
| 487 // Correct the index in the nonpurgeable array stored on the
resource post-sort. |
| 488 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIn
dex() = currNP; |
| 489 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp
(fTimestamp++); |
| 490 } |
| 491 } |
| 492 |
| 493 // The above loop ended when we hit the end of one array. Finish the
other one. |
| 494 while (currP < sortedPurgeableResources.count()) { |
| 495 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fT
imestamp++); |
| 496 } |
| 497 while (currNP < fNonpurgeableResources.count()) { |
| 498 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex(
) = currNP; |
| 499 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTi
mestamp++); |
| 500 } |
| 501 |
| 502 // Rebuild the queue. |
| 503 for (int i = 0; i < sortedPurgeableResources.count(); ++i) { |
| 504 fPurgeableQueue.insert(sortedPurgeableResources[i]); |
| 505 } |
| 506 |
| 507 this->validate(); |
| 508 SkASSERT(count == this->getResourceCount()); |
| 509 |
| 510 // count should be the next timestamp we return. |
| 511 SkASSERT(fTimestamp == SkToU32(count)); |
| 512 } |
| 513 } |
| 514 return fTimestamp++; |
| 515 } |
| 516 |
444 #ifdef SK_DEBUG | 517 #ifdef SK_DEBUG |
445 void GrResourceCache::validate() const { | 518 void GrResourceCache::validate() const { |
446 // Reduce the frequency of validations for large resource counts. | 519 // Reduce the frequency of validations for large resource counts. |
447 static SkRandom gRandom; | 520 static SkRandom gRandom; |
448 int mask = (SkNextPow2(fCount + 1) >> 5) - 1; | 521 int mask = (SkNextPow2(fCount + 1) >> 5) - 1; |
449 if (~mask && (gRandom.nextU() & mask)) { | 522 if (~mask && (gRandom.nextU() & mask)) { |
450 return; | 523 return; |
451 } | 524 } |
452 | 525 |
453 struct Stats { | 526 struct Stats { |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
548 return true; | 621 return true; |
549 } | 622 } |
550 if (index < fNonpurgeableResources.count() && fNonpurgeableResources[index]
== resource) { | 623 if (index < fNonpurgeableResources.count() && fNonpurgeableResources[index]
== resource) { |
551 return true; | 624 return true; |
552 } | 625 } |
553 SkDEBUGFAIL("Resource index should be -1 or the resource should be in the ca
che."); | 626 SkDEBUGFAIL("Resource index should be -1 or the resource should be in the ca
che."); |
554 return false; | 627 return false; |
555 } | 628 } |
556 | 629 |
557 #endif | 630 #endif |
OLD | NEW |