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