| OLD | NEW |
| 1 | 1 |
| 2 /* | 2 /* |
| 3 * Copyright 2006 The Android Open Source Project | 3 * Copyright 2006 The Android Open Source Project |
| 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 "SkGlyphCache.h" | 10 #include "SkGlyphCache.h" |
| 11 #include "SkGraphics.h" | 11 #include "SkGraphics.h" |
| 12 #include "SkPaint.h" | 12 #include "SkPaint.h" |
| 13 #include "SkPath.h" | 13 #include "SkPath.h" |
| 14 #include "SkTemplates.h" | 14 #include "SkTemplates.h" |
| 15 #include "SkTLS.h" | 15 #include "SkTLS.h" |
| 16 #include "SkTypeface.h" | 16 #include "SkTypeface.h" |
| 17 | 17 |
| 18 //#define SPEW_PURGE_STATUS | 18 //#define SPEW_PURGE_STATUS |
| 19 //#define USE_CACHE_HASH | |
| 20 //#define RECORD_HASH_EFFICIENCY | 19 //#define RECORD_HASH_EFFICIENCY |
| 21 | 20 |
| 22 bool gSkSuppressFontCachePurgeSpew; | 21 bool gSkSuppressFontCachePurgeSpew; |
| 23 | 22 |
| 24 /////////////////////////////////////////////////////////////////////////////// | 23 /////////////////////////////////////////////////////////////////////////////// |
| 25 | 24 |
| 26 #ifdef RECORD_HASH_EFFICIENCY | 25 #ifdef RECORD_HASH_EFFICIENCY |
| 27 static uint32_t gHashSuccess; | 26 static uint32_t gHashSuccess; |
| 28 static uint32_t gHashCollision; | 27 static uint32_t gHashCollision; |
| 29 | 28 |
| (...skipping 365 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 395 } | 394 } |
| 396 } | 395 } |
| 397 | 396 |
| 398 /////////////////////////////////////////////////////////////////////////////// | 397 /////////////////////////////////////////////////////////////////////////////// |
| 399 /////////////////////////////////////////////////////////////////////////////// | 398 /////////////////////////////////////////////////////////////////////////////// |
| 400 | 399 |
| 401 #ifndef SK_DEFAULT_FONT_CACHE_LIMIT | 400 #ifndef SK_DEFAULT_FONT_CACHE_LIMIT |
| 402 #define SK_DEFAULT_FONT_CACHE_LIMIT (2 * 1024 * 1024) | 401 #define SK_DEFAULT_FONT_CACHE_LIMIT (2 * 1024 * 1024) |
| 403 #endif | 402 #endif |
| 404 | 403 |
| 405 #ifdef USE_CACHE_HASH | |
| 406 #define HASH_BITCOUNT 6 | |
| 407 #define HASH_COUNT (1 << HASH_BITCOUNT) | |
| 408 #define HASH_MASK (HASH_COUNT - 1) | |
| 409 | |
| 410 static unsigned desc_to_hashindex(const SkDescriptor* desc) | |
| 411 { | |
| 412 SkASSERT(HASH_MASK < 256); // since our munging reduces to 8 bits | |
| 413 | |
| 414 uint32_t n = *(const uint32_t*)desc; //desc->getChecksum(); | |
| 415 SkASSERT(n == desc->getChecksum()); | |
| 416 | |
| 417 // don't trust that the low bits of checksum vary enough, so... | |
| 418 n ^= (n >> 24) ^ (n >> 16) ^ (n >> 8) ^ (n >> 30); | |
| 419 | |
| 420 return n & HASH_MASK; | |
| 421 } | |
| 422 #endif | |
| 423 | |
| 424 #include "SkThread.h" | 404 #include "SkThread.h" |
| 425 | 405 |
| 426 class SkGlyphCache_Globals { | 406 class SkGlyphCache_Globals { |
| 427 public: | 407 public: |
| 428 enum UseMutex { | 408 enum UseMutex { |
| 429 kNo_UseMutex, // thread-local cache | 409 kNo_UseMutex, // thread-local cache |
| 430 kYes_UseMutex // shared cache | 410 kYes_UseMutex // shared cache |
| 431 }; | 411 }; |
| 432 | 412 |
| 433 SkGlyphCache_Globals(UseMutex um) { | 413 SkGlyphCache_Globals(UseMutex um) { |
| 434 fHead = NULL; | 414 fHead = NULL; |
| 435 fTotalMemoryUsed = 0; | 415 fTotalMemoryUsed = 0; |
| 436 fFontCacheLimit = SK_DEFAULT_FONT_CACHE_LIMIT; | 416 fFontCacheLimit = SK_DEFAULT_FONT_CACHE_LIMIT; |
| 437 fMutex = (kYes_UseMutex == um) ? SkNEW(SkMutex) : NULL; | 417 fMutex = (kYes_UseMutex == um) ? SkNEW(SkMutex) : NULL; |
| 438 | |
| 439 #ifdef USE_CACHE_HASH | |
| 440 sk_bzero(fHash, sizeof(fHash)); | |
| 441 #endif | |
| 442 } | 418 } |
| 443 | 419 |
| 444 ~SkGlyphCache_Globals() { | 420 ~SkGlyphCache_Globals() { |
| 445 SkGlyphCache* cache = fHead; | 421 SkGlyphCache* cache = fHead; |
| 446 while (cache) { | 422 while (cache) { |
| 447 SkGlyphCache* next = cache->fNext; | 423 SkGlyphCache* next = cache->fNext; |
| 448 SkDELETE(cache); | 424 SkDELETE(cache); |
| 449 cache = next; | 425 cache = next; |
| 450 } | 426 } |
| 451 | 427 |
| 452 SkDELETE(fMutex); | 428 SkDELETE(fMutex); |
| 453 } | 429 } |
| 454 | 430 |
| 455 SkMutex* fMutex; | 431 SkMutex* fMutex; |
| 456 SkGlyphCache* fHead; | 432 SkGlyphCache* fHead; |
| 457 size_t fTotalMemoryUsed; | 433 size_t fTotalMemoryUsed; |
| 458 #ifdef USE_CACHE_HASH | |
| 459 SkGlyphCache* fHash[HASH_COUNT]; | |
| 460 #endif | |
| 461 | 434 |
| 462 #ifdef SK_DEBUG | 435 #ifdef SK_DEBUG |
| 463 void validate() const; | 436 void validate() const; |
| 464 #else | 437 #else |
| 465 void validate() const {} | 438 void validate() const {} |
| 466 #endif | 439 #endif |
| 467 | 440 |
| 468 size_t getFontCacheLimit() const { return fFontCacheLimit; } | 441 size_t getFontCacheLimit() const { return fFontCacheLimit; } |
| 469 size_t setFontCacheLimit(size_t limit); | 442 size_t setFontCacheLimit(size_t limit); |
| 470 void purgeAll(); // does not change budget | 443 void purgeAll(); // does not change budget |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 560 } | 533 } |
| 561 SkASSERT(desc); | 534 SkASSERT(desc); |
| 562 | 535 |
| 563 SkGlyphCache_Globals& globals = getGlobals(); | 536 SkGlyphCache_Globals& globals = getGlobals(); |
| 564 SkAutoMutexAcquire ac(globals.fMutex); | 537 SkAutoMutexAcquire ac(globals.fMutex); |
| 565 SkGlyphCache* cache; | 538 SkGlyphCache* cache; |
| 566 bool insideMutex = true; | 539 bool insideMutex = true; |
| 567 | 540 |
| 568 globals.validate(); | 541 globals.validate(); |
| 569 | 542 |
| 570 #ifdef USE_CACHE_HASH | |
| 571 SkGlyphCache** hash = globals.fHash; | |
| 572 unsigned index = desc_to_hashindex(desc); | |
| 573 cache = hash[index]; | |
| 574 if (cache && *cache->fDesc == *desc) { | |
| 575 cache->detach(&globals.fHead); | |
| 576 goto FOUND_IT; | |
| 577 } | |
| 578 #endif | |
| 579 | |
| 580 for (cache = globals.fHead; cache != NULL; cache = cache->fNext) { | 543 for (cache = globals.fHead; cache != NULL; cache = cache->fNext) { |
| 581 if (cache->fDesc->equals(*desc)) { | 544 if (cache->fDesc->equals(*desc)) { |
| 582 cache->detach(&globals.fHead); | 545 cache->detach(&globals.fHead); |
| 583 goto FOUND_IT; | 546 goto FOUND_IT; |
| 584 } | 547 } |
| 585 } | 548 } |
| 586 | 549 |
| 587 /* Release the mutex now, before we create a new entry (which might have | 550 /* Release the mutex now, before we create a new entry (which might have |
| 588 side-effects like trying to access the cache/mutex (yikes!) | 551 side-effects like trying to access the cache/mutex (yikes!) |
| 589 */ | 552 */ |
| (...skipping 16 matching lines...) Expand all Loading... |
| 606 } | 569 } |
| 607 | 570 |
| 608 FOUND_IT: | 571 FOUND_IT: |
| 609 | 572 |
| 610 AutoValidate av(cache); | 573 AutoValidate av(cache); |
| 611 | 574 |
| 612 if (proc(cache, context)) { // stay detached | 575 if (proc(cache, context)) { // stay detached |
| 613 if (insideMutex) { | 576 if (insideMutex) { |
| 614 SkASSERT(globals.fTotalMemoryUsed >= cache->fMemoryUsed); | 577 SkASSERT(globals.fTotalMemoryUsed >= cache->fMemoryUsed); |
| 615 globals.fTotalMemoryUsed -= cache->fMemoryUsed; | 578 globals.fTotalMemoryUsed -= cache->fMemoryUsed; |
| 616 #ifdef USE_CACHE_HASH | |
| 617 hash[index] = NULL; | |
| 618 #endif | |
| 619 } | 579 } |
| 620 } else { // reattach | 580 } else { // reattach |
| 621 if (insideMutex) { | 581 if (insideMutex) { |
| 622 cache->attachToHead(&globals.fHead); | 582 cache->attachToHead(&globals.fHead); |
| 623 #ifdef USE_CACHE_HASH | |
| 624 hash[index] = cache; | |
| 625 #endif | |
| 626 } else { | 583 } else { |
| 627 AttachCache(cache); | 584 AttachCache(cache); |
| 628 } | 585 } |
| 629 cache = NULL; | 586 cache = NULL; |
| 630 } | 587 } |
| 631 return cache; | 588 return cache; |
| 632 } | 589 } |
| 633 | 590 |
| 634 void SkGlyphCache::AttachCache(SkGlyphCache* cache) { | 591 void SkGlyphCache::AttachCache(SkGlyphCache* cache) { |
| 635 SkASSERT(cache); | 592 SkASSERT(cache); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 646 size_t allocated = globals.fTotalMemoryUsed + cache->fMemoryUsed; | 603 size_t allocated = globals.fTotalMemoryUsed + cache->fMemoryUsed; |
| 647 size_t budgeted = globals.getFontCacheLimit(); | 604 size_t budgeted = globals.getFontCacheLimit(); |
| 648 if (allocated > budgeted) { | 605 if (allocated > budgeted) { |
| 649 (void)InternalFreeCache(&globals, allocated - budgeted); | 606 (void)InternalFreeCache(&globals, allocated - budgeted); |
| 650 } | 607 } |
| 651 } | 608 } |
| 652 | 609 |
| 653 cache->attachToHead(&globals.fHead); | 610 cache->attachToHead(&globals.fHead); |
| 654 globals.fTotalMemoryUsed += cache->fMemoryUsed; | 611 globals.fTotalMemoryUsed += cache->fMemoryUsed; |
| 655 | 612 |
| 656 #ifdef USE_CACHE_HASH | |
| 657 unsigned index = desc_to_hashindex(cache->fDesc); | |
| 658 SkASSERT(globals.fHash[index] != cache); | |
| 659 globals.fHash[index] = cache; | |
| 660 #endif | |
| 661 | |
| 662 globals.validate(); | 613 globals.validate(); |
| 663 } | 614 } |
| 664 | 615 |
| 665 /////////////////////////////////////////////////////////////////////////////// | 616 /////////////////////////////////////////////////////////////////////////////// |
| 666 | 617 |
| 667 SkGlyphCache* SkGlyphCache::FindTail(SkGlyphCache* cache) { | 618 SkGlyphCache* SkGlyphCache::FindTail(SkGlyphCache* cache) { |
| 668 if (cache) { | 619 if (cache) { |
| 669 while (cache->fNext) { | 620 while (cache->fNext) { |
| 670 cache = cache->fNext; | 621 cache = cache->fNext; |
| 671 } | 622 } |
| (...skipping 28 matching lines...) Expand all Loading... |
| 700 // don't do any "small" purges | 651 // don't do any "small" purges |
| 701 size_t minToPurge = globals->fTotalMemoryUsed >> 2; | 652 size_t minToPurge = globals->fTotalMemoryUsed >> 2; |
| 702 if (bytesNeeded < minToPurge) | 653 if (bytesNeeded < minToPurge) |
| 703 bytesNeeded = minToPurge; | 654 bytesNeeded = minToPurge; |
| 704 | 655 |
| 705 SkGlyphCache* cache = FindTail(globals->fHead); | 656 SkGlyphCache* cache = FindTail(globals->fHead); |
| 706 while (cache != NULL && bytesFreed < bytesNeeded) { | 657 while (cache != NULL && bytesFreed < bytesNeeded) { |
| 707 SkGlyphCache* prev = cache->fPrev; | 658 SkGlyphCache* prev = cache->fPrev; |
| 708 bytesFreed += cache->fMemoryUsed; | 659 bytesFreed += cache->fMemoryUsed; |
| 709 | 660 |
| 710 #ifdef USE_CACHE_HASH | |
| 711 unsigned index = desc_to_hashindex(cache->fDesc); | |
| 712 if (cache == globals->fHash[index]) { | |
| 713 globals->fHash[index] = NULL; | |
| 714 } | |
| 715 #endif | |
| 716 | |
| 717 cache->detach(&globals->fHead); | 661 cache->detach(&globals->fHead); |
| 718 SkDELETE(cache); | 662 SkDELETE(cache); |
| 719 cache = prev; | 663 cache = prev; |
| 720 count += 1; | 664 count += 1; |
| 721 } | 665 } |
| 722 | 666 |
| 723 SkASSERT(bytesFreed <= globals->fTotalMemoryUsed); | 667 SkASSERT(bytesFreed <= globals->fTotalMemoryUsed); |
| 724 globals->fTotalMemoryUsed -= bytesFreed; | 668 globals->fTotalMemoryUsed -= bytesFreed; |
| 725 globals->validate(); | 669 globals->validate(); |
| 726 | 670 |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 779 return tls ? tls->getFontCacheLimit() : 0; | 723 return tls ? tls->getFontCacheLimit() : 0; |
| 780 } | 724 } |
| 781 | 725 |
| 782 void SkGraphics::SetTLSFontCacheLimit(size_t bytes) { | 726 void SkGraphics::SetTLSFontCacheLimit(size_t bytes) { |
| 783 if (0 == bytes) { | 727 if (0 == bytes) { |
| 784 SkGlyphCache_Globals::DeleteTLS(); | 728 SkGlyphCache_Globals::DeleteTLS(); |
| 785 } else { | 729 } else { |
| 786 SkGlyphCache_Globals::GetTLS().setFontCacheLimit(bytes); | 730 SkGlyphCache_Globals::GetTLS().setFontCacheLimit(bytes); |
| 787 } | 731 } |
| 788 } | 732 } |
| OLD | NEW |