Force single glyph calls through bulk interface

Enance SkBulkGlyphMetrics, SkBulkGlyphMetricsAndPaths, and SkBulkGlyphMetricsAndImages
with single glyph calls. In addtion, add calls needed to have the rest of the system
work with these interfaces.

As a resulte move the glyph, prepareImage, and preparePath calls to private.

Change-Id: I8d383b649390e45f621dcb9d62fb8367a55cee02
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/254056
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Herb Derby <herb@google.com>
diff --git a/src/atlastext/SkAtlasTextTarget.cpp b/src/atlastext/SkAtlasTextTarget.cpp
index 80ef553..87ec89f 100644
--- a/src/atlastext/SkAtlasTextTarget.cpp
+++ b/src/atlastext/SkAtlasTextTarget.cpp
@@ -218,7 +218,6 @@
 
 void GrAtlasTextOp::executeForTextTarget(SkAtlasTextTarget* target) {
     FlushInfo flushInfo;
-    SkExclusiveStrikePtr autoGlyphCache;
     auto& context = target->context()->internal();
     auto glyphCache = context.grContext()->priv().getGrStrikeCache();
     auto atlasManager = context.grContext()->priv().getAtlasManager();
@@ -234,8 +233,7 @@
         GrTextBlob::VertexRegenerator regenerator(
                 resourceProvider, fGeoData[i].fBlob, fGeoData[i].fRun, fGeoData[i].fSubRun,
                 fGeoData[i].fViewMatrix, fGeoData[i].fX, fGeoData[i].fY,
-                fGeoData[i].fColor.toBytes_RGBA(), &context, glyphCache, atlasManager,
-                &autoGlyphCache);
+                fGeoData[i].fColor.toBytes_RGBA(), &context, glyphCache, atlasManager);
         bool done = false;
         while (!done) {
             GrTextBlob::VertexRegenerator::Result result;
diff --git a/src/core/SkStrike.h b/src/core/SkStrike.h
index 2eac378..0ae57ac 100644
--- a/src/core/SkStrike.h
+++ b/src/core/SkStrike.h
@@ -39,27 +39,18 @@
              std::unique_ptr<SkScalerContext> scaler,
              const SkFontMetrics&);
 
-    // Return a glyph. Create it if it doesn't exist, and initialize the glyph with metrics and
-    // advances using a scaler.
-    SkGlyph* glyph(SkPackedGlyphID packedID);
-
     // Return a glyph.  Create it if it doesn't exist, and initialize with the prototype.
     SkGlyph* glyphFromPrototype(const SkGlyphPrototype& p, void* image = nullptr);
 
     // Return a glyph or nullptr if it does not exits in the strike.
     SkGlyph* glyphOrNull(SkPackedGlyphID id) const;
 
-    const void* prepareImage(SkGlyph* glyph);
-
     // Lookup (or create if needed) the toGlyph using toID. If that glyph is not initialized with
     // an image, then use the information in from to initialize the width, height top, left,
     // format and image of the toGlyph. This is mainly used preserving the glyph if it was
     // created by a search of desperation.
     SkGlyph* mergeGlyphAndImage(SkPackedGlyphID toID, const SkGlyph& from);
 
-    // If the path has never been set, then use the scaler context to add the glyph.
-    const SkPath* preparePath(SkGlyph*);
-
     // If the path has never been set, then add a path to glyph.
     const SkPath* preparePath(SkGlyph* glyph, const SkPath* path);
 
@@ -163,11 +154,19 @@
     template <typename Fn>
     void commonFilterLoop(SkDrawableGlyphBuffer* drawables, Fn&& fn);
 
+    // Return a glyph. Create it if it doesn't exist, and initialize the glyph with metrics and
+    // advances using a scaler.
+    SkGlyph* glyph(SkPackedGlyphID packedID);
+
+    const void* prepareImage(SkGlyph* glyph);
+
+    // If the path has never been set, then use the scaler context to add the glyph.
+    const SkPath* preparePath(SkGlyph*);
+
     enum PathDetail {
         kMetricsOnly,
         kMetricsAndPath
     };
