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>
This commit is contained in:
Herb Derby 2018-02-28 18:47:27 -05:00 committed by Skia Commit-Bot
parent 2a4f983c94
commit dfeb2aa13b
5 changed files with 182 additions and 156 deletions

View File

@ -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

View File

@ -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<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 @@ 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<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);
for (cache = globals.internalGetHead(); cache != nullptr; cache = cache->fNext) {
if (*cache->fDesc == desc) {
globals.internalDetachCache(cache);
return SkExclusiveStrikePtr(cache);
}
}
return SkExclusiveStrikePtr(nullptr);
}
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) {
SkGlyphCache_Globals::AttachCache(cache);
}
void SkGlyphCache::ForEachStrike(std::function<void(const SkGlyphCache&)> visitor) {
SkGlyphCache_Globals& globals = get_globals();
SkAutoExclusive ac(globals.fLock);
SkGlyphCache* cache;
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;
visitor(*cache);
}
return 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));
}
AutoValidate av(cache);
if (!proc(cache, context)) { // need to reattach
globals.attachCacheToHead(cache);
cache = nullptr;
}
return cache;
}
void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
SkASSERT(cache);
SkASSERT(cache->fNext == nullptr);
get_globals().attachCacheToHead(cache);
}
static void dump_visitor(const SkGlyphCache& cache, void* context) {
int* counter = (int*)context;
int index = *counter;
*counter += 1;
const SkScalerContextRec& rec = cache.getScalerContext()->getRec();
SkDebugf("index %d\n", index);
SkDebugf("%s", rec.dump().c_str());
}
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<SkTraceMemoryDump*>(context);
const SkTypeface* face = cache.getScalerContext()->getTypeface();
auto visitor = [&counter](const SkGlyphCache& cache) {
const SkScalerContextRec& rec = cache.getScalerContext()->getRec();
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] = '_';
}
}
SkDebugf("index %d\n", counter);
SkDebugf("%s", rec.dump().c_str());
counter += 1;
};
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();
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] = '_';
}
}
void SkGlyphCache::VisitAll(Visitor visitor, void* context) {
SkGlyphCache_Globals& globals = get_globals();
SkAutoExclusive ac(globals.fLock);
SkGlyphCache* cache;
SkString dumpName = SkStringPrintf(
"%s/%s_%d/%p", gGlyphCacheDumpName, fontName.c_str(), rec.fFontID, &cache);
globals.validate();
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);
};
for (cache = globals.internalGetHead(); cache != nullptr; cache = cache->fNext) {
visitor(*cache, context);
}
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,

View File

@ -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 @@ 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<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 @@ 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<void(const SkGlyphCache&)> visitor);
#ifdef SK_DEBUG
void validate() const;
@ -182,6 +221,7 @@ private:
friend class SkGlyphCache_Globals;
enum MetricsType {
kNothing_MetricsType,
kJustAdvance_MetricsType,
kFull_MetricsType
};
@ -197,9 +237,12 @@ private:
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 @@ 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<SkDescriptor> fDesc;
const std::unique_ptr<SkScalerContext> fScalerContext;
SkPaint::FontMetrics fFontMetrics;
@ -253,7 +294,7 @@ private:
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 @@ public:
SkGlyphCache::DetachCacheUsingPaint(paint, surfaceProps, scalerContextFlags, matrix))
{}
private:
using INHERITED = std::unique_ptr<SkGlyphCache, SkGlyphCache::AttachCacheFunctor>;
using INHERITED = SkExclusiveStrikePtr;
};
class SkAutoGlyphCacheNoGamma : public SkAutoGlyphCache {

View File

@ -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;

View File

@ -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);