Smarter use of glyph cache

Change-Id: Ic9bea7310b375575503042881d9d54ff13996729
Reviewed-on: https://skia-review.googlesource.com/131924
Reviewed-by: Mike Klein <mtklein@chromium.org>
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index dcff1b0..4a6251f 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -590,6 +590,8 @@
 
         device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
     }
+
+    fScratchGlyphSet = skstd::make_unique<SkGlyphSet>();
 }
 
 SkCanvas::SkCanvas()
@@ -2448,7 +2450,7 @@
 
     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 @@
     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 @@
 
     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 1bad6db..8321718 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -160,10 +160,11 @@
 
         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 a25b9af..41f5013 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 @@
     }
 }
 
-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 @@
     // 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 @@
 
 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 @@
     }
 
     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 @@
     }
 
     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 @@
     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 @@
 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 17b4297..53d57af 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 @@
     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>