Updates nvpr text blobs to not store a direct reference to the
per-glyph GPU path object, but rather store a key for looking it up in
the resource cache. This allows the cache to purge glyphs when needed.
Also indirectly fixes a memory leak that was introduced with nvpr text
blobs.

BUG=skia:

Review URL: https://codereview.chromium.org/1374853004
diff --git a/include/gpu/GrDrawContext.h b/include/gpu/GrDrawContext.h
index 9365021..03dd8c4 100644
--- a/include/gpu/GrDrawContext.h
+++ b/include/gpu/GrDrawContext.h
@@ -18,6 +18,7 @@
 class GrDrawTarget;
 class GrPaint;
 class GrPathProcessor;
+class GrPathRange;
 class GrPathRangeDraw;
 class GrPipelineBuilder;
 class GrRenderTarget;
@@ -67,6 +68,7 @@
                             const SkMatrix& viewMatrix,
                             const SkMatrix& localMatrix,
                             GrColor color,
+                            GrPathRange* range,
                             GrPathRangeDraw* draw,
                             int /*GrPathRendering::FillType*/ fill);
 
diff --git a/src/gpu/GrDrawContext.cpp b/src/gpu/GrDrawContext.cpp
index 7e1e66f..96bc3f3 100644
--- a/src/gpu/GrDrawContext.cpp
+++ b/src/gpu/GrDrawContext.cpp
@@ -111,11 +111,12 @@
                                        const SkMatrix& viewMatrix,
                                        const SkMatrix& localMatrix,
                                        GrColor color,
+                                       GrPathRange* range,
                                        GrPathRangeDraw* draw,
                                        int /*GrPathRendering::FillType*/ fill) {
     RETURN_IF_ABANDONED
 
-    fDrawTarget->drawPathsFromRange(*pipelineBuilder, viewMatrix, localMatrix, color, draw,
+    fDrawTarget->drawPathsFromRange(*pipelineBuilder, viewMatrix, localMatrix, color, range, draw,
                                     (GrPathRendering::FillType) fill);
 }
 
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index ba0142e..cd86458 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -255,9 +255,11 @@
                                       const SkMatrix& viewMatrix,
                                       const SkMatrix& localMatrix,
                                       GrColor color,
+                                      GrPathRange* range,
                                       GrPathRangeDraw* draw,
                                       GrPathRendering::FillType fill) {
-    GrDrawPathBatchBase* batch = GrDrawPathRangeBatch::Create(viewMatrix, localMatrix, color, draw);
+    GrDrawPathBatchBase* batch = GrDrawPathRangeBatch::Create(viewMatrix, localMatrix, color,
+                                                              range, draw);
     this->drawPathBatch(pipelineBuilder, batch, fill);
     batch->unref();
 }
diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h
index 8c85e0a..98faffc 100644
--- a/src/gpu/GrDrawTarget.h
+++ b/src/gpu/GrDrawTarget.h
@@ -92,7 +92,7 @@
      *
      * TODO: Remove this function and construct the batch outside GrDrawTarget.
      *
-     * @param draw            The range, transforms, and indices for the draw.
+     * @param draw            The transforms and indices for the draw.
      *                        This object must only be drawn once. The draw
      *                        may modify its contents.
      * @param fill            Fill type for drawing all the paths
@@ -101,6 +101,7 @@
                             const SkMatrix& viewMatrix,
                             const SkMatrix& localMatrix,
                             GrColor color,
+                            GrPathRange* range,
                             GrPathRangeDraw* draw,
                             GrPathRendering::FillType fill);
 
diff --git a/src/gpu/GrPathRange.cpp b/src/gpu/GrPathRange.cpp
index 117051d..754aca0 100644
--- a/src/gpu/GrPathRange.cpp
+++ b/src/gpu/GrPathRange.cpp
@@ -8,7 +8,6 @@
 #include "GrPathRange.h"
 #include "SkPath.h"
 
-
 GrPathRange::GrPathRange(GrGpu* gpu,
                          PathGenerator* pathGenerator)
     : INHERITED(gpu, kCached_LifeCycle),
