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

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
bungeman-skia 2013/09/26 18:40:27 Spew!!!
reed1 2013/09/26 19:17:14 Done.
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 357 matching lines...) Expand 10 before | Expand all | Expand 10 after
390 rec->fProc(rec->fData); 405 rec->fProc(rec->fData);
391 AuxProcRec* next = rec->fNext; 406 AuxProcRec* next = rec->fNext;
392 SkDELETE(rec); 407 SkDELETE(rec);
393 rec = next; 408 rec = next;
394 } 409 }
395 } 410 }
396 411
397 /////////////////////////////////////////////////////////////////////////////// 412 ///////////////////////////////////////////////////////////////////////////////
398 /////////////////////////////////////////////////////////////////////////////// 413 ///////////////////////////////////////////////////////////////////////////////
399 414
400 #ifndef SK_DEFAULT_FONT_CACHE_LIMIT
401 #define SK_DEFAULT_FONT_CACHE_LIMIT (2 * 1024 * 1024)
402 #endif
403
404 #include "SkThread.h" 415 #include "SkThread.h"
405 416
406 class SkGlyphCache_Globals { 417 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; 418 static const size_t minLimit = 256 * 1024;
470 if (newLimit < minLimit) { 419 if (newLimit < minLimit) {
471 newLimit = minLimit; 420 newLimit = minLimit;
472 } 421 }
473 422
474 size_t prevLimit = fFontCacheLimit; 423 SkAutoMutexAcquire ac(fMutex);
475 fFontCacheLimit = newLimit;
476 424
477 size_t currUsed = fTotalMemoryUsed; 425 size_t prevLimit = fCacheSizeLimit;
478 if (currUsed > newLimit) { 426 fCacheSizeLimit = newLimit;
479 SkAutoMutexAcquire ac(fMutex); 427 this->internalPurge();
480 SkGlyphCache::InternalFreeCache(this, currUsed - newLimit);
481 }
482 return prevLimit; 428 return prevLimit;
483 } 429 }
484 430
485 void SkGlyphCache_Globals::purgeAll() { 431 void SkGlyphCache_Globals::purgeAll() {
486 SkAutoMutexAcquire ac(fMutex); 432 SkAutoMutexAcquire ac(fMutex);
487 SkGlyphCache::InternalFreeCache(this, fTotalMemoryUsed); 433 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 } 434 }
503 435
504 void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*), 436 void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*),
505 void* context) { 437 void* context) {
506 SkGlyphCache_Globals& globals = getGlobals(); 438 SkGlyphCache_Globals& globals = getGlobals();
507 SkAutoMutexAcquire ac(globals.fMutex); 439 SkAutoMutexAcquire ac(globals.fMutex);
508 SkGlyphCache* cache; 440 SkGlyphCache* cache;
509 441
510 globals.validate(); 442 globals.validate();
511 443
512 for (cache = globals.fHead; cache != NULL; cache = cache->fNext) { 444 for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) {
513 if (proc(cache, context)) { 445 if (proc(cache, context)) {
514 break; 446 break;
515 } 447 }
516 } 448 }
517 449
518 globals.validate(); 450 globals.validate();
519 } 451 }
520 452
521 /* This guy calls the visitor from within the mutext lock, so the visitor 453 /* This guy calls the visitor from within the mutext lock, so the visitor
522 cannot: 454 cannot:
(...skipping 10 matching lines...) Expand all
533 } 465 }
534 SkASSERT(desc); 466 SkASSERT(desc);
535 467
536 SkGlyphCache_Globals& globals = getGlobals(); 468 SkGlyphCache_Globals& globals = getGlobals();
537 SkAutoMutexAcquire ac(globals.fMutex); 469 SkAutoMutexAcquire ac(globals.fMutex);
538 SkGlyphCache* cache; 470 SkGlyphCache* cache;
539 bool insideMutex = true; 471 bool insideMutex = true;
540 472
541 globals.validate(); 473 globals.validate();
542 474
543 for (cache = globals.fHead; cache != NULL; cache = cache->fNext) { 475 for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) {
544 if (cache->fDesc->equals(*desc)) { 476 if (cache->fDesc->equals(*desc)) {
545 cache->detach(&globals.fHead); 477 globals.internalDetachCache(cache);
546 goto FOUND_IT; 478 goto FOUND_IT;
547 } 479 }
548 } 480 }
549 481
550 /* Release the mutex now, before we create a new entry (which might have 482 /* Release the mutex now, before we create a new entry (which might have
551 side-effects like trying to access the cache/mutex (yikes!) 483 side-effects like trying to access the cache/mutex (yikes!)
552 */ 484 */
553 ac.release(); // release the mutex now 485 ac.release(); // release the mutex now
554 insideMutex = false; // can't use globals anymore 486 insideMutex = false; // can't use globals anymore
555 487
556 // Check if we can create a scaler-context before creating the glyphcache. 488 // Check if we can create a scaler-context before creating the glyphcache.
557 // If not, we may have exhausted OS/font resources, so try purging the 489 // If not, we may have exhausted OS/font resources, so try purging the
558 // cache once and try again. 490 // cache once and try again.
559 { 491 {
560 // pass true the first time, to notice if the scalercontext failed, 492 // pass true the first time, to notice if the scalercontext failed,
561 // so we can try the purge. 493 // so we can try the purge.
562 SkScalerContext* ctx = typeface->createScalerContext(desc, true); 494 SkScalerContext* ctx = typeface->createScalerContext(desc, true);
563 if (!ctx) { 495 if (!ctx) {
564 getSharedGlobals().purgeAll(); 496 getSharedGlobals().purgeAll();
565 ctx = typeface->createScalerContext(desc, false); 497 ctx = typeface->createScalerContext(desc, false);
566 SkASSERT(ctx); 498 SkASSERT(ctx);
567 } 499 }
568 cache = SkNEW_ARGS(SkGlyphCache, (typeface, desc, ctx)); 500 cache = SkNEW_ARGS(SkGlyphCache, (typeface, desc, ctx));
569 } 501 }
570 502
571 FOUND_IT: 503 FOUND_IT:
572 504
573 AutoValidate av(cache); 505 AutoValidate av(cache);
574 506
575 if (proc(cache, context)) { // stay detached 507 if (!proc(cache, context)) { // need to reattach
576 if (insideMutex) { 508 if (insideMutex) {
577 SkASSERT(globals.fTotalMemoryUsed >= cache->fMemoryUsed); 509 globals.internalAttachCacheToHead(cache);
578 globals.fTotalMemoryUsed -= cache->fMemoryUsed;
579 }
580 } else { // reattach
581 if (insideMutex) {
582 cache->attachToHead(&globals.fHead);
583 } else { 510 } else {
584 AttachCache(cache); 511 globals.attachCacheToHead(cache);
585 } 512 }
586 cache = NULL; 513 cache = NULL;
587 } 514 }
588 return cache; 515 return cache;
589 } 516 }
590 517
591 void SkGlyphCache::AttachCache(SkGlyphCache* cache) { 518 void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
592 SkASSERT(cache); 519 SkASSERT(cache);
593 SkASSERT(cache->fNext == NULL); 520 SkASSERT(cache->fNext == NULL);
594 521
595 SkGlyphCache_Globals& globals = getGlobals(); 522 getGlobals().attachCacheToHead(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 } 523 }
615 524
616 /////////////////////////////////////////////////////////////////////////////// 525 ///////////////////////////////////////////////////////////////////////////////
617 526
618 SkGlyphCache* SkGlyphCache::FindTail(SkGlyphCache* cache) { 527 void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) {
528 SkAutoMutexAcquire ac(fMutex);
529
530 this->validate();
531 cache->validate();
532
533 this->internalAttachCacheToHead(cache);
534 this->internalPurge();
535 }
536
537 SkGlyphCache* SkGlyphCache_Globals::internalGetTail() const {
538 SkGlyphCache* cache = fHead;
619 if (cache) { 539 if (cache) {
620 while (cache->fNext) { 540 while (cache->fNext) {
621 cache = cache->fNext; 541 cache = cache->fNext;
622 } 542 }
623 } 543 }
624 return cache; 544 return cache;
625 } 545 }
626 546
627 #ifdef SK_DEBUG 547 size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) {
628 void SkGlyphCache_Globals::validate() const { 548 this->validate();
629 size_t computed = 0;
630 549
631 const SkGlyphCache* head = fHead; 550 size_t bytesNeeded = 0;
632 while (head != NULL) { 551 if (fTotalMemoryUsed > fCacheSizeLimit) {
633 computed += head->fMemoryUsed; 552 bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
634 head = head->fNext; 553 }
554 bytesNeeded = SkMax32(bytesNeeded, minBytesNeeded);
555 if (bytesNeeded) {
556 // no small purges!
557 bytesNeeded = SkMax32(bytesNeeded, fTotalMemoryUsed >> 2);
635 } 558 }
636 559
637 if (fTotalMemoryUsed != computed) { 560 int countNeeded = 0;
638 printf("total %d, computed %d\n", (int)fTotalMemoryUsed, (int)computed); 561 if (fCacheCount > fCacheCountLimit) {
562 countNeeded = fCacheCount - fCacheCountLimit;
563 // no small purges!
564 countNeeded = SkMax32(countNeeded, fCacheCount >> 2);
639 } 565 }
640 SkASSERT(fTotalMemoryUsed == computed);
641 }
642 #endif
643 566
644 size_t SkGlyphCache::InternalFreeCache(SkGlyphCache_Globals* globals, 567 // early exit
645 size_t bytesNeeded) { 568 if (!countNeeded && !bytesNeeded) {
646 globals->validate(); 569 return 0;
570 }
647 571
648 size_t bytesFreed = 0; 572 size_t bytesFreed = 0;
649 int count = 0; 573 int countFreed = 0;
650 574
651 // don't do any "small" purges 575 // we start at the tail and proceed backwards, as the linklist is in LRU
652 size_t minToPurge = globals->fTotalMemoryUsed >> 2; 576 // order, with unimportant entries at the tail.
653 if (bytesNeeded < minToPurge) 577 SkGlyphCache* cache = this->internalGetTail();
654 bytesNeeded = minToPurge; 578 while (cache != NULL &&
655 579 (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
656 SkGlyphCache* cache = FindTail(globals->fHead);
657 while (cache != NULL && bytesFreed < bytesNeeded) {
658 SkGlyphCache* prev = cache->fPrev; 580 SkGlyphCache* prev = cache->fPrev;
659 bytesFreed += cache->fMemoryUsed; 581 bytesFreed += cache->fMemoryUsed;
582 countFreed += 1;
660 583
661 cache->detach(&globals->fHead); 584 this->internalDetachCache(cache);
662 SkDELETE(cache); 585 SkDELETE(cache);
663 cache = prev; 586 cache = prev;
664 count += 1;
665 } 587 }
666 588
667 SkASSERT(bytesFreed <= globals->fTotalMemoryUsed); 589 this->validate();
668 globals->fTotalMemoryUsed -= bytesFreed;
669 globals->validate();
670 590
671 #ifdef SPEW_PURGE_STATUS 591 #ifdef SPEW_PURGE_STATUS
672 if (count && !gSkSuppressFontCachePurgeSpew) { 592 if (countFreed && !gSkSuppressFontCachePurgeSpew) {
673 SkDebugf("purging %dK from font cache [%d entries]\n", 593 SkDebugf("purging %dK from font cache [%d entries]\n",
674 (int)(bytesFreed >> 10), count); 594 (int)(bytesFreed >> 10), countFreed);
675 } 595 }
676 #endif 596 #endif
677 597
678 return bytesFreed; 598 return bytesFreed;
679 } 599 }
680 600
601 void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) {
602 SkASSERT(NULL == cache->fPrev && NULL == cache->fNext);
603 if (fHead) {
604 fHead->fPrev = cache;
605 cache->fNext = fHead;
606 }
607 fHead = cache;
608
609 fCacheCount += 1;
610 fTotalMemoryUsed += cache->fMemoryUsed;
611 }
612
613 void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) {
614 SkASSERT(fCacheCount > 0);
615 fCacheCount -= 1;
616 fTotalMemoryUsed -= cache->fMemoryUsed;
617
618 if (cache->fPrev) {
619 cache->fPrev->fNext = cache->fNext;
620 } else {
621 fHead = cache->fNext;
622 }
623 if (cache->fNext) {
624 cache->fNext->fPrev = cache->fPrev;
625 }
626 cache->fPrev = cache->fNext = NULL;
627 }
628
681 /////////////////////////////////////////////////////////////////////////////// 629 ///////////////////////////////////////////////////////////////////////////////
682 630
683 #ifdef SK_DEBUG 631 #ifdef SK_DEBUG
632
684 void SkGlyphCache::validate() const { 633 void SkGlyphCache::validate() const {
685 #ifdef SK_DEBUG_GLYPH_CACHE 634 #ifdef SK_DEBUG_GLYPH_CACHE
686 int count = fGlyphArray.count(); 635 int count = fGlyphArray.count();
687 for (int i = 0; i < count; i++) { 636 for (int i = 0; i < count; i++) {
688 const SkGlyph* glyph = fGlyphArray[i]; 637 const SkGlyph* glyph = fGlyphArray[i];
689 SkASSERT(glyph); 638 SkASSERT(glyph);
690 SkASSERT(fGlyphAlloc.contains(glyph)); 639 SkASSERT(fGlyphAlloc.contains(glyph));
691 if (glyph->fImage) { 640 if (glyph->fImage) {
692 SkASSERT(fGlyphAlloc.contains(glyph->fImage)); 641 SkASSERT(fGlyphAlloc.contains(glyph->fImage));
693 } 642 }
694 } 643 }
695 #endif 644 #endif
696 } 645 }
646
647 void SkGlyphCache_Globals::validate() const {
648 size_t computedBytes = 0;
649 int computedCount = 0;
650
651 const SkGlyphCache* head = fHead;
652 while (head != NULL) {
653 computedBytes += head->fMemoryUsed;
654 computedCount += 1;
655 head = head->fNext;
656 }
657
658 SkASSERT(fTotalMemoryUsed == computedBytes);
659 SkASSERT(fCacheCount == computedCount);
660 }
661
697 #endif 662 #endif
698 663
699 /////////////////////////////////////////////////////////////////////////////// 664 ///////////////////////////////////////////////////////////////////////////////
700 /////////////////////////////////////////////////////////////////////////////// 665 ///////////////////////////////////////////////////////////////////////////////
701 666
702 #include "SkTypefaceCache.h" 667 #include "SkTypefaceCache.h"
703 668
704 size_t SkGraphics::GetFontCacheLimit() { 669 size_t SkGraphics::GetFontCacheLimit() {
705 return getSharedGlobals().getFontCacheLimit(); 670 return getSharedGlobals().getCacheSizeLimit();
706 } 671 }
707 672
708 size_t SkGraphics::SetFontCacheLimit(size_t bytes) { 673 size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
709 return getSharedGlobals().setFontCacheLimit(bytes); 674 return getSharedGlobals().setCacheSizeLimit(bytes);
710 } 675 }
711 676
712 size_t SkGraphics::GetFontCacheUsed() { 677 size_t SkGraphics::GetFontCacheUsed() {
713 return getSharedGlobals().fTotalMemoryUsed; 678 return getSharedGlobals().getTotalMemoryUsed();
714 } 679 }
715 680
716 void SkGraphics::PurgeFontCache() { 681 void SkGraphics::PurgeFontCache() {
717 getSharedGlobals().purgeAll(); 682 getSharedGlobals().purgeAll();
718 SkTypefaceCache::PurgeAll(); 683 SkTypefaceCache::PurgeAll();
719 } 684 }
720 685
721 size_t SkGraphics::GetTLSFontCacheLimit() { 686 size_t SkGraphics::GetTLSFontCacheLimit() {
722 const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS(); 687 const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
723 return tls ? tls->getFontCacheLimit() : 0; 688 return tls ? tls->getCacheSizeLimit() : 0;
724 } 689 }
725 690
726 void SkGraphics::SetTLSFontCacheLimit(size_t bytes) { 691 void SkGraphics::SetTLSFontCacheLimit(size_t bytes) {
727 if (0 == bytes) { 692 if (0 == bytes) {
728 SkGlyphCache_Globals::DeleteTLS(); 693 SkGlyphCache_Globals::DeleteTLS();
729 } else { 694 } else {
730 SkGlyphCache_Globals::GetTLS().setFontCacheLimit(bytes); 695 SkGlyphCache_Globals::GetTLS().setCacheSizeLimit(bytes);
731 } 696 }
732 } 697 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698