add testing code for serialized Slugs

By setting the compile time flag:
SK_EXPERIMENTAL_SIMULATE_DRAWGLYPHRUNLIST_WITH_SLUG_STRIKE_SERIALIZE
will cause all SkTextBlobs to be rendered by analyzing the Slug
to create strike cache differences and serialize the Blob to a Slug.
Then the serialized strike cache differences are used to populate
the strike cache using a different TypefaceIDs using SkTypeface_Remote
as a proxy for the real SkTypeface. This will create a hard break
between the original glyph data, and the proxied glyph data.
It will then deserialize the Slug doing TypefaceID translation to
the SkTypeface_Remote, and draw the unflattened Slug.

Bug: chromium:1278340

Change-Id: I0f1980dee966b1092a99741793aed9d138451f4d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/510228
Reviewed-by: Robert Phillips <robertphillips@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Herb Derby <herb@google.com>
diff --git a/include/gpu/GrContextOptions.h b/include/gpu/GrContextOptions.h
index 9eaaa07..60444ea 100644
--- a/include/gpu/GrContextOptions.h
+++ b/include/gpu/GrContextOptions.h
@@ -261,7 +261,8 @@
      * rendering of all glyphs. This must be set to true to use GrSlug.
      */
     #if defined(SK_EXPERIMENTAL_SIMULATE_DRAWGLYPHRUNLIST_WITH_SLUG) || \
-        defined(SK_EXPERIMENTAL_SIMULATE_DRAWGLYPHRUNLIST_WITH_SLUG_SERIALIZE)
+        defined(SK_EXPERIMENTAL_SIMULATE_DRAWGLYPHRUNLIST_WITH_SLUG_SERIALIZE) || \
+        defined(SK_EXPERIMENTAL_SIMULATE_DRAWGLYPHRUNLIST_WITH_SLUG_STRIKE_SERIALIZE)
     bool fSupportBilerpFromGlyphAtlas = true;
     #else
     bool fSupportBilerpFromGlyphAtlas = false;
diff --git a/src/gpu/text/GrTextBlob.cpp b/src/gpu/text/GrTextBlob.cpp
index 26106cc..a5b3b28 100644
--- a/src/gpu/text/GrTextBlob.cpp
+++ b/src/gpu/text/GrTextBlob.cpp
@@ -1804,7 +1804,6 @@
                             skgpu::v1::SurfaceDrawContext* sdc,
                             GrAtlasSubRunOwner) const {
     SkASSERT(this->glyphCount() != 0);
-    SkASSERT(!viewMatrix.localToDevice().hasPerspective());
 
     const SkMatrix& drawMatrix = viewMatrix.localToDevice();
 
diff --git a/src/gpu/v1/Device.cpp b/src/gpu/v1/Device.cpp
index 7e51255..97a19ce 100644
--- a/src/gpu/v1/Device.cpp
+++ b/src/gpu/v1/Device.cpp
@@ -50,6 +50,10 @@
 #include "src/image/SkSurface_Gpu.h"
 #include "src/utils/SkUTF.h"
 
+#if defined(SK_EXPERIMENTAL_SIMULATE_DRAWGLYPHRUNLIST_WITH_SLUG_STRIKE_SERIALIZE)
+    #include "include/private/chromium/SkChromeRemoteGlyphCache.h"
+#endif
+
 #define ASSERT_SINGLE_OWNER SKGPU_ASSERT_SINGLE_OWNER(fContext->priv().singleOwner())
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -987,6 +991,108 @@
 }
 #endif
 