@@ -27,10 +26,14 @@
 
 void GrPathRange::loadPathsIfNeeded(const void* indices, PathIndexType indexType, int count) const {
     switch (indexType) {
-        case kU8_PathIndexType: return this->loadPathsIfNeeded<uint8_t>(indices, count);
-        case kU16_PathIndexType: return this->loadPathsIfNeeded<uint16_t>(indices, count);
-        case kU32_PathIndexType: return this->loadPathsIfNeeded<uint32_t>(indices, count);
-        default: SkFAIL("Unknown path index type");
+        case kU8_PathIndexType:
+            return this->loadPathsIfNeeded(reinterpret_cast<const uint8_t*>(indices), count);
+        case kU16_PathIndexType:
+            return this->loadPathsIfNeeded(reinterpret_cast<const uint16_t*>(indices), count);
+        case kU32_PathIndexType:
+            return this->loadPathsIfNeeded(reinterpret_cast<const uint32_t*>(indices), count);
+        default:
+            SkFAIL("Unknown path index type");
     }
 }
 
@@ -38,10 +41,14 @@
 
 void GrPathRange::assertPathsLoaded(const void* indices, PathIndexType indexType, int count) const {
     switch (indexType) {
-        case kU8_PathIndexType: return this->assertPathsLoaded<uint8_t>(indices, count);
-        case kU16_PathIndexType: return this->assertPathsLoaded<uint16_t>(indices, count);
-        case kU32_PathIndexType: return this->assertPathsLoaded<uint32_t>(indices, count);
-        default: SkFAIL("Unknown path index type");
+        case kU8_PathIndexType:
+            return this->assertPathsLoaded(reinterpret_cast<const uint8_t*>(indices), count);
+        case kU16_PathIndexType:
+            return this->assertPathsLoaded(reinterpret_cast<const uint16_t*>(indices), count);
+        case kU32_PathIndexType:
+            return this->assertPathsLoaded(reinterpret_cast<const uint32_t*>(indices), count);
+        default:
+            SkFAIL("Unknown path index type");
     }
 }
 
diff --git a/src/gpu/GrPathRange.h b/src/gpu/GrPathRange.h
index 23b8beb..00165d1 100644
--- a/src/gpu/GrPathRange.h
+++ b/src/gpu/GrPathRange.h
@@ -72,18 +72,17 @@
 
     void loadPathsIfNeeded(const void* indices, PathIndexType, int count) const;
 
-    template<typename IndexType> void loadPathsIfNeeded(const void* indices, int count) const {
+    template<typename IndexType> void loadPathsIfNeeded(const IndexType* indices, int count) const {
         if (!fPathGenerator) {
             return;
         }
 
-        const IndexType* indexArray = reinterpret_cast<const IndexType*>(indices);
         bool didLoadPaths = false;
 
         for (int i = 0; i < count; ++i) {
-            SkASSERT(indexArray[i] < static_cast<uint32_t>(fNumPaths));
+            SkASSERT(indices[i] < static_cast<uint32_t>(fNumPaths));
 
-            const int groupIndex = indexArray[i] / kPathsPerGroup;
+            const int groupIndex = indices[i] / kPathsPerGroup;
             const int groupByte = groupIndex / 8;
             const uint8_t groupBit = 1 << (groupIndex % 8);
 
@@ -113,17 +112,15 @@
 #ifdef SK_DEBUG
     void assertPathsLoaded(const void* indices, PathIndexType, int count) const;
 
-    template<typename IndexType> void assertPathsLoaded(const void* indices, int count) const {
+    template<typename IndexType> void assertPathsLoaded(const IndexType* indices, int count) const {
         if (!fPathGenerator) {
             return;
         }
 
-        const IndexType* indexArray = reinterpret_cast<const IndexType*>(indices);
-
         for (int i = 0; i < count; ++i) {
-            SkASSERT(indexArray[i] < static_cast<uint32_t>(fNumPaths));
+            SkASSERT(indices[i] < static_cast<uint32_t>(fNumPaths));
 
-            const int groupIndex = indexArray[i] / kPathsPerGroup;
+            const int groupIndex = indices[i] / kPathsPerGroup;
             const int groupByte = groupIndex / 8;
             const uint8_t groupBit = 1 << (groupIndex % 8);
 
diff --git a/src/gpu/GrStencilAndCoverTextContext.cpp b/src/gpu/GrStencilAndCoverTextContext.cpp
index 0dc9029..ee1f1af 100644
--- a/src/gpu/GrStencilAndCoverTextContext.cpp
+++ b/src/gpu/GrStencilAndCoverTextContext.cpp
@@ -81,8 +81,8 @@
                                               const SkIRect& clipBounds) {
     TextRun run(skPaint);
     GrPipelineBuilder pipelineBuilder(paint, rt, clip);
-    run.setText(text, byteLength, x, y, fContext, &fSurfaceProps);
-    run.draw(dc, &pipelineBuilder, paint.getColor(), viewMatrix, 0, 0, clipBounds,
+    run.setText(text, byteLength, x, y);
+    run.draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, 0, 0, clipBounds,
              fFallbackTextContext, skPaint);
 }
 
@@ -99,8 +99,8 @@
                                                  const SkIRect& clipBounds) {
     TextRun run(skPaint);
     GrPipelineBuilder pipelineBuilder(paint, rt, clip);
-    run.setPosText(text, byteLength, pos, scalarsPerPosition, offset, fContext, &fSurfaceProps);
-    run.draw(dc, &pipelineBuilder, paint.getColor(), viewMatrix, 0, 0, clipBounds,
+    run.setPosText(text, byteLength, pos, scalarsPerPosition, offset);
+    run.draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, 0, 0, clipBounds,
              fFallbackTextContext, skPaint);
 }
 
@@ -137,8 +137,9 @@
 
     TextBlob::Iter iter(blob);
     for (TextRun* run = iter.get(); run; run = iter.next()) {
-        run->draw(dc, &pipelineBuilder, paint.getColor(), viewMatrix, x, y, clipBounds,
+        run->draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, x, y, clipBounds,
                   fFallbackTextContext, skPaint);
+        run->releaseGlyphCache();
     }
 }
 
@@ -153,8 +154,7 @@
             fLRUList.addToTail(*found);
             return **found;
         }
-        TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint, fContext,
-                                      &fSurfaceProps);
+        TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint);
         this->purgeToFit(*blob);
         fBlobIdCache.set(skBlob->uniqueID(), blob);
         fLRUList.addToTail(blob);
