Move cache difference code into remote cache files

BUG=skia:7515

Change-Id: I59e75d460b4ed4d0a737c833520b2335808a4ce4
Reviewed-on: https://skia-review.googlesource.com/115706
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Herb Derby <herb@google.com>
diff --git a/tools/remote_demo.cpp b/tools/remote_demo.cpp
index 7cedaea..0649ec9 100644
--- a/tools/remote_demo.cpp
+++ b/tools/remote_demo.cpp
@@ -5,817 +5,76 @@
  * found in the LICENSE file.
  */
 
-#include "SkCanvas.h"
-#include "SkGlyph.h"
-#include "SkPathEffect.h"
-#include "SkMaskFilter.h"
-#include "SkData.h"
-#include "SkDescriptor.h"
-#include "SkGraphics.h"
-#include "SkNoDrawCanvas.h"
-#include "SkPictureRecorder.h"
-#include "SkSerialProcs.h"
-#include "SkSurface.h"
-#include "SkTypeface.h"
-#include "SkWriteBuffer.h"
-#include "SkTextBlobRunIterator.h"
-#include "SkGlyphCache.h"
-#include "SkDrawFilter.h"
-#include "SkDevice.h"
-
-#include <type_traits>
 #include <chrono>
-#include <ctype.h>
 #include <err.h>
-#include <memory>
-#include <stdio.h>
-#include <thread>
-#include <tuple>
 #include <iostream>
-#include <unordered_map>
-#include <iomanip>
-
+#include <memory>
+#include <string>
 #include <sys/types.h>
 #include <sys/uio.h>
 #include <sys/wait.h>
+#include <thread>
 #include <unistd.h>
-#include <sys/mman.h>
-#include <SkFindAndPlaceGlyph.h>
-#include <SkDrawLooper.h>
-#include "SkTypeface_remote.h"
+
 #include "SkRemoteGlyphCache.h"
-#include "SkMakeUnique.h"
+#include "SkGraphics.h"
+#include "SkSurface.h"
 
-static const size_t kPageSize = 4096;
-
+static std::string gSkpName;
 static bool gUseGpu = true;
 static bool gPurgeFontCaches = true;
 static bool gUseProcess = true;
 
-static int gFontMetrics;
-static int gMetricsImage;
-static int gPath;
 
