1/* 2 * Copyright 2018 Google Inc. 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 "src/core/SkStrikeCache.h" 9 10#include <cctype> 11 12#include "include/core/SkGraphics.h" 13#include "include/core/SkRefCnt.h" 14#include "include/core/SkTraceMemoryDump.h" 15#include "include/core/SkTypeface.h" 16#include "include/private/SkMutex.h" 17#include "include/private/SkTemplates.h" 18#include "src/core/SkGlyphRunPainter.h" 19#include "src/core/SkScalerCache.h" 20 21#if SK_SUPPORT_GPU 22#include "src/gpu/text/GrStrikeCache.h" 23#endif 24 25bool gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental = false; 26 27SkStrikeCache* SkStrikeCache::GlobalStrikeCache() { 28 if (gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental) { 29 static thread_local auto* cache = new SkStrikeCache; 30 return cache; 31 } 32 static auto* cache = new SkStrikeCache; 33 return cache; 34} 35 36auto SkStrikeCache::findOrCreateStrike(const SkStrikeSpec& strikeSpec) -> sk_sp<SkStrike> { 37 SkAutoMutexExclusive ac(fLock); 38 sk_sp<SkStrike> strike = this->internalFindStrikeOrNull(strikeSpec.descriptor()); 39 if (strike == nullptr) { 40 strike = this->internalCreateStrike(strikeSpec); 41 } 42 this->internalPurge(); 43 return strike; 44} 45 46SkScopedStrikeForGPU SkStrikeCache::findOrCreateScopedStrike(const SkStrikeSpec& strikeSpec) { 47 return SkScopedStrikeForGPU{this->findOrCreateStrike(strikeSpec).release()}; 48} 49 50void SkStrikeCache::PurgeAll() { 51 GlobalStrikeCache()->purgeAll(); 52} 53 54void SkStrikeCache::Dump() { 55 SkDebugf("GlyphCache [ used budget ]\n"); 56 SkDebugf(" bytes [ %8zu %8zu ]\n", 57 SkGraphics::GetFontCacheUsed(), SkGraphics::GetFontCacheLimit()); 58 SkDebugf(" count [ %8d %8d ]\n", 59 SkGraphics::GetFontCacheCountUsed(), SkGraphics::GetFontCacheCountLimit()); 60 61 int counter = 0; 62 63 auto visitor = [&counter](const SkStrike& strike) { 64 const SkScalerContextRec& rec = strike.fScalerCache.getScalerContext()->getRec(); 65 66 SkDebugf("index %d\n", counter); 67 SkDebugf("%s", rec.dump().c_str()); 68 counter += 1; 69 }; 70 71 GlobalStrikeCache()->forEachStrike(visitor); 72} 73 74namespace { 75 const char gGlyphCacheDumpName[] = "skia/sk_glyph_cache"; 76} // namespace 77 78void SkStrikeCache::DumpMemoryStatistics(SkTraceMemoryDump* dump) { 79 dump->dumpNumericValue(gGlyphCacheDumpName, "size", "bytes", SkGraphics::GetFontCacheUsed()); 80 dump->dumpNumericValue(gGlyphCacheDumpName, "budget_size", "bytes", 81 SkGraphics::GetFontCacheLimit()); 82 dump->dumpNumericValue(gGlyphCacheDumpName, "glyph_count", "objects", 83 SkGraphics::GetFontCacheCountUsed()); 84 dump->dumpNumericValue(gGlyphCacheDumpName, "budget_glyph_count", "objects", 85 SkGraphics::GetFontCacheCountLimit()); 86 87 if (dump->getRequestedDetails() == SkTraceMemoryDump::kLight_LevelOfDetail) { 88 dump->setMemoryBacking(gGlyphCacheDumpName, "malloc", nullptr); 89 return; 90 } 91 92 auto visitor = [&dump](const SkStrike& strike) { 93 const SkTypeface* face = strike.fScalerCache.getScalerContext()->getTypeface(); 94 const SkScalerContextRec& rec = strike.fScalerCache.getScalerContext()->getRec(); 95 96 SkString fontName; 97 face->getFamilyName(&fontName); 98 // Replace all special characters with '_'. 99 for (size_t index = 0; index < fontName.size(); ++index) { 100 if (!std::isalnum(fontName[index])) { 101 fontName[index] = '_'; 102 } 103 } 104 105 SkString dumpName = SkStringPrintf( 106 "%s/%s_%d/%p", gGlyphCacheDumpName, fontName.c_str(), rec.fFontID, &strike); 107 108 dump->dumpNumericValue(dumpName.c_str(), 109 "size", "bytes", strike.fMemoryUsed); 110 dump->dumpNumericValue(dumpName.c_str(), 111 "glyph_count", "objects", 112 strike.fScalerCache.countCachedGlyphs()); 113 dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr); 114 }; 115 116 GlobalStrikeCache()->forEachStrike(visitor); 117} 118 119sk_sp<SkStrike> SkStrikeCache::findStrike(const SkDescriptor& desc) { 120 SkAutoMutexExclusive ac(fLock); 121 sk_sp<SkStrike> result = this->internalFindStrikeOrNull(desc); 122 this->internalPurge(); 123 return result; 124} 125 126auto SkStrikeCache::internalFindStrikeOrNull(const SkDescriptor& desc) -> sk_sp<SkStrike> { 127 128 // Check head because it is likely the strike we are looking for. 129 if (fHead != nullptr && fHead->getDescriptor() == desc) { return sk_ref_sp(fHead); } 130 131 // Do the heavy search looking for the strike. 132 sk_sp<SkStrike>* strikeHandle = fStrikeLookup.find(desc); 133 if (strikeHandle == nullptr) { return nullptr; } 134 SkStrike* strikePtr = strikeHandle->get(); 135 SkASSERT(strikePtr != nullptr); 136 if (fHead != strikePtr) { 137 // Make most recently used 138 strikePtr->fPrev->fNext = strikePtr->fNext; 139 if (strikePtr->fNext != nullptr) { 140 strikePtr->fNext->fPrev = strikePtr->fPrev; 141 } else { 142 fTail = strikePtr->fPrev; 143 } 144 fHead->fPrev = strikePtr; 145 strikePtr->fNext = fHead; 146 strikePtr->fPrev = nullptr; 147 fHead = strikePtr; 148 } 149 return sk_ref_sp(strikePtr); 150} 151 152sk_sp<SkStrike> SkStrikeCache::createStrike( 153 const SkStrikeSpec& strikeSpec, 154 SkFontMetrics* maybeMetrics, 155 std::unique_ptr<SkStrikePinner> pinner) { 156 SkAutoMutexExclusive ac(fLock); 157 return this->internalCreateStrike(strikeSpec, maybeMetrics, std::move(pinner)); 158} 159 160auto SkStrikeCache::internalCreateStrike( 161 const SkStrikeSpec& strikeSpec, 162 SkFontMetrics* maybeMetrics, 163 std::unique_ptr<SkStrikePinner> pinner) -> sk_sp<SkStrike> { 164 std::unique_ptr<SkScalerContext> scaler = strikeSpec.createScalerContext(); 165 auto strike = 166 sk_make_sp<SkStrike>(this, strikeSpec, std::move(scaler), maybeMetrics, std::move(pinner)); 167 this->internalAttachToHead(strike); 168 return strike; 169} 170 171void SkStrikeCache::purgeAll() { 172 SkAutoMutexExclusive ac(fLock); 173 this->internalPurge(fTotalMemoryUsed); 174} 175 176size_t SkStrikeCache::getTotalMemoryUsed() const { 177 SkAutoMutexExclusive ac(fLock); 178 return fTotalMemoryUsed; 179} 180 181int SkStrikeCache::getCacheCountUsed() const { 182 SkAutoMutexExclusive ac(fLock); 183 return fCacheCount; 184} 185 186int SkStrikeCache::getCacheCountLimit() const { 187 SkAutoMutexExclusive ac(fLock); 188 return fCacheCountLimit; 189} 190 191size_t SkStrikeCache::setCacheSizeLimit(size_t newLimit) { 192 SkAutoMutexExclusive ac(fLock); 193 194 size_t prevLimit = fCacheSizeLimit; 195 fCacheSizeLimit = newLimit; 196 this->internalPurge(); 197 return prevLimit; 198} 199 200size_t SkStrikeCache::getCacheSizeLimit() const { 201 SkAutoMutexExclusive ac(fLock); 202 return fCacheSizeLimit; 203} 204 205int SkStrikeCache::setCacheCountLimit(int newCount) { 206 if (newCount < 0) { 207 newCount = 0; 208 } 209 210 SkAutoMutexExclusive ac(fLock); 211 212 int prevCount = fCacheCountLimit; 213 fCacheCountLimit = newCount; 214 this->internalPurge(); 215 return prevCount; 216} 217 218void SkStrikeCache::forEachStrike(std::function<void(const SkStrike&)> visitor) const { 219 SkAutoMutexExclusive ac(fLock); 220 221 this->validate(); 222 223 for (SkStrike* strike = fHead; strike != nullptr; strike = strike->fNext) { 224 visitor(*strike); 225 } 226} 227 228size_t SkStrikeCache::internalPurge(size_t minBytesNeeded) { 229 size_t bytesNeeded = 0; 230 if (fTotalMemoryUsed > fCacheSizeLimit) { 231 bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit; 232 } 233 bytesNeeded = std::max(bytesNeeded, minBytesNeeded); 234 if (bytesNeeded) { 235 // no small purges! 236 bytesNeeded = std::max(bytesNeeded, fTotalMemoryUsed >> 2); 237 } 238 239 int countNeeded = 0; 240 if (fCacheCount > fCacheCountLimit) { 241 countNeeded = fCacheCount - fCacheCountLimit; 242 // no small purges! 243 countNeeded = std::max(countNeeded, fCacheCount >> 2); 244 } 245 246 // early exit 247 if (!countNeeded && !bytesNeeded) { 248 return 0; 249 } 250 251 size_t bytesFreed = 0; 252 int countFreed = 0; 253 254 // Start at the tail and proceed backwards deleting; the list is in LRU 255 // order, with unimportant entries at the tail. 256 SkStrike* strike = fTail; 257 while (strike != nullptr && (bytesFreed < bytesNeeded || countFreed < countNeeded)) { 258 SkStrike* prev = strike->fPrev; 259 260 // Only delete if the strike is not pinned. 261 if (strike->fPinner == nullptr || strike->fPinner->canDelete()) { 262 bytesFreed += strike->fMemoryUsed; 263 countFreed += 1; 264 this->internalRemoveStrike(strike); 265 } 266 strike = prev; 267 } 268 269 this->validate(); 270 271#ifdef SPEW_PURGE_STATUS 272 if (countFreed) { 273 SkDebugf("purging %dK from font cache [%d entries]\n", 274 (int)(bytesFreed >> 10), countFreed); 275 } 276#endif 277 278 return bytesFreed; 279} 280 281void SkStrikeCache::internalAttachToHead(sk_sp<SkStrike> strike) { 282 SkASSERT(fStrikeLookup.find(strike->getDescriptor()) == nullptr); 283 SkStrike* strikePtr = strike.get(); 284 fStrikeLookup.set(std::move(strike)); 285 SkASSERT(nullptr == strikePtr->fPrev && nullptr == strikePtr->fNext); 286 287 fCacheCount += 1; 288 fTotalMemoryUsed += strikePtr->fMemoryUsed; 289 290 if (fHead != nullptr) { 291 fHead->fPrev = strikePtr; 292 strikePtr->fNext = fHead; 293 } 294 295 if (fTail == nullptr) { 296 fTail = strikePtr; 297 } 298 299 fHead = strikePtr; // Transfer ownership of strike to the cache list. 300} 301 302void SkStrikeCache::internalRemoveStrike(SkStrike* strike) { 303 SkASSERT(fCacheCount > 0); 304 fCacheCount -= 1; 305 fTotalMemoryUsed -= strike->fMemoryUsed; 306 307 if (strike->fPrev) { 308 strike->fPrev->fNext = strike->fNext; 309 } else { 310 fHead = strike->fNext; 311 } 312 if (strike->fNext) { 313 strike->fNext->fPrev = strike->fPrev; 314 } else { 315 fTail = strike->fPrev; 316 } 317 318 strike->fPrev = strike->fNext = nullptr; 319 strike->fRemoved = true; 320 fStrikeLookup.remove(strike->getDescriptor()); 321} 322 323void SkStrikeCache::validate() const { 324#ifdef SK_DEBUG 325 size_t computedBytes = 0; 326 int computedCount = 0; 327 328 const SkStrike* strike = fHead; 329 while (strike != nullptr) { 330 computedBytes += strike->fMemoryUsed; 331 computedCount += 1; 332 SkASSERT(fStrikeLookup.findOrNull(strike->getDescriptor()) != nullptr); 333 strike = strike->fNext; 334 } 335 336 if (fCacheCount != computedCount) { 337 SkDebugf("fCacheCount: %d, computedCount: %d", fCacheCount, computedCount); 338 SK_ABORT("fCacheCount != computedCount"); 339 } 340 if (fTotalMemoryUsed != computedBytes) { 341 SkDebugf("fTotalMemoryUsed: %zu, computedBytes: %zu", fTotalMemoryUsed, computedBytes); 342 SK_ABORT("fTotalMemoryUsed == computedBytes"); 343 } 344#endif 345} 346 347#if SK_SUPPORT_GPU 348 sk_sp<GrTextStrike> SkStrike::findOrCreateGrStrike(GrStrikeCache* grStrikeCache) const { 349 return grStrikeCache->findOrCreateStrike(fStrikeSpec); 350 } 351#endif 352 353void SkStrike::updateDelta(size_t increase) { 354 if (increase != 0) { 355 SkAutoMutexExclusive lock{fStrikeCache->fLock}; 356 fMemoryUsed += increase; 357 if (!fRemoved) { 358 fStrikeCache->fTotalMemoryUsed += increase; 359 } 360 } 361} 362 363