| 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 |