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 <bungeman@google.com>
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Herb Derby <herb@google.com>
diff --git a/include/core/SkTypeface.h b/include/core/SkTypeface.h
index 61620a4..8fe8088 100644
--- a/include/core/SkTypeface.h
+++ b/include/core/SkTypeface.h
@@ -365,6 +365,9 @@
};
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 50a1b5f..087fb52 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 @@
///////////////////////////////////////////////////////////////////////////////
-SkGlyphCache::SkGlyphCache(const SkDescriptor* desc, std::unique_ptr<SkScalerContext> ctx)
- : fDesc(desc->copy())
- , fScalerContext(std::move(ctx)) {
- SkASSERT(desc);
+SkGlyphCache::SkGlyphCache(const SkDescriptor& desc, std::unique_ptr<SkScalerContext> ctx)
+ : fDesc(desc.copy())
+ , fScalerContext(std::move(ctx))
+{
SkASSERT(fScalerContext);
- fPrev = fNext = nullptr;
-
fScalerContext->getFontMetrics(&fFontMetrics);
fMemoryUsed = sizeof(*this);
@@ -55,6 +52,10 @@
});
}
+void SkGlyphCache::PurgeAll() {
+ get_globals().purgeAll();
+}
+
SkGlyphCache::CharGlyphRec* SkGlyphCache::getCharGlyphRec(SkPackedUnicharID packedUnicharID) {
if (!fPackedUnicharIDToPackedGlyphID) {
fPackedUnicharIDToPackedGlyphID.reset(new CharGlyphRec[kHashCount]);
@@ -102,6 +103,15 @@
///////////////////////////////////////////////////////////////////////////////
+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 @@
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 @@
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<const SkScalerContextRec*>(
- 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<SkScalerContext> 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<void(const SkGlyphCache&)> 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 @@
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<SkTraceMemoryDump*>(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 @@
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 @@
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 052e072..8c335af 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 <memory>
class SkTraceMemoryDump;
-class SkGlyphCache_Globals;
+class SkGlyphCache;
+using SkExclusiveStrikePtr = std::unique_ptr<
+ SkGlyphCache,
+ SkFunctionWrapper<void, SkGlyphCache, SkGlyphCache_Globals::AttachCache>>;
/** \class SkGlyphCache
@@ -30,10 +32,19 @@
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 @@
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<void, SkGlyphCache, AttachCache>;
+
+ static SkExclusiveStrikePtr FindStrikeExclusive(const SkDescriptor& desc);
+
+ template <typename ScalerContextCreator>
+ 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 <typename ScalerContextCreator>
+ 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 @@
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 @@
*/
static void DumpMemoryStatistics(SkTraceMemoryDump* dump);
- typedef void (*Visitor)(const SkGlyphCache&, void* context);
- static void VisitAll(Visitor, void* context);
+ static void ForEachStrike(std::function<void(const SkGlyphCache&)> visitor);
#ifdef SK_DEBUG
void validate() const;
@@ -182,14 +221,15 @@
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 @@
SkPackedGlyphID fPackedGlyphID;
};
- SkGlyphCache(const SkDescriptor*, std::unique_ptr<SkScalerContext>);
+ SkGlyphCache(const SkDescriptor& desc, std::unique_ptr<SkScalerContext> 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 @@
// 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 @@
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<SkDescriptor> fDesc;
const std::unique_ptr<SkScalerContext> fScalerContext;
SkPaint::FontMetrics fFontMetrics;
@@ -253,7 +294,7 @@
size_t fMemoryUsed;
};
-class SkAutoGlyphCache : public std::unique_ptr<SkGlyphCache, SkGlyphCache::AttachCacheFunctor> {
+class SkAutoGlyphCache : public SkExclusiveStrikePtr {
public:
/** deprecated: use get() */
SkGlyphCache* getCache() const { return this->get(); }
@@ -280,7 +321,7 @@
SkGlyphCache::DetachCacheUsingPaint(paint, surfaceProps, scalerContextFlags, matrix))
{}
private:
- using INHERITED = std::unique_ptr<SkGlyphCache, SkGlyphCache::AttachCacheFunctor>;
+ using INHERITED = SkExclusiveStrikePtr;
};
class SkAutoGlyphCacheNoGamma : public SkAutoGlyphCache {
diff --git a/src/core/SkGlyphCache_Globals.h b/src/core/SkGlyphCache_Globals.h
index a1e0ac0..de37bbb 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 @@
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 f754c02..76f50ce 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -905,11 +905,6 @@
///////////////////////////////////////////////////////////////////////////////
-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 @@
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);