1 /* libs/graphics/sgl/SkGlyphCache.cpp 2 ** 3 ** Copyright 2006, The Android Open Source Project 4 ** 5 ** Licensed under the Apache License, Version 2.0 (the "License"); 6 ** you may not use this file except in compliance with the License. 7 ** You may obtain a copy of the License at 8 ** 9 ** http://www.apache.org/licenses/LICENSE-2.0 10 ** 11 ** Unless required by applicable law or agreed to in writing, software 12 ** distributed under the License is distributed on an "AS IS" BASIS, 13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 ** See the License for the specific language governing permissions and 15 ** limitations under the License. 16 */ 17 18 #include "SkGlyphCache.h" 19 #include "SkFontHost.h" 20 #include "SkPaint.h" 21 #include "SkTemplates.h" 22 23 #define SPEW_PURGE_STATUS 24 //#define USE_CACHE_HASH 25 //#define RECORD_HASH_EFFICIENCY 26 27 /////////////////////////////////////////////////////////////////////////////// 28 29 #ifdef RECORD_HASH_EFFICIENCY 30 static uint32_t gHashSuccess; 31 static uint32_t gHashCollision; 32 33 static void RecordHashSuccess() { 34 gHashSuccess += 1; 35 } 36 37 static void RecordHashCollisionIf(bool pred) { 38 if (pred) { 39 gHashCollision += 1; 40 41 uint32_t total = gHashSuccess + gHashCollision; 42 SkDebugf("Font Cache Hash success rate: %d%%\n", 43 100 * gHashSuccess / total); 44 } 45 } 46 #else 47 #define RecordHashSuccess() (void)0 48 #define RecordHashCollisionIf(pred) (void)0 49 #endif 50 #define RecordHashCollision() RecordHashCollisionIf(true) 51 52 /////////////////////////////////////////////////////////////////////////////// 53 54 #define kMinGlphAlloc (sizeof(SkGlyph) * 64) 55 #define kMinImageAlloc (24 * 64) // should be pointsize-dependent 56 57 #define METRICS_RESERVE_COUNT 128 // so we don't grow this array a lot 58 59 SkGlyphCache::SkGlyphCache(const SkDescriptor* desc) 60 : fGlyphAlloc(kMinGlphAlloc), fImageAlloc(kMinImageAlloc) { 61 fPrev = fNext = NULL; 62 63 fDesc = desc->copy(); 64 fScalerContext = SkScalerContext::Create(desc); 65 fScalerContext->getFontMetrics(NULL, &fFontMetricsY); 66 67 // init to 0 so that all of the pointers will be null 68 memset(fGlyphHash, 0, sizeof(fGlyphHash)); 69 // init with 0xFF so that the charCode field will be -1, which is invalid 70 memset(fCharToGlyphHash, 0xFF, sizeof(fCharToGlyphHash)); 71 72 fMemoryUsed = sizeof(*this) + kMinGlphAlloc + kMinImageAlloc; 73 74 fGlyphArray.setReserve(METRICS_RESERVE_COUNT); 75 76 fMetricsCount = 0; 77 fAdvanceCount = 0; 78 fAuxProcList = NULL; 79 } 80 81 SkGlyphCache::~SkGlyphCache() { 82 SkGlyph** gptr = fGlyphArray.begin(); 83 SkGlyph** stop = fGlyphArray.end(); 84 while (gptr < stop) { 85 SkPath* path = (*gptr)->fPath; 86 if (path) { 87 SkDELETE(path); 88 } 89 gptr += 1; 90 } 91 SkDescriptor::Free(fDesc); 92 SkDELETE(fScalerContext); 93 this->invokeAndRemoveAuxProcs(); 94 } 95 96 /////////////////////////////////////////////////////////////////////////////// 97 98 #ifdef SK_DEBUG 99 class AutoCheckForNull { 100 public: 101 AutoCheckForNull(const SkTDArray<SkGlyph*>& array) : fArray(array) { 102 for (int i = 0; i < array.count(); i++) 103 SkASSERT(array[i]); 104 } 105 ~AutoCheckForNull() { 106 const SkTDArray<SkGlyph*>& array = fArray; 107 for (int i = 0; i < array.count(); i++) { 108 SkASSERT(array[i]); 109 } 110 } 111 private: 112 const SkTDArray<SkGlyph*>& fArray; 113 }; 114 #define VALIDATE() AutoCheckForNull acfn(fGlyphArray) 115 #else 116 #define VALIDATE() 117 #endif 118 119 uint16_t SkGlyphCache::unicharToGlyph(SkUnichar charCode) { 120 VALIDATE(); 121 uint32_t id = SkGlyph::MakeID(charCode); 122 const CharGlyphRec& rec = fCharToGlyphHash[ID2HashIndex(id)]; 123 124 if (rec.fID == id) { 125 return rec.fGlyph->getGlyphID(); 126 } else { 127 return fScalerContext->charToGlyphID(charCode); 128 } 129 } 130 131 SkUnichar SkGlyphCache::glyphToUnichar(uint16_t glyphID) { 132 return fScalerContext->glyphIDToChar(glyphID); 133 } 134 135 /////////////////////////////////////////////////////////////////////////////// 136 137 const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) { 138 VALIDATE(); 139 uint32_t id = SkGlyph::MakeID(charCode); 140 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)]; 141 142 if (rec->fID != id) { 143 // this ID is based on the UniChar 144 rec->fID = id; 145 // this ID is based on the glyph index 146 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode)); 147 rec->fGlyph = this->lookupMetrics(id, kJustAdvance_MetricsType); 148 } 149 return *rec->fGlyph; 150 } 151 152 const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) { 153 VALIDATE(); 154 uint32_t id = SkGlyph::MakeID(glyphID); 155 unsigned index = ID2HashIndex(id); 156 SkGlyph* glyph = fGlyphHash[index]; 157 158 if (NULL == glyph || glyph->fID != id) { 159 glyph = this->lookupMetrics(glyphID, kJustAdvance_MetricsType); 160 fGlyphHash[index] = glyph; 161 } 162 return *glyph; 163 } 164 165 /////////////////////////////////////////////////////////////////////////////// 166 167 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) { 168 VALIDATE(); 169 uint32_t id = SkGlyph::MakeID(charCode); 170 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)]; 171 172 if (rec->fID != id) { 173 RecordHashCollisionIf(rec->fGlyph != NULL); 174 // this ID is based on the UniChar 175 rec->fID = id; 176 // this ID is based on the glyph index 177 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode)); 178 rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType); 179 } else { 180 RecordHashSuccess(); 181 if (rec->fGlyph->isJustAdvance()) { 182 fScalerContext->getMetrics(rec->fGlyph); 183 } 184 } 185 SkASSERT(rec->fGlyph->isFullMetrics()); 186 return *rec->fGlyph; 187 } 188 189 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode, 190 SkFixed x, SkFixed y) { 191 VALIDATE(); 192 uint32_t id = SkGlyph::MakeID(charCode, x, y); 193 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)]; 194 195 if (rec->fID != id) { 196 RecordHashCollisionIf(rec->fGlyph != NULL); 197 // this ID is based on the UniChar 198 rec->fID = id; 199 // this ID is based on the glyph index 200 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode), x, y); 201 rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType); 202 } else { 203 RecordHashSuccess(); 204 if (rec->fGlyph->isJustAdvance()) { 205 fScalerContext->getMetrics(rec->fGlyph); 206 } 207 } 208 SkASSERT(rec->fGlyph->isFullMetrics()); 209 return *rec->fGlyph; 210 } 211 212 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) { 213 VALIDATE(); 214 uint32_t id = SkGlyph::MakeID(glyphID); 215 unsigned index = ID2HashIndex(id); 216 SkGlyph* glyph = fGlyphHash[index]; 217 218 if (NULL == glyph || glyph->fID != id) { 219 RecordHashCollisionIf(glyph != NULL); 220 glyph = this->lookupMetrics(glyphID, kFull_MetricsType); 221 fGlyphHash[index] = glyph; 222 } else { 223 RecordHashSuccess(); 224 if (glyph->isJustAdvance()) { 225 fScalerContext->getMetrics(glyph); 226 } 227 } 228 SkASSERT(glyph->isFullMetrics()); 229 return *glyph; 230 } 231 232 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID, 233 SkFixed x, SkFixed y) { 234 VALIDATE(); 235 uint32_t id = SkGlyph::MakeID(glyphID, x, y); 236 unsigned index = ID2HashIndex(id); 237 SkGlyph* glyph = fGlyphHash[index]; 238 239 if (NULL == glyph || glyph->fID != id) { 240 RecordHashCollisionIf(glyph != NULL); 241 glyph = this->lookupMetrics(id, kFull_MetricsType); 242 fGlyphHash[index] = glyph; 243 } else { 244 RecordHashSuccess(); 245 if (glyph->isJustAdvance()) { 246 fScalerContext->getMetrics(glyph); 247 } 248 } 249 SkASSERT(glyph->isFullMetrics()); 250 return *glyph; 251 } 252 253 SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) { 254 SkGlyph* glyph; 255 256 int hi = 0; 257 int count = fGlyphArray.count(); 258 259 if (count) { 260 SkGlyph** gptr = fGlyphArray.begin(); 261 int lo = 0; 262 263 hi = count - 1; 264 while (lo < hi) { 265 int mid = (hi + lo) >> 1; 266 if (gptr[mid]->fID < id) { 267 lo = mid + 1; 268 } else { 269 hi = mid; 270 } 271 } 272 glyph = gptr[hi]; 273 if (glyph->fID == id) { 274 if (kFull_MetricsType == mtype && glyph->isJustAdvance()) { 275 fScalerContext->getMetrics(glyph); 276 } 277 return glyph; 278 } 279 280 // check if we need to bump hi before falling though to the allocator 281 if (glyph->fID < id) { 282 hi += 1; 283 } 284 } 285 286 // not found, but hi tells us where to inser the new glyph 287 fMemoryUsed += sizeof(SkGlyph); 288 289 glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph), 290 SkChunkAlloc::kThrow_AllocFailType); 291 glyph->fID = id; 292 glyph->fImage = NULL; 293 glyph->fPath = NULL; 294 *fGlyphArray.insert(hi) = glyph; 295 296 if (kJustAdvance_MetricsType == mtype) { 297 fScalerContext->getAdvance(glyph); 298 fAdvanceCount += 1; 299 } else { 300 SkASSERT(kFull_MetricsType == mtype); 301 fScalerContext->getMetrics(glyph); 302 fMetricsCount += 1; 303 } 304 305 return glyph; 306 } 307 308 const void* SkGlyphCache::findImage(const SkGlyph& glyph) { 309 if (glyph.fWidth) { 310 if (glyph.fImage == NULL) { 311 size_t size = glyph.computeImageSize(); 312 const_cast<SkGlyph&>(glyph).fImage = fImageAlloc.alloc(size, 313 SkChunkAlloc::kReturnNil_AllocFailType); 314 fScalerContext->getImage(glyph); 315 fMemoryUsed += size; 316 } 317 } 318 return glyph.fImage; 319 } 320 321 const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) { 322 if (glyph.fWidth) { 323 if (glyph.fPath == NULL) { 324 const_cast<SkGlyph&>(glyph).fPath = SkNEW(SkPath); 325 fScalerContext->getPath(glyph, glyph.fPath); 326 fMemoryUsed += sizeof(SkPath) + 327 glyph.fPath->getPoints(NULL, 0x7FFFFFFF) * sizeof(SkPoint); 328 } 329 } 330 return glyph.fPath; 331 } 332 333 /////////////////////////////////////////////////////////////////////////////// 334 335 bool SkGlyphCache::getAuxProcData(void (*proc)(void*), void** dataPtr) const { 336 const AuxProcRec* rec = fAuxProcList; 337 while (rec) { 338 if (rec->fProc == proc) { 339 if (dataPtr) { 340 *dataPtr = rec->fData; 341 } 342 return true; 343 } 344 rec = rec->fNext; 345 } 346 return false; 347 } 348 349 void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) { 350 if (proc == NULL) { 351 return; 352 } 353 354 AuxProcRec* rec = fAuxProcList; 355 while (rec) { 356 if (rec->fProc == proc) { 357 rec->fData = data; 358 return; 359 } 360 rec = rec->fNext; 361 } 362 // not found, create a new rec 363 rec = SkNEW(AuxProcRec); 364 rec->fProc = proc; 365 rec->fData = data; 366 rec->fNext = fAuxProcList; 367 fAuxProcList = rec; 368 } 369 370 void SkGlyphCache::removeAuxProc(void (*proc)(void*)) { 371 AuxProcRec* rec = fAuxProcList; 372 AuxProcRec* prev = NULL; 373 while (rec) { 374 AuxProcRec* next = rec->fNext; 375 if (rec->fProc == proc) { 376 if (prev) { 377 prev->fNext = next; 378 } else { 379 fAuxProcList = next; 380 } 381 SkDELETE(rec); 382 return; 383 } 384 prev = rec; 385 rec = next; 386 } 387 } 388 389 void SkGlyphCache::invokeAndRemoveAuxProcs() { 390 AuxProcRec* rec = fAuxProcList; 391 while (rec) { 392 rec->fProc(rec->fData); 393 AuxProcRec* next = rec->fNext; 394 SkDELETE(rec); 395 rec = next; 396 } 397 } 398 399 /////////////////////////////////////////////////////////////////////////////// 400 /////////////////////////////////////////////////////////////////////////////// 401 402 #include "SkGlobals.h" 403 #include "SkThread.h" 404 405 #define SkGlyphCache_GlobalsTag SkSetFourByteTag('g', 'l', 'f', 'c') 406 407 #ifdef USE_CACHE_HASH 408 #define HASH_BITCOUNT 6 409 #define HASH_COUNT (1 << HASH_BITCOUNT) 410 #define HASH_MASK (HASH_COUNT - 1) 411 412 static unsigned desc_to_hashindex(const SkDescriptor* desc) 413 { 414 SkASSERT(HASH_MASK < 256); // since our munging reduces to 8 bits 415 416 uint32_t n = *(const uint32_t*)desc; //desc->getChecksum(); 417 SkASSERT(n == desc->getChecksum()); 418 419 // don't trust that the low bits of checksum vary enough, so... 420 n ^= (n >> 24) ^ (n >> 16) ^ (n >> 8) ^ (n >> 30); 421 422 return n & HASH_MASK; 423 } 424 #endif 425 426 class SkGlyphCache_Globals : public SkGlobals::Rec { 427 public: 428 SkMutex fMutex; 429 SkGlyphCache* fHead; 430 size_t fTotalMemoryUsed; 431 #ifdef USE_CACHE_HASH 432 SkGlyphCache* fHash[HASH_COUNT]; 433 #endif 434 435 #ifdef SK_DEBUG 436 void validate() const; 437 #else 438 void validate() const {} 439 #endif 440 }; 441 442 #ifdef SK_USE_RUNTIME_GLOBALS 443 static SkGlobals::Rec* create_globals() { 444 SkGlyphCache_Globals* rec = SkNEW(SkGlyphCache_Globals); 445 rec->fHead = NULL; 446 rec->fTotalMemoryUsed = 0; 447 #ifdef USE_CACHE_HASH 448 memset(rec->fHash, 0, sizeof(rec->fHash)); 449 #endif 450 return rec; 451 } 452 453 #define FIND_GC_GLOBALS() *(SkGlyphCache_Globals*)SkGlobals::Find(SkGlyphCache_GlobalsTag, create_globals) 454 #define GET_GC_GLOBALS() *(SkGlyphCache_Globals*)SkGlobals::Get(SkGlyphCache_GlobalsTag) 455 #else 456 static SkGlyphCache_Globals gGCGlobals; 457 #define FIND_GC_GLOBALS() gGCGlobals 458 #define GET_GC_GLOBALS() gGCGlobals 459 #endif 460 461 void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*), 462 void* context) { 463 SkGlyphCache_Globals& globals = FIND_GC_GLOBALS(); 464 SkAutoMutexAcquire ac(globals.fMutex); 465 SkGlyphCache* cache; 466 467 globals.validate(); 468 469 for (cache = globals.fHead; cache != NULL; cache = cache->fNext) { 470 if (proc(cache, context)) { 471 break; 472 } 473 } 474 475 globals.validate(); 476 } 477 478 /* This guy calls the visitor from within the mutext lock, so the visitor 479 cannot: 480 - take too much time 481 - try to acquire the mutext again 482 - call a fontscaler (which might call into the cache) 483 */ 484 SkGlyphCache* SkGlyphCache::VisitCache(const SkDescriptor* desc, 485 bool (*proc)(const SkGlyphCache*, void*), 486 void* context) { 487 SkASSERT(desc); 488 489 SkGlyphCache_Globals& globals = FIND_GC_GLOBALS(); 490 SkAutoMutexAcquire ac(globals.fMutex); 491 SkGlyphCache* cache; 492 bool insideMutex = true; 493 494 globals.validate(); 495 496 #ifdef USE_CACHE_HASH 497 SkGlyphCache** hash = globals.fHash; 498 unsigned index = desc_to_hashindex(desc); 499 cache = hash[index]; 500 if (cache && *cache->fDesc == *desc) { 501 cache->detach(&globals.fHead); 502 goto FOUND_IT; 503 } 504 #endif 505 506 for (cache = globals.fHead; cache != NULL; cache = cache->fNext) { 507 if (cache->fDesc->equals(*desc)) { 508 cache->detach(&globals.fHead); 509 goto FOUND_IT; 510 } 511 } 512 513 /* Release the mutex now, before we create a new entry (which might have 514 side-effects like trying to access the cache/mutex (yikes!) 515 */ 516 ac.release(); // release the mutex now 517 insideMutex = false; // can't use globals anymore 518 519 cache = SkNEW_ARGS(SkGlyphCache, (desc)); 520 521 FOUND_IT: 522 if (proc(cache, context)) { // stay detached 523 if (insideMutex) { 524 SkASSERT(globals.fTotalMemoryUsed >= cache->fMemoryUsed); 525 globals.fTotalMemoryUsed -= cache->fMemoryUsed; 526 #ifdef USE_CACHE_HASH 527 hash[index] = NULL; 528 #endif 529 } 530 } else { // reattach 531 if (insideMutex) { 532 cache->attachToHead(&globals.fHead); 533 #ifdef USE_CACHE_HASH 534 hash[index] = cache; 535 #endif 536 } else { 537 AttachCache(cache); 538 } 539 cache = NULL; 540 } 541 return cache; 542 } 543 544 void SkGlyphCache::AttachCache(SkGlyphCache* cache) { 545 SkASSERT(cache); 546 SkASSERT(cache->fNext == NULL); 547 548 SkGlyphCache_Globals& globals = GET_GC_GLOBALS(); 549 SkAutoMutexAcquire ac(globals.fMutex); 550 551 globals.validate(); 552 553 // if we have a fixed budget for our cache, do a purge here 554 { 555 size_t allocated = globals.fTotalMemoryUsed + cache->fMemoryUsed; 556 size_t amountToFree = SkFontHost::ShouldPurgeFontCache(allocated); 557 if (amountToFree) 558 (void)InternalFreeCache(&globals, amountToFree); 559 } 560 561 cache->attachToHead(&globals.fHead); 562 globals.fTotalMemoryUsed += cache->fMemoryUsed; 563 564 #ifdef USE_CACHE_HASH 565 unsigned index = desc_to_hashindex(cache->fDesc); 566 SkASSERT(globals.fHash[index] != cache); 567 globals.fHash[index] = cache; 568 #endif 569 570 globals.validate(); 571 } 572 573 size_t SkGlyphCache::GetCacheUsed() { 574 SkGlyphCache_Globals& globals = FIND_GC_GLOBALS(); 575 SkAutoMutexAcquire ac(globals.fMutex); 576 577 return SkGlyphCache::ComputeMemoryUsed(globals.fHead); 578 } 579 580 bool SkGlyphCache::SetCacheUsed(size_t bytesUsed) { 581 size_t curr = SkGlyphCache::GetCacheUsed(); 582 583 if (curr > bytesUsed) { 584 SkGlyphCache_Globals& globals = FIND_GC_GLOBALS(); 585 SkAutoMutexAcquire ac(globals.fMutex); 586 587 return InternalFreeCache(&globals, curr - bytesUsed) > 0; 588 } 589 return false; 590 } 591 592 /////////////////////////////////////////////////////////////////////////////// 593 594 SkGlyphCache* SkGlyphCache::FindTail(SkGlyphCache* cache) { 595 if (cache) { 596 while (cache->fNext) { 597 cache = cache->fNext; 598 } 599 } 600 return cache; 601 } 602 603 size_t SkGlyphCache::ComputeMemoryUsed(const SkGlyphCache* head) { 604 size_t size = 0; 605 606 while (head != NULL) { 607 size += head->fMemoryUsed; 608 head = head->fNext; 609 } 610 return size; 611 } 612 613 #ifdef SK_DEBUG 614 void SkGlyphCache_Globals::validate() const { 615 size_t computed = SkGlyphCache::ComputeMemoryUsed(fHead); 616 if (fTotalMemoryUsed != computed) { 617 printf("total %d, computed %d\n", (int)fTotalMemoryUsed, (int)computed); 618 } 619 SkASSERT(fTotalMemoryUsed == computed); 620 } 621 #endif 622 623 size_t SkGlyphCache::InternalFreeCache(SkGlyphCache_Globals* globals, 624 size_t bytesNeeded) { 625 globals->validate(); 626 627 size_t bytesFreed = 0; 628 int count = 0; 629 630 // don't do any "small" purges 631 size_t minToPurge = globals->fTotalMemoryUsed >> 2; 632 if (bytesNeeded < minToPurge) 633 bytesNeeded = minToPurge; 634 635 SkGlyphCache* cache = FindTail(globals->fHead); 636 while (cache != NULL && bytesFreed < bytesNeeded) { 637 SkGlyphCache* prev = cache->fPrev; 638 bytesFreed += cache->fMemoryUsed; 639 640 #ifdef USE_CACHE_HASH 641 unsigned index = desc_to_hashindex(cache->fDesc); 642 if (cache == globals->fHash[index]) { 643 globals->fHash[index] = NULL; 644 } 645 #endif 646 647 cache->detach(&globals->fHead); 648 SkDELETE(cache); 649 cache = prev; 650 count += 1; 651 } 652 653 SkASSERT(bytesFreed <= globals->fTotalMemoryUsed); 654 globals->fTotalMemoryUsed -= bytesFreed; 655 globals->validate(); 656 657 #ifdef SPEW_PURGE_STATUS 658 if (count) { 659 SkDebugf("purging %dK from font cache [%d entries]\n", 660 (int)(bytesFreed >> 10), count); 661 } 662 #endif 663 664 return bytesFreed; 665 } 666 667