OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2014 Google Inc. | 2 * Copyright 2014 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 | 8 |
9 #include "GrResourceCache.h" | 9 #include "GrResourceCache.h" |
10 | 10 |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
67 #if GR_CACHE_STATS | 67 #if GR_CACHE_STATS |
68 , fHighWaterCount(0) | 68 , fHighWaterCount(0) |
69 , fHighWaterBytes(0) | 69 , fHighWaterBytes(0) |
70 , fBudgetedHighWaterCount(0) | 70 , fBudgetedHighWaterCount(0) |
71 , fBudgetedHighWaterBytes(0) | 71 , fBudgetedHighWaterBytes(0) |
72 #endif | 72 #endif |
73 , fBytes(0) | 73 , fBytes(0) |
74 , fBudgetedCount(0) | 74 , fBudgetedCount(0) |
75 , fBudgetedBytes(0) | 75 , fBudgetedBytes(0) |
76 , fRequestFlush(false) | 76 , fRequestFlush(false) |
77 , fFlushTimestamps(nullptr) | 77 , fExternalFlushCnt(0) |
78 , fLastFlushTimestampIndex(0) | |
79 , fPreferVRAMUseOverFlushes(caps->preferVRAMUseOverFlushes()) { | 78 , fPreferVRAMUseOverFlushes(caps->preferVRAMUseOverFlushes()) { |
80 SkDEBUGCODE(fCount = 0;) | 79 SkDEBUGCODE(fCount = 0;) |
81 SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr;) | 80 SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr;) |
82 this->resetFlushTimestamps(); | |
83 } | 81 } |
84 | 82 |
85 GrResourceCache::~GrResourceCache() { | 83 GrResourceCache::~GrResourceCache() { |
86 this->releaseAll(); | 84 this->releaseAll(); |
87 delete[] fFlushTimestamps; | |
88 } | 85 } |
89 | 86 |
90 void GrResourceCache::setLimits(int count, size_t bytes, int maxUnusedFlushes) { | 87 void GrResourceCache::setLimits(int count, size_t bytes, int maxUnusedFlushes) { |
91 fMaxCount = count; | 88 fMaxCount = count; |
92 fMaxBytes = bytes; | 89 fMaxBytes = bytes; |
93 fMaxUnusedFlushes = maxUnusedFlushes; | 90 fMaxUnusedFlushes = maxUnusedFlushes; |
94 this->resetFlushTimestamps(); | |
95 this->purgeAsNeeded(); | 91 this->purgeAsNeeded(); |
96 } | 92 } |
97 | 93 |
98 void GrResourceCache::resetFlushTimestamps() { | |
99 delete[] fFlushTimestamps; | |
100 | |
101 // We assume this number is a power of two when wrapping indices into the ti
mestamp array. | |
102 fMaxUnusedFlushes = SkNextPow2(fMaxUnusedFlushes); | |
103 | |
104 // Since our implementation is to store the timestamps of the last fMaxUnuse
dFlushes flush calls | |
105 // we just turn the feature off if that array would be large. | |
106 static const int kMaxSupportedTimestampHistory = 128; | |
107 | |
108 if (fMaxUnusedFlushes > kMaxSupportedTimestampHistory) { | |
109 fFlushTimestamps = nullptr; | |
110 return; | |
111 } | |
112 | |
113 fFlushTimestamps = new uint32_t[fMaxUnusedFlushes]; | |
114 fLastFlushTimestampIndex = 0; | |
115 // Set all the historical flush timestamps to initially be at the beginning
of time (timestamp | |
116 // 0). | |
117 sk_bzero(fFlushTimestamps, fMaxUnusedFlushes * sizeof(uint32_t)); | |
118 } | |
119 | |
120 void GrResourceCache::insertResource(GrGpuResource* resource) { | 94 void GrResourceCache::insertResource(GrGpuResource* resource) { |
121 SkASSERT(resource); | 95 SkASSERT(resource); |
122 SkASSERT(!this->isInCache(resource)); | 96 SkASSERT(!this->isInCache(resource)); |
123 SkASSERT(!resource->wasDestroyed()); | 97 SkASSERT(!resource->wasDestroyed()); |
124 SkASSERT(!resource->isPurgeable()); | 98 SkASSERT(!resource->isPurgeable()); |
125 | 99 |
126 // We must set the timestamp before adding to the array in case the timestam
p wraps and we wind | 100 // We must set the timestamp before adding to the array in case the timestam
p wraps and we wind |
127 // up iterating over all the resources that already have timestamps. | 101 // up iterating over all the resources that already have timestamps. |
128 resource->cacheAccess().setTimestamp(this->getNextTimestamp()); | 102 resource->cacheAccess().setTimestamp(this->getNextTimestamp()); |
129 | 103 |
(...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
383 } | 357 } |
384 | 358 |
385 if (!SkToBool(ResourceAccess::kAllCntsReachedZero_RefNotificationFlag & flag
s)) { | 359 if (!SkToBool(ResourceAccess::kAllCntsReachedZero_RefNotificationFlag & flag
s)) { |
386 SkASSERT(!resource->isPurgeable()); | 360 SkASSERT(!resource->isPurgeable()); |
387 return; | 361 return; |
388 } | 362 } |
389 | 363 |
390 SkASSERT(resource->isPurgeable()); | 364 SkASSERT(resource->isPurgeable()); |
391 this->removeFromNonpurgeableArray(resource); | 365 this->removeFromNonpurgeableArray(resource); |
392 fPurgeableQueue.insert(resource); | 366 fPurgeableQueue.insert(resource); |
| 367 resource->cacheAccess().setFlushCntWhenResourceBecamePurgeable(fExternalFlus
hCnt); |
393 | 368 |
394 if (SkBudgeted::kNo == resource->resourcePriv().isBudgeted()) { | 369 if (SkBudgeted::kNo == resource->resourcePriv().isBudgeted()) { |
395 // Check whether this resource could still be used as a scratch resource
. | 370 // Check whether this resource could still be used as a scratch resource
. |
396 if (!resource->resourcePriv().refsWrappedObjects() && | 371 if (!resource->resourcePriv().refsWrappedObjects() && |
397 resource->resourcePriv().getScratchKey().isValid()) { | 372 resource->resourcePriv().getScratchKey().isValid()) { |
398 // We won't purge an existing resource to make room for this one. | 373 // We won't purge an existing resource to make room for this one. |
399 if (fBudgetedCount < fMaxCount && | 374 if (fBudgetedCount < fMaxCount && |
400 fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes) { | 375 fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes) { |
401 resource->resourcePriv().makeBudgeted(); | 376 resource->resourcePriv().makeBudgeted(); |
402 return; | 377 return; |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
467 this->validate(); | 442 this->validate(); |
468 } | 443 } |
469 | 444 |
470 void GrResourceCache::purgeAsNeeded() { | 445 void GrResourceCache::purgeAsNeeded() { |
471 SkTArray<GrUniqueKeyInvalidatedMessage> invalidKeyMsgs; | 446 SkTArray<GrUniqueKeyInvalidatedMessage> invalidKeyMsgs; |
472 fInvalidUniqueKeyInbox.poll(&invalidKeyMsgs); | 447 fInvalidUniqueKeyInbox.poll(&invalidKeyMsgs); |
473 if (invalidKeyMsgs.count()) { | 448 if (invalidKeyMsgs.count()) { |
474 this->processInvalidUniqueKeys(invalidKeyMsgs); | 449 this->processInvalidUniqueKeys(invalidKeyMsgs); |
475 } | 450 } |
476 | 451 |
477 if (fFlushTimestamps) { | 452 if (fMaxUnusedFlushes > 0) { |
478 // Assuming kNumFlushesToDeleteUnusedResource is a power of 2. | 453 // We want to know how many complete flushes have occurred without the r
esource being used. |
479 SkASSERT(SkIsPow2(fMaxUnusedFlushes)); | 454 // If the resource was tagged when fExternalFlushCnt was N then this mea
ns it became |
480 int oldestFlushIndex = (fLastFlushTimestampIndex + 1) & (fMaxUnusedFlush
es - 1); | 455 // purgeable during activity that became the N+1th flush. So when the fl
ush count is N+2 |
481 | 456 // it has sat in the purgeable queue for one entire flush. |
482 uint32_t oldestAllowedTimestamp = fFlushTimestamps[oldestFlushIndex]; | 457 uint32_t oldestAllowedFlushCnt = fExternalFlushCnt - fMaxUnusedFlushes -
1; |
483 while (fPurgeableQueue.count()) { | 458 // check for underflow |
484 uint32_t oldestResourceTimestamp = fPurgeableQueue.peek()->cacheAcce
ss().timestamp(); | 459 if (oldestAllowedFlushCnt < fExternalFlushCnt) { |
485 if (oldestAllowedTimestamp < oldestResourceTimestamp) { | 460 while (fPurgeableQueue.count()) { |
486 break; | 461 uint32_t flushWhenResourceBecamePurgeable = |
| 462 fPurgeableQueue.peek()->cacheAccess().flushCntWhenResour
ceBecamePurgeable(); |
| 463 if (oldestAllowedFlushCnt < flushWhenResourceBecamePurgeable) { |
| 464 // Resources were given both LRU timestamps and tagged with
a flush cnt when |
| 465 // they first became purgeable. The LRU timestamp won't chan
ge again until the |
| 466 // resource is made non-purgeable again. So, at this point a
ll the remaining |
| 467 // resources in the timestamp-sorted queue will have a flush
count >= to this |
| 468 // one. |
| 469 break; |
| 470 } |
| 471 GrGpuResource* resource = fPurgeableQueue.peek(); |
| 472 SkASSERT(resource->isPurgeable()); |
| 473 resource->cacheAccess().release(); |
487 } | 474 } |
488 GrGpuResource* resource = fPurgeableQueue.peek(); | |
489 SkASSERT(resource->isPurgeable()); | |
490 resource->cacheAccess().release(); | |
491 } | 475 } |
492 } | 476 } |
493 | 477 |
494 bool stillOverbudget = this->overBudget(); | 478 bool stillOverbudget = this->overBudget(); |
495 while (stillOverbudget && fPurgeableQueue.count()) { | 479 while (stillOverbudget && fPurgeableQueue.count()) { |
496 GrGpuResource* resource = fPurgeableQueue.peek(); | 480 GrGpuResource* resource = fPurgeableQueue.peek(); |
497 SkASSERT(resource->isPurgeable()); | 481 SkASSERT(resource->isPurgeable()); |
498 resource->cacheAccess().release(); | 482 resource->cacheAccess().release(); |
499 stillOverbudget = this->overBudget(); | 483 stillOverbudget = this->overBudget(); |
500 } | 484 } |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
559 // sequential timestamps beginning with 0. This is O(n*lg(n)) but it
should be extremely | 543 // sequential timestamps beginning with 0. This is O(n*lg(n)) but it
should be extremely |
560 // rare. | 544 // rare. |
561 SkTDArray<GrGpuResource*> sortedPurgeableResources; | 545 SkTDArray<GrGpuResource*> sortedPurgeableResources; |
562 sortedPurgeableResources.setReserve(fPurgeableQueue.count()); | 546 sortedPurgeableResources.setReserve(fPurgeableQueue.count()); |
563 | 547 |
564 while (fPurgeableQueue.count()) { | 548 while (fPurgeableQueue.count()) { |
565 *sortedPurgeableResources.append() = fPurgeableQueue.peek(); | 549 *sortedPurgeableResources.append() = fPurgeableQueue.peek(); |
566 fPurgeableQueue.pop(); | 550 fPurgeableQueue.pop(); |
567 } | 551 } |
568 | 552 |
569 struct Less { | 553 SkTQSort(fNonpurgeableResources.begin(), fNonpurgeableResources.end(
) - 1, |
570 bool operator()(GrGpuResource* a, GrGpuResource* b) { | 554 CompareTimestamp); |
571 return CompareTimestamp(a,b); | |
572 } | |
573 }; | |
574 Less less; | |
575 SkTQSort(fNonpurgeableResources.begin(), fNonpurgeableResources.end(
) - 1, less); | |
576 | 555 |
577 // Pick resources out of the purgeable and non-purgeable arrays base
d on lowest | 556 // Pick resources out of the purgeable and non-purgeable arrays base
d on lowest |
578 // timestamp and assign new timestamps. | 557 // timestamp and assign new timestamps. |
579 int currP = 0; | 558 int currP = 0; |
580 int currNP = 0; | 559 int currNP = 0; |
581 while (currP < sortedPurgeableResources.count() && | 560 while (currP < sortedPurgeableResources.count() && |
582 currNP < fNonpurgeableResources.count()) { | 561 currNP < fNonpurgeableResources.count()) { |
583 uint32_t tsP = sortedPurgeableResources[currP]->cacheAccess().ti
mestamp(); | 562 uint32_t tsP = sortedPurgeableResources[currP]->cacheAccess().ti
mestamp(); |
584 uint32_t tsNP = fNonpurgeableResources[currNP]->cacheAccess().ti
mestamp(); | 563 uint32_t tsNP = fNonpurgeableResources[currNP]->cacheAccess().ti
mestamp(); |
585 SkASSERT(tsP != tsNP); | 564 SkASSERT(tsP != tsNP); |
(...skipping 18 matching lines...) Expand all Loading... |
604 // Rebuild the queue. | 583 // Rebuild the queue. |
605 for (int i = 0; i < sortedPurgeableResources.count(); ++i) { | 584 for (int i = 0; i < sortedPurgeableResources.count(); ++i) { |
606 fPurgeableQueue.insert(sortedPurgeableResources[i]); | 585 fPurgeableQueue.insert(sortedPurgeableResources[i]); |
607 } | 586 } |
608 | 587 |
609 this->validate(); | 588 this->validate(); |
610 SkASSERT(count == this->getResourceCount()); | 589 SkASSERT(count == this->getResourceCount()); |
611 | 590 |
612 // count should be the next timestamp we return. | 591 // count should be the next timestamp we return. |
613 SkASSERT(fTimestamp == SkToU32(count)); | 592 SkASSERT(fTimestamp == SkToU32(count)); |
614 | |
615 // The historical timestamps of flushes are now invalid. | |
616 this->resetFlushTimestamps(); | |
617 } | 593 } |
618 } | 594 } |
619 return fTimestamp++; | 595 return fTimestamp++; |
620 } | 596 } |
621 | 597 |
622 void GrResourceCache::notifyFlushOccurred(FlushType type) { | 598 void GrResourceCache::notifyFlushOccurred(FlushType type) { |
623 switch (type) { | 599 switch (type) { |
624 case FlushType::kImmediateMode: | 600 case FlushType::kImmediateMode: |
625 break; | 601 break; |
626 case FlushType::kCacheRequested: | 602 case FlushType::kCacheRequested: |
627 SkASSERT(fRequestFlush); | 603 SkASSERT(fRequestFlush); |
628 fRequestFlush = false; | 604 fRequestFlush = false; |
629 break; | 605 break; |
630 case FlushType::kExternal: | 606 case FlushType::kExternal: |
631 if (fFlushTimestamps) { | 607 ++fExternalFlushCnt; |
632 SkASSERT(SkIsPow2(fMaxUnusedFlushes)); | 608 if (0 == fExternalFlushCnt) { |
633 fLastFlushTimestampIndex = (fLastFlushTimestampIndex + 1) & (fMa
xUnusedFlushes - 1); | 609 // When this wraps just reset all the purgeable resources' last
used flush state. |
634 // get the timestamp before accessing fFlushTimestamps because g
etNextTimestamp will | 610 for (int i = 0; i < fPurgeableQueue.count(); ++i) { |
635 // reallocate fFlushTimestamps on timestamp overflow. | 611 fPurgeableQueue.at(i)->cacheAccess().setFlushCntWhenResource
BecamePurgeable(0); |
636 uint32_t timestamp = this->getNextTimestamp(); | 612 } |
637 fFlushTimestamps[fLastFlushTimestampIndex] = timestamp; | |
638 } | 613 } |
639 break; | 614 break; |
640 } | 615 } |
641 this->purgeAsNeeded(); | 616 this->purgeAsNeeded(); |
642 } | 617 } |
643 | 618 |
644 void GrResourceCache::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) c
onst { | 619 void GrResourceCache::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) c
onst { |
645 for (int i = 0; i < fNonpurgeableResources.count(); ++i) { | 620 for (int i = 0; i < fNonpurgeableResources.count(); ++i) { |
646 fNonpurgeableResources[i]->dumpMemoryStatistics(traceMemoryDump); | 621 fNonpurgeableResources[i]->dumpMemoryStatistics(traceMemoryDump); |
647 } | 622 } |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
779 return true; | 754 return true; |
780 } | 755 } |
781 if (index < fNonpurgeableResources.count() && fNonpurgeableResources[index]
== resource) { | 756 if (index < fNonpurgeableResources.count() && fNonpurgeableResources[index]
== resource) { |
782 return true; | 757 return true; |
783 } | 758 } |
784 SkDEBUGFAIL("Resource index should be -1 or the resource should be in the ca
che."); | 759 SkDEBUGFAIL("Resource index should be -1 or the resource should be in the ca
che."); |
785 return false; | 760 return false; |
786 } | 761 } |
787 | 762 |
788 #endif | 763 #endif |
OLD | NEW |