+// This testing method draws a blob by analyzing it to create strike cache
+// differences and then serializing the Blob to a Slug. This creates a hard
+// break between the original glyph data, and the proxied glyph data - and
+// closely mimics how Chrome draws text.
+#if defined(SK_EXPERIMENTAL_SIMULATE_DRAWGLYPHRUNLIST_WITH_SLUG_STRIKE_SERIALIZE)
+namespace {
+class DiscardableManager : public SkStrikeServer::DiscardableHandleManager,
+                           public SkStrikeClient::DiscardableHandleManager {
+public:
+    DiscardableManager() { sk_bzero(&fCacheMissCount, sizeof(fCacheMissCount)); }
+    ~DiscardableManager() override = default;
+
+    // Server implementation.
+    SkDiscardableHandleId createHandle() override SK_EXCLUDES(fLock) {
+        SkAutoMutexExclusive l(fLock);
+
+        // Handles starts as locked.
+        fLockedHandles.add(++fNextHandleId);
+        return fNextHandleId;
+    }
+    bool lockHandle(SkDiscardableHandleId id) override SK_EXCLUDES(fLock) {
+        SkAutoMutexExclusive l(fLock);
+
+        fLockedHandles.add(id);
+        return true;
+    }
+
+    // Client implementation.
+    bool deleteHandle(SkDiscardableHandleId id) override SK_EXCLUDES(fLock) {
+        return false;
+    }
+
+    void notifyCacheMiss(
+            SkStrikeClient::CacheMissType type, int fontSize) override SK_EXCLUDES(fLock) {
+        SkAutoMutexExclusive l(fLock);
+
+        fCacheMissCount[type]++;
+    }
+    bool isHandleDeleted(SkDiscardableHandleId id) override SK_EXCLUDES(fLock) {
+        return false;
+    }
+
+private:
+    // The tests below run in parallel on multiple threads and use the same
+    // process global SkStrikeCache. So the implementation needs to be
+    // thread-safe.
+    mutable SkMutex fLock;
+
+    SkDiscardableHandleId fNextHandleId SK_GUARDED_BY(fLock) = 0u;
+    SkTHashSet<SkDiscardableHandleId> fLockedHandles SK_GUARDED_BY(fLock);
+    int fCacheMissCount[SkStrikeClient::CacheMissType::kLast + 1u] SK_GUARDED_BY(fLock);
+};
+}  // namespace
+
+void Device::testingOnly_drawGlyphRunListWithSerializedSlugAndStrike(
+        SkCanvas* canvas, const SkGlyphRunList& glyphRunList, const SkPaint& paint) {
+    if (glyphRunList.blob() == nullptr) {
+        fSurfaceDrawContext->drawGlyphRunList(
+                canvas, this->clip(), this->asMatrixProvider(), glyphRunList, paint);
+        return;
+    }
+
+    sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
+    SkStrikeServer server{discardableManager.get()};
+
+    SkStrikeClient client{discardableManager, false};
+    SkSurfaceProps surfaceProps;
+    if (!canvas->getProps(&surfaceProps)) {
+        SK_ABORT("Ahhhhh! can't get the surface props.");
+    }
+    sk_sp<SkColorSpace> colorSpace = canvas->imageInfo().refColorSpace();
+    bool useDFT = this->recordingContext()->asDirectContext()->supportsDistanceFieldText();
+    auto analysisCanvas = server.makeAnalysisCanvas(
+            canvas->getBaseLayerSize().width(), canvas->getBaseLayerSize().width(),
+            surfaceProps,
+            std::move(colorSpace),
+            useDFT
+    );
+
+    analysisCanvas->setMatrix(canvas->getTotalMatrix());
+    auto srcSlug = GrSlug::ConvertBlob(analysisCanvas.get(),
+                                       *glyphRunList.blob(),
+                                       glyphRunList.origin(),
+                                       paint);
+
+    if (srcSlug == nullptr) {
+        return;
+    }
+
+    std::vector<uint8_t> serverStrikeData;
+    server.writeStrikeData(&serverStrikeData);
+
+    if (!client.readStrikeData(serverStrikeData.data(), serverStrikeData.size())) {
+        SK_ABORT("Problem reading the strike cache updates");
+    }
+
+    auto dstSlugData = srcSlug->serialize();
+    auto dstSlug = client.deserializeSlug(dstSlugData->data(), dstSlugData->size());
+    this->drawSlug(canvas, dstSlug.get());
+}
+#endif
+
 void Device::onDrawGlyphRunList(SkCanvas* canvas,
                                 const SkGlyphRunList& glyphRunList,
                                 const SkPaint& paint) {
@@ -994,14 +1100,16 @@
     GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::v1::Device", "drawGlyphRunList", fContext.get());
     SkASSERT(!glyphRunList.hasRSXForm());
 
-    #if defined(SK_EXPERIMENTAL_SIMULATE_DRAWGLYPHRUNLIST_WITH_SLUG)
+#if defined(SK_EXPERIMENTAL_SIMULATE_DRAWGLYPHRUNLIST_WITH_SLUG)
     this->testingOnly_drawGlyphRunListWithSlug(canvas, glyphRunList, paint);
-    #elif defined(SK_EXPERIMENTAL_SIMULATE_DRAWGLYPHRUNLIST_WITH_SLUG_SERIALIZE)
+#elif defined(SK_EXPERIMENTAL_SIMULATE_DRAWGLYPHRUNLIST_WITH_SLUG_SERIALIZE)
     this->testingOnly_drawGlyphRunListWithSerializedSlug(canvas, glyphRunList, paint);
-    #else
+#elif defined(SK_EXPERIMENTAL_SIMULATE_DRAWGLYPHRUNLIST_WITH_SLUG_STRIKE_SERIALIZE)
+    this->testingOnly_drawGlyphRunListWithSerializedSlugAndStrike(canvas, glyphRunList, paint);
+#else
     fSurfaceDrawContext->drawGlyphRunList(
             canvas, this->clip(), this->asMatrixProvider(), glyphRunList, paint);
-    #endif
+#endif
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/v1/Device_v1.h b/src/gpu/v1/Device_v1.h
index c963ede..876f415 100644
--- a/src/gpu/v1/Device_v1.h
+++ b/src/gpu/v1/Device_v1.h
@@ -210,17 +210,28 @@
     bool forceConservativeRasterClip() const override { return true; }
 
     const GrClip* clip() const { return &fClip; }
-    #if defined(SK_EXPERIMENTAL_SIMULATE_DRAWGLYPHRUNLIST_WITH_SLUG)
+#if defined(SK_EXPERIMENTAL_SIMULATE_DRAWGLYPHRUNLIST_WITH_SLUG)
     void testingOnly_drawGlyphRunListWithSlug(SkCanvas* canvas,
                                               const SkGlyphRunList& glyphRunList,
                                               const SkPaint& paint);
-    #endif
 
-    #if defined(SK_EXPERIMENTAL_SIMULATE_DRAWGLYPHRUNLIST_WITH_SLUG_SERIALIZE)
+    void testingOnly_drawGlyphRunListWithSlug(SkCanvas* canvas,
+                                              const SkGlyphRunList& glyphRunList,
+                                              const SkPaint& paint);
+#endif
+
+#if defined(SK_EXPERIMENTAL_SIMULATE_DRAWGLYPHRUNLIST_WITH_SLUG_SERIALIZE)
     void testingOnly_drawGlyphRunListWithSerializedSlug(SkCanvas* canvas,
                                                         const SkGlyphRunList& glyphRunList,
                                                         const SkPaint& paint);
-    #endif
+#endif
+
+#if defined(SK_EXPERIMENTAL_SIMULATE_DRAWGLYPHRUNLIST_WITH_SLUG_STRIKE_SERIALIZE)
+    void testingOnly_drawGlyphRunListWithSerializedSlugAndStrike(SkCanvas* canvas,
+                                                                 const SkGlyphRunList& glyphRunList,
+                                                                 const SkPaint& paint);
+#endif
+
     // If not null, dstClip must be contained inside dst and will also respect the edge AA flags.
     // If 'preViewMatrix' is not null, final CTM will be this->ctm() * preViewMatrix.
     void drawImageQuad(const SkImage*, const SkRect* src, const SkRect* dst,
diff --git a/tests/SkRemoteGlyphCacheTest.cpp b/tests/SkRemoteGlyphCacheTest.cpp
index d8a19ca..88f2d99 100644
--- a/tests/SkRemoteGlyphCacheTest.cpp
+++ b/tests/SkRemoteGlyphCacheTest.cpp
@@ -30,6 +30,10 @@
 #include "tools/ToolUtils.h"
 #include "tools/fonts/TestEmptyTypeface.h"
 
+// Since SkRemoteGlyphCache is not re-entrant, we can't use it while drawing slugs to simulate
+// text blobs in the GPU stack.
+#if !defined(SK_EXPERIMENTAL_SIMULATE_DRAWGLYPHRUNLIST_WITH_SLUG_STRIKE_SERIALIZE)
+
 class DiscardableManager : public SkStrikeServer::DiscardableHandleManager,
                            public SkStrikeClient::DiscardableHandleManager {
 public:
@@ -1080,3 +1084,4 @@
     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
     discardableManager->unlockAndDeleteAll();
 }
+#endif