@@ -171,7 +171,7 @@
             fLRUList.addToTail(*found);
             return **found;
         }
-        TextBlob* blob = new TextBlob(key, skBlob, skPaint, fContext, &fSurfaceProps);
+        TextBlob* blob = new TextBlob(key, skBlob, skPaint);
         this->purgeToFit(*blob);
         fBlobKeyCache.set(blob);
         fLRUList.addToTail(blob);
@@ -181,9 +181,9 @@
 }
 
 void GrStencilAndCoverTextContext::purgeToFit(const TextBlob& blob) {
-    static const int maxCacheSize = 4 * 1024 * 1024; // Allow up to 4 MB for caching text blobs.
+    static const size_t maxCacheSize = 4 * 1024 * 1024; // Allow up to 4 MB for caching text blobs.
 
-    int maxSizeForNewBlob = maxCacheSize - blob.cpuMemorySize();
+    size_t maxSizeForNewBlob = maxCacheSize - blob.cpuMemorySize();
     while (fCacheSize && fCacheSize > maxSizeForNewBlob) {
         TextBlob* lru = fLRUList.head();
         if (1 == lru->key().count()) {
@@ -200,8 +200,8 @@
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 
-void GrStencilAndCoverTextContext::TextBlob::init(const SkTextBlob* skBlob, const SkPaint& skPaint,
-                                                  GrContext* ctx, const SkSurfaceProps* props) {
+void GrStencilAndCoverTextContext::TextBlob::init(const SkTextBlob* skBlob,
+                                                  const SkPaint& skPaint) {
     fCpuMemorySize = sizeof(TextBlob);
     SkPaint runPaint(skPaint);
     for (SkTextBlob::RunIterator iter(skBlob); !iter.done(); iter.next()) {
@@ -214,18 +214,17 @@
 
         switch (iter.positioning()) {
             case SkTextBlob::kDefault_Positioning:
-                run->setText(text, byteLength, runOffset.fX, runOffset.fY, ctx, props);
+                run->setText(text, byteLength, runOffset.fX, runOffset.fY);
                 break;
             case SkTextBlob::kHorizontal_Positioning:
-                run->setPosText(text, byteLength, iter.pos(), 1, SkPoint::Make(0, runOffset.fY),
-                                ctx, props);
+                run->setPosText(text, byteLength, iter.pos(), 1, SkPoint::Make(0, runOffset.fY));
                 break;
             case SkTextBlob::kFull_Positioning:
-                run->setPosText(text, byteLength, iter.pos(), 2, SkPoint::Make(0, 0), ctx, props);
+                run->setPosText(text, byteLength, iter.pos(), 2, SkPoint::Make(0, 0));
                 break;
         }
 
-        fCpuMemorySize += run->cpuMemorySize();
+        fCpuMemorySize += run->computeSizeInCache();
     }
 }
 
@@ -260,7 +259,9 @@
 GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke)
     : fStroke(fontAndStroke),
       fFont(fontAndStroke),
-      fTotalGlyphCount(0) {
+      fTotalGlyphCount(0),
+      fDetachedGlyphCache(nullptr),
+      fLastDrawnGlyphsID(SK_InvalidUniqueID) {
     SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported.
 
     // Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path
@@ -307,28 +308,55 @@
         fUsingRawGlyphPaths = false;
     }
 
+    // Generate the key that will be used to cache the GPU glyph path objects.
+    if (fUsingRawGlyphPaths && fStroke.isFillStyle()) {
+        static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::GenerateDomain();
+
+        const SkTypeface* typeface = fFont.getTypeface();
+        GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1);
+        reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
+    } else {
+        static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
+
+        int strokeDataCount = fStroke.computeUniqueKeyFragmentData32Cnt();
+        if (fUsingRawGlyphPaths) {
+            const SkTypeface* typeface = fFont.getTypeface();
+            GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 + strokeDataCount);
+            reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
+            reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount;
+            fStroke.asUniqueKeyFragment(&builder[2]);
+        } else {
+            SkGlyphCache* glyphCache = this->getGlyphCache();
+            const SkTypeface* typeface = glyphCache->getScalerContext()->getTypeface();
+            const SkDescriptor* desc = &glyphCache->getDescriptor();
+            int descDataCount = (desc->getLength() + 3) / 4;
+            GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain,
+                                         2 + strokeDataCount + descDataCount);
+            reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
+            reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount | (descDataCount << 16);
+            fStroke.asUniqueKeyFragment(&builder[2]);
+            memcpy(&builder[2 + strokeDataCount], desc, desc->getLength());
+        }
+    }
+
     // When drawing from canonically sized paths, the actual local coords are fTextRatio * coords.
     fLocalMatrixTemplate.setScale(fTextRatio, fTextRatio);
 }
 
 GrStencilAndCoverTextContext::TextRun::~TextRun() {
+    this->releaseGlyphCache();
 }
 
 void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t byteLength,
