1 /* 2 * Copyright 2006 The Android Open Source Project 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "SkGlyphCache.h" 9 #include "SkGlyphCache_Globals.h" 10 #include "SkGraphics.h" 11 #include "SkOnce.h" 12 #include "SkPath.h" 13 #include "SkTemplates.h" 14 #include "SkTraceMemoryDump.h" 15 #include "SkTypeface.h" 16 17 #include <cctype> 18 19 //#define SPEW_PURGE_STATUS 20 21 namespace { 22 const char gGlyphCacheDumpName[] = "skia/sk_glyph_cache"; 23 } // namespace 24 25 // Returns the shared globals 26 static SkGlyphCache_Globals& get_globals() { 27 static SkOnce once; 28 static SkGlyphCache_Globals* globals; 29 30 once([]{ globals = new SkGlyphCache_Globals; }); 31 return *globals; 32 } 33 34 /////////////////////////////////////////////////////////////////////////////// 35 36 SkGlyphCache::SkGlyphCache(const SkDescriptor* desc, std::unique_ptr<SkScalerContext> ctx) 37 : fDesc(desc->copy()) 38 , fScalerContext(std::move(ctx)) { 39 SkASSERT(desc); 40 SkASSERT(fScalerContext); 41 42 fPrev = fNext = nullptr; 43 44 fScalerContext->getFontMetrics(&fFontMetrics); 45 46 fMemoryUsed = sizeof(*this); 47 } 48 49 SkGlyphCache::~SkGlyphCache() { 50 fGlyphMap.foreach([](SkGlyph* g) { 51 if (g->fPathData) { 52 delete g->fPathData->fPath; 53 } 54 }); 55 } 56 57 SkGlyphCache::CharGlyphRec* SkGlyphCache::getCharGlyphRec(SkPackedUnicharID packedUnicharID) { 58 if (!fPackedUnicharIDToPackedGlyphID) { 59 fPackedUnicharIDToPackedGlyphID.reset(new CharGlyphRec[kHashCount]); 60 } 61 62 return &fPackedUnicharIDToPackedGlyphID[packedUnicharID.hash() & kHashMask]; 63 } 64 65 /////////////////////////////////////////////////////////////////////////////// 66 67 #ifdef SK_DEBUG 68 #define VALIDATE() AutoValidate av(this) 69 #else 70 #define VALIDATE() 71 #endif 72 73 SkGlyphID SkGlyphCache::unicharToGlyph(SkUnichar charCode) { 74 VALIDATE(); 75 SkPackedUnicharID packedUnicharID(charCode); 76 CharGlyphRec* rec = this->getCharGlyphRec(packedUnicharID); 77 78 if (rec->fPackedUnicharID == packedUnicharID) { 79 // The glyph exists in the unichar to glyph mapping cache. Return it. 80 return rec->fPackedGlyphID.code(); 81 } else { 82 // The glyph is not in the unichar to glyph mapping cache. Insert it. 83 rec->fPackedUnicharID = packedUnicharID; 84 SkGlyphID glyphID = fScalerContext->charToGlyphID(charCode); 85 rec->fPackedGlyphID = SkPackedGlyphID(glyphID); 86 return glyphID; 87 } 88 } 89 90 SkUnichar SkGlyphCache::glyphToUnichar(SkGlyphID glyphID) { 91 return fScalerContext->glyphIDToChar(glyphID); 92 } 93 94 unsigned SkGlyphCache::getGlyphCount() const { 95 return fScalerContext->getGlyphCount(); 96 } 97 98 int SkGlyphCache::countCachedGlyphs() const { 99 return fGlyphMap.count(); 100 } 101 102 /////////////////////////////////////////////////////////////////////////////// 103 104 const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) { 105 VALIDATE(); 106 return *this->lookupByChar(charCode, kJustAdvance_MetricsType); 107 } 108 109 const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) { 110 VALIDATE(); 111 SkPackedGlyphID packedGlyphID(glyphID); 112 return *this->lookupByPackedGlyphID(packedGlyphID, kJustAdvance_MetricsType); 113 } 114 115 /////////////////////////////////////////////////////////////////////////////// 116 117 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) { 118 VALIDATE(); 119 return *this->lookupByChar(charCode, kFull_MetricsType); 120 } 121 122 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode, SkFixed x, SkFixed y) { 123 VALIDATE(); 124 return *this->lookupByChar(charCode, kFull_MetricsType, x, y); 125 } 126 127 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) { 128 VALIDATE(); 129 SkPackedGlyphID packedGlyphID(glyphID); 130 return *this->lookupByPackedGlyphID(packedGlyphID, kFull_MetricsType); 131 } 132 133 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID, SkFixed x, SkFixed y) { 134 VALIDATE(); 135 SkPackedGlyphID packedGlyphID(glyphID, x, y); 136 return *this->lookupByPackedGlyphID(packedGlyphID, kFull_MetricsType); 137 } 138 139 SkGlyph* SkGlyphCache::lookupByChar(SkUnichar charCode, MetricsType type, SkFixed x, SkFixed y) { 140 SkPackedUnicharID id(charCode, x, y); 141 CharGlyphRec* rec = this->getCharGlyphRec(id); 142 if (rec->fPackedUnicharID != id) { 143 rec->fPackedUnicharID = id; 144 rec->fPackedGlyphID = SkPackedGlyphID(fScalerContext->charToGlyphID(charCode), x, y); 145 } 146 return this->lookupByPackedGlyphID(rec->fPackedGlyphID, type); 147 } 148 149 SkGlyph* SkGlyphCache::lookupByPackedGlyphID(SkPackedGlyphID packedGlyphID, MetricsType type) { 150 SkGlyph* glyph = fGlyphMap.find(packedGlyphID); 151 152 if (nullptr == glyph) { 153 glyph = this->allocateNewGlyph(packedGlyphID, type); 154 } else { 155 if (type == kFull_MetricsType && glyph->isJustAdvance()) { 156 fScalerContext->getMetrics(glyph); 157 } 158 } 159 return glyph; 160 } 161 162 SkGlyph* SkGlyphCache::allocateNewGlyph(SkPackedGlyphID packedGlyphID, MetricsType mtype) { 163 fMemoryUsed += sizeof(SkGlyph); 164 165 SkGlyph* glyphPtr; 166 { 167 SkGlyph glyph; 168 glyph.initWithGlyphID(packedGlyphID); 169 glyphPtr = fGlyphMap.set(glyph); 170 } 171 172 if (kJustAdvance_MetricsType == mtype) { 173 fScalerContext->getAdvance(glyphPtr); 174 } else { 175 SkASSERT(kFull_MetricsType == mtype); 176 fScalerContext->getMetrics(glyphPtr); 177 } 178 179 SkASSERT(glyphPtr->fID != SkPackedGlyphID()); 180 return glyphPtr; 181 } 182 183 const void* SkGlyphCache::findImage(const SkGlyph& glyph) { 184 if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) { 185 if (nullptr == glyph.fImage) { 186 size_t size = const_cast<SkGlyph&>(glyph).allocImage(&fAlloc); 187 // check that alloc() actually succeeded 188 if (glyph.fImage) { 189 fScalerContext->getImage(glyph); 190 // TODO: the scaler may have changed the maskformat during 191 // getImage (e.g. from AA or LCD to BW) which means we may have 192 // overallocated the buffer. Check if the new computedImageSize 193 // is smaller, and if so, strink the alloc size in fImageAlloc. 194 fMemoryUsed += size; 195 } 196 } 197 } 198 return glyph.fImage; 199 } 200 201 const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) { 202 if (glyph.fWidth) { 203 if (glyph.fPathData == nullptr) { 204 SkGlyph::PathData* pathData = fAlloc.make<SkGlyph::PathData>(); 205 const_cast<SkGlyph&>(glyph).fPathData = pathData; 206 pathData->fIntercept = nullptr; 207 SkPath* path = pathData->fPath = new SkPath; 208 fScalerContext->getPath(glyph.getPackedID(), path); 209 fMemoryUsed += sizeof(SkPath) + path->countPoints() * sizeof(SkPoint); 210 } 211 } 212 return glyph.fPathData ? glyph.fPathData->fPath : nullptr; 213 } 214 215 #include "../pathops/SkPathOpsCubic.h" 216 #include "../pathops/SkPathOpsQuad.h" 217 218 static bool quad_in_bounds(const SkScalar* pts, const SkScalar bounds[2]) { 219 SkScalar min = SkTMin(SkTMin(pts[0], pts[2]), pts[4]); 220 if (bounds[1] < min) { 221 return false; 222 } 223 SkScalar max = SkTMax(SkTMax(pts[0], pts[2]), pts[4]); 224 return bounds[0] < max; 225 } 226 227 static bool cubic_in_bounds(const SkScalar* pts, const SkScalar bounds[2]) { 228 SkScalar min = SkTMin(SkTMin(SkTMin(pts[0], pts[2]), pts[4]), pts[6]); 229 if (bounds[1] < min) { 230 return false; 231 } 232 SkScalar max = SkTMax(SkTMax(SkTMax(pts[0], pts[2]), pts[4]), pts[6]); 233 return bounds[0] < max; 234 } 235 236 void SkGlyphCache::OffsetResults(const SkGlyph::Intercept* intercept, SkScalar scale, 237 SkScalar xPos, SkScalar* array, int* count) { 238 if (array) { 239 array += *count; 240 for (int index = 0; index < 2; index++) { 241 *array++ = intercept->fInterval[index] * scale + xPos; 242 } 243 } 244 *count += 2; 245 } 246 247 void SkGlyphCache::AddInterval(SkScalar val, SkGlyph::Intercept* intercept) { 248 intercept->fInterval[0] = SkTMin(intercept->fInterval[0], val); 249 intercept->fInterval[1] = SkTMax(intercept->fInterval[1], val); 250 } 251 252 void SkGlyphCache::AddPoints(const SkPoint* pts, int ptCount, const SkScalar bounds[2], 253 bool yAxis, SkGlyph::Intercept* intercept) { 254 for (int i = 0; i < ptCount; ++i) { 255 SkScalar val = *(&pts[i].fY - yAxis); 256 if (bounds[0] < val && val < bounds[1]) { 257 AddInterval(*(&pts[i].fX + yAxis), intercept); 258 } 259 } 260 } 261 262 void SkGlyphCache::AddLine(const SkPoint pts[2], SkScalar axis, bool yAxis, 263 SkGlyph::Intercept* intercept) { 264 SkScalar t = yAxis ? (axis - pts[0].fX) / (pts[1].fX - pts[0].fX) 265 : (axis - pts[0].fY) / (pts[1].fY - pts[0].fY); 266 if (0 <= t && t < 1) { // this handles divide by zero above 267 AddInterval(yAxis ? pts[0].fY + t * (pts[1].fY - pts[0].fY) 268 : pts[0].fX + t * (pts[1].fX - pts[0].fX), intercept); 269 } 270 } 271 272 void SkGlyphCache::AddQuad(const SkPoint pts[3], SkScalar axis, bool yAxis, 273 SkGlyph::Intercept* intercept) { 274 SkDQuad quad; 275 quad.set(pts); 276 double roots[2]; 277 int count = yAxis ? quad.verticalIntersect(axis, roots) 278 : quad.horizontalIntersect(axis, roots); 279 while (--count >= 0) { 280 SkPoint pt = quad.ptAtT(roots[count]).asSkPoint(); 281 AddInterval(*(&pt.fX + yAxis), intercept); 282 } 283 } 284 285 void SkGlyphCache::AddCubic(const SkPoint pts[4], SkScalar axis, bool yAxis, 286 SkGlyph::Intercept* intercept) { 287 SkDCubic cubic; 288 cubic.set(pts); 289 double roots[3]; 290 int count = yAxis ? cubic.verticalIntersect(axis, roots) 291 : cubic.horizontalIntersect(axis, roots); 292 while (--count >= 0) { 293 SkPoint pt = cubic.ptAtT(roots[count]).asSkPoint(); 294 AddInterval(*(&pt.fX + yAxis), intercept); 295 } 296 } 297 298 const SkGlyph::Intercept* SkGlyphCache::MatchBounds(const SkGlyph* glyph, 299 const SkScalar bounds[2]) { 300 if (!glyph->fPathData) { 301 return nullptr; 302 } 303 const SkGlyph::Intercept* intercept = glyph->fPathData->fIntercept; 304 while (intercept) { 305 if (bounds[0] == intercept->fBounds[0] && bounds[1] == intercept->fBounds[1]) { 306 return intercept; 307 } 308 intercept = intercept->fNext; 309 } 310 return nullptr; 311 } 312 313 void SkGlyphCache::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos, 314 bool yAxis, SkGlyph* glyph, SkScalar* array, int* count) { 315 const SkGlyph::Intercept* match = MatchBounds(glyph, bounds); 316 317 if (match) { 318 if (match->fInterval[0] < match->fInterval[1]) { 319 OffsetResults(match, scale, xPos, array, count); 320 } 321 return; 322 } 323 324 SkGlyph::Intercept* intercept = fAlloc.make<SkGlyph::Intercept>(); 325 intercept->fNext = glyph->fPathData->fIntercept; 326 intercept->fBounds[0] = bounds[0]; 327 intercept->fBounds[1] = bounds[1]; 328 intercept->fInterval[0] = SK_ScalarMax; 329 intercept->fInterval[1] = SK_ScalarMin; 330 glyph->fPathData->fIntercept = intercept; 331 const SkPath* path = glyph->fPathData->fPath; 332 const SkRect& pathBounds = path->getBounds(); 333 if (*(&pathBounds.fBottom - yAxis) < bounds[0] || bounds[1] < *(&pathBounds.fTop - yAxis)) { 334 return; 335 } 336 SkPath::Iter iter(*path, false); 337 SkPoint pts[4]; 338 SkPath::Verb verb; 339 while (SkPath::kDone_Verb != (verb = iter.next(pts))) { 340 switch (verb) { 341 case SkPath::kMove_Verb: 342 break; 343 case SkPath::kLine_Verb: 344 AddLine(pts, bounds[0], yAxis, intercept); 345 AddLine(pts, bounds[1], yAxis, intercept); 346 AddPoints(pts, 2, bounds, yAxis, intercept); 347 break; 348 case SkPath::kQuad_Verb: 349 if (!quad_in_bounds(&pts[0].fY - yAxis, bounds)) { 350 break; 351 } 352 AddQuad(pts, bounds[0], yAxis, intercept); 353 AddQuad(pts, bounds[1], yAxis, intercept); 354 AddPoints(pts, 3, bounds, yAxis, intercept); 355 break; 356 case SkPath::kConic_Verb: 357 SkASSERT(0); // no support for text composed of conics 358 break; 359 case SkPath::kCubic_Verb: 360 if (!cubic_in_bounds(&pts[0].fY - yAxis, bounds)) { 361 break; 362 } 363 AddCubic(pts, bounds[0], yAxis, intercept); 364 AddCubic(pts, bounds[1], yAxis, intercept); 365 AddPoints(pts, 4, bounds, yAxis, intercept); 366 break; 367 case SkPath::kClose_Verb: 368 break; 369 default: 370 SkASSERT(0); 371 break; 372 } 373 } 374 if (intercept->fInterval[0] >= intercept->fInterval[1]) { 375 intercept->fInterval[0] = SK_ScalarMax; 376 intercept->fInterval[1] = SK_ScalarMin; 377 return; 378 } 379 OffsetResults(intercept, scale, xPos, array, count); 380 } 381 382 void SkGlyphCache::dump() const { 383 const SkTypeface* face = fScalerContext->getTypeface(); 384 const SkScalerContextRec& rec = fScalerContext->getRec(); 385 SkMatrix matrix; 386 rec.getSingleMatrix(&matrix); 387 matrix.preScale(SkScalarInvert(rec.fTextSize), SkScalarInvert(rec.fTextSize)); 388 SkString name; 389 face->getFamilyName(&name); 390 391 SkString msg; 392 msg.printf("cache typeface:%x %25s:%d size:%2g [%g %g %g %g] lum:%02X devG:%d pntG:%d cntr:%d glyphs:%3d", 393 face->uniqueID(), name.c_str(), face->style(), rec.fTextSize, 394 matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewX], 395 matrix[SkMatrix::kMSkewY], matrix[SkMatrix::kMScaleY], 396 rec.fLumBits & 0xFF, rec.fDeviceGamma, rec.fPaintGamma, rec.fContrast, 397 fGlyphMap.count()); 398 SkDebugf("%s\n", msg.c_str()); 399 } 400 401 /////////////////////////////////////////////////////////////////////////////// 402 /////////////////////////////////////////////////////////////////////////////// 403 404 size_t SkGlyphCache_Globals::getTotalMemoryUsed() const { 405 SkAutoExclusive ac(fLock); 406 return fTotalMemoryUsed; 407 } 408 409 int SkGlyphCache_Globals::getCacheCountUsed() const { 410 SkAutoExclusive ac(fLock); 411 return fCacheCount; 412 } 413 414 int SkGlyphCache_Globals::getCacheCountLimit() const { 415 SkAutoExclusive ac(fLock); 416 return fCacheCountLimit; 417 } 418 419 size_t SkGlyphCache_Globals::setCacheSizeLimit(size_t newLimit) { 420 static const size_t minLimit = 256 * 1024; 421 if (newLimit < minLimit) { 422 newLimit = minLimit; 423 } 424 425 SkAutoExclusive ac(fLock); 426 427 size_t prevLimit = fCacheSizeLimit; 428 fCacheSizeLimit = newLimit; 429 this->internalPurge(); 430 return prevLimit; 431 } 432 433 size_t SkGlyphCache_Globals::getCacheSizeLimit() const { 434 SkAutoExclusive ac(fLock); 435 return fCacheSizeLimit; 436 } 437 438 int SkGlyphCache_Globals::setCacheCountLimit(int newCount) { 439 if (newCount < 0) { 440 newCount = 0; 441 } 442 443 SkAutoExclusive ac(fLock); 444 445 int prevCount = fCacheCountLimit; 446 fCacheCountLimit = newCount; 447 this->internalPurge(); 448 return prevCount; 449 } 450 451 int SkGlyphCache_Globals::getCachePointSizeLimit() const { 452 SkAutoExclusive ac(fLock); 453 return fPointSizeLimit; 454 } 455 456 int SkGlyphCache_Globals::setCachePointSizeLimit(int newLimit) { 457 if (newLimit < 0) { 458 newLimit = 0; 459 } 460 461 SkAutoExclusive ac(fLock); 462 463 int prevLimit = fPointSizeLimit; 464 fPointSizeLimit = newLimit; 465 return prevLimit; 466 } 467 468 void SkGlyphCache_Globals::purgeAll() { 469 SkAutoExclusive ac(fLock); 470 this->internalPurge(fTotalMemoryUsed); 471 } 472 473 /* This guy calls the visitor from within the mutext lock, so the visitor 474 cannot: 475 - take too much time 476 - try to acquire the mutext again 477 - call a fontscaler (which might call into the cache) 478 */ 479 SkGlyphCache* SkGlyphCache::VisitCache(SkTypeface* typeface, 480 const SkScalerContextEffects& effects, 481 const SkDescriptor* desc, 482 bool (*proc)(const SkGlyphCache*, void*), 483 void* context) { 484 if (!typeface) { 485 typeface = SkTypeface::GetDefaultTypeface(); 486 } 487 SkASSERT(desc); 488 489 // Precondition: the typeface id must be the fFontID in the descriptor 490 SkDEBUGCODE( 491 uint32_t length = 0; 492 const SkScalerContext::Rec* rec = static_cast<const SkScalerContext::Rec*>( 493 desc->findEntry(kRec_SkDescriptorTag, &length)); 494 SkASSERT(rec); 495 SkASSERT(length == sizeof(*rec)); 496 SkASSERT(typeface->uniqueID() == rec->fFontID); 497 ) 498 499 SkGlyphCache_Globals& globals = get_globals(); 500 SkGlyphCache* cache; 501 502 { 503 SkAutoExclusive ac(globals.fLock); 504 505 globals.validate(); 506 507 for (cache = globals.internalGetHead(); cache != nullptr; cache = cache->fNext) { 508 if (*cache->fDesc == *desc) { 509 globals.internalDetachCache(cache); 510 if (!proc(cache, context)) { 511 globals.internalAttachCacheToHead(cache); 512 cache = nullptr; 513 } 514 return cache; 515 } 516 } 517 } 518 519 // Check if we can create a scaler-context before creating the glyphcache. 520 // If not, we may have exhausted OS/font resources, so try purging the 521 // cache once and try again. 522 { 523 // pass true the first time, to notice if the scalercontext failed, 524 // so we can try the purge. 525 std::unique_ptr<SkScalerContext> ctx = typeface->createScalerContext(effects, desc, true); 526 if (!ctx) { 527 get_globals().purgeAll(); 528 ctx = typeface->createScalerContext(effects, desc, false); 529 SkASSERT(ctx); 530 } 531 cache = new SkGlyphCache(desc, std::move(ctx)); 532 } 533 534 AutoValidate av(cache); 535 536 if (!proc(cache, context)) { // need to reattach 537 globals.attachCacheToHead(cache); 538 cache = nullptr; 539 } 540 return cache; 541 } 542 543 void SkGlyphCache::AttachCache(SkGlyphCache* cache) { 544 SkASSERT(cache); 545 SkASSERT(cache->fNext == nullptr); 546 547 get_globals().attachCacheToHead(cache); 548 } 549 550 static void dump_visitor(const SkGlyphCache& cache, void* context) { 551 int* counter = (int*)context; 552 int index = *counter; 553 *counter += 1; 554 555 const SkScalerContextRec& rec = cache.getScalerContext()->getRec(); 556 557 SkDebugf("[%3d] ID %3d, glyphs %3d, size %g, scale %g, skew %g, [%g %g %g %g]\n", 558 index, rec.fFontID, cache.countCachedGlyphs(), 559 rec.fTextSize, rec.fPreScaleX, rec.fPreSkewX, 560 rec.fPost2x2[0][0], rec.fPost2x2[0][1], rec.fPost2x2[1][0], rec.fPost2x2[1][1]); 561 } 562 563 void SkGlyphCache::Dump() { 564 SkDebugf("GlyphCache [ used budget ]\n"); 565 SkDebugf(" bytes [ %8zu %8zu ]\n", 566 SkGraphics::GetFontCacheUsed(), SkGraphics::GetFontCacheLimit()); 567 SkDebugf(" count [ %8zu %8zu ]\n", 568 SkGraphics::GetFontCacheCountUsed(), SkGraphics::GetFontCacheCountLimit()); 569 570 int counter = 0; 571 SkGlyphCache::VisitAll(dump_visitor, &counter); 572 } 573 574 static void sk_trace_dump_visitor(const SkGlyphCache& cache, void* context) { 575 SkTraceMemoryDump* dump = static_cast<SkTraceMemoryDump*>(context); 576 577 const SkTypeface* face = cache.getScalerContext()->getTypeface(); 578 const SkScalerContextRec& rec = cache.getScalerContext()->getRec(); 579 580 SkString fontName; 581 face->getFamilyName(&fontName); 582 // Replace all special characters with '_'. 583 for (size_t index = 0; index < fontName.size(); ++index) { 584 if (!std::isalnum(fontName[index])) { 585 fontName[index] = '_'; 586 } 587 } 588 589 SkString dumpName = SkStringPrintf("%s/%s_%d/%p", 590 gGlyphCacheDumpName, fontName.c_str(), rec.fFontID, &cache); 591 592 dump->dumpNumericValue(dumpName.c_str(), "size", "bytes", cache.getMemoryUsed()); 593 dump->dumpNumericValue(dumpName.c_str(), "glyph_count", "objects", cache.countCachedGlyphs()); 594 dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr); 595 } 596 597 void SkGlyphCache::DumpMemoryStatistics(SkTraceMemoryDump* dump) { 598 dump->dumpNumericValue(gGlyphCacheDumpName, "size", "bytes", SkGraphics::GetFontCacheUsed()); 599 dump->dumpNumericValue(gGlyphCacheDumpName, "budget_size", "bytes", 600 SkGraphics::GetFontCacheLimit()); 601 dump->dumpNumericValue(gGlyphCacheDumpName, "glyph_count", "objects", 602 SkGraphics::GetFontCacheCountUsed()); 603 dump->dumpNumericValue(gGlyphCacheDumpName, "budget_glyph_count", "objects", 604 SkGraphics::GetFontCacheCountLimit()); 605 606 if (dump->getRequestedDetails() == SkTraceMemoryDump::kLight_LevelOfDetail) { 607 dump->setMemoryBacking(gGlyphCacheDumpName, "malloc", nullptr); 608 return; 609 } 610 611 SkGlyphCache::VisitAll(sk_trace_dump_visitor, dump); 612 } 613 614 void SkGlyphCache::VisitAll(Visitor visitor, void* context) { 615 SkGlyphCache_Globals& globals = get_globals(); 616 SkAutoExclusive ac(globals.fLock); 617 SkGlyphCache* cache; 618 619 globals.validate(); 620 621 for (cache = globals.internalGetHead(); cache != nullptr; cache = cache->fNext) { 622 visitor(*cache, context); 623 } 624 } 625 626 /////////////////////////////////////////////////////////////////////////////// 627 628 void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) { 629 SkAutoExclusive ac(fLock); 630 631 this->validate(); 632 cache->validate(); 633 634 this->internalAttachCacheToHead(cache); 635 this->internalPurge(); 636 } 637 638 SkGlyphCache* SkGlyphCache_Globals::internalGetTail() const { 639 SkGlyphCache* cache = fHead; 640 if (cache) { 641 while (cache->fNext) { 642 cache = cache->fNext; 643 } 644 } 645 return cache; 646 } 647 648 size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) { 649 this->validate(); 650 651 size_t bytesNeeded = 0; 652 if (fTotalMemoryUsed > fCacheSizeLimit) { 653 bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit; 654 } 655 bytesNeeded = SkTMax(bytesNeeded, minBytesNeeded); 656 if (bytesNeeded) { 657 // no small purges! 658 bytesNeeded = SkTMax(bytesNeeded, fTotalMemoryUsed >> 2); 659 } 660 661 int countNeeded = 0; 662 if (fCacheCount > fCacheCountLimit) { 663 countNeeded = fCacheCount - fCacheCountLimit; 664 // no small purges! 665 countNeeded = SkMax32(countNeeded, fCacheCount >> 2); 666 } 667 668 // early exit 669 if (!countNeeded && !bytesNeeded) { 670 return 0; 671 } 672 673 size_t bytesFreed = 0; 674 int countFreed = 0; 675 676 // we start at the tail and proceed backwards, as the linklist is in LRU 677 // order, with unimportant entries at the tail. 678 SkGlyphCache* cache = this->internalGetTail(); 679 while (cache != nullptr && 680 (bytesFreed < bytesNeeded || countFreed < countNeeded)) { 681 SkGlyphCache* prev = cache->fPrev; 682 bytesFreed += cache->fMemoryUsed; 683 countFreed += 1; 684 685 this->internalDetachCache(cache); 686 delete cache; 687 cache = prev; 688 } 689 690 this->validate(); 691 692 #ifdef SPEW_PURGE_STATUS 693 if (countFreed) { 694 SkDebugf("purging %dK from font cache [%d entries]\n", 695 (int)(bytesFreed >> 10), countFreed); 696 } 697 #endif 698 699 return bytesFreed; 700 } 701 702 void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) { 703 SkASSERT(nullptr == cache->fPrev && nullptr == cache->fNext); 704 if (fHead) { 705 fHead->fPrev = cache; 706 cache->fNext = fHead; 707 } 708 fHead = cache; 709 710 fCacheCount += 1; 711 fTotalMemoryUsed += cache->fMemoryUsed; 712 } 713 714 void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) { 715 SkASSERT(fCacheCount > 0); 716 fCacheCount -= 1; 717 fTotalMemoryUsed -= cache->fMemoryUsed; 718 719 if (cache->fPrev) { 720 cache->fPrev->fNext = cache->fNext; 721 } else { 722 fHead = cache->fNext; 723 } 724 if (cache->fNext) { 725 cache->fNext->fPrev = cache->fPrev; 726 } 727 cache->fPrev = cache->fNext = nullptr; 728 } 729 730 /////////////////////////////////////////////////////////////////////////////// 731 732 #ifdef SK_DEBUG 733 734 void SkGlyphCache::validate() const { 735 #ifdef SK_DEBUG_GLYPH_CACHE 736 int count = fGlyphArray.count(); 737 for (int i = 0; i < count; i++) { 738 const SkGlyph* glyph = &fGlyphArray[i]; 739 SkASSERT(glyph); 740 if (glyph->fImage) { 741 SkASSERT(fGlyphAlloc.contains(glyph->fImage)); 742 } 743 } 744 #endif 745 } 746 747 void SkGlyphCache_Globals::validate() const { 748 size_t computedBytes = 0; 749 int computedCount = 0; 750 751 const SkGlyphCache* head = fHead; 752 while (head != nullptr) { 753 computedBytes += head->fMemoryUsed; 754 computedCount += 1; 755 head = head->fNext; 756 } 757 758 SkASSERTF(fCacheCount == computedCount, "fCacheCount: %d, computedCount: %d", fCacheCount, 759 computedCount); 760 SkASSERTF(fTotalMemoryUsed == computedBytes, "fTotalMemoryUsed: %d, computedBytes: %d", 761 fTotalMemoryUsed, computedBytes); 762 } 763 764 #endif 765 766 /////////////////////////////////////////////////////////////////////////////// 767 /////////////////////////////////////////////////////////////////////////////// 768 769 #include "SkTypefaceCache.h" 770 771 size_t SkGraphics::GetFontCacheLimit() { 772 return get_globals().getCacheSizeLimit(); 773 } 774 775 size_t SkGraphics::SetFontCacheLimit(size_t bytes) { 776 return get_globals().setCacheSizeLimit(bytes); 777 } 778 779 size_t SkGraphics::GetFontCacheUsed() { 780 return get_globals().getTotalMemoryUsed(); 781 } 782 783 int SkGraphics::GetFontCacheCountLimit() { 784 return get_globals().getCacheCountLimit(); 785 } 786 787 int SkGraphics::SetFontCacheCountLimit(int count) { 788 return get_globals().setCacheCountLimit(count); 789 } 790 791 int SkGraphics::GetFontCacheCountUsed() { 792 return get_globals().getCacheCountUsed(); 793 } 794 795 int SkGraphics::GetFontCachePointSizeLimit() { 796 return get_globals().getCachePointSizeLimit(); 797 } 798 799 int SkGraphics::SetFontCachePointSizeLimit(int limit) { 800 return get_globals().setCachePointSizeLimit(limit); 801 } 802 803 void SkGraphics::PurgeFontCache() { 804 get_globals().purgeAll(); 805 SkTypefaceCache::PurgeAll(); 806 } 807 808 // TODO(herb): clean up TLS apis. 809 size_t SkGraphics::GetTLSFontCacheLimit() { return 0; } 810 void SkGraphics::SetTLSFontCacheLimit(size_t bytes) { } 811