gfx/skia/trunk/src/core/SkGlyphCache.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1
michael@0 2 /*
michael@0 3 * Copyright 2006 The Android Open Source Project
michael@0 4 *
michael@0 5 * Use of this source code is governed by a BSD-style license that can be
michael@0 6 * found in the LICENSE file.
michael@0 7 */
michael@0 8
michael@0 9
michael@0 10 #include "SkGlyphCache.h"
michael@0 11 #include "SkGlyphCache_Globals.h"
michael@0 12 #include "SkGraphics.h"
michael@0 13 #include "SkPaint.h"
michael@0 14 #include "SkPath.h"
michael@0 15 #include "SkTemplates.h"
michael@0 16 #include "SkTLS.h"
michael@0 17 #include "SkTypeface.h"
michael@0 18
michael@0 19 //#define SPEW_PURGE_STATUS
michael@0 20 //#define RECORD_HASH_EFFICIENCY
michael@0 21
michael@0 22 bool gSkSuppressFontCachePurgeSpew;
michael@0 23
michael@0 24 // Returns the shared globals
michael@0 25 static SkGlyphCache_Globals& getSharedGlobals() {
michael@0 26 // we leak this, so we don't incur any shutdown cost of the destructor
michael@0 27 static SkGlyphCache_Globals* gGlobals = SkNEW_ARGS(SkGlyphCache_Globals,
michael@0 28 (SkGlyphCache_Globals::kYes_UseMutex));
michael@0 29 return *gGlobals;
michael@0 30 }
michael@0 31
michael@0 32 // Returns the TLS globals (if set), or the shared globals
michael@0 33 static SkGlyphCache_Globals& getGlobals() {
michael@0 34 SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
michael@0 35 return tls ? *tls : getSharedGlobals();
michael@0 36 }
michael@0 37
michael@0 38 ///////////////////////////////////////////////////////////////////////////////
michael@0 39
michael@0 40 #ifdef RECORD_HASH_EFFICIENCY
michael@0 41 static uint32_t gHashSuccess;
michael@0 42 static uint32_t gHashCollision;
michael@0 43
michael@0 44 static void RecordHashSuccess() {
michael@0 45 gHashSuccess += 1;
michael@0 46 }
michael@0 47
michael@0 48 static void RecordHashCollisionIf(bool pred) {
michael@0 49 if (pred) {
michael@0 50 gHashCollision += 1;
michael@0 51
michael@0 52 uint32_t total = gHashSuccess + gHashCollision;
michael@0 53 SkDebugf("Font Cache Hash success rate: %d%%\n",
michael@0 54 100 * gHashSuccess / total);
michael@0 55 }
michael@0 56 }
michael@0 57 #else
michael@0 58 #define RecordHashSuccess() (void)0
michael@0 59 #define RecordHashCollisionIf(pred) (void)0
michael@0 60 #endif
michael@0 61 #define RecordHashCollision() RecordHashCollisionIf(true)
michael@0 62
michael@0 63 ///////////////////////////////////////////////////////////////////////////////
michael@0 64
michael@0 65 // so we don't grow our arrays a lot
michael@0 66 #define kMinGlyphCount 16
michael@0 67 #define kMinGlyphImageSize (16*2)
michael@0 68 #define kMinAllocAmount ((sizeof(SkGlyph) + kMinGlyphImageSize) * kMinGlyphCount)
michael@0 69
michael@0 70 SkGlyphCache::SkGlyphCache(SkTypeface* typeface, const SkDescriptor* desc, SkScalerContext* ctx)
michael@0 71 : fScalerContext(ctx), fGlyphAlloc(kMinAllocAmount) {
michael@0 72 SkASSERT(typeface);
michael@0 73 SkASSERT(desc);
michael@0 74 SkASSERT(ctx);
michael@0 75
michael@0 76 fPrev = fNext = NULL;
michael@0 77
michael@0 78 fDesc = desc->copy();
michael@0 79 fScalerContext->getFontMetrics(&fFontMetrics);
michael@0 80
michael@0 81 // init to 0 so that all of the pointers will be null
michael@0 82 memset(fGlyphHash, 0, sizeof(fGlyphHash));
michael@0 83 // init with 0xFF so that the charCode field will be -1, which is invalid
michael@0 84 memset(fCharToGlyphHash, 0xFF, sizeof(fCharToGlyphHash));
michael@0 85
michael@0 86 fMemoryUsed = sizeof(*this);
michael@0 87
michael@0 88 fGlyphArray.setReserve(kMinGlyphCount);
michael@0 89
michael@0 90 fAuxProcList = NULL;
michael@0 91 }
michael@0 92
michael@0 93 SkGlyphCache::~SkGlyphCache() {
michael@0 94 #if 0
michael@0 95 {
michael@0 96 size_t ptrMem = fGlyphArray.count() * sizeof(SkGlyph*);
michael@0 97 size_t glyphAlloc = fGlyphAlloc.totalCapacity();
michael@0 98 size_t glyphHashUsed = 0;
michael@0 99 size_t uniHashUsed = 0;
michael@0 100 for (int i = 0; i < kHashCount; ++i) {
michael@0 101 glyphHashUsed += fGlyphHash[i] ? sizeof(fGlyphHash[0]) : 0;
michael@0 102 uniHashUsed += fCharToGlyphHash[i].fID != 0xFFFFFFFF ? sizeof(fCharToGlyphHash[0]) : 0;
michael@0 103 }
michael@0 104 size_t glyphUsed = fGlyphArray.count() * sizeof(SkGlyph);
michael@0 105 size_t imageUsed = 0;
michael@0 106 for (int i = 0; i < fGlyphArray.count(); ++i) {
michael@0 107 const SkGlyph& g = *fGlyphArray[i];
michael@0 108 if (g.fImage) {
michael@0 109 imageUsed += g.fHeight * g.rowBytes();
michael@0 110 }
michael@0 111 }
michael@0 112
michael@0 113 printf("glyphPtrArray,%zu, Alloc,%zu, imageUsed,%zu, glyphUsed,%zu, glyphHashAlloc,%zu, glyphHashUsed,%zu, unicharHashAlloc,%zu, unicharHashUsed,%zu\n",
michael@0 114 ptrMem, glyphAlloc, imageUsed, glyphUsed, sizeof(fGlyphHash), glyphHashUsed, sizeof(fCharToGlyphHash), uniHashUsed);
michael@0 115
michael@0 116 }
michael@0 117 #endif
michael@0 118 SkGlyph** gptr = fGlyphArray.begin();
michael@0 119 SkGlyph** stop = fGlyphArray.end();
michael@0 120 while (gptr < stop) {
michael@0 121 SkPath* path = (*gptr)->fPath;
michael@0 122 if (path) {
michael@0 123 SkDELETE(path);
michael@0 124 }
michael@0 125 gptr += 1;
michael@0 126 }
michael@0 127 SkDescriptor::Free(fDesc);
michael@0 128 SkDELETE(fScalerContext);
michael@0 129 this->invokeAndRemoveAuxProcs();
michael@0 130 }
michael@0 131
michael@0 132 ///////////////////////////////////////////////////////////////////////////////
michael@0 133
michael@0 134 #ifdef SK_DEBUG
michael@0 135 #define VALIDATE() AutoValidate av(this)
michael@0 136 #else
michael@0 137 #define VALIDATE()
michael@0 138 #endif
michael@0 139
michael@0 140 uint16_t SkGlyphCache::unicharToGlyph(SkUnichar charCode) {
michael@0 141 VALIDATE();
michael@0 142 uint32_t id = SkGlyph::MakeID(charCode);
michael@0 143 const CharGlyphRec& rec = fCharToGlyphHash[ID2HashIndex(id)];
michael@0 144
michael@0 145 if (rec.fID == id) {
michael@0 146 return rec.fGlyph->getGlyphID();
michael@0 147 } else {
michael@0 148 return fScalerContext->charToGlyphID(charCode);
michael@0 149 }
michael@0 150 }
michael@0 151
michael@0 152 SkUnichar SkGlyphCache::glyphToUnichar(uint16_t glyphID) {
michael@0 153 return fScalerContext->glyphIDToChar(glyphID);
michael@0 154 }
michael@0 155
michael@0 156 unsigned SkGlyphCache::getGlyphCount() {
michael@0 157 return fScalerContext->getGlyphCount();
michael@0 158 }
michael@0 159
michael@0 160 ///////////////////////////////////////////////////////////////////////////////
michael@0 161
michael@0 162 const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) {
michael@0 163 VALIDATE();
michael@0 164 uint32_t id = SkGlyph::MakeID(charCode);
michael@0 165 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
michael@0 166
michael@0 167 if (rec->fID != id) {
michael@0 168 // this ID is based on the UniChar
michael@0 169 rec->fID = id;
michael@0 170 // this ID is based on the glyph index
michael@0 171 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
michael@0 172 rec->fGlyph = this->lookupMetrics(id, kJustAdvance_MetricsType);
michael@0 173 }
michael@0 174 return *rec->fGlyph;
michael@0 175 }
michael@0 176
michael@0 177 const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) {
michael@0 178 VALIDATE();
michael@0 179 uint32_t id = SkGlyph::MakeID(glyphID);
michael@0 180 unsigned index = ID2HashIndex(id);
michael@0 181 SkGlyph* glyph = fGlyphHash[index];
michael@0 182
michael@0 183 if (NULL == glyph || glyph->fID != id) {
michael@0 184 glyph = this->lookupMetrics(glyphID, kJustAdvance_MetricsType);
michael@0 185 fGlyphHash[index] = glyph;
michael@0 186 }
michael@0 187 return *glyph;
michael@0 188 }
michael@0 189
michael@0 190 ///////////////////////////////////////////////////////////////////////////////
michael@0 191
michael@0 192 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) {
michael@0 193 VALIDATE();
michael@0 194 uint32_t id = SkGlyph::MakeID(charCode);
michael@0 195 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
michael@0 196
michael@0 197 if (rec->fID != id) {
michael@0 198 RecordHashCollisionIf(rec->fGlyph != NULL);
michael@0 199 // this ID is based on the UniChar
michael@0 200 rec->fID = id;
michael@0 201 // this ID is based on the glyph index
michael@0 202 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
michael@0 203 rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
michael@0 204 } else {
michael@0 205 RecordHashSuccess();
michael@0 206 if (rec->fGlyph->isJustAdvance()) {
michael@0 207 fScalerContext->getMetrics(rec->fGlyph);
michael@0 208 }
michael@0 209 }
michael@0 210 SkASSERT(rec->fGlyph->isFullMetrics());
michael@0 211 return *rec->fGlyph;
michael@0 212 }
michael@0 213
michael@0 214 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode,
michael@0 215 SkFixed x, SkFixed y) {
michael@0 216 VALIDATE();
michael@0 217 uint32_t id = SkGlyph::MakeID(charCode, x, y);
michael@0 218 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
michael@0 219
michael@0 220 if (rec->fID != id) {
michael@0 221 RecordHashCollisionIf(rec->fGlyph != NULL);
michael@0 222 // this ID is based on the UniChar
michael@0 223 rec->fID = id;
michael@0 224 // this ID is based on the glyph index
michael@0 225 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode), x, y);
michael@0 226 rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
michael@0 227 } else {
michael@0 228 RecordHashSuccess();
michael@0 229 if (rec->fGlyph->isJustAdvance()) {
michael@0 230 fScalerContext->getMetrics(rec->fGlyph);
michael@0 231 }
michael@0 232 }
michael@0 233 SkASSERT(rec->fGlyph->isFullMetrics());
michael@0 234 return *rec->fGlyph;
michael@0 235 }
michael@0 236
michael@0 237 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) {
michael@0 238 VALIDATE();
michael@0 239 uint32_t id = SkGlyph::MakeID(glyphID);
michael@0 240 unsigned index = ID2HashIndex(id);
michael@0 241 SkGlyph* glyph = fGlyphHash[index];
michael@0 242
michael@0 243 if (NULL == glyph || glyph->fID != id) {
michael@0 244 RecordHashCollisionIf(glyph != NULL);
michael@0 245 glyph = this->lookupMetrics(glyphID, kFull_MetricsType);
michael@0 246 fGlyphHash[index] = glyph;
michael@0 247 } else {
michael@0 248 RecordHashSuccess();
michael@0 249 if (glyph->isJustAdvance()) {
michael@0 250 fScalerContext->getMetrics(glyph);
michael@0 251 }
michael@0 252 }
michael@0 253 SkASSERT(glyph->isFullMetrics());
michael@0 254 return *glyph;
michael@0 255 }
michael@0 256
michael@0 257 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID,
michael@0 258 SkFixed x, SkFixed y) {
michael@0 259 VALIDATE();
michael@0 260 uint32_t id = SkGlyph::MakeID(glyphID, x, y);
michael@0 261 unsigned index = ID2HashIndex(id);
michael@0 262 SkGlyph* glyph = fGlyphHash[index];
michael@0 263
michael@0 264 if (NULL == glyph || glyph->fID != id) {
michael@0 265 RecordHashCollisionIf(glyph != NULL);
michael@0 266 glyph = this->lookupMetrics(id, kFull_MetricsType);
michael@0 267 fGlyphHash[index] = glyph;
michael@0 268 } else {
michael@0 269 RecordHashSuccess();
michael@0 270 if (glyph->isJustAdvance()) {
michael@0 271 fScalerContext->getMetrics(glyph);
michael@0 272 }
michael@0 273 }
michael@0 274 SkASSERT(glyph->isFullMetrics());
michael@0 275 return *glyph;
michael@0 276 }
michael@0 277
michael@0 278 SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) {
michael@0 279 SkGlyph* glyph;
michael@0 280
michael@0 281 int hi = 0;
michael@0 282 int count = fGlyphArray.count();
michael@0 283
michael@0 284 if (count) {
michael@0 285 SkGlyph** gptr = fGlyphArray.begin();
michael@0 286 int lo = 0;
michael@0 287
michael@0 288 hi = count - 1;
michael@0 289 while (lo < hi) {
michael@0 290 int mid = (hi + lo) >> 1;
michael@0 291 if (gptr[mid]->fID < id) {
michael@0 292 lo = mid + 1;
michael@0 293 } else {
michael@0 294 hi = mid;
michael@0 295 }
michael@0 296 }
michael@0 297 glyph = gptr[hi];
michael@0 298 if (glyph->fID == id) {
michael@0 299 if (kFull_MetricsType == mtype && glyph->isJustAdvance()) {
michael@0 300 fScalerContext->getMetrics(glyph);
michael@0 301 }
michael@0 302 return glyph;
michael@0 303 }
michael@0 304
michael@0 305 // check if we need to bump hi before falling though to the allocator
michael@0 306 if (glyph->fID < id) {
michael@0 307 hi += 1;
michael@0 308 }
michael@0 309 }
michael@0 310
michael@0 311 // not found, but hi tells us where to inser the new glyph
michael@0 312 fMemoryUsed += sizeof(SkGlyph);
michael@0 313
michael@0 314 glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph),
michael@0 315 SkChunkAlloc::kThrow_AllocFailType);
michael@0 316 glyph->init(id);
michael@0 317 *fGlyphArray.insert(hi) = glyph;
michael@0 318
michael@0 319 if (kJustAdvance_MetricsType == mtype) {
michael@0 320 fScalerContext->getAdvance(glyph);
michael@0 321 } else {
michael@0 322 SkASSERT(kFull_MetricsType == mtype);
michael@0 323 fScalerContext->getMetrics(glyph);
michael@0 324 }
michael@0 325
michael@0 326 return glyph;
michael@0 327 }
michael@0 328
michael@0 329 const void* SkGlyphCache::findImage(const SkGlyph& glyph) {
michael@0 330 if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) {
michael@0 331 if (glyph.fImage == NULL) {
michael@0 332 size_t size = glyph.computeImageSize();
michael@0 333 const_cast<SkGlyph&>(glyph).fImage = fGlyphAlloc.alloc(size,
michael@0 334 SkChunkAlloc::kReturnNil_AllocFailType);
michael@0 335 // check that alloc() actually succeeded
michael@0 336 if (glyph.fImage) {
michael@0 337 fScalerContext->getImage(glyph);
michael@0 338 // TODO: the scaler may have changed the maskformat during
michael@0 339 // getImage (e.g. from AA or LCD to BW) which means we may have
michael@0 340 // overallocated the buffer. Check if the new computedImageSize
michael@0 341 // is smaller, and if so, strink the alloc size in fImageAlloc.
michael@0 342 fMemoryUsed += size;
michael@0 343 }
michael@0 344 }
michael@0 345 }
michael@0 346 return glyph.fImage;
michael@0 347 }
michael@0 348
michael@0 349 const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) {
michael@0 350 if (glyph.fWidth) {
michael@0 351 if (glyph.fPath == NULL) {
michael@0 352 const_cast<SkGlyph&>(glyph).fPath = SkNEW(SkPath);
michael@0 353 fScalerContext->getPath(glyph, glyph.fPath);
michael@0 354 fMemoryUsed += sizeof(SkPath) +
michael@0 355 glyph.fPath->countPoints() * sizeof(SkPoint);
michael@0 356 }
michael@0 357 }
michael@0 358 return glyph.fPath;
michael@0 359 }
michael@0 360
michael@0 361 ///////////////////////////////////////////////////////////////////////////////
michael@0 362
michael@0 363 bool SkGlyphCache::getAuxProcData(void (*proc)(void*), void** dataPtr) const {
michael@0 364 const AuxProcRec* rec = fAuxProcList;
michael@0 365 while (rec) {
michael@0 366 if (rec->fProc == proc) {
michael@0 367 if (dataPtr) {
michael@0 368 *dataPtr = rec->fData;
michael@0 369 }
michael@0 370 return true;
michael@0 371 }
michael@0 372 rec = rec->fNext;
michael@0 373 }
michael@0 374 return false;
michael@0 375 }
michael@0 376
michael@0 377 void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) {
michael@0 378 if (proc == NULL) {
michael@0 379 return;
michael@0 380 }
michael@0 381
michael@0 382 AuxProcRec* rec = fAuxProcList;
michael@0 383 while (rec) {
michael@0 384 if (rec->fProc == proc) {
michael@0 385 rec->fData = data;
michael@0 386 return;
michael@0 387 }
michael@0 388 rec = rec->fNext;
michael@0 389 }
michael@0 390 // not found, create a new rec
michael@0 391 rec = SkNEW(AuxProcRec);
michael@0 392 rec->fProc = proc;
michael@0 393 rec->fData = data;
michael@0 394 rec->fNext = fAuxProcList;
michael@0 395 fAuxProcList = rec;
michael@0 396 }
michael@0 397
michael@0 398 void SkGlyphCache::invokeAndRemoveAuxProcs() {
michael@0 399 AuxProcRec* rec = fAuxProcList;
michael@0 400 while (rec) {
michael@0 401 rec->fProc(rec->fData);
michael@0 402 AuxProcRec* next = rec->fNext;
michael@0 403 SkDELETE(rec);
michael@0 404 rec = next;
michael@0 405 }
michael@0 406 }
michael@0 407
michael@0 408 ///////////////////////////////////////////////////////////////////////////////
michael@0 409 ///////////////////////////////////////////////////////////////////////////////
michael@0 410
michael@0 411 #include "SkThread.h"
michael@0 412
michael@0 413 size_t SkGlyphCache_Globals::setCacheSizeLimit(size_t newLimit) {
michael@0 414 static const size_t minLimit = 256 * 1024;
michael@0 415 if (newLimit < minLimit) {
michael@0 416 newLimit = minLimit;
michael@0 417 }
michael@0 418
michael@0 419 SkAutoMutexAcquire ac(fMutex);
michael@0 420
michael@0 421 size_t prevLimit = fCacheSizeLimit;
michael@0 422 fCacheSizeLimit = newLimit;
michael@0 423 this->internalPurge();
michael@0 424 return prevLimit;
michael@0 425 }
michael@0 426
michael@0 427 int SkGlyphCache_Globals::setCacheCountLimit(int newCount) {
michael@0 428 if (newCount < 0) {
michael@0 429 newCount = 0;
michael@0 430 }
michael@0 431
michael@0 432 SkAutoMutexAcquire ac(fMutex);
michael@0 433
michael@0 434 int prevCount = fCacheCountLimit;
michael@0 435 fCacheCountLimit = newCount;
michael@0 436 this->internalPurge();
michael@0 437 return prevCount;
michael@0 438 }
michael@0 439
michael@0 440 void SkGlyphCache_Globals::purgeAll() {
michael@0 441 SkAutoMutexAcquire ac(fMutex);
michael@0 442 this->internalPurge(fTotalMemoryUsed);
michael@0 443 }
michael@0 444
michael@0 445 void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*),
michael@0 446 void* context) {
michael@0 447 SkGlyphCache_Globals& globals = getGlobals();
michael@0 448 SkAutoMutexAcquire ac(globals.fMutex);
michael@0 449 SkGlyphCache* cache;
michael@0 450
michael@0 451 globals.validate();
michael@0 452
michael@0 453 for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) {
michael@0 454 if (proc(cache, context)) {
michael@0 455 break;
michael@0 456 }
michael@0 457 }
michael@0 458
michael@0 459 globals.validate();
michael@0 460 }
michael@0 461
michael@0 462 /* This guy calls the visitor from within the mutext lock, so the visitor
michael@0 463 cannot:
michael@0 464 - take too much time
michael@0 465 - try to acquire the mutext again
michael@0 466 - call a fontscaler (which might call into the cache)
michael@0 467 */
michael@0 468 SkGlyphCache* SkGlyphCache::VisitCache(SkTypeface* typeface,
michael@0 469 const SkDescriptor* desc,
michael@0 470 bool (*proc)(const SkGlyphCache*, void*),
michael@0 471 void* context) {
michael@0 472 if (!typeface) {
michael@0 473 typeface = SkTypeface::GetDefaultTypeface();
michael@0 474 }
michael@0 475 SkASSERT(desc);
michael@0 476
michael@0 477 SkGlyphCache_Globals& globals = getGlobals();
michael@0 478 SkAutoMutexAcquire ac(globals.fMutex);
michael@0 479 SkGlyphCache* cache;
michael@0 480 bool insideMutex = true;
michael@0 481
michael@0 482 globals.validate();
michael@0 483
michael@0 484 for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) {
michael@0 485 if (cache->fDesc->equals(*desc)) {
michael@0 486 globals.internalDetachCache(cache);
michael@0 487 goto FOUND_IT;
michael@0 488 }
michael@0 489 }
michael@0 490
michael@0 491 /* Release the mutex now, before we create a new entry (which might have
michael@0 492 side-effects like trying to access the cache/mutex (yikes!)
michael@0 493 */
michael@0 494 ac.release(); // release the mutex now
michael@0 495 insideMutex = false; // can't use globals anymore
michael@0 496
michael@0 497 // Check if we can create a scaler-context before creating the glyphcache.
michael@0 498 // If not, we may have exhausted OS/font resources, so try purging the
michael@0 499 // cache once and try again.
michael@0 500 {
michael@0 501 // pass true the first time, to notice if the scalercontext failed,
michael@0 502 // so we can try the purge.
michael@0 503 SkScalerContext* ctx = typeface->createScalerContext(desc, true);
michael@0 504 if (!ctx) {
michael@0 505 getSharedGlobals().purgeAll();
michael@0 506 ctx = typeface->createScalerContext(desc, false);
michael@0 507 SkASSERT(ctx);
michael@0 508 }
michael@0 509 cache = SkNEW_ARGS(SkGlyphCache, (typeface, desc, ctx));
michael@0 510 }
michael@0 511
michael@0 512 FOUND_IT:
michael@0 513
michael@0 514 AutoValidate av(cache);
michael@0 515
michael@0 516 if (!proc(cache, context)) { // need to reattach
michael@0 517 if (insideMutex) {
michael@0 518 globals.internalAttachCacheToHead(cache);
michael@0 519 } else {
michael@0 520 globals.attachCacheToHead(cache);
michael@0 521 }
michael@0 522 cache = NULL;
michael@0 523 }
michael@0 524 return cache;
michael@0 525 }
michael@0 526
michael@0 527 void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
michael@0 528 SkASSERT(cache);
michael@0 529 SkASSERT(cache->fNext == NULL);
michael@0 530
michael@0 531 getGlobals().attachCacheToHead(cache);
michael@0 532 }
michael@0 533
michael@0 534 ///////////////////////////////////////////////////////////////////////////////
michael@0 535
michael@0 536 void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) {
michael@0 537 SkAutoMutexAcquire ac(fMutex);
michael@0 538
michael@0 539 this->validate();
michael@0 540 cache->validate();
michael@0 541
michael@0 542 this->internalAttachCacheToHead(cache);
michael@0 543 this->internalPurge();
michael@0 544 }
michael@0 545
michael@0 546 SkGlyphCache* SkGlyphCache_Globals::internalGetTail() const {
michael@0 547 SkGlyphCache* cache = fHead;
michael@0 548 if (cache) {
michael@0 549 while (cache->fNext) {
michael@0 550 cache = cache->fNext;
michael@0 551 }
michael@0 552 }
michael@0 553 return cache;
michael@0 554 }
michael@0 555
michael@0 556 size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) {
michael@0 557 this->validate();
michael@0 558
michael@0 559 size_t bytesNeeded = 0;
michael@0 560 if (fTotalMemoryUsed > fCacheSizeLimit) {
michael@0 561 bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
michael@0 562 }
michael@0 563 bytesNeeded = SkTMax(bytesNeeded, minBytesNeeded);
michael@0 564 if (bytesNeeded) {
michael@0 565 // no small purges!
michael@0 566 bytesNeeded = SkTMax(bytesNeeded, fTotalMemoryUsed >> 2);
michael@0 567 }
michael@0 568
michael@0 569 int countNeeded = 0;
michael@0 570 if (fCacheCount > fCacheCountLimit) {
michael@0 571 countNeeded = fCacheCount - fCacheCountLimit;
michael@0 572 // no small purges!
michael@0 573 countNeeded = SkMax32(countNeeded, fCacheCount >> 2);
michael@0 574 }
michael@0 575
michael@0 576 // early exit
michael@0 577 if (!countNeeded && !bytesNeeded) {
michael@0 578 return 0;
michael@0 579 }
michael@0 580
michael@0 581 size_t bytesFreed = 0;
michael@0 582 int countFreed = 0;
michael@0 583
michael@0 584 // we start at the tail and proceed backwards, as the linklist is in LRU
michael@0 585 // order, with unimportant entries at the tail.
michael@0 586 SkGlyphCache* cache = this->internalGetTail();
michael@0 587 while (cache != NULL &&
michael@0 588 (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
michael@0 589 SkGlyphCache* prev = cache->fPrev;
michael@0 590 bytesFreed += cache->fMemoryUsed;
michael@0 591 countFreed += 1;
michael@0 592
michael@0 593 this->internalDetachCache(cache);
michael@0 594 SkDELETE(cache);
michael@0 595 cache = prev;
michael@0 596 }
michael@0 597
michael@0 598 this->validate();
michael@0 599
michael@0 600 #ifdef SPEW_PURGE_STATUS
michael@0 601 if (countFreed && !gSkSuppressFontCachePurgeSpew) {
michael@0 602 SkDebugf("purging %dK from font cache [%d entries]\n",
michael@0 603 (int)(bytesFreed >> 10), countFreed);
michael@0 604 }
michael@0 605 #endif
michael@0 606
michael@0 607 return bytesFreed;
michael@0 608 }
michael@0 609
michael@0 610 void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) {
michael@0 611 SkASSERT(NULL == cache->fPrev && NULL == cache->fNext);
michael@0 612 if (fHead) {
michael@0 613 fHead->fPrev = cache;
michael@0 614 cache->fNext = fHead;
michael@0 615 }
michael@0 616 fHead = cache;
michael@0 617
michael@0 618 fCacheCount += 1;
michael@0 619 fTotalMemoryUsed += cache->fMemoryUsed;
michael@0 620 }
michael@0 621
michael@0 622 void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) {
michael@0 623 SkASSERT(fCacheCount > 0);
michael@0 624 fCacheCount -= 1;
michael@0 625 fTotalMemoryUsed -= cache->fMemoryUsed;
michael@0 626
michael@0 627 if (cache->fPrev) {
michael@0 628 cache->fPrev->fNext = cache->fNext;
michael@0 629 } else {
michael@0 630 fHead = cache->fNext;
michael@0 631 }
michael@0 632 if (cache->fNext) {
michael@0 633 cache->fNext->fPrev = cache->fPrev;
michael@0 634 }
michael@0 635 cache->fPrev = cache->fNext = NULL;
michael@0 636 }
michael@0 637
michael@0 638 ///////////////////////////////////////////////////////////////////////////////
michael@0 639
michael@0 640 #ifdef SK_DEBUG
michael@0 641
michael@0 642 void SkGlyphCache::validate() const {
michael@0 643 #ifdef SK_DEBUG_GLYPH_CACHE
michael@0 644 int count = fGlyphArray.count();
michael@0 645 for (int i = 0; i < count; i++) {
michael@0 646 const SkGlyph* glyph = fGlyphArray[i];
michael@0 647 SkASSERT(glyph);
michael@0 648 SkASSERT(fGlyphAlloc.contains(glyph));
michael@0 649 if (glyph->fImage) {
michael@0 650 SkASSERT(fGlyphAlloc.contains(glyph->fImage));
michael@0 651 }
michael@0 652 }
michael@0 653 #endif
michael@0 654 }
michael@0 655
michael@0 656 void SkGlyphCache_Globals::validate() const {
michael@0 657 size_t computedBytes = 0;
michael@0 658 int computedCount = 0;
michael@0 659
michael@0 660 const SkGlyphCache* head = fHead;
michael@0 661 while (head != NULL) {
michael@0 662 computedBytes += head->fMemoryUsed;
michael@0 663 computedCount += 1;
michael@0 664 head = head->fNext;
michael@0 665 }
michael@0 666
michael@0 667 SkASSERT(fTotalMemoryUsed == computedBytes);
michael@0 668 SkASSERT(fCacheCount == computedCount);
michael@0 669 }
michael@0 670
michael@0 671 #endif
michael@0 672
michael@0 673 ///////////////////////////////////////////////////////////////////////////////
michael@0 674 ///////////////////////////////////////////////////////////////////////////////
michael@0 675
michael@0 676 #include "SkTypefaceCache.h"
michael@0 677
michael@0 678 size_t SkGraphics::GetFontCacheLimit() {
michael@0 679 return getSharedGlobals().getCacheSizeLimit();
michael@0 680 }
michael@0 681
michael@0 682 size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
michael@0 683 return getSharedGlobals().setCacheSizeLimit(bytes);
michael@0 684 }
michael@0 685
michael@0 686 size_t SkGraphics::GetFontCacheUsed() {
michael@0 687 return getSharedGlobals().getTotalMemoryUsed();
michael@0 688 }
michael@0 689
michael@0 690 int SkGraphics::GetFontCacheCountLimit() {
michael@0 691 return getSharedGlobals().getCacheCountLimit();
michael@0 692 }
michael@0 693
michael@0 694 int SkGraphics::SetFontCacheCountLimit(int count) {
michael@0 695 return getSharedGlobals().setCacheCountLimit(count);
michael@0 696 }
michael@0 697
michael@0 698 int SkGraphics::GetFontCacheCountUsed() {
michael@0 699 return getSharedGlobals().getCacheCountUsed();
michael@0 700 }
michael@0 701
michael@0 702 void SkGraphics::PurgeFontCache() {
michael@0 703 getSharedGlobals().purgeAll();
michael@0 704 SkTypefaceCache::PurgeAll();
michael@0 705 }
michael@0 706
michael@0 707 size_t SkGraphics::GetTLSFontCacheLimit() {
michael@0 708 const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
michael@0 709 return tls ? tls->getCacheSizeLimit() : 0;
michael@0 710 }
michael@0 711
michael@0 712 void SkGraphics::SetTLSFontCacheLimit(size_t bytes) {
michael@0 713 if (0 == bytes) {
michael@0 714 SkGlyphCache_Globals::DeleteTLS();
michael@0 715 } else {
michael@0 716 SkGlyphCache_Globals::GetTLS().setCacheSizeLimit(bytes);
michael@0 717 }
michael@0 718 }

mercurial