-                                                    SkScalar x, SkScalar y, GrContext* ctx,
-                                                    const SkSurfaceProps* surfaceProps) {
+                                                    SkScalar x, SkScalar y) {
     SkASSERT(byteLength == 0 || text != nullptr);
 
-    SkAutoGlyphCacheNoGamma autoGlyphCache(fFont, surfaceProps, nullptr);
-    SkGlyphCache* glyphCache = autoGlyphCache.getCache();
-
-    fTotalGlyphCount = fFont.countText(text, byteLength);
-    fDraw.reset(GrPathRangeDraw::Create(this->createGlyphs(ctx, glyphCache),
-                                        GrPathRendering::kTranslate_PathTransformType,
-                                        fTotalGlyphCount));
-
+    SkGlyphCache* glyphCache = this->getGlyphCache();
     SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc();
 
+    fDraw.reset(GrPathRangeDraw::Create(GrPathRendering::kTranslate_PathTransformType,
+                                        fTotalGlyphCount = fFont.countText(text, byteLength)));
+
     const char* stop = text + byteLength;
 
     // Measure first if needed.
@@ -378,28 +406,21 @@
         fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio);
     }
 
-    fDraw->loadGlyphPathsIfNeeded();
-
     fFallbackTextBlob.reset(fallback.buildIfInitialized());
 }
 
 void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t byteLength,
                                                        const SkScalar pos[], int scalarsPerPosition,
-                                                       const SkPoint& offset, GrContext* ctx,
-                                                       const SkSurfaceProps* surfaceProps) {
+                                                       const SkPoint& offset) {
     SkASSERT(byteLength == 0 || text != nullptr);
     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
 
-    SkAutoGlyphCacheNoGamma autoGlyphCache(fFont, surfaceProps, nullptr);
-    SkGlyphCache* glyphCache = autoGlyphCache.getCache();
-
-    fTotalGlyphCount = fFont.countText(text, byteLength);
-    fDraw.reset(GrPathRangeDraw::Create(this->createGlyphs(ctx, glyphCache),
-                                        GrPathRendering::kTranslate_PathTransformType,
-                                        fTotalGlyphCount));
-
+    SkGlyphCache* glyphCache = this->getGlyphCache();
     SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc();
 
+    fDraw.reset(GrPathRangeDraw::Create(GrPathRendering::kTranslate_PathTransformType,
+                                        fTotalGlyphCount = fFont.countText(text, byteLength)));
+
     const char* stop = text + byteLength;
 
     SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
@@ -418,39 +439,24 @@
         pos += scalarsPerPosition;
     }
 
