diff options
author | Herb Derby <herb@google.com> | 2018-06-04 15:49:15 -0400 |
---|---|---|
committer | Mike Klein <mtklein@chromium.org> | 2018-06-06 19:07:52 +0000 |
commit | 4ffa027cf6232ec7c314f6d81ff494fc0b5ccd42 (patch) | |
tree | a089748a5787adefdc14ca792fd188218cd18c47 | |
parent | 975f8eeb447a9981977f7db715f95935ad583b70 (diff) |
Smarter use of glyph cache
Change-Id: Ic9bea7310b375575503042881d9d54ff13996729
Reviewed-on: https://skia-review.googlesource.com/131924
Reviewed-by: Mike Klein <mtklein@chromium.org>
-rw-r--r-- | include/core/SkCanvas.h | 3 | ||||
-rw-r--r-- | src/core/SkCanvas.cpp | 10 | ||||
-rw-r--r-- | src/core/SkDevice.cpp | 5 | ||||
-rw-r--r-- | src/core/SkGlyphRun.cpp | 172 | ||||
-rw-r--r-- | src/core/SkGlyphRun.h | 31 | ||||
-rw-r--r-- | src/gpu/text/GrTextContext.cpp | 10 | ||||
-rw-r--r-- | tests/GlyphRunTest.cpp | 4 |
7 files changed, 125 insertions, 110 deletions
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index 44044b5cd0..2c8a0ebe4f 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -26,6 +26,7 @@ class SkDraw; class SkDrawable; class SkDrawFilter; struct SkDrawShadowRec; +class SkGlyphSet; class SkImage; class SkImageFilter; class SkMetaData; @@ -2709,6 +2710,8 @@ private: void validateClip() const {} #endif + std::unique_ptr<SkGlyphSet> fScratchGlyphSet; + typedef SkRefCnt INHERITED; }; diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index dcff1b0cc3..4a6251f110 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -590,6 +590,8 @@ void SkCanvas::init(sk_sp<SkBaseDevice> device, InitFlags flags) { device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect); } + + fScratchGlyphSet = skstd::make_unique<SkGlyphSet>(); } SkCanvas::SkCanvas() @@ -2448,7 +2450,7 @@ void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkSca while (iter.next()) { auto glyphRun = SkGlyphRun::MakeFromDrawText( - looper.paint(), text, byteLength, SkPoint::Make(x, y)); + looper.paint(), text, byteLength, SkPoint::Make(x, y), fScratchGlyphSet.get()); iter.fDevice->drawGlyphRun(looper.paint(), &glyphRun); } @@ -2461,7 +2463,8 @@ void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr) while (iter.next()) { - auto glyphRun = SkGlyphRun::MakeFromDrawPosText(looper.paint(), text, byteLength, pos); + auto glyphRun = SkGlyphRun::MakeFromDrawPosText( + looper.paint(), text, byteLength, pos, fScratchGlyphSet.get()); iter.fDevice->drawGlyphRun(looper.paint(), &glyphRun); } @@ -2475,7 +2478,8 @@ void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScala while (iter.next()) { auto glyphRun = - SkGlyphRun::MakeFromDrawPosTextH(looper.paint(), text, byteLength, xpos, constY); + SkGlyphRun::MakeFromDrawPosTextH( + looper.paint(), text, byteLength, xpos, constY, fScratchGlyphSet.get()); iter.fDevice->drawGlyphRun(looper.paint(), &glyphRun); } diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index 1bad6dba6e..8321718bab 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -160,10 +160,11 @@ void SkBaseDevice::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, switch (it.positioning()) { case SkTextBlob::kDefault_Positioning: { + SkGlyphSet glyphSet; auto origin = SkPoint::Make(x + offset.x(), y + offset.y()); auto glyphRun = - SkGlyphRun::MakeFromDrawText(runPaint, - (const char*) it.glyphs(), textLen, origin); + SkGlyphRun::MakeFromDrawText( + runPaint, (const char*) it.glyphs(), textLen, origin, &glyphSet); this->drawPosText( it.glyphs(), textLen, glyphRun.getPositions(), 2, SkPoint::Make(0, 0), runPaint); diff --git a/src/core/SkGlyphRun.cpp b/src/core/SkGlyphRun.cpp index a25b9afa8e..41f50134e9 100644 --- a/src/core/SkGlyphRun.cpp +++ b/src/core/SkGlyphRun.cpp @@ -21,61 +21,7 @@ namespace { -// A faster set implementation that does not need any initialization, and reading the set items -// is order the number of items, and not the size of the universe. -// This implementation is based on the paper by Briggs and Torczon, "An Efficient Representation -// for Sparse Sets" -class GlyphSet { -public: - GlyphSet(uint32_t glyphUniverseSize) - : fUniverseSize{glyphUniverseSize} - , fIndexes{skstd::make_unique_default<uint16_t[]>(2 * glyphUniverseSize)} - , fUniqueGlyphIDs{&fIndexes[glyphUniverseSize]} { - SkASSERT(glyphUniverseSize <= (1 << 16)); - sk_msan_mark_initialized(fIndexes.get(), &fIndexes[glyphUniverseSize], "works with uninited"); - } - - uint16_t add(SkGlyphID glyphID) { - if (glyphID >= fUniverseSize) { - glyphID = kUndefGlyph; - } - auto index = fIndexes[glyphID]; - if (index < fUniqueCount && fUniqueGlyphIDs[index] == glyphID) { - return index; - } - - fUniqueGlyphIDs[fUniqueCount] = glyphID; - fIndexes[glyphID] = fUniqueCount; - fUniqueCount += 1; - return fUniqueCount - 1; - } - - std::tuple<uint16_t, std::unique_ptr<SkGlyphID[]>> uniqueGlyphIDs() const { - auto uniqueGlyphs = skstd::make_unique_default<SkGlyphID[]>(fUniqueCount); - memcpy(uniqueGlyphs.get(), fUniqueGlyphIDs, fUniqueCount * sizeof(SkGlyphID)); - return std::make_tuple(fUniqueCount, std::move(uniqueGlyphs)); - } - -private: - static constexpr SkGlyphID kUndefGlyph{0}; - const uint32_t fUniverseSize; - uint16_t fUniqueCount{0}; - std::unique_ptr<uint16_t[]> fIndexes; - SkGlyphID* fUniqueGlyphIDs; - }; - -template<typename T> -bool is_aligned(const void* ptr) { - uintptr_t bits = reinterpret_cast<uintptr_t>(ptr); - return (bits & (alignof(T) - 1)) == 0; -} - -template<typename T> -bool is_aligned_size(size_t size) { - return size % sizeof(T) == 0; -} - -SkTypeface::Encoding convert_encoding(SkPaint::TextEncoding encoding) { +static SkTypeface::Encoding convert_encoding(SkPaint::TextEncoding encoding) { switch (encoding) { case SkPaint::kUTF8_TextEncoding: return SkTypeface::kUTF8_Encoding; case SkPaint::kUTF16_TextEncoding: return SkTypeface::kUTF16_Encoding; @@ -84,32 +30,28 @@ SkTypeface::Encoding convert_encoding(SkPaint::TextEncoding encoding) { } } -using Core = std::tuple<size_t, std::unique_ptr<uint16_t[]>, - uint16_t, std::unique_ptr<SkGlyphID[]>>; +using Core = std::tuple<size_t, std::unique_ptr<uint16_t[]>, std::vector<SkGlyphID>>; -Core make_from_glyphids(size_t glyphCount, const SkGlyphID* glyphs, SkGlyphID maxGlyphID) { - if (glyphCount == 0) { return Core(0, nullptr, 0, nullptr); } +Core make_from_glyphids( + size_t glyphCount, const SkGlyphID* glyphs, SkGlyphID maxGlyphID, SkGlyphSet* glyphSet) { + if (glyphCount == 0) { return Core(0, nullptr, std::vector<SkGlyphID>()); } - GlyphSet glyphSet{maxGlyphID}; + glyphSet->reuse(maxGlyphID); auto denseIndex = skstd::make_unique_default<uint16_t[]>(glyphCount); for (size_t i = 0; i < glyphCount; i++) { - denseIndex[i] = glyphSet.add(glyphs[i]); + denseIndex[i] = glyphSet->add(glyphs[i]); } - std::unique_ptr<SkGlyphID[]> uniqueGlyphIDs; - uint16_t uniqueCount; - std::tie(uniqueCount, uniqueGlyphIDs) = glyphSet.uniqueGlyphIDs(); - - return Core(glyphCount, std::move(denseIndex), uniqueCount, std::move(uniqueGlyphIDs)); + return Core(glyphCount, std::move(denseIndex), glyphSet->uniqueGlyphIDs()); } Core make_from_utfn(size_t byteLength, const void* utfN, const SkTypeface& typeface, - SkTypeface::Encoding encoding) { + SkTypeface::Encoding encoding, SkGlyphSet* glyphSet) { auto count = SkUTFN_CountUnichars(encoding, utfN, byteLength); if (count <= 0) { - return Core(0, nullptr, 0, nullptr); + return Core(0, nullptr, std::vector<SkGlyphID>()); } auto glyphs = skstd::make_unique_default<SkGlyphID[]>(count); @@ -117,17 +59,18 @@ Core make_from_utfn(size_t byteLength, const void* utfN, const SkTypeface& typef // TODO: move to using cached version. typeface.charsToGlyphs(utfN, encoding, glyphs.get(), count); - return make_from_glyphids(count, glyphs.get(), typeface.countGlyphs()); + return make_from_glyphids(count, glyphs.get(), typeface.countGlyphs(), glyphSet); } -Core make_core(const SkPaint& paint, const void* bytes, size_t byteLength) { +Core make_core(const SkPaint& paint, const void* bytes, size_t byteLength, SkGlyphSet* glyphSet) { auto encoding = paint.getTextEncoding(); auto typeface = SkPaintPriv::GetTypefaceOrDefault(paint); if (encoding == SkPaint::kGlyphID_TextEncoding) { return make_from_glyphids( - byteLength / 2, reinterpret_cast<const SkGlyphID*>(bytes), typeface->countGlyphs()); + byteLength / 2, reinterpret_cast<const SkGlyphID*>(bytes), + typeface->countGlyphs(), glyphSet); } else { - return make_from_utfn(byteLength, bytes, *typeface, convert_encoding(encoding)); + return make_from_utfn(byteLength, bytes, *typeface, convert_encoding(encoding), glyphSet); } } @@ -135,20 +78,20 @@ Core make_core(const SkPaint& paint, const void* bytes, size_t byteLength) { SkGlyphRun SkGlyphRun::MakeFromDrawText( const SkPaint& paint, const void* bytes, size_t byteLength, - const SkPoint origin) { + const SkPoint origin, SkGlyphSet* glyphSet) { size_t runSize; std::unique_ptr<uint16_t[]> denseIndex; - uint16_t uniqueSize; - std::unique_ptr<SkGlyphID[]> uniqueGlyphIDs; - std::tie(runSize, denseIndex, uniqueSize, uniqueGlyphIDs) = make_core(paint, bytes, byteLength); + std::vector<SkGlyphID> uniqueGlyphIDs; + std::tie(runSize, denseIndex, uniqueGlyphIDs) = make_core(paint, bytes, byteLength, glyphSet); if (runSize == 0) { return SkGlyphRun{}; } - auto advances = skstd::make_unique_default<SkPoint[]>(uniqueSize); + auto advances = skstd::make_unique_default<SkPoint[]>(uniqueGlyphIDs.size()); { auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(paint); - cache->getAdvances(SkSpan<SkGlyphID>{uniqueGlyphIDs.get(), uniqueSize}, advances.get()); + cache->getAdvances(SkSpan<SkGlyphID>{uniqueGlyphIDs.data(), + uniqueGlyphIDs.size()}, advances.get()); } auto positions = skstd::make_unique_default<SkPoint[]>(runSize); @@ -171,17 +114,16 @@ SkGlyphRun SkGlyphRun::MakeFromDrawText( } return SkGlyphRun{ - runSize, std::move(denseIndex), std::move(positions), uniqueSize, std::move(uniqueGlyphIDs)}; + runSize, std::move(denseIndex), std::move(positions), std::move(uniqueGlyphIDs)}; } SkGlyphRun SkGlyphRun::MakeFromDrawPosTextH( const SkPaint& paint, const void* bytes, size_t byteLength, - const SkScalar xpos[], SkScalar constY) { + const SkScalar xpos[], SkScalar constY, SkGlyphSet* glyphSet) { size_t runSize; std::unique_ptr<uint16_t[]> denseIndex; - uint16_t uniqueSize; - std::unique_ptr<SkGlyphID[]> uniqueGlyphIDs; - std::tie(runSize, denseIndex, uniqueSize, uniqueGlyphIDs) = make_core(paint, bytes, byteLength); + std::vector<SkGlyphID> uniqueGlyphIDs; + std::tie(runSize, denseIndex, uniqueGlyphIDs) = make_core(paint, bytes, byteLength, glyphSet); if (runSize == 0) { return SkGlyphRun{}; } @@ -192,17 +134,16 @@ SkGlyphRun SkGlyphRun::MakeFromDrawPosTextH( } return SkGlyphRun{ - runSize, std::move(denseIndex), std::move(positions), uniqueSize, std::move(uniqueGlyphIDs)}; + runSize, std::move(denseIndex), std::move(positions), std::move(uniqueGlyphIDs)}; } SkGlyphRun SkGlyphRun::MakeFromDrawPosText( const SkPaint& paint, const void* bytes, size_t byteLength, - const SkPoint pos[]) { + const SkPoint pos[], SkGlyphSet* glyphSet) { size_t runSize; std::unique_ptr<uint16_t[]> denseIndex; - uint16_t uniqueSize; - std::unique_ptr<SkGlyphID[]> uniqueGlyphIDs; - std::tie(runSize, denseIndex, uniqueSize, uniqueGlyphIDs) = make_core(paint, bytes, byteLength); + std::vector<SkGlyphID> uniqueGlyphIDs; + std::tie(runSize, denseIndex, uniqueGlyphIDs) = make_core(paint, bytes, byteLength, glyphSet); if (runSize == 0) { return SkGlyphRun{}; } @@ -211,7 +152,7 @@ SkGlyphRun SkGlyphRun::MakeFromDrawPosText( memcpy(positions.get(), pos, sizeof(SkPoint) * runSize); return SkGlyphRun{ - runSize, std::move(denseIndex), std::move(positions), uniqueSize, std::move(uniqueGlyphIDs)}; + runSize, std::move(denseIndex), std::move(positions), std::move(uniqueGlyphIDs)}; } std::unique_ptr<SkGlyphID[]> SkGlyphRun::copyGlyphIDs() const { @@ -227,10 +168,55 @@ std::unique_ptr<SkGlyphID[]> SkGlyphRun::copyGlyphIDs() const { SkGlyphRun::SkGlyphRun(size_t runSize, std::unique_ptr<uint16_t[]>&& denseIndex, std::unique_ptr<SkPoint[]>&& positions, - uint16_t uniqueSize, - std::unique_ptr<SkGlyphID[]>&& uniqueGlyphIDs) + std::vector<SkGlyphID>&& uniqueGlyphIDs) : fDenseIndex{std::move(denseIndex)} , fPositions{std::move(positions)} , fUniqueGlyphs{std::move(uniqueGlyphIDs)} - , fRunSize{runSize} - , fUniqueSize{uniqueSize} { } + , fRunSize{runSize} { } + +uint16_t SkGlyphSet::add(SkGlyphID glyphID) { + static constexpr SkGlyphID kUndefGlyph{0}; + + if (glyphID >= fUniverseSize) { + glyphID = kUndefGlyph; + } + + if (glyphID >= fIndices.size()) { + fIndices.resize(glyphID + 1); + } + + auto index = fIndices[glyphID]; + if (index < fUniqueGlyphIDs.size() && fUniqueGlyphIDs[index] == glyphID) { + return index; + } + + uint16_t newIndex = SkTo<uint16_t>(fUniqueGlyphIDs.size()); + fUniqueGlyphIDs.push_back(glyphID); + fIndices[glyphID] = newIndex; + return newIndex; +} + +std::vector<SkGlyphID> SkGlyphSet::uniqueGlyphIDs() { + return fUniqueGlyphIDs; +} + +void SkGlyphSet::reuse(uint32_t glyphUniverseSize) { + SkASSERT(glyphUniverseSize <= (1 << 16)); + fUniverseSize = glyphUniverseSize; + // If we're hanging onto these arrays for a long time, we don't want their size to drift + // endlessly upwards. It's unusual to see more than 256 unique glyphs used in a run, + // or a typeface with more than 4096 possible glyphs. + if (fUniqueGlyphIDs.size() > 256) { + fUniqueGlyphIDs.resize(256); + fUniqueGlyphIDs.shrink_to_fit(); + } + fUniqueGlyphIDs.clear(); + + if (glyphUniverseSize < 4096 && fIndices.size() > 4096) { + fIndices.resize(4096); + fIndices.shrink_to_fit(); + } + + // No need to clear fIndices here... SkGlyphSet's set insertion algorithm is designed to work + // correctly even when the fIndexes buffer is uninitialized! +}
\ No newline at end of file diff --git a/src/core/SkGlyphRun.h b/src/core/SkGlyphRun.h index 17b4297208..53d57af8c4 100644 --- a/src/core/SkGlyphRun.h +++ b/src/core/SkGlyphRun.h @@ -17,22 +17,39 @@ #include "SkPoint.h" #include "SkTypes.h" +// A faster set implementation that does not need any initialization, and reading the set items +// is order the number of items, and not the size of the universe. +// This implementation is based on the paper by Briggs and Torczon, "An Efficient Representation +// for Sparse Sets" +class SkGlyphSet { +public: + SkGlyphSet() = default; + uint16_t add(SkGlyphID glyphID); + std::vector<SkGlyphID> uniqueGlyphIDs(); + void reuse(uint32_t glyphUniverseSize); + +private: + uint32_t fUniverseSize{0}; + std::vector<uint16_t> fIndices; + std::vector<SkGlyphID> fUniqueGlyphIDs; +}; + class SkGlyphRun { public: SkGlyphRun() = default; SkGlyphRun(SkGlyphRun&&) = default; static SkGlyphRun MakeFromDrawText( const SkPaint& paint, const void* bytes, size_t byteLength, - SkPoint origin); + SkPoint origin, SkGlyphSet* glyphSet); static SkGlyphRun MakeFromDrawPosTextH( const SkPaint& paint, const void* bytes, size_t byteLength, - const SkScalar xpos[], SkScalar constY); + const SkScalar xpos[], SkScalar constY, SkGlyphSet* glyphSet); static SkGlyphRun MakeFromDrawPosText( const SkPaint& paint, const void* bytes, size_t byteLength, - const SkPoint pos[]); + const SkPoint pos[], SkGlyphSet* glyphSet); size_t runSize() const { return fRunSize; } - uint16_t uniqueSize() const { return fUniqueSize; } + uint16_t uniqueSize() const { return fUniqueGlyphs.size(); } // copyGlyphIDs is temporary glue to work with the existing system. Don't use with new code. std::unique_ptr<SkGlyphID[]> copyGlyphIDs() const; @@ -44,14 +61,12 @@ private: SkGlyphRun(size_t runSize, std::unique_ptr<uint16_t[]>&& denseIndex, std::unique_ptr<SkPoint[]>&& positions, - uint16_t uniqueSize, - std::unique_ptr<SkGlyphID[]>&& uniqueGlyphIDs); + std::vector<SkGlyphID>&& uniqueGlyphIDs); std::unique_ptr<uint16_t[]> fDenseIndex; std::unique_ptr<SkPoint[]> fPositions; - std::unique_ptr<SkGlyphID[]> fUniqueGlyphs; + std::vector<SkGlyphID> fUniqueGlyphs; const size_t fRunSize{0}; - const uint16_t fUniqueSize{0}; }; template <typename T> diff --git a/src/gpu/text/GrTextContext.cpp b/src/gpu/text/GrTextContext.cpp index 72926fe280..033506f220 100644 --- a/src/gpu/text/GrTextContext.cpp +++ b/src/gpu/text/GrTextContext.cpp @@ -210,10 +210,11 @@ void GrTextContext::regenerateTextBlob(GrTextBlob* cacheBlob, shaderCaps.supportsDistanceFieldText(), fOptions)) { switch (it.positioning()) { case SkTextBlob::kDefault_Positioning: { + SkGlyphSet glyphSet; auto origin = SkPoint::Make(x + offset.x(), y + offset.y()); auto glyphRun = SkGlyphRun::MakeFromDrawText(runPaint.skPaint(), - (const char*)it.glyphs(), textLen, origin); + (const char*)it.glyphs(), textLen, origin, &glyphSet); this->drawDFPosText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags, viewMatrix, (const char*)it.glyphs(), @@ -239,10 +240,12 @@ void GrTextContext::regenerateTextBlob(GrTextBlob* cacheBlob, } else { switch (it.positioning()) { case SkTextBlob::kDefault_Positioning: { + SkGlyphSet glyphSet; auto origin = SkPoint::Make(x + offset.x(), y + offset.y()); auto glyphRun = SkGlyphRun::MakeFromDrawText( - runPaint.skPaint(), (const char*) it.glyphs(), textLen, origin); + runPaint.skPaint(), (const char*) it.glyphs(), textLen, origin, + &glyphSet); this->DrawBmpPosText(cacheBlob, run, glyphCache, props, runPaint, scalerContextFlags, viewMatrix, (const char*) it.glyphs(), @@ -758,8 +761,9 @@ std::unique_ptr<GrDrawOp> GrTextContext::createOp_TestingOnly(GrContext* context // right now we don't handle textblobs, nor do we handle drawPosText. Since we only intend to // test the text op with this unit test, that is okay. + SkGlyphSet glyphSet; auto origin = SkPoint::Make(x, y); - auto glyphRun = SkGlyphRun::MakeFromDrawText(skPaint, text, textLen, origin); + auto glyphRun = SkGlyphRun::MakeFromDrawText(skPaint, text, textLen, origin, &glyphSet); sk_sp<GrTextBlob> blob(textContext->makeDrawPosTextBlob( context->contextPriv().getTextBlobCache(), glyphCache, diff --git a/tests/GlyphRunTest.cpp b/tests/GlyphRunTest.cpp index cad01acef7..7a80b7be93 100644 --- a/tests/GlyphRunTest.cpp +++ b/tests/GlyphRunTest.cpp @@ -16,5 +16,7 @@ DEF_TEST(GlyphRunInfo, reporter) { SkPaint paint; paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - SkGlyphRun::MakeFromDrawText(paint, glyphs, count, SkPoint::Make(0, 0)); + SkGlyphSet glyphSet; + + SkGlyphRun::MakeFromDrawText(paint, glyphs, count, SkPoint::Make(0, 0), &glyphSet); }
\ No newline at end of file |