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