From dfeb2aa13baab291ea794502a16daa270b11e153 Mon Sep 17 00:00:00 2001 From: Herb Derby Date: Wed, 28 Feb 2018 18:47:27 -0500 Subject: [PATCH] SkGlyphCache - update core routines. - Allows different methods for creating SkGlyphCaches - Enables passing FontMetrics for cache creation. - Removes VisitCache - Removes VisitAll - Introduces SkExclusiveStrikePtr which should replaces SkAutoGlyphCache with simpler mechanism. BUG=skia:7515 Change-Id: Ibada35e3985335179d2cc8284a837fc525224c92 Reviewed-on: https://skia-review.googlesource.com/111063 Reviewed-by: Ben Wagner Reviewed-by: Herb Derby Commit-Queue: Herb Derby --- include/core/SkTypeface.h | 3 + src/core/SkGlyphCache.cpp | 210 +++++++++++++++----------------- src/core/SkGlyphCache.h | 99 ++++++++++----- src/core/SkGlyphCache_Globals.h | 15 +-- src/core/SkPaint.cpp | 11 +- 5 files changed, 182 insertions(+), 156 deletions(-) diff --git a/include/core/SkTypeface.h b/include/core/SkTypeface.h index 61620a46d3..8fe80887dd 100644 --- a/include/core/SkTypeface.h +++ b/include/core/SkTypeface.h @@ -365,6 +365,9 @@ private: }; static SkFontStyle FromOldStyle(Style oldStyle); static SkTypeface* GetDefaultTypeface(Style style = SkTypeface::kNormal); + static SkTypeface* NormalizeTypeface(SkTypeface* typeface) { + return typeface != nullptr ? typeface : SkTypeface::GetDefaultTypeface(); + } friend class GrPathRendering; // GetDefaultTypeface friend class SkGlyphCache; // GetDefaultTypeface friend class SkPaint; // GetDefaultTypeface diff --git a/src/core/SkGlyphCache.cpp b/src/core/SkGlyphCache.cpp index 50a1b5fe30..087fb523cf 100644 --- a/src/core/SkGlyphCache.cpp +++ b/src/core/SkGlyphCache.cpp @@ -7,7 +7,6 @@ #include "SkGlyphCache.h" -#include "SkGlyphCache_Globals.h" #include "SkGraphics.h" #include "SkOnce.h" #include "SkPath.h" @@ -34,14 +33,12 @@ static SkGlyphCache_Globals& get_globals() { /////////////////////////////////////////////////////////////////////////////// -SkGlyphCache::SkGlyphCache(const SkDescriptor* desc, std::unique_ptr ctx) - : fDesc(desc->copy()) - , fScalerContext(std::move(ctx)) { - SkASSERT(desc); +SkGlyphCache::SkGlyphCache(const SkDescriptor& desc, std::unique_ptr ctx) + : fDesc(desc.copy()) + , fScalerContext(std::move(ctx)) +{ SkASSERT(fScalerContext); - fPrev = fNext = nullptr; - fScalerContext->getFontMetrics(&fFontMetrics); fMemoryUsed = sizeof(*this); @@ -55,6 +52,10 @@ SkGlyphCache::~SkGlyphCache() { }); } +void SkGlyphCache::PurgeAll() { + get_globals().purgeAll(); +} + SkGlyphCache::CharGlyphRec* SkGlyphCache::getCharGlyphRec(SkPackedUnicharID packedUnicharID) { if (!fPackedUnicharIDToPackedGlyphID) { fPackedUnicharIDToPackedGlyphID.reset(new CharGlyphRec[kHashCount]); @@ -102,6 +103,15 @@ int SkGlyphCache::countCachedGlyphs() const { /////////////////////////////////////////////////////////////////////////////// +bool SkGlyphCache::isGlyphCached(SkGlyphID glyphID, SkFixed x, SkFixed y) const { + SkPackedGlyphID packedGlyphID{glyphID, x, y}; + return fGlyphMap.find(packedGlyphID) != nullptr; +} + +SkGlyph* SkGlyphCache::getRawGlyphByID(SkPackedGlyphID id) { + return lookupByPackedGlyphID(id, kNothing_MetricsType); +} + const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) { VALIDATE(); return *this->lookupByChar(charCode, kJustAdvance_MetricsType); @@ -170,7 +180,9 @@ SkGlyph* SkGlyphCache::allocateNewGlyph(SkPackedGlyphID packedGlyphID, MetricsTy glyphPtr = fGlyphMap.set(glyph); } - if (kJustAdvance_MetricsType == mtype) { + if (kNothing_MetricsType == mtype) { + return glyphPtr; + } else if (kJustAdvance_MetricsType == mtype) { fScalerContext->getAdvance(glyphPtr); } else { SkASSERT(kFull_MetricsType == mtype); @@ -469,92 +481,43 @@ void SkGlyphCache_Globals::purgeAll() { this->internalPurge(fTotalMemoryUsed); } -/* This guy calls the visitor from within the mutext lock, so the visitor - cannot: - - take too much time - - try to acquire the mutext again - - call a fontscaler (which might call into the cache) -*/ -SkGlyphCache* SkGlyphCache::VisitCache(SkTypeface* typeface, - const SkScalerContextEffects& effects, - const SkDescriptor* desc, - bool (*proc)(const SkGlyphCache*, void*), - void* context) { - if (!typeface) { - typeface = SkTypeface::GetDefaultTypeface(); - } - SkASSERT(desc); - - // Precondition: the typeface id must be the fFontID in the descriptor - SkDEBUGCODE( - uint32_t length = 0; - const SkScalerContextRec* rec = static_cast( - desc->findEntry(kRec_SkDescriptorTag, &length)); - SkASSERT(rec); - SkASSERT(length == sizeof(*rec)); - SkASSERT(typeface->uniqueID() == rec->fFontID); - ) - +SkExclusiveStrikePtr SkGlyphCache::FindStrikeExclusive(const SkDescriptor& desc) { SkGlyphCache_Globals& globals = get_globals(); SkGlyphCache* cache; + SkAutoExclusive ac(globals.fLock); - { - SkAutoExclusive ac(globals.fLock); - - globals.validate(); - - for (cache = globals.internalGetHead(); cache != nullptr; cache = cache->fNext) { - if (*cache->fDesc == *desc) { - globals.internalDetachCache(cache); - if (!proc(cache, context)) { - globals.internalAttachCacheToHead(cache); - cache = nullptr; - } - return cache; - } + for (cache = globals.internalGetHead(); cache != nullptr; cache = cache->fNext) { + if (*cache->fDesc == desc) { + globals.internalDetachCache(cache); + return SkExclusiveStrikePtr(cache); } } - // Check if we can create a scaler-context before creating the glyphcache. - // If not, we may have exhausted OS/font resources, so try purging the - // cache once and try again. - { - // pass true the first time, to notice if the scalercontext failed, - // so we can try the purge. - std::unique_ptr ctx = typeface->createScalerContext(effects, desc, true); - if (!ctx) { - get_globals().purgeAll(); - ctx = typeface->createScalerContext(effects, desc, false); - SkASSERT(ctx); - } - cache = new SkGlyphCache(desc, std::move(ctx)); - } + return SkExclusiveStrikePtr(nullptr); +} - AutoValidate av(cache); - - if (!proc(cache, context)) { // need to reattach - globals.attachCacheToHead(cache); - cache = nullptr; - } - return cache; +SkExclusiveStrikePtr SkGlyphCache::FindOrCreateStrikeExclusive( + const SkDescriptor& desc, const SkScalerContextEffects& effects, const SkTypeface& typeface) { + auto creator = [&effects, &typeface](const SkDescriptor& descriptor, bool canFail) { + return typeface.createScalerContext(effects, &descriptor, canFail); + }; + return FindOrCreateStrikeExclusive(desc, creator); } void SkGlyphCache::AttachCache(SkGlyphCache* cache) { - SkASSERT(cache); - SkASSERT(cache->fNext == nullptr); - - get_globals().attachCacheToHead(cache); + SkGlyphCache_Globals::AttachCache(cache); } -static void dump_visitor(const SkGlyphCache& cache, void* context) { - int* counter = (int*)context; - int index = *counter; - *counter += 1; +void SkGlyphCache::ForEachStrike(std::function visitor) { + SkGlyphCache_Globals& globals = get_globals(); + SkAutoExclusive ac(globals.fLock); + SkGlyphCache* cache; - const SkScalerContextRec& rec = cache.getScalerContext()->getRec(); + globals.validate(); - SkDebugf("index %d\n", index); - SkDebugf("%s", rec.dump().c_str()); + for (cache = globals.internalGetHead(); cache != nullptr; cache = cache->fNext) { + visitor(*cache); + } } void SkGlyphCache::Dump() { @@ -565,30 +528,16 @@ void SkGlyphCache::Dump() { SkGraphics::GetFontCacheCountUsed(), SkGraphics::GetFontCacheCountLimit()); int counter = 0; - SkGlyphCache::VisitAll(dump_visitor, &counter); -} -static void sk_trace_dump_visitor(const SkGlyphCache& cache, void* context) { - SkTraceMemoryDump* dump = static_cast(context); + auto visitor = [&counter](const SkGlyphCache& cache) { + const SkScalerContextRec& rec = cache.getScalerContext()->getRec(); - const SkTypeface* face = cache.getScalerContext()->getTypeface(); - const SkScalerContextRec& rec = cache.getScalerContext()->getRec(); + SkDebugf("index %d\n", counter); + SkDebugf("%s", rec.dump().c_str()); + counter += 1; + }; - SkString fontName; - face->getFamilyName(&fontName); - // Replace all special characters with '_'. - for (size_t index = 0; index < fontName.size(); ++index) { - if (!std::isalnum(fontName[index])) { - fontName[index] = '_'; - } - } - - SkString dumpName = SkStringPrintf("%s/%s_%d/%p", - gGlyphCacheDumpName, fontName.c_str(), rec.fFontID, &cache); - - dump->dumpNumericValue(dumpName.c_str(), "size", "bytes", cache.getMemoryUsed()); - dump->dumpNumericValue(dumpName.c_str(), "glyph_count", "objects", cache.countCachedGlyphs()); - dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr); + ForEachStrike(visitor); } void SkGlyphCache::DumpMemoryStatistics(SkTraceMemoryDump* dump) { @@ -605,23 +554,53 @@ void SkGlyphCache::DumpMemoryStatistics(SkTraceMemoryDump* dump) { return; } - SkGlyphCache::VisitAll(sk_trace_dump_visitor, dump); -} + auto visitor = [&dump](const SkGlyphCache& cache) { + const SkTypeface* face = cache.getScalerContext()->getTypeface(); + const SkScalerContextRec& rec = cache.getScalerContext()->getRec(); -void SkGlyphCache::VisitAll(Visitor visitor, void* context) { - SkGlyphCache_Globals& globals = get_globals(); - SkAutoExclusive ac(globals.fLock); - SkGlyphCache* cache; + SkString fontName; + face->getFamilyName(&fontName); + // Replace all special characters with '_'. + for (size_t index = 0; index < fontName.size(); ++index) { + if (!std::isalnum(fontName[index])) { + fontName[index] = '_'; + } + } - globals.validate(); + SkString dumpName = SkStringPrintf( + "%s/%s_%d/%p", gGlyphCacheDumpName, fontName.c_str(), rec.fFontID, &cache); - for (cache = globals.internalGetHead(); cache != nullptr; cache = cache->fNext) { - visitor(*cache, context); - } + dump->dumpNumericValue(dumpName.c_str(), + "size", "bytes", cache.getMemoryUsed()); + dump->dumpNumericValue(dumpName.c_str(), + "glyph_count", "objects", cache.countCachedGlyphs()); + dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr); + }; + + ForEachStrike(visitor); } /////////////////////////////////////////////////////////////////////////////// +SkGlyphCache_Globals::~SkGlyphCache_Globals() { + SkGlyphCache* cache = fHead; + while (cache) { + SkGlyphCache* next = cache->fNext; + delete cache; + cache = next; + } +} + +void SkGlyphCache_Globals::AttachCache(SkGlyphCache* cache) { + if (cache == nullptr) { + return; + } + SkASSERT(cache->fNext == nullptr); + + get_globals().attachCacheToHead(cache); +} + + void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) { SkAutoExclusive ac(fLock); @@ -806,6 +785,15 @@ void SkGraphics::PurgeFontCache() { size_t SkGraphics::GetTLSFontCacheLimit() { return 0; } void SkGraphics::SetTLSFontCacheLimit(size_t bytes) { } +SkGlyphCache* SkGlyphCache::DetachCache( + SkTypeface* typeface, const SkScalerContextEffects& effects, const SkDescriptor* desc) +{ + + auto cache = FindOrCreateStrikeExclusive( + *desc, effects, *SkTypeface::NormalizeTypeface(typeface)); + return cache.release(); +} + SkGlyphCache* SkGlyphCache::DetachCacheUsingPaint(const SkPaint& paint, const SkSurfaceProps* surfaceProps, SkScalerContextFlags scalerContextFlags, diff --git a/src/core/SkGlyphCache.h b/src/core/SkGlyphCache.h index 052e07277e..8c335af859 100644 --- a/src/core/SkGlyphCache.h +++ b/src/core/SkGlyphCache.h @@ -8,19 +8,21 @@ #define SkGlyphCache_DEFINED #include "SkArenaAlloc.h" -#include "SkBitmap.h" #include "SkDescriptor.h" #include "SkGlyph.h" +#include "SkGlyphCache_Globals.h" #include "SkPaint.h" #include "SkTHash.h" #include "SkScalerContext.h" #include "SkTemplates.h" -#include "SkTDArray.h" #include class SkTraceMemoryDump; -class SkGlyphCache_Globals; +class SkGlyphCache; +using SkExclusiveStrikePtr = std::unique_ptr< + SkGlyphCache, + SkFunctionWrapper>; /** \class SkGlyphCache @@ -30,10 +32,19 @@ class SkGlyphCache_Globals; it and then adding it to the strike. The strikes are held in a global list, available to all threads. To interact with one, call - either VisitCache() or DetachCache(). + either Find*() or (Deprecated)DetachCache(). + + The Find*Exclusive() method returns SkExclusiveStrikePtr, which releases exclusive ownership + when they go out of scope. */ class SkGlyphCache { public: + /** Return true if glyph is cached. */ + bool isGlyphCached(SkGlyphID glyphID, SkFixed x, SkFixed y) const; + + /** Return a glyph that has no information if it is not already filled out. */ + SkGlyph* getRawGlyphByID(SkPackedGlyphID); + /** Returns a glyph with valid fAdvance and fDevKern fields. The remaining fields may be valid, but that is not guaranteed. If you require those, call getUnicharMetrics or getGlyphIDMetrics instead. @@ -112,19 +123,49 @@ public: SkScalerContext* getScalerContext() const { return fScalerContext.get(); } - /** Find a matching cache entry, and call proc() with it. If none is found create a new one. - If the proc() returns true, detach the cache and return it, otherwise leave it and return - nullptr. - */ - static SkGlyphCache* VisitCache(SkTypeface*, const SkScalerContextEffects&, const SkDescriptor*, - bool (*proc)(const SkGlyphCache*, void*), - void* context); - /** Given a strike that was returned by either VisitCache() or DetachCache() add it back into + /** Given a strike that was returned by DetachCache() add it back into the global cache list (after which the caller should not reference it anymore. + DEPRECATED - Use Find* and rely on RAII. */ static void AttachCache(SkGlyphCache*); - using AttachCacheFunctor = SkFunctionWrapper; + + static SkExclusiveStrikePtr FindStrikeExclusive(const SkDescriptor& desc); + + template + static SkExclusiveStrikePtr FindOrCreateStrikeExclusive( + const SkDescriptor& desc, ScalerContextCreator&& creator) + { + auto cache = FindStrikeExclusive(desc); + if (cache == nullptr) { + cache = CreateStrikeExclusive(desc, creator); + } + return cache; + } + + static SkExclusiveStrikePtr FindOrCreateStrikeExclusive( + const SkDescriptor& desc, + const SkScalerContextEffects& effects, + const SkTypeface& typeface); + + template + static SkExclusiveStrikePtr CreateStrikeExclusive( + const SkDescriptor& desc, ScalerContextCreator creator) + { + // Check if we can create a scaler-context before creating the glyphcache. + // If not, we may have exhausted OS/font resources, so try purging the + // cache once and try again + // pass true the first time, to notice if the scalercontext failed, + // so we can try the purge. + auto context = creator(desc, true/* can fail */); + if (!context) { + PurgeAll(); + context = creator(desc, false/* must succeed */); + SkASSERT(context); + } + + return SkExclusiveStrikePtr(new SkGlyphCache(desc, std::move(context))); + } /** Detach a strike from the global cache matching the specified descriptor. Once detached, it can be queried/modified by the current thread, and when finished, be reattached to the @@ -132,11 +173,10 @@ public: descriptor, a different strike will be generated. This is fine. It does mean we can have more than 1 strike for the same descriptor, but that will eventually get purged, and the win is that different thread will never block each other while a strike is being used. + DEPRECATED */ - static SkGlyphCache* DetachCache(SkTypeface* typeface, const SkScalerContextEffects& effects, - const SkDescriptor* desc) { - return VisitCache(typeface, effects, desc, DetachProc, nullptr); - } + static SkGlyphCache* DetachCache( + SkTypeface* typeface, const SkScalerContextEffects& effects, const SkDescriptor* desc); static SkGlyphCache* DetachCacheUsingPaint(const SkPaint& paint, const SkSurfaceProps* surfaceProps, @@ -150,8 +190,7 @@ public: */ static void DumpMemoryStatistics(SkTraceMemoryDump* dump); - typedef void (*Visitor)(const SkGlyphCache&, void* context); - static void VisitAll(Visitor, void* context); + static void ForEachStrike(std::function visitor); #ifdef SK_DEBUG void validate() const; @@ -182,14 +221,15 @@ private: friend class SkGlyphCache_Globals; enum MetricsType { + kNothing_MetricsType, kJustAdvance_MetricsType, kFull_MetricsType }; enum { - kHashBits = 8, - kHashCount = 1 << kHashBits, - kHashMask = kHashCount - 1 + kHashBits = 8, + kHashCount = 1 << kHashBits, + kHashMask = kHashCount - 1 }; struct CharGlyphRec { @@ -197,9 +237,12 @@ private: SkPackedGlyphID fPackedGlyphID; }; - SkGlyphCache(const SkDescriptor*, std::unique_ptr); + SkGlyphCache(const SkDescriptor& desc, std::unique_ptr scaler); ~SkGlyphCache(); + // Purge all the things. + static void PurgeAll(); + // Return the SkGlyph* associated with MakeID. The id parameter is the // combined glyph/x/y id generated by MakeID. If it is just a glyph id // then x and y are assumed to be zero. @@ -212,8 +255,6 @@ private: // of work using type. SkGlyph* allocateNewGlyph(SkPackedGlyphID packedGlyphID, MetricsType type); - static bool DetachProc(const SkGlyphCache*, void*) { return true; } - // The id arg is a combined id generated by MakeID. CharGlyphRec* getCharGlyphRec(SkPackedUnicharID id); @@ -231,8 +272,8 @@ private: static const SkGlyph::Intercept* MatchBounds(const SkGlyph* glyph, const SkScalar bounds[2]); - SkGlyphCache* fNext; - SkGlyphCache* fPrev; + SkGlyphCache* fNext{nullptr}; + SkGlyphCache* fPrev{nullptr}; const std::unique_ptr fDesc; const std::unique_ptr fScalerContext; SkPaint::FontMetrics fFontMetrics; @@ -253,7 +294,7 @@ private: size_t fMemoryUsed; }; -class SkAutoGlyphCache : public std::unique_ptr { +class SkAutoGlyphCache : public SkExclusiveStrikePtr { public: /** deprecated: use get() */ SkGlyphCache* getCache() const { return this->get(); } @@ -280,7 +321,7 @@ public: SkGlyphCache::DetachCacheUsingPaint(paint, surfaceProps, scalerContextFlags, matrix)) {} private: - using INHERITED = std::unique_ptr; + using INHERITED = SkExclusiveStrikePtr; }; class SkAutoGlyphCacheNoGamma : public SkAutoGlyphCache { diff --git a/src/core/SkGlyphCache_Globals.h b/src/core/SkGlyphCache_Globals.h index a1e0ac04e2..de37bbb7e8 100644 --- a/src/core/SkGlyphCache_Globals.h +++ b/src/core/SkGlyphCache_Globals.h @@ -8,10 +8,10 @@ #ifndef SkGlyphCache_Globals_DEFINED #define SkGlyphCache_Globals_DEFINED -#include "SkGlyphCache.h" #include "SkMutex.h" #include "SkSpinlock.h" -#include "SkTLS.h" + +class SkGlyphCache; #ifndef SK_DEFAULT_FONT_CACHE_COUNT_LIMIT #define SK_DEFAULT_FONT_CACHE_COUNT_LIMIT 2048 @@ -38,14 +38,9 @@ public: fPointSizeLimit = SK_DEFAULT_FONT_CACHE_POINT_SIZE_LIMIT; } - ~SkGlyphCache_Globals() { - SkGlyphCache* cache = fHead; - while (cache) { - SkGlyphCache* next = cache->fNext; - delete cache; - cache = next; - } - } + ~SkGlyphCache_Globals(); + + static void AttachCache(SkGlyphCache* cache); mutable SkSpinlock fLock; diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index f754c021eb..76f50ce1a1 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -905,11 +905,6 @@ size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth, /////////////////////////////////////////////////////////////////////////////// -static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) { - *(SkPaint::FontMetrics*)context = cache->getFontMetrics(); - return false; // don't detach the cache -} - SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const { SkCanonicalizePaint canon(*this); const SkPaint& paint = canon.getPaint(); @@ -932,7 +927,11 @@ SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const { auto desc = SkScalerContext::CreateDescriptorAndEffectsUsingPaint( paint, nullptr, SkScalerContextFlags::kNone, zoomPtr, &ad, &effects); - SkGlyphCache::VisitCache(paint.getTypeface(), effects, desc, FontMetricsCacheProc, metrics); + { + auto typeface = SkTypeface::NormalizeTypeface(paint.getTypeface()); + auto cache = SkGlyphCache::FindOrCreateStrikeExclusive(*desc, effects, *typeface); + *metrics = cache->getFontMetrics(); + } if (scale) { SkPaintPriv::ScaleFontMetrics(metrics, scale);