Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(153)

Side by Side Diff: src/core/SkGlyphCache.cpp

Issue 24447003: add counting to glyphcache, and refactor some for clarity (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 "SkGlyphCache_Globals.h"
11 #include "SkGraphics.h" 12 #include "SkGraphics.h"
12 #include "SkPaint.h" 13 #include "SkPaint.h"
13 #include "SkPath.h" 14 #include "SkPath.h"
14 #include "SkTemplates.h" 15 #include "SkTemplates.h"
15 #include "SkTLS.h" 16 #include "SkTLS.h"
16 #include "SkTypeface.h" 17 #include "SkTypeface.h"
17 18
18 //#define SPEW_PURGE_STATUS 19 //#define SPEW_PURGE_STATUS
19 //#define RECORD_HASH_EFFICIENCY 20 //#define RECORD_HASH_EFFICIENCY
20 21
21 bool gSkSuppressFontCachePurgeSpew; 22 bool gSkSuppressFontCachePurgeSpew;
22 23
24 // Returns the shared globals
25 static SkGlyphCache_Globals& getSharedGlobals() {
26 // we leak this, so we don't incur any shutdown cost of the destructor
27 static SkGlyphCache_Globals* gGlobals = SkNEW_ARGS(SkGlyphCache_Globals,
28 (SkGlyphCache_Globals::kY es_UseMutex));
29 return *gGlobals;
30 }
31
32 // Returns the TLS globals (if set), or the shared globals
33 static SkGlyphCache_Globals& getGlobals() {
34 SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
35 return tls ? *tls : getSharedGlobals();
36 }
37
23 /////////////////////////////////////////////////////////////////////////////// 38 ///////////////////////////////////////////////////////////////////////////////
24 39
25 #ifdef RECORD_HASH_EFFICIENCY 40 #ifdef RECORD_HASH_EFFICIENCY
26 static uint32_t gHashSuccess; 41 static uint32_t gHashSuccess;
27 static uint32_t gHashCollision; 42 static uint32_t gHashCollision;
28 43
29 static void RecordHashSuccess() { 44 static void RecordHashSuccess() {
30 gHashSuccess += 1; 45 gHashSuccess += 1;
31 } 46 }
32 47
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
68 // init with 0xFF so that the charCode field will be -1, which is invalid 83 // init with 0xFF so that the charCode field will be -1, which is invalid
69 memset(fCharToGlyphHash, 0xFF, sizeof(fCharToGlyphHash)); 84 memset(fCharToGlyphHash, 0xFF, sizeof(fCharToGlyphHash));
70 85
71 fMemoryUsed = sizeof(*this); 86 fMemoryUsed = sizeof(*this);
72 87
73 fGlyphArray.setReserve(kMinGlyphCount); 88 fGlyphArray.setReserve(kMinGlyphCount);
74 89
75 fMetricsCount = 0; 90 fMetricsCount = 0;
76 fAdvanceCount = 0; 91 fAdvanceCount = 0;
77 fAuxProcList = NULL; 92 fAuxProcList = NULL;
93
94 getGlobals().registerCache(this);
78 } 95 }
79 96
80 SkGlyphCache::~SkGlyphCache() { 97 SkGlyphCache::~SkGlyphCache() {
81 #if 0 98 #if 0
82 { 99 {
83 size_t ptrMem = fGlyphArray.count() * sizeof(SkGlyph*); 100 size_t ptrMem = fGlyphArray.count() * sizeof(SkGlyph*);
84 size_t glyphAlloc = fGlyphAlloc.totalCapacity(); 101 size_t glyphAlloc = fGlyphAlloc.totalCapacity();
85 size_t glyphHashUsed = 0; 102 size_t glyphHashUsed = 0;
86 size_t uniHashUsed = 0; 103 size_t uniHashUsed = 0;
87 for (int i = 0; i < kHashCount; ++i) { 104 for (int i = 0; i < kHashCount; ++i) {
(...skipping 19 matching lines...) Expand all
107 while (gptr < stop) { 124 while (gptr < stop) {
108 SkPath* path = (*gptr)->fPath; 125 SkPath* path = (*gptr)->fPath;
109 if (path) { 126 if (path) {
110 SkDELETE(path); 127 SkDELETE(path);
111 } 128 }
112 gptr += 1; 129 gptr += 1;
113 } 130 }
114 SkDescriptor::Free(fDesc); 131 SkDescriptor::Free(fDesc);
115 SkDELETE(fScalerContext); 132 SkDELETE(fScalerContext);
116 this->invokeAndRemoveAuxProcs(); 133 this->invokeAndRemoveAuxProcs();
134
135 getGlobals().unregisterCache(this);
117 } 136 }
118 137
119 /////////////////////////////////////////////////////////////////////////////// 138 ///////////////////////////////////////////////////////////////////////////////
120 139
121 #ifdef SK_DEBUG 140 #ifdef SK_DEBUG
122 #define VALIDATE() AutoValidate av(this) 141 #define VALIDATE() AutoValidate av(this)
123 #else 142 #else
124 #define VALIDATE() 143 #define VALIDATE()
125 #endif 144 #endif
126 145
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after
390 rec->fProc(rec->fData); 409 rec->fProc(rec->fData);
391 AuxProcRec* next = rec->fNext; 410 AuxProcRec* next = rec->fNext;
392 SkDELETE(rec); 411 SkDELETE(rec);
393 rec = next; 412 rec = next;
394 } 413 }
395 } 414 }
396 415
397 /////////////////////////////////////////////////////////////////////////////// 416 ///////////////////////////////////////////////////////////////////////////////
398 /////////////////////////////////////////////////////////////////////////////// 417 ///////////////////////////////////////////////////////////////////////////////
399 418
400 #ifndef SK_DEFAULT_FONT_CACHE_LIMIT
401 #define SK_DEFAULT_FONT_CACHE_LIMIT (2 * 1024 * 1024)
402 #endif
403
404 #include "SkThread.h" 419 #include "SkThread.h"
405 420
406 class SkGlyphCache_Globals { 421 size_t SkGlyphCache_Globals::setCacheSizeLimit(size_t newLimit) {
407 public:
408 enum UseMutex {
409 kNo_UseMutex, // thread-local cache
410 kYes_UseMutex // shared cache
411 };
412
413 SkGlyphCache_Globals(UseMutex um) {
414 fHead = NULL;
415 fTotalMemoryUsed = 0;
416 fFontCacheLimit = SK_DEFAULT_FONT_CACHE_LIMIT;
417 fMutex = (kYes_UseMutex == um) ? SkNEW(SkMutex) : NULL;
418 }
419
420 ~SkGlyphCache_Globals() {
421 SkGlyphCache* cache = fHead;
422 while (cache) {
423 SkGlyphCache* next = cache->fNext;
424 SkDELETE(cache);
425 cache = next;
426 }
427
428 SkDELETE(fMutex);
429 }
430
431 SkMutex* fMutex;
432 SkGlyphCache* fHead;
433 size_t fTotalMemoryUsed;
434
435 #ifdef SK_DEBUG
436 void validate() const;
437 #else
438 void validate() const {}
439 #endif
440
441 size_t getFontCacheLimit() const { return fFontCacheLimit; }
442 size_t setFontCacheLimit(size_t limit);
443 void purgeAll(); // does not change budget
444
445 // can return NULL
446 static SkGlyphCache_Globals* FindTLS() {
447 return (SkGlyphCache_Globals*)SkTLS::Find(CreateTLS);
448 }
449
450 static SkGlyphCache_Globals& GetTLS() {
451 return *(SkGlyphCache_Globals*)SkTLS::Get(CreateTLS, DeleteTLS);
452 }
453
454 static void DeleteTLS() { SkTLS::Delete(CreateTLS); }
455
456 private:
457 size_t fFontCacheLimit;
458
459 static void* CreateTLS() {
460 return SkNEW_ARGS(SkGlyphCache_Globals, (kNo_UseMutex));
461 }
462
463 static void DeleteTLS(void* ptr) {
464 SkDELETE((SkGlyphCache_Globals*)ptr);
465 }
466 };
467
468 size_t SkGlyphCache_Globals::setFontCacheLimit(size_t newLimit) {
469 static const size_t minLimit = 256 * 1024; 422 static const size_t minLimit = 256 * 1024;
470 if (newLimit < minLimit) { 423 if (newLimit < minLimit) {
471 newLimit = minLimit; 424 newLimit = minLimit;
472 } 425 }
473 426
474 size_t prevLimit = fFontCacheLimit; 427 SkAutoMutexAcquire ac(fMutex);
475 fFontCacheLimit = newLimit;
476 428
477 size_t currUsed = fTotalMemoryUsed; 429 size_t prevLimit = fCacheSizeLimit;
478 if (currUsed > newLimit) { 430 fCacheSizeLimit = newLimit;
479 SkAutoMutexAcquire ac(fMutex); 431 this->internalPurge();
480 SkGlyphCache::InternalFreeCache(this, currUsed - newLimit);
481 }
482 return prevLimit; 432 return prevLimit;
483 } 433 }
484 434
485 void SkGlyphCache_Globals::purgeAll() { 435 void SkGlyphCache_Globals::purgeAll() {
486 SkAutoMutexAcquire ac(fMutex); 436 SkAutoMutexAcquire ac(fMutex);
487 SkGlyphCache::InternalFreeCache(this, fTotalMemoryUsed); 437 this->internalPurge(fTotalMemoryUsed);
488 }
489
490 // Returns the shared globals
491 static SkGlyphCache_Globals& getSharedGlobals() {
492 // we leak this, so we don't incur any shutdown cost of the destructor
493 static SkGlyphCache_Globals* gGlobals = SkNEW_ARGS(SkGlyphCache_Globals,
494 (SkGlyphCache_Globals::kYes_UseMutex));
495 return *gGlobals;
496 }
497
498 // Returns the TLS globals (if set), or the shared globals
499 static SkGlyphCache_Globals& getGlobals() {
500 SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
501 return tls ? *tls : getSharedGlobals();
502 } 438 }
503 439
504 void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*), 440 void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*),
505 void* context) { 441 void* context) {
506 SkGlyphCache_Globals& globals = getGlobals(); 442 SkGlyphCache_Globals& globals = getGlobals();
507 SkAutoMutexAcquire ac(globals.fMutex); 443 SkAutoMutexAcquire ac(globals.fMutex);
508 SkGlyphCache* cache; 444 SkGlyphCache* cache;
509 445
510 globals.validate(); 446 globals.validate();
511 447
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
585 } 521 }
586 cache = NULL; 522 cache = NULL;
587 } 523 }
588 return cache; 524 return cache;
589 } 525 }
590 526
591 void SkGlyphCache::AttachCache(SkGlyphCache* cache) { 527 void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
592 SkASSERT(cache); 528 SkASSERT(cache);
593 SkASSERT(cache->fNext == NULL); 529 SkASSERT(cache->fNext == NULL);
594 530
595 SkGlyphCache_Globals& globals = getGlobals(); 531 getGlobals().attachCache(cache);
596 SkAutoMutexAcquire ac(globals.fMutex);
597
598 globals.validate();
599 cache->validate();
600
601 // if we have a fixed budget for our cache, do a purge here
602 {
603 size_t allocated = globals.fTotalMemoryUsed + cache->fMemoryUsed;
604 size_t budgeted = globals.getFontCacheLimit();
605 if (allocated > budgeted) {
606 (void)InternalFreeCache(&globals, allocated - budgeted);
607 }
608 }
609
610 cache->attachToHead(&globals.fHead);
611 globals.fTotalMemoryUsed += cache->fMemoryUsed;
612
613 globals.validate();
614 } 532 }
615 533
616 /////////////////////////////////////////////////////////////////////////////// 534 ///////////////////////////////////////////////////////////////////////////////
617 535
618 SkGlyphCache* SkGlyphCache::FindTail(SkGlyphCache* cache) { 536 SkGlyphCache* SkGlyphCache::FindTail(SkGlyphCache* cache) {
619 if (cache) { 537 if (cache) {
620 while (cache->fNext) { 538 while (cache->fNext) {
621 cache = cache->fNext; 539 cache = cache->fNext;
622 } 540 }
623 } 541 }
624 return cache; 542 return cache;
625 } 543 }
626 544
627 #ifdef SK_DEBUG 545 ///////////////////////////////////////////////////////////////////////////////
628 void SkGlyphCache_Globals::validate() const {
629 size_t computed = 0;
630 546
631 const SkGlyphCache* head = fHead; 547 void SkGlyphCache_Globals::attachCache(SkGlyphCache* cache) {
632 while (head != NULL) { 548 SkAutoMutexAcquire ac(fMutex);
633 computed += head->fMemoryUsed; 549
634 head = head->fNext; 550 this->validate();
551 cache->validate();
552
553 cache->attachToHead(&fHead);
554 fTotalMemoryUsed += cache->fMemoryUsed;
555
556 this->internalPurge();
557 }
558
559 size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) {
560 this->validate();
561
562 size_t bytesNeeded = 0;
563 if (fTotalMemoryUsed > fCacheSizeLimit) {
564 bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
565 }
566 bytesNeeded = SkMax32(bytesNeeded, minBytesNeeded);
567 if (bytesNeeded) {
568 // no small purges!
569 bytesNeeded = SkMax32(bytesNeeded, fTotalMemoryUsed >> 2);
635 } 570 }
636 571
637 if (fTotalMemoryUsed != computed) { 572 int countNeeded = 0;
638 printf("total %d, computed %d\n", (int)fTotalMemoryUsed, (int)computed); 573 if (fCacheCount > fCacheCountLimit) {
574 countNeeded = fCacheCount - fCacheCountLimit;
575 // no small purges!
576 countNeeded = SkMax32(countNeeded, fCacheCount >> 2);
639 } 577 }
640 SkASSERT(fTotalMemoryUsed == computed);
641 }
642 #endif
643 578
644 size_t SkGlyphCache::InternalFreeCache(SkGlyphCache_Globals* globals, 579 // early exit
645 size_t bytesNeeded) { 580 if (!countNeeded && !bytesNeeded) {
646 globals->validate(); 581 return 0;
582 }
647 583
648 size_t bytesFreed = 0; 584 size_t bytesFreed = 0;
649 int count = 0; 585 int countFreed = 0;
650 586
651 // don't do any "small" purges 587 // we start at the tail and proceed backwards, as the linklist is in LRU
652 size_t minToPurge = globals->fTotalMemoryUsed >> 2; 588 // order, with unimportant entries at the tail.
653 if (bytesNeeded < minToPurge) 589 SkGlyphCache* cache = SkGlyphCache::FindTail(fHead);
654 bytesNeeded = minToPurge; 590 while (cache != NULL &&
655 591 (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
656 SkGlyphCache* cache = FindTail(globals->fHead);
657 while (cache != NULL && bytesFreed < bytesNeeded) {
658 SkGlyphCache* prev = cache->fPrev; 592 SkGlyphCache* prev = cache->fPrev;
659 bytesFreed += cache->fMemoryUsed; 593 bytesFreed += cache->fMemoryUsed;
594 countFreed += 1;
660 595
661 cache->detach(&globals->fHead); 596 cache->detach(&fHead);
662 SkDELETE(cache); 597 SkDELETE(cache);
663 cache = prev; 598 cache = prev;
664 count += 1;
665 } 599 }
666 600
667 SkASSERT(bytesFreed <= globals->fTotalMemoryUsed); 601 SkASSERT(bytesFreed <= fTotalMemoryUsed);
668 globals->fTotalMemoryUsed -= bytesFreed; 602 SkASSERT(countFreed <= fCacheCount);
669 globals->validate(); 603
604 fTotalMemoryUsed -= bytesFreed;
605 fCacheCount -= countFreed;
606 this->validate();
670 607
671 #ifdef SPEW_PURGE_STATUS 608 #ifdef SPEW_PURGE_STATUS
672 if (count && !gSkSuppressFontCachePurgeSpew) { 609 if (countFreed && !gSkSuppressFontCachePurgeSpew) {
673 SkDebugf("purging %dK from font cache [%d entries]\n", 610 SkDebugf("purging %dK from font cache [%d entries]\n",
674 (int)(bytesFreed >> 10), count); 611 (int)(bytesFreed >> 10), countFreed);
675 } 612 }
676 #endif 613 #endif
677 614
678 return bytesFreed; 615 return bytesFreed;
679 } 616 }
680 617
618 void SkGlyphCache_Globals::registerCache(SkGlyphCache*) {
619 sk_atomic_inc(&fCacheCount);
bungeman-skia 2013/09/25 14:46:07 This is going to set off the chromium tsan bots. O
reed1 2013/09/25 17:30:31 Help me understand what you're saying. We use sk_a
mtklein 2013/09/25 18:15:25 I'm confused too. Memory barriers are about preve
bungeman-skia 2013/09/25 22:37:08 This use case probably isn't that different from o
620 }
621
622 // call when SkGlyphCache is destroyed
623 void SkGlyphCache_Globals::unregisterCache(SkGlyphCache*) {
624 sk_atomic_dec(&fCacheCount);
625 }
626
681 /////////////////////////////////////////////////////////////////////////////// 627 ///////////////////////////////////////////////////////////////////////////////
682 628
683 #ifdef SK_DEBUG 629 #ifdef SK_DEBUG
630
684 void SkGlyphCache::validate() const { 631 void SkGlyphCache::validate() const {
685 #ifdef SK_DEBUG_GLYPH_CACHE 632 #ifdef SK_DEBUG_GLYPH_CACHE
686 int count = fGlyphArray.count(); 633 int count = fGlyphArray.count();
687 for (int i = 0; i < count; i++) { 634 for (int i = 0; i < count; i++) {
688 const SkGlyph* glyph = fGlyphArray[i]; 635 const SkGlyph* glyph = fGlyphArray[i];
689 SkASSERT(glyph); 636 SkASSERT(glyph);
690 SkASSERT(fGlyphAlloc.contains(glyph)); 637 SkASSERT(fGlyphAlloc.contains(glyph));
691 if (glyph->fImage) { 638 if (glyph->fImage) {
692 SkASSERT(fGlyphAlloc.contains(glyph->fImage)); 639 SkASSERT(fGlyphAlloc.contains(glyph->fImage));
693 } 640 }
694 } 641 }
695 #endif 642 #endif
696 } 643 }
644
645 void SkGlyphCache_Globals::validate() const {
646 size_t computedBytes = 0;
647
648 const SkGlyphCache* head = fHead;
649 while (head != NULL) {
650 computedBytes += head->fMemoryUsed;
651 head = head->fNext;
652 }
653
654 SkASSERT(fTotalMemoryUsed == computedBytes);
655 }
656
697 #endif 657 #endif
698 658
699 /////////////////////////////////////////////////////////////////////////////// 659 ///////////////////////////////////////////////////////////////////////////////
700 /////////////////////////////////////////////////////////////////////////////// 660 ///////////////////////////////////////////////////////////////////////////////
701 661
702 #include "SkTypefaceCache.h" 662 #include "SkTypefaceCache.h"
703 663
704 size_t SkGraphics::GetFontCacheLimit() { 664 size_t SkGraphics::GetFontCacheLimit() {
705 return getSharedGlobals().getFontCacheLimit(); 665 return getSharedGlobals().getCacheSizeLimit();
706 } 666 }
707 667
708 size_t SkGraphics::SetFontCacheLimit(size_t bytes) { 668 size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
709 return getSharedGlobals().setFontCacheLimit(bytes); 669 return getSharedGlobals().setCacheSizeLimit(bytes);
710 } 670 }
711 671
712 size_t SkGraphics::GetFontCacheUsed() { 672 size_t SkGraphics::GetFontCacheUsed() {
713 return getSharedGlobals().fTotalMemoryUsed; 673 return getSharedGlobals().fTotalMemoryUsed;
714 } 674 }
715 675
716 void SkGraphics::PurgeFontCache() { 676 void SkGraphics::PurgeFontCache() {
717 getSharedGlobals().purgeAll(); 677 getSharedGlobals().purgeAll();
718 SkTypefaceCache::PurgeAll(); 678 SkTypefaceCache::PurgeAll();
719 } 679 }
720 680
721 size_t SkGraphics::GetTLSFontCacheLimit() { 681 size_t SkGraphics::GetTLSFontCacheLimit() {
722 const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS(); 682 const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
723 return tls ? tls->getFontCacheLimit() : 0; 683 return tls ? tls->getCacheSizeLimit() : 0;
724 } 684 }
725 685
726 void SkGraphics::SetTLSFontCacheLimit(size_t bytes) { 686 void SkGraphics::SetTLSFontCacheLimit(size_t bytes) {
bungeman-skia 2013/09/25 14:46:07 I realize that this isn't part of this change, but
reed1 2013/09/25 17:30:31 Agreed, we should rename/document this "feature" b
727 if (0 == bytes) { 687 if (0 == bytes) {
728 SkGlyphCache_Globals::DeleteTLS(); 688 SkGlyphCache_Globals::DeleteTLS();
729 } else { 689 } else {
730 SkGlyphCache_Globals::GetTLS().setFontCacheLimit(bytes); 690 SkGlyphCache_Globals::GetTLS().setCacheSizeLimit(bytes);
731 } 691 }
732 } 692 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698