-    fDraw->loadGlyphPathsIfNeeded();
-
     fFallbackTextBlob.reset(fallback.buildIfInitialized());
 }
 
-GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx,
-                                                                 SkGlyphCache* glyphCache) {
-    SkTypeface* typeface = fUsingRawGlyphPaths ? fFont.getTypeface()
-                                               : glyphCache->getScalerContext()->getTypeface();
-    const SkDescriptor* desc = fUsingRawGlyphPaths ? nullptr : &glyphCache->getDescriptor();
-
-    static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
-    int strokeDataCount = fStroke.computeUniqueKeyFragmentData32Cnt();
-    GrUniqueKey glyphKey;
-    GrUniqueKey::Builder builder(&glyphKey, kPathGlyphDomain, 2 + strokeDataCount);
-    reinterpret_cast<uint32_t&>(builder[0]) = desc ? desc->getChecksum() : 0;
-    reinterpret_cast<uint32_t&>(builder[1]) = typeface ? typeface->uniqueID() : 0;
-    if (strokeDataCount > 0) {
-        fStroke.asUniqueKeyFragment(&builder[2]);
-    }
-    builder.finish();
-
-    SkAutoTUnref<GrPathRange> glyphs(
-        static_cast<GrPathRange*>(
-            ctx->resourceProvider()->findAndRefResourceByUniqueKey(glyphKey)));
+GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx) const {
+    GrPathRange* glyphs = static_cast<GrPathRange*>(
+            ctx->resourceProvider()->findAndRefResourceByUniqueKey(fGlyphPathsKey));
     if (nullptr == glyphs) {
-        glyphs.reset(ctx->resourceProvider()->createGlyphs(typeface, desc, fStroke));
-        ctx->resourceProvider()->assignUniqueKeyToResource(glyphKey, glyphs);
-    } else {
-        SkASSERT(nullptr == desc || glyphs->isEqualTo(*desc));
+        if (fUsingRawGlyphPaths) {
+            glyphs = ctx->resourceProvider()->createGlyphs(fFont.getTypeface(), nullptr, fStroke);
+        } else {
+            SkGlyphCache* cache = this->getGlyphCache();
+            glyphs = ctx->resourceProvider()->createGlyphs(cache->getScalerContext()->getTypeface(),
+                                                           &cache->getDescriptor(),
+                                                           fStroke);
+        }
+        ctx->resourceProvider()->assignUniqueKeyToResource(fGlyphPathsKey, glyphs);
     }
-
-    return glyphs.detach();
+    return glyphs;
 }
 
 inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& glyph,
@@ -468,7 +474,8 @@
     }
 }
 
-void GrStencilAndCoverTextContext::TextRun::draw(GrDrawContext* dc,
+void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx,
+                                                 GrDrawContext* dc,
                                                  GrPipelineBuilder* pipelineBuilder,
                                                  GrColor color,
                                                  const SkMatrix& viewMatrix,
@@ -493,6 +500,13 @@
 
         *pipelineBuilder->stencil() = kStencilPass;
 
+        SkAutoTUnref<GrPathRange> glyphs(this->createGlyphs(ctx));
+        if (fLastDrawnGlyphsID != glyphs->getUniqueID()) {
+            // Either this is the first draw or the glyphs object was purged since last draw.
+            glyphs->loadPathsIfNeeded(fDraw->indices(), fDraw->count());
+            fLastDrawnGlyphsID = glyphs->getUniqueID();
+        }
+
         SkMatrix drawMatrix(viewMatrix);
         drawMatrix.preTranslate(x, y);
         drawMatrix.preScale(fTextRatio, fTextRatio);
@@ -501,7 +515,7 @@
         localMatrix.setTranslateX(x);
         localMatrix.setTranslateY(y);
 
-        dc->drawPathsFromRange(pipelineBuilder, drawMatrix, localMatrix, color, fDraw,
+        dc->drawPathsFromRange(pipelineBuilder, drawMatrix, localMatrix, color, glyphs, fDraw,
                                GrPathRendering::kWinding_FillType);
     }
 
@@ -518,8 +532,25 @@
     }
 }
 