-
     // internalPrepare will only be called with a mutex already held.
     SkSpan<const SkGlyph*> internalPrepare(
             SkSpan<const SkGlyphID> glyphIDs,
diff --git a/src/core/SkStrikeSpec.cpp b/src/core/SkStrikeSpec.cpp
index 505aefc..5b10bae 100644
--- a/src/core/SkStrikeSpec.cpp
+++ b/src/core/SkStrikeSpec.cpp
@@ -13,6 +13,11 @@
 #include "src/core/SkStrikeCache.h"
 #include "src/core/SkTLazy.h"
 
+#if SK_SUPPORT_GPU
+#include "src/gpu/text/GrStrikeCache.h"
+#include "src/gpu/text/GrTextContext.h"
+#endif
+
 SkStrikeSpec SkStrikeSpec::MakeMask(const SkFont& font, const SkPaint& paint,
                                     const SkSurfaceProps& surfaceProps,
                                     SkScalerContextFlags scalerContextFlags,
@@ -239,18 +244,48 @@
     return fStrike->metrics(glyphIDs, fGlyphs.get());
 }
 
+const SkGlyph* SkBulkGlyphMetrics::glyph(SkGlyphID glyphID) {
+    return this->glyphs(SkSpan<const SkGlyphID>{&glyphID, 1})[0];
+}
+
 SkBulkGlyphMetricsAndPaths::SkBulkGlyphMetricsAndPaths(const SkStrikeSpec& spec)
     : fStrike{spec.findOrCreateExclusiveStrike()} { }
 
+SkBulkGlyphMetricsAndPaths::SkBulkGlyphMetricsAndPaths(SkExclusiveStrikePtr&& strike)
+        : fStrike{std::move(strike)} { }
+
 SkSpan<const SkGlyph*> SkBulkGlyphMetricsAndPaths::glyphs(SkSpan<const SkGlyphID> glyphIDs) {
     fGlyphs.reset(glyphIDs.size());
     return fStrike->preparePaths(glyphIDs, fGlyphs.get());
 }
 
+const SkGlyph* SkBulkGlyphMetricsAndPaths::glyph(SkGlyphID glyphID) {
+    return this->glyphs(SkSpan<const SkGlyphID>{&glyphID, 1})[0];
+}
+
+void SkBulkGlyphMetricsAndPaths::findIntercepts(
+    const SkScalar* bounds, SkScalar scale, SkScalar xPos,
+    const SkGlyph* glyph, SkScalar* array, int* count) {
+    // TODO(herb): remove this abominable const_cast. Do the intercepts really need to be on the
+    //  glyph?
+    fStrike->findIntercepts(bounds, scale, xPos, const_cast<SkGlyph*>(glyph), array, count);
+}
+
 SkBulkGlyphMetricsAndImages::SkBulkGlyphMetricsAndImages(const SkStrikeSpec& spec)
         : fStrike{spec.findOrCreateExclusiveStrike()} { }
 
+SkBulkGlyphMetricsAndImages::SkBulkGlyphMetricsAndImages(SkExclusiveStrikePtr&& strike)
+        : fStrike{std::move(strike)} { }
+
 SkSpan<const SkGlyph*> SkBulkGlyphMetricsAndImages::glyphs(SkSpan<const SkPackedGlyphID> glyphIDs) {
     fGlyphs.reset(glyphIDs.size());
     return fStrike->prepareImages(glyphIDs, fGlyphs.get());
 }
+
+const SkGlyph* SkBulkGlyphMetricsAndImages::glyph(SkPackedGlyphID packedID) {
+    return this->glyphs(SkSpan<const SkPackedGlyphID>{&packedID, 1})[0];
+}
+
+const SkDescriptor& SkBulkGlyphMetricsAndImages::descriptor() const {
+    return fStrike->getDescriptor();
+}
diff --git a/src/core/SkStrikeSpec.h b/src/core/SkStrikeSpec.h
index d34e711..f40032a 100644
--- a/src/core/SkStrikeSpec.h
+++ b/src/core/SkStrikeSpec.h
@@ -13,8 +13,9 @@
 #include "src/core/SkStrikeForGPU.h"
 
 #if SK_SUPPORT_GPU
-#include "src/gpu/text/GrStrikeCache.h"
 #include "src/gpu/text/GrTextContext.h"
+class GrStrikeCache;
+class GrTextStrike;
 #endif
 
 class SkFont;
@@ -40,10 +41,10 @@
             SkScalerContextFlags scalerContextFlags);
 
     static SkStrikeSpec MakeSourceFallback(const SkFont& font,
-                                                  const SkPaint& paint,
-                                                  const SkSurfaceProps& surfaceProps,
-                                                  SkScalerContextFlags scalerContextFlags,
-                                                  SkScalar maxSourceGlyphDimension);
+                                           const SkPaint& paint,
+                                           const SkSurfaceProps& surfaceProps,
+                                           SkScalerContextFlags scalerContextFlags,
+                                           SkScalar maxSourceGlyphDimension);
 
     // Create a canonical strike spec for device-less measurements.
     static SkStrikeSpec MakeCanonicalized(
@@ -99,6 +100,7 @@
 public:
     explicit SkBulkGlyphMetrics(const SkStrikeSpec& spec);
     SkSpan<const SkGlyph*> glyphs(SkSpan<const SkGlyphID> glyphIDs);
+    const SkGlyph* glyph(SkGlyphID glyphID);
 
 private:
     static constexpr int kTypicalGlyphCount = 20;
@@ -109,7 +111,11 @@
 class SkBulkGlyphMetricsAndPaths {
 public:
     explicit SkBulkGlyphMetricsAndPaths(const SkStrikeSpec& spec);
+    explicit SkBulkGlyphMetricsAndPaths(SkExclusiveStrikePtr&& strike);
     SkSpan<const SkGlyph*> glyphs(SkSpan<const SkGlyphID> glyphIDs);
+    const SkGlyph* glyph(SkGlyphID glyphID);
+    void findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos,
+                        const SkGlyph* glyph, SkScalar* array, int* count);
 
 private:
     static constexpr int kTypicalGlyphCount = 20;
@@ -120,7 +126,11 @@
 class SkBulkGlyphMetricsAndImages {
 public:
     explicit SkBulkGlyphMetricsAndImages(const SkStrikeSpec& spec);
-    SkSpan<const SkGlyph*> glyphs(SkSpan<const SkPackedGlyphID> glyphIDs);
+    explicit SkBulkGlyphMetricsAndImages(SkExclusiveStrikePtr&& strike);
+    SkSpan<const SkGlyph*> glyphs(SkSpan<const SkPackedGlyphID> packedIDs);
+    const SkGlyph* glyph(SkPackedGlyphID packedID);
+    const SkDescriptor& descriptor() const;
+
 
 private:
     static constexpr int kTypicalGlyphCount = 64;
diff --git a/src/core/SkTextBlob.cpp b/src/core/SkTextBlob.cpp
index 2912ac4..7044b50 100644
--- a/src/core/SkTextBlob.cpp
+++ b/src/core/SkTextBlob.cpp
@@ -875,27 +875,27 @@
     interceptPaint.setPathEffect(nullptr);
 
     SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(interceptFont, &interceptPaint);
-    auto cache = strikeSpec.findOrCreateExclusiveStrike();
+    SkBulkGlyphMetricsAndPaths metricsAndPaths{strikeSpec};
 
     SkScalar xOffset = 0;
     SkScalar xPos = xOffset;
     SkScalar prevAdvance = 0;
 
     const SkPoint* posCursor = glyphRun.positions().begin();
-    for (auto glyphID : glyphRun.glyphsIDs()) {
+    for (const SkGlyph* glyph : metricsAndPaths.glyphs(glyphRun.glyphsIDs())) {
         SkPoint pos = *posCursor++;
 
-        SkGlyph* glyph = cache->glyph(SkPackedGlyphID{glyphID});
         xPos += prevAdvance * scale;
         prevAdvance = glyph->advanceX();
-        if (cache->preparePath(glyph) != nullptr) {
+        if (glyph->path() != nullptr) {
             // The typeface is scaled, so un-scale the bounds to be in the space of the typeface.
             // Also ensure the bounds are properly offset by the vertical positioning of the glyph.
             SkScalar scaledBounds[2] = {
                 (bounds[0] - pos.y()) / scale,
                 (bounds[1] - pos.y()) / scale
             };
-            cache->findIntercepts(scaledBounds, scale, pos.x(), glyph, intervals, intervalCount);
+            metricsAndPaths.findIntercepts(
+                    scaledBounds, scale, pos.x(), glyph, intervals, intervalCount);
         }
     }
     return *intervalCount;
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index ae2076e..1b42aaf 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -340,7 +340,6 @@
 
     char* currVertex = reinterpret_cast<char*>(vertices);
 
-    SkExclusiveStrikePtr autoGlyphCache;
     // each of these is a SubRun
     for (int i = 0; i < fGeoCount; i++) {
         const Geometry& args = fGeoData[i];
@@ -349,7 +348,7 @@
         GrTextBlob::VertexRegenerator regenerator(
                 resourceProvider, blob, args.fRun, args.fSubRun, args.fViewMatrix, args.fX, args.fY,
                 args.fColor.toBytes_RGBA(), target->deferredUploadTarget(), glyphCache,
-                atlasManager, &autoGlyphCache);
+                atlasManager);
         bool done = false;
         while (!done) {
             GrTextBlob::VertexRegenerator::Result result;
diff --git a/src/gpu/text/GrStrikeCache.cpp b/src/gpu/text/GrStrikeCache.cpp
index f445c65..4a2cd29a 100644
--- a/src/gpu/text/GrStrikeCache.cpp
+++ b/src/gpu/text/GrStrikeCache.cpp
@@ -11,8 +11,10 @@
 #include "src/gpu/text/GrAtlasManager.h"
 #include "src/gpu/text/GrStrikeCache.h"
 
+#include "src/core/SkArenaAlloc.h"
 #include "src/core/SkAutoMalloc.h"
 #include "src/core/SkDistanceFieldGen.h"
+#include "src/core/SkStrikeSpec.h"
 
 GrStrikeCache::GrStrikeCache(const GrCaps* caps, size_t maxTextureBytes)
         : fPreserveStrike(nullptr)
@@ -80,12 +82,12 @@
     }
 }
 
-static bool get_packed_glyph_image(SkStrike* cache, SkGlyph* glyph, int width,
+static bool get_packed_glyph_image(const SkGlyph* glyph, int width,
                                    int height, int dstRB, GrMaskFormat expectedMaskFormat,
                                    void* dst, const SkMasks& masks) {
     SkASSERT(glyph->width() == width);
     SkASSERT(glyph->height() == height);
-    const void* src = cache->prepareImage(glyph);
+    const void* src = glyph->image();
     if (src == nullptr) {
         return false;
     }
@@ -190,11 +192,11 @@
                                    GrStrikeCache* glyphCache,
                                    GrAtlasManager* fullAtlasManager,
                                    GrGlyph* glyph,
-                                   SkStrike* skStrikeCache,
+                                   SkBulkGlyphMetricsAndImages* metricsAndImages,
                                    GrMaskFormat expectedMaskFormat,
                                    bool isScaledGlyph) {
     SkASSERT(glyph);
-    SkASSERT(skStrikeCache);
+    SkASSERT(metricsAndImages);
     SkASSERT(fCache.find(glyph->fPackedID));
 
     expectedMaskFormat = fullAtlasManager->resolveMaskFormat(expectedMaskFormat);
@@ -215,13 +217,13 @@
     }
     SkAutoSMalloc<1024> storage(size);
 
-    SkGlyph* skGlyph = skStrikeCache->glyph(glyph->fPackedID);
+    const SkGlyph* skGlyph = metricsAndImages->glyph(glyph->fPackedID);
     void* dataPtr = storage.get();
     if (addPad) {
         sk_bzero(dataPtr, size);
         dataPtr = (char*)(dataPtr) + rowBytes + bytesPerPixel;
     }
-    if (!get_packed_glyph_image(skStrikeCache, skGlyph, glyph->width(), glyph->height(),
+    if (!get_packed_glyph_image(skGlyph, glyph->width(), glyph->height(),
                                 rowBytes, expectedMaskFormat,
                                 dataPtr, glyphCache->getMasks())) {
         return GrDrawOpAtlas::ErrorCode::kError;
@@ -242,3 +244,25 @@
     }
     return result;
 }
+
+GrGlyph* GrTextStrike::getGlyph(const SkGlyph& skGlyph) {
+    GrGlyph* grGlyph = fCache.find(skGlyph.getPackedID());
+    if (grGlyph == nullptr) {
+        grGlyph = fAlloc.make<GrGlyph>(skGlyph);
+        fCache.add(grGlyph);
+    }
+    return grGlyph;
+}
+
+GrGlyph*
+GrTextStrike::getGlyph(SkPackedGlyphID packed, SkBulkGlyphMetricsAndImages* metricsAndImages) {
+    GrGlyph* grGlyph = fCache.find(packed);
+    if (grGlyph == nullptr) {
+        // We could return this to the caller, but in practice it adds code complexity for
+        // potentially little benefit(ie, if the glyph is not in our font cache, then its not
+        // in the atlas and we're going to be doing a texture upload anyways).
+        grGlyph = fAlloc.make<GrGlyph>(*metricsAndImages->glyph(packed));
+        fCache.add(grGlyph);
+    }
+    return grGlyph;
+}
diff --git a/src/gpu/text/GrStrikeCache.h b/src/gpu/text/GrStrikeCache.h
index 365e0a7..a0eaf7c 100644
--- a/src/gpu/text/GrStrikeCache.h
+++ b/src/gpu/text/GrStrikeCache.h
@@ -9,15 +9,16 @@
 #define GrStrikeCache_DEFINED
 
 #include "src/codec/SkMasks.h"
-#include "src/core/SkArenaAlloc.h"
-#include "src/core/SkStrike.h"
+#include "src/core/SkDescriptor.h"
 #include "src/core/SkTDynamicHash.h"
 #include "src/gpu/GrDrawOpAtlas.h"
 #include "src/gpu/GrGlyph.h"
 
+
 class GrAtlasManager;
 class GrGpu;
 class GrStrikeCache;
+class SkBulkGlyphMetricsAndImages;
 
 /**
  *  The GrTextStrike manages a pool of CPU backing memory for GrGlyphs. This backing memory
@@ -30,30 +31,13 @@
 public:
     GrTextStrike(const SkDescriptor& fontScalerKey);
 
-    GrGlyph* getGlyph(const SkGlyph& skGlyph) {
-        GrGlyph* grGlyph = fCache.find(skGlyph.getPackedID());
-        if (grGlyph == nullptr) {
-            grGlyph = fAlloc.make<GrGlyph>(skGlyph);
-            fCache.add(grGlyph);
-        }
-        return grGlyph;
-    }
+    GrGlyph* getGlyph(const SkGlyph& skGlyph);
 
     // This variant of the above function is called by GrAtlasTextOp. At this point, it is possible
     // that the maskformat of the glyph differs from what we expect.  In these cases we will just
     // draw a clear square.
     // skbug:4143 crbug:510931
-    GrGlyph* getGlyph(SkPackedGlyphID packed, SkStrike* skStrike) {
-        GrGlyph* grGlyph = fCache.find(packed);
-        if (grGlyph == nullptr) {
-            // We could return this to the caller, but in practice it adds code complexity for
-            // potentially little benefit(ie, if the glyph is not in our font cache, then its not
-            // in the atlas and we're going to be doing a texture upload anyways).
-            grGlyph = fAlloc.make<GrGlyph>(*skStrike->glyph(packed));
-            fCache.add(grGlyph);
-        }
-        return grGlyph;
-    }
+    GrGlyph* getGlyph(SkPackedGlyphID packed, SkBulkGlyphMetricsAndImages* metricsAndImages);
 
     // returns true if glyph successfully added to texture atlas, false otherwise.  If the glyph's
     // mask format has changed, then addGlyphToAtlas will draw a clear box.  This will almost never
@@ -62,7 +46,8 @@
     // get the actual glyph image itself when we get the glyph metrics.
     GrDrawOpAtlas::ErrorCode addGlyphToAtlas(GrResourceProvider*, GrDeferredUploadTarget*,
                                              GrStrikeCache*, GrAtlasManager*, GrGlyph*,
-                                             SkStrike*, GrMaskFormat expectedMaskFormat,
+                                             SkBulkGlyphMetricsAndImages*,
+                                             GrMaskFormat expectedMaskFormat,
                                              bool isScaledGlyph);
 
     // testing
diff --git a/src/gpu/text/GrTextBlob.h b/src/gpu/text/GrTextBlob.h
index b354510..5f6b800 100644
--- a/src/gpu/text/GrTextBlob.h
+++ b/src/gpu/text/GrTextBlob.h
@@ -593,8 +593,7 @@
      */
     VertexRegenerator(GrResourceProvider*, GrTextBlob*, int runIdx, int subRunIdx,
                       const SkMatrix& viewMatrix, SkScalar x, SkScalar y, GrColor color,
-                      GrDeferredUploadTarget*, GrStrikeCache*, GrAtlasManager*,
-                      SkExclusiveStrikePtr*);
+                      GrDeferredUploadTarget*, GrStrikeCache*, GrAtlasManager*);
 
     struct Result {
         /**
@@ -626,7 +625,7 @@
     GrDeferredUploadTarget* fUploadTarget;
     GrStrikeCache* fGlyphCache;
     GrAtlasManager* fFullAtlasManager;
-    SkExclusiveStrikePtr* fLazyStrike;
+    SkTLazy<SkBulkGlyphMetricsAndImages> fMetricsAndImages;
     SubRun* fSubRun;
     GrColor fColor;
     SkScalar fTransX;
diff --git a/src/gpu/text/GrTextBlobVertexRegenerator.cpp b/src/gpu/text/GrTextBlobVertexRegenerator.cpp
index 976beaf..c28a0a1 100644
--- a/src/gpu/text/GrTextBlobVertexRegenerator.cpp
+++ b/src/gpu/text/GrTextBlobVertexRegenerator.cpp
@@ -122,15 +122,13 @@
                                                  GrColor color,
                                                  GrDeferredUploadTarget* uploadTarget,
                                                  GrStrikeCache* glyphCache,
-                                                 GrAtlasManager* fullAtlasManager,
-                                                 SkExclusiveStrikePtr* lazyStrike)
+                                                 GrAtlasManager* fullAtlasManager)
         : fResourceProvider(resourceProvider)
         , fViewMatrix(viewMatrix)
         , fBlob(blob)
         , fUploadTarget(uploadTarget)
         , fGlyphCache(glyphCache)
         , fFullAtlasManager(fullAtlasManager)
-        , fLazyStrike(lazyStrike)
         , fSubRun(&blob->fRuns[runIdx].fSubRunInfo[subRunIdx])
         , fColor(color) {
     // Compute translation if any
@@ -166,9 +164,9 @@
 
         const SkStrikeSpec& strikeSpec = fSubRun->strikeSpec();
 
-        if (!*fLazyStrike || (*fLazyStrike)->getDescriptor() != strikeSpec.descriptor()) {
-            *fLazyStrike =
-                    strikeSpec.findOrCreateExclusiveStrike(SkStrikeCache::GlobalStrikeCache());
+        if (!fMetricsAndImages.isValid()
+            || fMetricsAndImages->descriptor() != strikeSpec.descriptor()) {
+            fMetricsAndImages.init(strikeSpec);
         }
 
         if (regenGlyphs) {
@@ -193,7 +191,7 @@
                 // Get the id from the old glyph, and use the new strike to lookup
                 // the glyph.
                 SkPackedGlyphID id = fBlob->fGlyphs[glyphOffset]->fPackedID;
-                fBlob->fGlyphs[glyphOffset] = strike->getGlyph(id, fLazyStrike->get());
+                fBlob->fGlyphs[glyphOffset] = strike->getGlyph(id, fMetricsAndImages.get());
                 SkASSERT(id == fBlob->fGlyphs[glyphOffset]->fPackedID);
             }
             glyph = fBlob->fGlyphs[glyphOffset];
@@ -203,7 +201,7 @@
                 GrDrawOpAtlas::ErrorCode code;
                 code = strike->addGlyphToAtlas(fResourceProvider, fUploadTarget, fGlyphCache,
                                               fFullAtlasManager, glyph,
-                                              fLazyStrike->get(), fSubRun->maskFormat(),
+                                              fMetricsAndImages.get(), fSubRun->maskFormat(),
                                               fSubRun->needsTransform());
                 if (GrDrawOpAtlas::ErrorCode::kError == code) {
                     // Something horrible has happened - drop the op
diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp
index b752406..426a9f9 100644
--- a/src/pdf/SkPDFFont.cpp
+++ b/src/pdf/SkPDFFont.cpp
@@ -442,9 +442,8 @@
     sk_sp<SkImage> fImage;
     SkIPoint fOffset;
 };
-static ImageAndOffset to_image(SkGlyphID gid, SkStrike* cache) {
-    SkGlyph* glyph = cache->glyph(SkPackedGlyphID{gid});
-    (void)cache->prepareImage(glyph);
+static ImageAndOffset to_image(SkGlyphID gid, SkBulkGlyphMetricsAndImages* smallGlyphs) {
+    const SkGlyph* glyph = smallGlyphs->glyph(SkPackedGlyphID{gid});
     SkMask mask = glyph->mask();
     if (!mask.fImage) {
         return {nullptr, {0, 0}};
@@ -485,7 +484,7 @@
 
 static SkPDFIndirectReference type3_descriptor(SkPDFDocument* doc,
                                                const SkTypeface* typeface,
-                                               SkStrike* cache) {
+                                               SkScalar xHeight) {
     if (SkPDFIndirectReference* ptr = doc->fType3FontDescriptors.find(typeface->uniqueID())) {
         return *ptr;
     }
@@ -501,7 +500,6 @@
         // to "greatly help our workflow downstream".
         if (metrics->fCapHeight != 0) { descriptor.insertInt("CapHeight", metrics->fCapHeight); }
         if (metrics->fStemV     != 0) { descriptor.insertInt("StemV",     metrics->fStemV);     }
-        SkScalar xHeight = cache->getFontMetrics().fXHeight;
         if (xHeight != 0) {
             descriptor.insertScalar("XHeight", xHeight);
         }
@@ -545,11 +543,13 @@
     auto cache = strikeSpec.findOrCreateExclusiveStrike();
     SkASSERT(cache);
     SkScalar emSize = (SkScalar)unitsPerEm;
+    SkScalar xHeight = cache->getFontMetrics().fXHeight;
+    SkBulkGlyphMetricsAndPaths metricsAndPaths(std::move(cache));
 
     SkStrikeSpec strikeSpecSmall = kBitmapFontSize > 0 ? make_small_strike(*typeface)
                                                        : strikeSpec;
-    auto smallCache = strikeSpecSmall.findOrCreateExclusiveStrike();
-    SkASSERT(smallCache);
+
+    SkBulkGlyphMetricsAndImages smallGlyphs(strikeSpecSmall);
     float bitmapScale = kBitmapFontSize > 0 ? emSize / kBitmapFontSize : 1.0f;
 
     SkPDFDict font("Font");
@@ -587,18 +587,18 @@
             characterName.set("g0");
         } else {
             characterName.printf("g%X", gID);
-            SkGlyph* glyph = cache->glyph(SkPackedGlyphID{gID});
+            const SkGlyph* glyph = metricsAndPaths.glyph(gID);
             advance = glyph->advanceX();
             glyphBBox = glyph->iRect();
             bbox.join(glyphBBox);
-            const SkPath* path = cache->preparePath(glyph);
+            const SkPath* path = glyph->path();
             SkDynamicMemoryWStream content;
             if (path && !path->isEmpty()) {
                 setGlyphWidthAndBoundingBox(glyph->advanceX(), glyphBBox, &content);
                 SkPDFUtils::EmitPath(*path, SkPaint::kFill_Style, &content);
                 SkPDFUtils::PaintPath(SkPaint::kFill_Style, path->getFillType(), &content);
             } else {
-                auto pimg = to_image(gID, smallCache.get());
+                auto pimg = to_image(gID, &smallGlyphs);
                 if (!pimg.fImage) {
                     setGlyphWidthAndBoundingBox(glyph->advanceX(), glyphBBox, &content);
                 } else {
@@ -659,7 +659,7 @@
                                                 firstGlyphID,
                                                 lastGlyphID);
     font.insertRef("ToUnicode", SkPDFStreamOut(nullptr, std::move(toUnicodeCmap), doc));
-    font.insertRef("FontDescriptor", type3_descriptor(doc, typeface, cache.get()));
+    font.insertRef("FontDescriptor", type3_descriptor(doc, typeface, xHeight));
     font.insertObject("Widths", std::move(widthArray));
     font.insertObject("Encoding", std::move(encoding));
     font.insertObject("CharProcs", std::move(charProcs));
diff --git a/tests/SkRemoteGlyphCacheTest.cpp b/tests/SkRemoteGlyphCacheTest.cpp
index 22033ec..fd327e8 100644
--- a/tests/SkRemoteGlyphCacheTest.cpp
+++ b/tests/SkRemoteGlyphCacheTest.cpp
@@ -891,14 +891,17 @@
     auto scalerProxy = static_cast<SkScalerContextProxy*>(testCache->getScalerContext());
     scalerProxy->initCache(testCache.get(), &strikeCache);
 
+    auto rounding = testCache->roundingSpec();
+    SkBulkGlyphMetricsAndImages metricsAndImages{std::move(testCache)};
+
     // Look for the lost glyph.
     {
         SkPoint pt{SkFixedToScalar(lostGlyphID.getSubXFixed()),
                    SkFixedToScalar(lostGlyphID.getSubYFixed())};
         SkPackedGlyphID packedID{
-            lostGlyphID.glyphID(), pt, testCache->roundingSpec().ignorePositionFieldMask};
-        SkGlyph* lostGlyph = testCache->glyph(packedID);
-        testCache->prepareImage(lostGlyph);
+            lostGlyphID.glyphID(), pt, rounding.ignorePositionFieldMask};
+
+        const SkGlyph* lostGlyph = metricsAndImages.glyph(packedID);
 
         REPORTER_ASSERT(reporter, lostGlyph->height() == 1);
         REPORTER_ASSERT(reporter, lostGlyph->width() == 2);
@@ -911,9 +914,8 @@
         SkPoint pt{SkFixedToScalar(SK_FixedQuarter),
                    SkFixedToScalar(SK_FixedQuarter)};
         SkPackedGlyphID packedID{
-                lostGlyphID.glyphID(), pt, testCache->roundingSpec().ignorePositionFieldMask};
-        SkGlyph* lostGlyph = testCache->glyph(packedID);
-        testCache->prepareImage(lostGlyph);
+                lostGlyphID.glyphID(), pt, rounding.ignorePositionFieldMask};
+        const SkGlyph* lostGlyph = metricsAndImages.glyph(packedID);
 
         REPORTER_ASSERT(reporter, lostGlyph->height() == 1);
         REPORTER_ASSERT(reporter, lostGlyph->width() == 2);