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