-int GrStencilAndCoverTextContext::TextRun::cpuMemorySize() const {
-    int size = sizeof(TextRun) + fTotalGlyphCount * (sizeof(uint16_t) + 2 * sizeof(float));
+SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const {
+    if (!fDetachedGlyphCache) {
+        fDetachedGlyphCache = fFont.detachCache(nullptr, nullptr, true /*ignoreGamma*/);
+    }
+    return fDetachedGlyphCache;
+}
+
+
+void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const {
+    if (fDetachedGlyphCache) {
+        SkGlyphCache::AttachCache(fDetachedGlyphCache);
+        fDetachedGlyphCache = nullptr;
+    }
+}
+
+size_t GrStencilAndCoverTextContext::TextRun::computeSizeInCache() const {
+    size_t size = sizeof(TextRun) +
+               fGlyphPathsKey.size() +
+               fTotalGlyphCount * (sizeof(uint16_t) + 2 * sizeof(float));
     if (fDraw) {
         size += sizeof(GrPathRangeDraw);
     }
diff --git a/src/gpu/GrStencilAndCoverTextContext.h b/src/gpu/GrStencilAndCoverTextContext.h
index 008f61e..3bf89d3 100644
--- a/src/gpu/GrStencilAndCoverTextContext.h
+++ b/src/gpu/GrStencilAndCoverTextContext.h
@@ -17,7 +17,6 @@
 
 class GrTextStrike;
 class GrPath;
-class GrPathRange;
 class SkSurfaceProps;
 
 /*
@@ -60,22 +59,22 @@
         TextRun(const SkPaint& fontAndStroke);
         ~TextRun();
 
-        void setText(const char text[], size_t byteLength, SkScalar x, SkScalar y,
-                     GrContext*, const SkSurfaceProps*);
+        void setText(const char text[], size_t byteLength, SkScalar x, SkScalar y);
 
-        void setPosText(const char text[], size_t byteLength,
-                        const SkScalar pos[], int scalarsPerPosition, const SkPoint& offset,
-                        GrContext*, const SkSurfaceProps*);
+        void setPosText(const char text[], size_t byteLength, const SkScalar pos[],
+                        int scalarsPerPosition, const SkPoint& offset);
 
-        void draw(GrDrawContext*, GrPipelineBuilder*, GrColor, const SkMatrix&,
+        void draw(GrContext*, GrDrawContext*, GrPipelineBuilder*, GrColor, const SkMatrix&,
                   SkScalar x, SkScalar y, const SkIRect& clipBounds,
                   GrTextContext* fallbackTextContext, const SkPaint& originalSkPaint) const;
 
-        int cpuMemorySize() const;
+        void releaseGlyphCache() const;
+
+        size_t computeSizeInCache() const;
 
     private:
-        GrPathRange* createGlyphs(GrContext*, SkGlyphCache*);
-
+        SkGlyphCache* getGlyphCache() const;
+        GrPathRange* createGlyphs(GrContext*) const;
         void appendGlyph(const SkGlyph&, const SkPoint&, FallbackBlobBuilder*);
 
         GrStrokeInfo                     fStroke;
@@ -83,9 +82,12 @@
         SkScalar                         fTextRatio;
         float                            fTextInverseRatio;
         bool                             fUsingRawGlyphPaths;
+        GrUniqueKey                      fGlyphPathsKey;
         int                              fTotalGlyphCount;
         SkAutoTUnref<GrPathRangeDraw>    fDraw;
         SkAutoTUnref<const SkTextBlob>   fFallbackTextBlob;
+        mutable SkGlyphCache*            fDetachedGlyphCache;
+        mutable uint32_t                 fLastDrawnGlyphsID;
         mutable SkMatrix                 fLocalMatrixTemplate;
     };
 
@@ -102,27 +104,25 @@
             return SkChecksum::Murmur3(key.begin(), sizeof(uint32_t) * key.count());
         }
 
-        TextBlob(uint32_t blobId, const SkTextBlob* skBlob, const SkPaint& skPaint,
-                 GrContext* ctx, const SkSurfaceProps* props)
-            : fKey(&blobId, 1) { this->init(skBlob, skPaint, ctx, props); }
+        TextBlob(uint32_t blobId, const SkTextBlob* skBlob, const SkPaint& skPaint)
+            : fKey(&blobId, 1) { this->init(skBlob, skPaint); }
 
-        TextBlob(const Key& key, const SkTextBlob* skBlob, const SkPaint& skPaint,
-                 GrContext* ctx, const SkSurfaceProps* props)
+        TextBlob(const Key& key, const SkTextBlob* skBlob, const SkPaint& skPaint)
             : fKey(key) {
             // 1-length keys are unterstood to be the blob id and must use the other constructor.
             SkASSERT(fKey.count() > 1);
-            this->init(skBlob, skPaint, ctx, props);
+            this->init(skBlob, skPaint);
         }
 
         const Key& key() const { return fKey; }
 
-        int cpuMemorySize() const { return fCpuMemorySize; }
+        size_t cpuMemorySize() const { return fCpuMemorySize; }
 
     private:
-        void init(const SkTextBlob*, const SkPaint&, GrContext*, const SkSurfaceProps*);
+        void init(const SkTextBlob*, const SkPaint&);
 
         const SkSTArray<1, uint32_t, true>   fKey;
-        int                                  fCpuMemorySize;
+        size_t                               fCpuMemorySize;
 
         SK_DECLARE_INTERNAL_LLIST_INTERFACE(TextBlob);
     };
@@ -133,7 +133,7 @@
     SkTHashMap<uint32_t, TextBlob*>                           fBlobIdCache;
     SkTHashTable<TextBlob*, const TextBlob::Key&, TextBlob>   fBlobKeyCache;
     SkTInternalLList<TextBlob>                                fLRUList;
-    int                                                       fCacheSize;
+    size_t                                                    fCacheSize;
 
     typedef GrTextContext INHERITED;
 };
diff --git a/src/gpu/batches/GrDrawPathBatch.cpp b/src/gpu/batches/GrDrawPathBatch.cpp
index c56c4ad..9beee8e 100644
--- a/src/gpu/batches/GrDrawPathBatch.cpp
+++ b/src/gpu/batches/GrDrawPathBatch.cpp
@@ -56,13 +56,14 @@
 }
 
 GrDrawPathRangeBatch::GrDrawPathRangeBatch(const SkMatrix& viewMatrix, const SkMatrix& localMatrix,
-                                           GrColor color, GrPathRangeDraw* pathRangeDraw)
+                                           GrColor color, GrPathRange* range, GrPathRangeDraw* draw)
     : INHERITED(ClassID(), viewMatrix, color)
+    , fPathRange(range)
     , fDraws(4)
     , fLocalMatrix(localMatrix) {
-    SkDEBUGCODE(pathRangeDraw->fUsedInBatch = true;)
-    fDraws.addToHead(SkRef(pathRangeDraw));
-    fTotalPathCount = pathRangeDraw->count();
+    SkDEBUGCODE(draw->fUsedInBatch = true;)
+    fDraws.addToHead(SkRef(draw));
+    fTotalPathCount = draw->count();
     // Don't compute a bounding box. For dst copy texture, we'll opt instead for it to just copy
     // the entire dst. Realistically this is a moot point, because any context that supports
     // NV_path_rendering will also support NV_blend_equation_advanced.
@@ -72,6 +73,9 @@
 
 bool GrDrawPathRangeBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) {
     GrDrawPathRangeBatch* that = t->cast<GrDrawPathRangeBatch>();
+    if (this->fPathRange.get() != that->fPathRange.get()) {
+        return false;
+    }
     if (!GrPathRangeDraw::CanMerge(**this->fDraws.head(), **that->fDraws.head())) {
         return false;
     }
@@ -116,13 +120,12 @@
                                         &desc, &this->stencilSettings());
     if (fDraws.count() == 1) {
         const GrPathRangeDraw& draw = **fDraws.head();
-        state->gpu()->pathRendering()->drawPaths(args, draw.range(), draw.indices(),
+        state->gpu()->pathRendering()->drawPaths(args, fPathRange.get(), draw.indices(),
             GrPathRange::kU16_PathIndexType, draw.transforms(), draw.transformType(),
             draw.count());
         return;
     }
 
-    const GrPathRange* range = (*fDraws.head())->range();
     GrPathRendering::PathTransformType transformType = (*fDraws.head())->transformType();
     int floatsPerTransform = GrPathRendering::PathTransformSize(transformType);
     SkAutoSTMalloc<512, float> transformStorage(floatsPerTransform * fTotalPathCount);
@@ -131,7 +134,6 @@
     float* transforms = transformStorage.get();
     for (DrawList::Iter iter(fDraws); iter.get(); iter.next()) {
         SkASSERT((*iter.get())->transformType() == transformType);
-        SkASSERT((*iter.get())->range() == range);
         int cnt = (*iter.get())->count();
         memcpy(indices, (*iter.get())->indices(), cnt * sizeof(uint16_t));
         indices += cnt;
@@ -139,7 +141,7 @@
         transforms += cnt * floatsPerTransform;
     }
     SkASSERT(indices - indexStorage.get() == fTotalPathCount);
-    state->gpu()->pathRendering()->drawPaths(args, range, indexStorage.get(),
+    state->gpu()->pathRendering()->drawPaths(args, fPathRange.get(), indexStorage.get(),
         GrPathRange::kU16_PathIndexType, transformStorage.get(), transformType,
         fTotalPathCount);
 }
diff --git a/src/gpu/batches/GrDrawPathBatch.h b/src/gpu/batches/GrDrawPathBatch.h
index 91e0e41..d6d0711 100644
--- a/src/gpu/batches/GrDrawPathBatch.h
+++ b/src/gpu/batches/GrDrawPathBatch.h
@@ -95,9 +95,8 @@
 public:
     typedef GrPathRendering::PathTransformType TransformType;
 
-    static GrPathRangeDraw* Create(GrPathRange* range, TransformType transformType,
-        int reserveCnt) {
-        return new GrPathRangeDraw(range, transformType, reserveCnt);
+    static GrPathRangeDraw* Create(TransformType transformType, int reserveCnt) {
+        return new GrPathRangeDraw(transformType, reserveCnt);
     }
 
     void append(uint16_t index, float transform[]) {
@@ -113,20 +112,13 @@
 
     const uint16_t* indices() const { return fIndices.begin(); }
 
-    const GrPathRange* range() const { return fPathRange.get(); }
-
-    void loadGlyphPathsIfNeeded() {
-        fPathRange.get()->loadPathsIfNeeded<uint16_t>(fIndices.begin(), fIndices.count());
-    }
-
     static bool CanMerge(const GrPathRangeDraw& a, const GrPathRangeDraw& b) {
-        return a.transformType() == b.transformType() && a.range() == b.range();
+        return a.transformType() == b.transformType();
     }
 
 private:
-    GrPathRangeDraw(GrPathRange* range, TransformType transformType, int reserveCnt)
-        : fPathRange(range)
-        , fTransformType(transformType)
+    GrPathRangeDraw(TransformType transformType, int reserveCnt)
+        : fTransformType(transformType)
         , fIndices(reserveCnt)
         , fTransforms(reserveCnt * GrPathRendering::PathTransformSize(transformType)) {
         SkDEBUGCODE(fUsedInBatch = false;)
@@ -136,7 +128,6 @@
     static const int kIndexReserveCnt = 64;
     static const int kTransformBufferReserveCnt = 2 * 64;
 
-    GrPendingIOResource<const GrPathRange, kRead_GrIOType> fPathRange;
     GrPathRendering::PathTransformType                     fTransformType;
     SkSTArray<kIndexReserveCnt, uint16_t, true>            fIndices;
     SkSTArray<kTransformBufferReserveCnt, float, true>     fTransforms;
@@ -157,8 +148,8 @@
 
     // This can't return a more abstract type because we install the stencil settings late :(
     static GrDrawPathBatchBase* Create(const SkMatrix& viewMatrix, const SkMatrix& localMatrix,
-                                       GrColor color, GrPathRangeDraw* pathRangeDraw) {
-        return new GrDrawPathRangeBatch(viewMatrix, localMatrix, color, pathRangeDraw);
+                                       GrColor color, GrPathRange* range, GrPathRangeDraw* draw) {
+        return new GrDrawPathRangeBatch(viewMatrix, localMatrix, color, range, draw);
     }
 
     ~GrDrawPathRangeBatch() override;
@@ -171,7 +162,7 @@
     inline bool isWinding() const;
 
     GrDrawPathRangeBatch(const SkMatrix& viewMatrix, const SkMatrix& localMatrix, GrColor color,
-                         GrPathRangeDraw* pathRangeDraw);
+                         GrPathRange* range, GrPathRangeDraw* draw);
 
     bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override;
 
@@ -179,10 +170,12 @@
 
     void onDraw(GrBatchFlushState* state) override;
 
+    typedef GrPendingIOResource<const GrPathRange, kRead_GrIOType> PendingPathRange;
     typedef SkTLList<GrPathRangeDraw*> DrawList;
-    DrawList    fDraws;
-    int         fTotalPathCount;
-    SkMatrix    fLocalMatrix;
+    PendingPathRange    fPathRange;
+    DrawList            fDraws;
+    int                 fTotalPathCount;
+    SkMatrix            fLocalMatrix;
 
     typedef GrDrawPathBatchBase INHERITED;
 };