-enum direction : int {kRead = 0, kWrite = 1};
-
-#define INSTRUMENT 0
-
-template <typename T>
-class SkArraySlice : public std::tuple<const T*, size_t> {
+class ReadWriteTransport : public SkTransport {
 public:
-    // Additional constructors as needed.
-    SkArraySlice(const T* data, size_t size) : std::tuple<const T*, size_t>{data, size} { }
-    SkArraySlice() : SkArraySlice<T>(nullptr, 0) { }
-    friend const T* begin(const SkArraySlice<T>& slice) {
-        return slice.data();
+    ReadWriteTransport(int readFd, int writeFd) : fReadFd{readFd}, fWriteFd{writeFd} {}
+    ~ReadWriteTransport() override {
+        close(fWriteFd);
+        close(fReadFd);
     }
-
-    friend const T* end(const SkArraySlice<T>& slice) {
-        return &slice.data()[slice.size()];
-    }
-
-    const T* data() const {
-        return std::get<0>(*this);
-    }
-
-    size_t size() const {
-        return std::get<1>(*this);
-    }
-};
-
-// TODO: handle alignment
-// TODO: handle overflow
-class Transport {
-public:
-    enum IOResult : bool {kFail = false, kSuccess = true};
-
-    Transport(Transport&& t)
-        : fReadFd{t.fReadFd}
-        , fWriteFd{t.fWriteFd}
-        , fBuffer{std::move(t.fBuffer)}
-        , fCloser{t.fCloser} { }
-
-    Transport(const Transport& t)
-        : fReadFd{t.fReadFd}
-        , fWriteFd{t.fWriteFd}
-        , fBuffer{new uint8_t[kBufferSize]}
-        , fCloser{t.fCloser} { }
-
-    Transport(int readFd, int writeFd)
-        : fReadFd{readFd}
-        , fWriteFd{writeFd}
-        , fCloser{std::make_shared<Closer>(readFd, writeFd)} { }
-
-    static Transport DoubleBuffer(const Transport& transport) {
-        return Transport{transport};
-    }
-
-    struct Closer {
-        Closer(int readFd, int writeFd) : fReadFd{readFd}, fWriteFd{writeFd} { }
-        ~Closer() {
-            close(fWriteFd);
-            close(fReadFd);
-        }
-        int fReadFd,
-            fWriteFd;
-    };
-
-    void startRead() {
-        fCursor = 0;
-        fEnd = 0;
-    }
-
-    template <typename T>
-    T* startRead() {
-        this->startRead();
-        return this->read<T>();
-    }
-
-    template <typename T>
-    T* read() {
-        T* result = (T*)this->ensureAtLeast(sizeof(T));
-        fCursor += sizeof(T);
-        return result;
-    }
-
-    SkDescriptor* readDescriptor() {
-        SkDescriptor* result = (SkDescriptor*)this->ensureAtLeast(sizeof(SkDescriptor));
-        size_t size = result->getLength();
-        this->ensureAtLeast(size);
-        fCursor += size;
-        return result;
-    }
-
-    template <typename T>
-    SkArraySlice<T> readArray(int count) {
-        size_t size = count * sizeof(T);
-        const T* base = (const T*)this->ensureAtLeast(size);
-        SkArraySlice<T> result = SkArraySlice<T>{base, (uint32_t)count};
-        fCursor += size;
-        return result;
-    }
-
-    size_t endRead() {return size();}
-
-    sk_sp<SkData> readEntireData() {
-        size_t* size = this->startRead<size_t>();
-        if (size == nullptr) {
-            return nullptr;
-        }
-        const uint8_t* data = this->readArray<uint8_t>(*size).data();
-        if (size == nullptr || data == nullptr) {
-            this->endRead();
-            return sk_sp<SkData>(nullptr);
-        }
-        auto result = SkData::MakeWithCopy(data, *size);
-        this->endRead();
-        return result;
-    }
-
-    void startWrite() {
-        fCursor = 0;
-    }
-
-    template <typename T>
-    void startWrite(const T& data) {
-        this->startWrite();
-        this->write<T>(data);
-    }
-
-    template <typename T, typename... Args>
-    T* startEmplace(Args&&... args) {
-        this->startWrite();
-        return this->emplace<T>(std::forward<Args>(args)...);
-    }
-
-    template <typename T, typename... Args>
-    T* emplace(Args&&... args) {
-        T* result = new (&fBuffer[fCursor]) T{std::forward<Args>(args)...};
-        fCursor += sizeof(T);
-        return result;
-    }
-
-    template <typename T>
-    void write(const T& data) {
-        // TODO: guard against bad T.
-        memcpy(&fBuffer[fCursor], &data, sizeof(data));
-        fCursor += sizeof(data);
-    }
-
-    void writeDescriptor(const SkDescriptor& desc) {
-        memcpy(&fBuffer[fCursor], &desc, desc.getLength());
-        fCursor += desc.getLength();
-    }
-
-    template <typename T>
-    T* allocateArray(int count) {
-        T* result = (T*)&fBuffer[fCursor];
-        fCursor += count * sizeof(T);
-        return result;
-    }
-
-    IOResult endWrite() {
-        ssize_t written;
-        if((written = ::write(fWriteFd, fBuffer.get(), fCursor)) < 0) {
+    IOResult write(const void* buffer, size_t size) override {
+        ssize_t writeSize = ::write(fWriteFd, buffer, size);
+        if (writeSize < 0) {
+            err(1,"Failed write %zu", size);
             return kFail;
         }
         return kSuccess;
     }
 
-    IOResult writeEntireData(const SkData& data) {
-        size_t size = data.size();
-        iovec vec[2];
-        vec[0].iov_base = &size;
-        vec[0].iov_len = sizeof(size);
-        vec[1].iov_base = (void *)data.data();
-        vec[1].iov_len = size;
-
-        if(::writev(fWriteFd, vec, 2) < 0) {
-            return kFail;
+    std::tuple<size_t, IOResult> read(void* buffer, size_t size) override {
+        ssize_t readSize = ::read(fReadFd, buffer, size);
+        if (readSize < 0) {
+            err(1,"Failed read %zu", size);
+            return {size, kFail};
         }
-        return kSuccess;
+        return {readSize, kSuccess};
     }
 
-    size_t size() {return fCursor;}
-
 private:
-    void* ensureAtLeast(size_t size) {
-        if (size > fEnd - fCursor) {
-            if (readAtLeast(size) == kFail) {
-                return nullptr;
-            }
-        }
-        return &fBuffer[fCursor];
-    }
-
-    IOResult readAtLeast(size_t size) {
-        size_t readSoFar = 0;
-        size_t bufferLeft = kBufferSize - fCursor;
-        size_t needed = size - (fEnd - fCursor);
-        while (readSoFar < needed) {
-            ssize_t readSize;
-            if ((readSize = ::read(fReadFd, &fBuffer[fEnd+readSoFar], bufferLeft - readSoFar)) <= 0) {
-                if (readSize != 0) {
-                    err(1,"Failed read %zu", size);
-                }
-                return kFail;
-            }
-            readSoFar += readSize;
-        }
-        fEnd += readSoFar;
-        return kSuccess;
-    }
-
-    static constexpr size_t kBufferSize = kPageSize * 2000;
     const int fReadFd,
               fWriteFd;
-
-    std::unique_ptr<uint8_t[]> fBuffer{new uint8_t[kBufferSize]};
-    std::shared_ptr<Closer> fCloser;
-
-    size_t fCursor{0};
-    size_t fEnd{0};
 };
 
-enum class OpCode : int32_t {
-    kFontMetrics          = 0,
-    kGlyphPath            = 1,
-    kGlyphMetricsAndImage = 2,
-    kPrepopulateCache     = 3,
-};
+static void prime_cache_spec(const SkIRect& bounds,
+                        const SkSurfaceProps& props,
+                        const SkPicture& pic,
+                        SkStrikeCacheDifferenceSpec* strikeDifference) {
+    SkMatrix deviceMatrix = SkMatrix::I();
 
-class Op {
-public:
-    Op(OpCode opCode, SkFontID typefaceId, const SkScalerContextRec& rec)
-        : opCode{opCode}
-        , typefaceId{typefaceId}
-        , descriptor{rec} { }
-    const OpCode opCode;
-    const SkFontID typefaceId;
-    const SkScalerContextRecDescriptor descriptor;
-    union {
-        // op 0
-        SkPaint::FontMetrics fontMetrics;
-        // op 1, 2, and 4
-        SkGlyph glyph;
-        // op 3
-        struct {
-            SkGlyphID glyphId;
-            size_t pathSize;
-        };
-    };
-};
+    SkTextBlobCacheDiffCanvas filter(
+            bounds.width(), bounds.height(), deviceMatrix, props,
+            SkScalerContextFlags::kFakeGammaAndBoostContrast,
+            strikeDifference);
 
-
-class TrackLayerDevice : public SkNoPixelsDevice {
-public:
-    TrackLayerDevice(const SkIRect& bounds, const SkSurfaceProps& props)
-            : SkNoPixelsDevice(bounds, props) { }
-    SkBaseDevice* onCreateDevice(const CreateInfo& cinfo, const SkPaint*) override {
-        const SkSurfaceProps surfaceProps(this->surfaceProps().flags(), cinfo.fPixelGeometry);
-        return new TrackLayerDevice(this->getGlobalBounds(), surfaceProps);
-    }
-};
-
-
-class TextBlobFilterCanvas : public SkNoDrawCanvas {
-public:
-    struct StrikeSpec {
-        StrikeSpec(SkFontID typefaceID_, uint32_t descLength_, int glyphCount_)
-            : typefaceID{typefaceID_}
-            , descLength{descLength_}
-            , glyphCount{glyphCount_} { }
-        SkFontID typefaceID;
-        uint32_t descLength;
-        int glyphCount;
-        /* desc */
-        /* n X (glyphs ids) */
-    };
-
-    struct Header {
-        Header(int strikeCount_) : strikeCount{strikeCount_} {}
-        const int strikeCount;
-    };
-
-    TextBlobFilterCanvas(int width, int height,
-                         const SkMatrix& deviceMatrix,
-                         const SkSurfaceProps& props,
-                         SkScalerContextFlags flags)
-        : SkNoDrawCanvas{new TrackLayerDevice{SkIRect::MakeWH(width, height), props}}
-        , fDeviceMatrix{deviceMatrix}
-        , fSurfaceProps{props}
-        , fScalerContextFlags{flags} { }
-
-    void writeSpecToTransport(Transport* transport) {
-        transport->emplace<Header>((int)fDescMap.size());
-        for (auto& i : fDescMap) {
-            auto accum = &i.second;
-            transport->emplace<StrikeSpec>(
-                accum->typefaceID, accum->desc->getLength(), accum->glyphIDs->count());
-            transport->writeDescriptor(*accum->desc);
-            accum->glyphIDs->foreach([&](SkPackedGlyphID id) {
-                transport->write<SkPackedGlyphID>(id);
-            });
-        }
-    }
-
-    static void WriteDataToTransport(
-        Transport* in, Transport* out, SkRemoteGlyphCacheRenderer* rc) {
-        auto perHeader = [out](Header* header) {
-          out->write<Header>(*header);
-        };
-
-        struct {
-            SkScalerContext* scaler{nullptr};
-        } strikeData;
-
-        auto perStrike = [out, &strikeData, rc](StrikeSpec* spec, SkDescriptor* desc) {
-            out->write<StrikeSpec>(*spec);
-            out->writeDescriptor(*desc);
-            SkScalerContextRecDescriptor recDesc{*desc};
-            strikeData.scaler = rc->generateScalerContext(recDesc, spec->typefaceID);
-            SkPaint::FontMetrics fontMetrics;
-            strikeData.scaler->getFontMetrics(&fontMetrics);
-            out->write<SkPaint::FontMetrics>(fontMetrics);
-        };
-
-        auto perGlyph = [out, &strikeData](SkPackedGlyphID glyphID) {
-            SkGlyph glyph;
-            glyph.initWithGlyphID(glyphID);
-            strikeData.scaler->getMetrics(&glyph);
-            auto imageSize = glyph.computeImageSize();
-            glyph.fImage = nullptr;
-            glyph.fPathData = nullptr;
-            out->write<SkGlyph>(glyph);
-
-            if (imageSize > 0) {
-                glyph.fImage = out->allocateArray<uint8_t>(imageSize);
-                strikeData.scaler->getImage(glyph);
-            }
-        };
-
-        ReadSpecFromTransport(in, perHeader, perStrike, perGlyph);
-    }
-
-    template <typename PerHeader, typename PerStrike, typename PerGlyph>
-    static void ReadSpecFromTransport(Transport* transport,
-                               PerHeader perHeader,
-                               PerStrike perStrike,
-                               PerGlyph perGlyph) {
-        auto header = transport->read<TextBlobFilterCanvas::Header>();
-        perHeader(header);
-        for (int i = 0; i < header->strikeCount; i++) {
-            auto strike = transport->read<TextBlobFilterCanvas::StrikeSpec>();
-            auto desc = transport->readDescriptor();
-            //desc->assertChecksum();
-            perStrike(strike, desc);
-            auto glyphIDs = transport->readArray<SkPackedGlyphID>(strike->glyphCount);
-            for (auto glyphID : glyphIDs) {
-                perGlyph(glyphID);
-            }
-        }
-    }
-
-    template <typename PerStrike, typename PerGlyph, typename FinishStrike>
-    void readDataFromTransport(
-        Transport* transport, PerStrike perStrike, PerGlyph perGlyph, FinishStrike finishStrike) {
-        auto header = transport->read<Header>();
-        for (int i = 0; i < header->strikeCount; i++) {
-            auto strike = transport->read<StrikeSpec>();
-            auto desc = transport->readDescriptor();
-            auto fontMetrics = transport->read<SkPaint::FontMetrics>();
-            perStrike(strike, desc, fontMetrics);
-            for (int j = 0; j < strike->glyphCount; j++) {
-                auto glyph = transport->read<SkGlyph>();
-                SkArraySlice<uint8_t> image = SkArraySlice<uint8_t>{};
-                auto imageSize = glyph->computeImageSize();
-                if (imageSize != 0) {
-                    image = transport->readArray<uint8_t>(imageSize);
-                }
-                perGlyph(glyph, image);
-            }
-            finishStrike();
-        }
-    }
-
-
-protected:
-    SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
-        return kFullLayer_SaveLayerStrategy;
-    }
-
-    void onDrawTextBlob(
-        const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) override
-    {
-        SkPoint position{x, y};
-
-        SkPaint runPaint{paint};
-        SkTextBlobRunIterator it(blob);
-        for (;!it.done(); it.next()) {
-            // applyFontToPaint() always overwrites the exact same attributes,
-            // so it is safe to not re-seed the paint for this reason.
-            it.applyFontToPaint(&runPaint);
-            runPaint.setFlags(this->getTopDevice()->filterTextFlags(runPaint));
-            if (auto looper = runPaint.getLooper()) {
-                this->processLooper(position, it, runPaint, looper, this);
-            } else {
-                this->processGlyphRun(position, it, runPaint);
-            }
-        }
-    }
-
-    void onDrawText(const void*, size_t, SkScalar, SkScalar, const SkPaint&) override {
-        SK_ABORT("DrawText");
-    }
-    void onDrawPosText(const void*, size_t, const SkPoint[], const SkPaint&) override {
-        SK_ABORT("DrawPosText");
-    }
-    void onDrawPosTextH(const void*, size_t, const SkScalar[], SkScalar, const SkPaint&) override {
-        SK_ABORT("DrawPosTextH");
-    }
-
-private:
-    using PosFn = SkPoint(*)(int index, const SkScalar* pos);
-    using MapFn = SkPoint(*)(const SkMatrix& m, SkPoint pt);
-
-    struct CacheAccum {
-        SkFontID typefaceID;
-        SkDescriptor* desc;
-        //std::vector<SkPackedGlyphID> glyphIDs;
-        std::unique_ptr<SkTHashSet<SkPackedGlyphID>> glyphIDs;
-    };
-
-    void processLooper(
-        const SkPoint& position,
-        const SkTextBlobRunIterator& it,
-        const SkPaint& origPaint,
-        SkDrawLooper* looper,
-        SkCanvas* canvas)
-    {
-        SkSTArenaAlloc<48> alloc;
-        auto context = looper->makeContext(canvas, &alloc);
-        SkPaint runPaint = origPaint;
-        while (context->next(this, &runPaint)) {
-            canvas->save();
-            this->processGlyphRun(position, it, runPaint);
-            canvas->restore();
-            runPaint = origPaint;
-        }
-    }
-
-    void processGlyphRun(
-        const SkPoint& position,
-        const SkTextBlobRunIterator& it,
-        const SkPaint& runPaint)
-    {
-
-        if (runPaint.getTextEncoding() != SkPaint::TextEncoding::kGlyphID_TextEncoding) {
-            return;
-        }
-
-        // All other alignment modes need the glyph advances. Use the slow drawing mode.
-        if (runPaint.getTextAlign() != SkPaint::kLeft_Align) {
-            return;
-        }
-
-        PosFn posFn;
-        switch (it.positioning()) {
-            case SkTextBlob::kDefault_Positioning:
-                // Default positioning needs advances. Can't do that.
-                return;
-
-            case SkTextBlob::kHorizontal_Positioning:
-                posFn = [](int index, const SkScalar* pos) {
-                    return SkPoint{pos[index], 0};
-                };
-
-                break;
-
-            case SkTextBlob::kFull_Positioning:
-                posFn = [](int index, const SkScalar* pos) {
-                    return SkPoint{pos[2 * index], pos[2 * index + 1]};
-                };
-                break;
-
-            default:
-                posFn = nullptr;
-                SK_ABORT("unhandled positioning mode");
-        }
-
-        SkMatrix blobMatrix{fDeviceMatrix};
-        blobMatrix.preConcat(this->getTotalMatrix());
-        if (blobMatrix.hasPerspective()) {
-            return;
-        }
-        blobMatrix.preTranslate(position.x(), position.y());
-
-        SkMatrix runMatrix{blobMatrix};
-        runMatrix.preTranslate(it.offset().x(), it.offset().y());
-
-        MapFn mapFn;
-        switch ((int)runMatrix.getType()) {
-            case SkMatrix::kIdentity_Mask:
-            case SkMatrix::kTranslate_Mask:
-                mapFn = [](const SkMatrix& m, SkPoint pt) {
-                    pt.offset(m.getTranslateX(), m.getTranslateY());
-                    return pt;
-                };
-                break;
-            case SkMatrix::kScale_Mask:
-            case SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask:
-                mapFn = [](const SkMatrix& m, SkPoint pt) {
-                    return SkPoint{pt.x() * m.getScaleX() + m.getTranslateX(),
-                                   pt.y() * m.getScaleY() + m.getTranslateY()};
-                };
-                break;
-            case SkMatrix::kAffine_Mask | SkMatrix::kScale_Mask:
-            case SkMatrix::kAffine_Mask | SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask:
-                mapFn = [](const SkMatrix& m, SkPoint pt) {
-                    return SkPoint{
-                        pt.x() * m.getScaleX() + pt.y() * m.getSkewX() + m.getTranslateX(),
-                        pt.x() * m.getSkewY() + pt.y() * m.getScaleY() + m.getTranslateY()};
-                };
-                break;
-            default:
-                mapFn = nullptr;
-                SK_ABORT("Bad matrix.");
-        }
-
-        SkAutoDescriptor ad;
-        SkScalerContextRec rec;
-        SkScalerContextEffects effects;
-
-        SkScalerContext::MakeRecAndEffects(runPaint, &fSurfaceProps, &runMatrix,
-                                           fScalerContextFlags, &rec, &effects);
-
-        SkAxisAlignment axisAlignment = SkAxisAlignment::kNone_SkAxisAlignment;
-        if (it.positioning() == SkTextBlob::kHorizontal_Positioning) {
-            axisAlignment = rec.computeAxisAlignmentForHText();
-        }
-
-        auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad);
-
-        auto mapIter = fDescMap.find(desc);
-        if (mapIter == fDescMap.end()) {
-            auto newDesc = desc->copy();
-            auto newDescPtr = newDesc.get();
-            fUniqueDescriptors.emplace_back(std::move(newDesc));
-            CacheAccum newAccum;
-            newAccum.desc = newDescPtr;
-
-            newAccum.typefaceID =
-                SkTypefaceProxy::DownCast(runPaint.getTypeface())->fontID();
-
-            newAccum.glyphIDs = skstd::make_unique<SkTHashSet<SkPackedGlyphID>>();
-            mapIter = fDescMap.emplace_hint(mapIter, newDescPtr, std::move(newAccum));
-        }
-
-        auto accum = &mapIter->second;
-
-        auto cache = SkGlyphCache::FindStrikeExclusive(*desc);
-        bool isSubpixel = SkToBool(rec.fFlags & SkScalerContext::kSubpixelPositioning_Flag);
-
-        auto pos = it.pos();
-        const uint16_t* glyphs = it.glyphs();
-        for (uint32_t index = 0; index < it.glyphCount(); index++) {
-            SkIPoint subPixelPos{0, 0};
-            if (runPaint.isAntiAlias() && isSubpixel) {
-                SkPoint glyphPos = mapFn(runMatrix, posFn(index, pos));
-                subPixelPos = SkFindAndPlaceGlyph::SubpixelAlignment(axisAlignment, glyphPos);
-            }
-
-            if (cache &&
-                cache->isGlyphCached(glyphs[index], subPixelPos.x(), subPixelPos.y())) {
-                continue;
-            }
-
-            SkPackedGlyphID glyphID{glyphs[index], subPixelPos.x(), subPixelPos.y()};
-            accum->glyphIDs->add(glyphID);
-        }
-    }
-
-    const SkMatrix fDeviceMatrix;
-    const SkSurfaceProps fSurfaceProps;
-    const SkScalerContextFlags fScalerContextFlags;
-
-    struct DescHash {
-        size_t operator()(const SkDescriptor* key) const {
-            return key->getChecksum();
-        }
-    };
-
-    struct DescEq {
-        bool operator()(const SkDescriptor* lhs, const SkDescriptor* rhs) const {
-            return lhs->getChecksum() == rhs->getChecksum();
-        }
-    };
-
-    using DescMap = std::unordered_map<SkDescriptor*, CacheAccum, DescHash, DescEq>;
-    DescMap fDescMap{16, DescHash(), DescEq()};
-    std::vector<std::unique_ptr<SkDescriptor>> fUniqueDescriptors;
-
-    std::vector<SkPackedGlyphID> fTempGlyphs;
-    std::vector<SkPackedGlyphID> runGlyphs;
-};
-
-
-class RemoteScalerContextFIFO : public SkRemoteScalerContext {
-public:
-    explicit RemoteScalerContextFIFO(Transport* transport)
-        : fTransport{transport} { }
-    void generateFontMetrics(const SkTypefaceProxy& tf,
-                             const SkScalerContextRec& rec,
-                             SkPaint::FontMetrics* metrics) override {
-        gFontMetrics += 1;
-
-        //SK_ABORT("generateFontMetrics should not be called.");
-        // Send generateFontMetrics
-        Op* op = this->startOpWrite(OpCode::kFontMetrics, tf, rec);
-        fTransport->endWrite();
-
-#if INSTRUMENT
-        SkScalerContextRecDescriptor rd{rec};
-        std::cout << " metrics font op rec tf: " << rec.fFontID
-                  << " tf id: " << tf.fontID()
-                  << " rec: " << rd.desc().getChecksum()
-                  << rec.dump().c_str() << std::endl;
-#endif
-        // Receive generateFontMetrics
-        op = fTransport->startRead<Op>();
-        *metrics = op->fontMetrics;
-        fTransport->endRead();
-    }
-
-    void generateMetricsAndImage(const SkTypefaceProxy& tf,
-                                 const SkScalerContextRec& rec,
-                                 SkArenaAlloc* alloc,
-                                 SkGlyph* glyph) override {
-        gMetricsImage += 1;
-        //SK_ABORT("generateMetricsAndImage should not be called.");
-        // Send generateMetricsAndImage
-        SkScalerContextRecDescriptor rd{rec};
-
-#if INSTRUMENT
-        std::cout << " metrics image op rec tf: " << rec.fFontID
-                  << " tf id: " << tf.fontID()
-                  << " rec: " << rd.desc().getChecksum()
-                  << " glyphid: " << glyph->getPackedID().getPackedID() << "\n"
-                  << rec.dump().c_str() << std::endl;
-#endif
-        Op* op = this->startOpWrite(OpCode::kGlyphMetricsAndImage, tf, rec);
-        op->glyph = *glyph;
-        fTransport->endWrite();
-
-        // Receive generateMetricsAndImage
-        op = fTransport->startRead<Op>();
-        *glyph = op->glyph;
-        auto imageSize = op->glyph.computeImageSize();
-        glyph->fPathData = nullptr;
-        if (imageSize > 0) {
-            auto image = fTransport->readArray<uint8_t>(imageSize);
-            SkASSERT(imageSize == image.size());
-            glyph->allocImage(alloc);
-            memcpy(glyph->fImage, image.data(), imageSize);
-        } else {
-            glyph->fImage = nullptr;
-        }
-        fTransport->endRead();
-    }
-
-    void generatePath(const SkTypefaceProxy& tf,
-                      const SkScalerContextRec& rec,
-                      SkGlyphID glyph, SkPath* path) override {
-        gPath += 1;
-        // Send generatePath
-        SkScalerContextRecDescriptor rd{rec};
-
-        std::cout << " path op rec tf: " << rec.fFontID
-                  << " tf id: " << tf.fontID()
-                  << " rec: " << rd.desc().getChecksum()
-                  << " glyphid: " << glyph << std::endl;
-        Op* op = this->startOpWrite(OpCode::kGlyphPath, tf, rec);
-        op->glyphId = glyph;
-        fTransport->endWrite();
-
-        op = fTransport->startRead<Op>();
-        auto rawPath = fTransport->readArray<uint8_t>(op->pathSize);
-        path->readFromMemory(rawPath.data(), rawPath.size());
-        fTransport->endRead();
-    }
-
-private:
-    Op* startOpWrite(OpCode opCode, const SkTypefaceProxy& tf,
-                     const SkScalerContextRec& rec) {
-        return fTransport->startEmplace<Op>(opCode, tf.fontID(), rec);
-    }
-
-    Transport* const fTransport;
-};
-
-static void prepopulate_cache(
-    Transport* transport,
-    SkRemoteGlyphCacheGPU* cache,
-    sk_sp<SkPicture> pic,
-    TextBlobFilterCanvas* filter) {
-
-    pic->playback(filter);
-
-    transport->startEmplace<Op>(OpCode::kPrepopulateCache, SkFontID{0},
-                                SkScalerContextRec{});
-    filter->writeSpecToTransport(transport);
-    transport->endWrite();
-
-    SkExclusiveStrikePtr strike;
-
-    auto perStrike = [&strike, cache](TextBlobFilterCanvas::StrikeSpec* spec,
-                                          SkDescriptor* desc,
-                                          SkPaint::FontMetrics* fontMetrics) {
-        auto tf = cache->lookupTypeface(spec->typefaceID);
-        // TODO: implement effects handling.
-        SkScalerContextEffects effects;
-        if ((strike = SkGlyphCache::FindStrikeExclusive(*desc)) == nullptr) {
-            auto scaler = SkGlyphCache::CreateScalerContext(*desc, effects, *tf);
-            strike = SkGlyphCache::CreateStrikeExclusive(*desc, std::move(scaler), fontMetrics);
-        }
-#if INSTRUMENT
-        std::cout << std::hex << "prepop cache " << (intptr_t)cache
-                  << " desc: " << desc->getChecksum()
-                  << " typeface id: " << tf->uniqueID()
-                  << " glyph count: " << spec->glyphCount << std::endl;
-        auto rec = (SkScalerContextRec*)desc->findEntry(kRec_SkDescriptorTag, nullptr);
-        SkDebugf("%s\n", rec->dump().c_str());
-#endif
-
-    };
-
-    auto perGlyph = [&strike](SkGlyph* glyph, SkArraySlice<uint8_t> image) {
-        SkGlyph* allocatedGlyph = strike->getRawGlyphByID(glyph->getPackedID());
-        *allocatedGlyph = *glyph;
-        allocatedGlyph->allocImage(strike->getAlloc());
-        memcpy(allocatedGlyph->fImage, image.data(), image.size());
-    };
-
-    auto finishStrike = [&strike]() {
-        strike.reset(nullptr);
-    };
-
-    // needed for font metrics mistake.
-    Transport in = Transport::DoubleBuffer(*transport);
-#if INSTRUMENT
-    SkDebugf("========= Sending prep cache ========\n");
-#endif
-
-    in.startRead();
-    filter->readDataFromTransport(&in, perStrike, perGlyph, finishStrike);
-    in.endRead();
+    pic.playback(&filter);
 }
 
-std::string gSkpName;
 static void final_draw(std::string outFilename,
-                       Transport* transport,
+                       SkTransport* transport,
                        SkDeserialProcs* procs,
                        SkData* picData,
-                       SkRemoteGlyphCacheGPU* cache) {
+                       SkStrikeClient* client) {
 
     auto pic = SkPicture::MakeFromData(picData, procs);
 
@@ -827,34 +86,43 @@
     auto picUnderTest = SkPicture::MakeFromData(picData, procs);
 
     SkMatrix deviceMatrix = SkMatrix::I();
-    // kFakeGammaAndBoostContrast
-    TextBlobFilterCanvas filter(
-        r.width(), r.height(), deviceMatrix, s->props(),
-        SkScalerContextFlags::kFakeGammaAndBoostContrast);
 
-    if (cache != nullptr) {
+    SkStrikeCacheDifferenceSpec strikeDifference;
+    SkTextBlobCacheDiffCanvas filter(
+        r.width(), r.height(), deviceMatrix, s->props(),
+        SkScalerContextFlags::kFakeGammaAndBoostContrast,
+        &strikeDifference);
+
+    if (client != nullptr) {
         for (int i = 0; i < 0; i++) {
             auto start = std::chrono::high_resolution_clock::now();
-            prepopulate_cache(transport, cache, picUnderTest, &filter);
+
+
+            SkStrikeCacheDifferenceSpec strikeDifference;
+
+            prime_cache_spec(r, s->props(), *picUnderTest, &strikeDifference);
+
+            client->primeStrikeCache(strikeDifference);
 
             auto end = std::chrono::high_resolution_clock::now();
             std::chrono::duration<double> elapsed_seconds = end - start;
             (void)elapsed_seconds;
             if (i == 0) {
-                std::cout << "filter time: " << elapsed_seconds.count() * 1e6
-                          << "us size: " << transport->size() << std::endl;
+                std::cout << "filter time: " << elapsed_seconds.count() * 1e6 << std::endl;
             }
         }
     }
 
     std::chrono::duration<double> total_seconds{0.0};
-    for (int i = 0; i < 100; i++) { // 20
+    for (int i = 0; i < 1; i++) { // 20
         if (gPurgeFontCaches) {
             SkGraphics::PurgeFontCache();
         }
         auto start = std::chrono::high_resolution_clock::now();
-        if (cache != nullptr) {
-            prepopulate_cache(transport, cache, picUnderTest, &filter);
+        if (client != nullptr) {
+            SkStrikeCacheDifferenceSpec strikeDifference;
+            prime_cache_spec(r, s->props(), *picUnderTest, &strikeDifference);
+            client->primeStrikeCache(strikeDifference);
         }
         c->drawPicture(picUnderTest);
         auto end = std::chrono::high_resolution_clock::now();
@@ -867,9 +135,6 @@
               << " purgeCache: " << gPurgeFontCaches << std::endl;
     fprintf(stderr, "%s use GPU %s elapsed time %8.6f s\n", gSkpName.c_str(),
             gUseGpu ? "true" : "false", total_seconds.count());
-    /*std::cerr << gSkpName << " use GPU " << std::boolalpha << gUseGpu << " elapsed time: "
-              << std::fixed << std::setw( 6 ) << std::setprecision( 1 )
-              << total_seconds.count() << " s\n";*/
 
     auto i = s->makeImageSnapshot();
     auto data = i->encodeToData();
@@ -879,139 +144,53 @@
 
 static void gpu(int readFd, int writeFd) {
 
-    Transport transport{readFd, writeFd};
+    ReadWriteTransport rwTransport{readFd, writeFd};
 
-    auto picData = transport.readEntireData();
+    auto picData = rwTransport.readSkData();
     if (picData == nullptr) {
         return;
     }
 
-    SkRemoteGlyphCacheGPU rc{
-        skstd::make_unique<RemoteScalerContextFIFO>(&transport)
-    };
+    SkStrikeClient client{&rwTransport};
+
     SkDeserialProcs procs;
-    rc.prepareDeserializeProcs(&procs);
+    client.prepareDeserializeProcs(&procs);
 
-    final_draw("test.png", &transport, &procs, picData.get(), &rc);
+    final_draw("test.png", &rwTransport, &procs, picData.get(), &client);
 
-    if (gFontMetrics + gMetricsImage + gPath > 0) {
-        fprintf(stderr, "exceptions - fm: %d mi: %d p: %d\n", gFontMetrics, gMetricsImage, gPath);
-    }
+    printf("GPU is exiting\n");
 }
 
 static int renderer(
     const std::string& skpName, int readFd, int writeFd)
 {
-    Transport transport{readFd, writeFd};
+    ReadWriteTransport rwTransport{readFd, writeFd};
+    SkStrikeServer server{&rwTransport};
 
     auto skpData = SkData::MakeFromFileName(skpName.c_str());
     std::cout << "skp stream is " << skpData->size() << " bytes long " << std::endl;
 
-    SkRemoteGlyphCacheRenderer rc;
     SkSerialProcs procs;
     sk_sp<SkData> stream;
     if (gUseGpu) {
         auto pic = SkPicture::MakeFromData(skpData.get());
-        rc.prepareSerializeProcs(&procs);
+        server.prepareSerializeProcs(&procs);
         stream = pic->serialize(&procs);
+
+        if (rwTransport.writeSkData(*stream) == SkTransport::kFail) {
+            return 1;
+        }
+
+        std::cout << "Waiting for scaler context ops." << std::endl;
+
+        return server.serve();
     } else {
         stream = skpData;
-    }
-
-    std::cout << "stream is " << stream->size() << " bytes long" << std::endl;
-
-    if (!gUseGpu) {
-        final_draw("test-direct.png", &transport, nullptr, stream.get(), nullptr);
+        final_draw("test-correct.png", &rwTransport, nullptr, stream.get(), nullptr);
         return 0;
     }
-
-    if (transport.writeEntireData(*stream) == Transport::kFail) {
-        return 1;
-    }
-
-    std::cout << "Waiting for scaler context ops." << std::endl;
-
-    while (true) {
-
-        // Share the buffer between read and write.
-        Op* op = transport.startRead<Op>();
-        if (op == nullptr) { std::cout << "Exit op loop" << std::endl; break;}
-
-            switch (op->opCode) {
-                case OpCode::kFontMetrics : {
-                    auto sc = rc.generateScalerContext(op->descriptor, op->typefaceId);
-                    sc->getFontMetrics(&op->fontMetrics);
-                    transport.endWrite();
-                    break;
-                }
-                case OpCode::kGlyphPath : {
-                    auto sc = rc.generateScalerContext(op->descriptor, op->typefaceId);
-                    // TODO: check for buffer overflow.
-                    SkPath path;
-                    sc->getPath(op->glyphId, &path);
-                    size_t pathSize = path.writeToMemory(nullptr);
-                    auto pathData = transport.allocateArray<uint8_t>(pathSize);
-                    op->pathSize = path.writeToMemory(pathData);
-                    transport.endWrite();
-                    break;
-                }
-                case OpCode::kGlyphMetricsAndImage : {
-                    auto sc = rc.generateScalerContext(op->descriptor, op->typefaceId);
-
-                    // TODO: check for buffer overflow.
-                    auto glyphId = op->glyph.getPackedID();
-                    op->glyph.initWithGlyphID(glyphId);
-                    sc->getMetrics(&op->glyph);
-                    auto imageSize = op->glyph.computeImageSize();
-                    op->glyph.fPathData = nullptr;
-
-                    if (imageSize > 0) {
-                        op->glyph.fImage = transport.allocateArray<uint8_t>(imageSize);
-                        sk_bzero(op->glyph.fImage, imageSize);
-                        sc->getImage(op->glyph);
-                    } else {
-                        op->glyph.fImage = nullptr;
-                    }
-                    transport.endWrite();
-                    break;
-                }
-                case OpCode::kPrepopulateCache : {
-
-                    Transport& in = transport;
-                    Transport out = Transport::DoubleBuffer(transport);
-
-                    out.startWrite();
-                    TextBlobFilterCanvas::WriteDataToTransport(&in ,&out, &rc);
-                    out.endWrite();
-                    in.endRead();
-
-                    //std::cout << "read prepopulate spec size: " << in.size() << std::endl;
-                    //std::cout << "write prepopulate data size: " << out.size() << std::endl;
-                    break;
-                }
-                default:
-                    SK_ABORT("Bad op");
-            }
-    }
-
-    std::cout << "Returning from render" << std::endl;
-
-    return 0;
 }
 
-static void start_gpu(int render_to_gpu[2], int gpu_to_render[2]) {
-    std::cout << "gpu - Starting GPU" << std::endl;
-    close(gpu_to_render[kRead]);
-    close(render_to_gpu[kWrite]);
-    gpu(render_to_gpu[kRead], gpu_to_render[kWrite]);
-}
-
-static void start_render(std::string& skpName, int render_to_gpu[2], int gpu_to_render[2]) {
-    std::cout << "renderer - Starting Renderer" << std::endl;
-    close(render_to_gpu[kRead]);
-    close(gpu_to_render[kWrite]);
-    renderer(skpName, gpu_to_render[kRead], render_to_gpu[kWrite]);
-}
 
 int main(int argc, char** argv) {
     std::string skpName = argc > 1 ? std::string{argv[1]} : std::string{"skps/desk_nytimes.skp"};
@@ -1020,6 +199,9 @@
 
     gSkpName = skpName;
 
+    enum direction : int {kRead = 0, kWrite = 1};
+
+
     int render_to_gpu[2],
         gpu_to_render[2];
 
@@ -1048,9 +230,13 @@
             SkGraphics::Init();
 
             if (child == 0) {
-                start_gpu(render_to_gpu, gpu_to_render);
+                close(gpu_to_render[kRead]);
+                close(render_to_gpu[kWrite]);
+                gpu(render_to_gpu[kRead], gpu_to_render[kWrite]);
             } else {
-                start_render(skpName, render_to_gpu, gpu_to_render);
+                close(render_to_gpu[kRead]);
+                close(gpu_to_render[kWrite]);
+                renderer(skpName, gpu_to_render[kRead], render_to_gpu[kWrite]);
                 waitpid(child, nullptr, 0);
             }
         } else {