Delete all low-level rendering from ccpr

This converts ccpr to just a poorly named atlas manager. The atlas
gets rendered by normal calls on its MSAA draw context:

  for (;;) {
      surfaceDrawContext->stencilPath();  // Stencil.
  }
  surfaceDrawContext->stencilRect(atlasBounds);  // Cover.

Bug: chromium:1158093
Change-Id: I758ffd372b2ed5bb8b370156b6f80f6204146700
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/381618
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/GrClipStack.cpp b/src/gpu/GrClipStack.cpp
index 43f0bbf..166693d 100644
--- a/src/gpu/GrClipStack.cpp
+++ b/src/gpu/GrClipStack.cpp
@@ -8,6 +8,7 @@
 #include "src/gpu/GrClipStack.h"
 
 #include "include/core/SkMatrix.h"
+#include "src/core/SkPathPriv.h"
 #include "src/core/SkRRectPriv.h"
 #include "src/core/SkRectPriv.h"
 #include "src/core/SkTaskGroup.h"
diff --git a/src/gpu/GrSurfaceDrawContext.cpp b/src/gpu/GrSurfaceDrawContext.cpp
index 7556d15..628932c 100644
--- a/src/gpu/GrSurfaceDrawContext.cpp
+++ b/src/gpu/GrSurfaceDrawContext.cpp
@@ -746,6 +746,43 @@
     }
 }
 
+bool GrSurfaceDrawContext::stencilPath(const GrHardClip* clip,
+                                       GrAA doStencilMSAA,
+                                       const SkMatrix& viewMatrix,
+                                       const SkPath& path) {
+    SkIRect clipBounds = clip ? clip->getConservativeBounds()
+                              : SkIRect::MakeSize(this->dimensions());
+    GrStyledShape shape(path, GrStyledShape::DoSimplify::kNo);
+
+    GrPathRenderer::CanDrawPathArgs canDrawArgs;
+    canDrawArgs.fCaps = fContext->priv().caps();
+    canDrawArgs.fProxy = this->asRenderTargetProxy();
+    canDrawArgs.fClipConservativeBounds = &clipBounds;
+    canDrawArgs.fViewMatrix = &viewMatrix;
+    canDrawArgs.fShape = &shape;
+    canDrawArgs.fPaint = nullptr;
+    canDrawArgs.fAAType = (doStencilMSAA == GrAA::kYes) ? GrAAType::kMSAA : GrAAType::kNone;
+    canDrawArgs.fHasUserStencilSettings = false;
+    canDrawArgs.fTargetIsWrappedVkSecondaryCB = this->wrapsVkSecondaryCB();
+    GrPathRenderer* pr = this->drawingManager()->getPathRenderer(
+            canDrawArgs, false, GrPathRendererChain::DrawType::kStencil);
+    if (!pr) {
+        SkDebugf("WARNING: No path renderer to stencil path.\n");
+        return false;
+    }
+
+    GrPathRenderer::StencilPathArgs args;
+    args.fContext = fContext;
+    args.fRenderTargetContext = this;
+    args.fClip = clip;
+    args.fClipConservativeBounds = &clipBounds;
+    args.fViewMatrix = &viewMatrix;
+    args.fShape = &shape;
+    args.fDoStencilMSAA = doStencilMSAA;
+    pr->stencilPath(args);
+    return true;
+}
+
 void GrSurfaceDrawContext::stencilPath(const GrHardClip* clip,
                                        GrAA doStencilMSAA,
                                        const SkMatrix& viewMatrix,
diff --git a/src/gpu/GrSurfaceDrawContext.h b/src/gpu/GrSurfaceDrawContext.h
index ceda8be..f67d61a 100644
--- a/src/gpu/GrSurfaceDrawContext.h
+++ b/src/gpu/GrSurfaceDrawContext.h
@@ -583,6 +583,15 @@
         this->drawFilledQuad(clip, std::move(paint), doStencilMSAA, &quad, ss);
     }
 
+    // Fills the user stencil bits with a non-zero value at every sample inside the path. This will
+    // likely be implemented with a Redbook algorithm, but it is not guaranteed. The samples being
+    // rendered to must be zero initially.
+    bool stencilPath(const GrHardClip*,
+                     GrAA doStencilMSAA,
+                     const SkMatrix& viewMatrix,
+                     const SkPath&);
+
+    // Same as for stencilPath, but for an NVPR path object.
     void stencilPath(const GrHardClip*,
                      GrAA doStencilMSAA,
                      const SkMatrix& viewMatrix,
diff --git a/src/gpu/GrTriangulator.cpp b/src/gpu/GrTriangulator.cpp
index 71f2349..2931f9a 100644
--- a/src/gpu/GrTriangulator.cpp
+++ b/src/gpu/GrTriangulator.cpp
@@ -1477,39 +1477,3 @@
     vertexAllocator->unlock(actualCount);
     return actualCount;
 }
-
-int GrTriangulator::PathToVertices(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
-                                   WindingVertex** verts) {
-    SkArenaAlloc alloc(kArenaDefaultChunkSize);
-    GrTriangulator triangulator(path, &alloc);
-    bool isLinear;
-    Poly* polys = triangulator.pathToPolys(tolerance, clipBounds, &isLinear);
-    int64_t count64 = CountPoints(polys, path.getFillType());
-    if (0 == count64 || count64 > SK_MaxS32) {
-        *verts = nullptr;
-        return 0;
-    }
-    int count = count64;
-
-    *verts = new WindingVertex[count];
-    WindingVertex* vertsEnd = *verts;
-    SkPoint* points = new SkPoint[count];
-    SkPoint* pointsEnd = points;
-    for (Poly* poly = polys; poly; poly = poly->fNext) {
-        if (apply_fill_type(path.getFillType(), poly)) {
-            SkPoint* start = pointsEnd;
-            pointsEnd = static_cast<SkPoint*>(triangulator.emitPoly(poly, pointsEnd));
-            while (start != pointsEnd) {
-                vertsEnd->fPos = *start;
-                vertsEnd->fWinding = poly->fWinding;
-                ++start;
-                ++vertsEnd;
-            }
-        }
-    }
-    int actualCount = static_cast<int>(vertsEnd - *verts);
-    SkASSERT(actualCount <= count);
-    SkASSERT(pointsEnd - points == actualCount);
-    delete[] points;
-    return actualCount;
-}
diff --git a/src/gpu/GrTriangulator.h b/src/gpu/GrTriangulator.h
index b90bcc0..2b45a18 100644
--- a/src/gpu/GrTriangulator.h
+++ b/src/gpu/GrTriangulator.h
@@ -36,21 +36,6 @@
         return count;
     }
 
-    struct WindingVertex {
-        SkPoint fPos;
-        int fWinding;
-    };
-
-    // *DEPRECATED*: Once CCPR is removed this method will go away.
-    //
-    // Triangulates a path to an array of vertices. Each triangle is represented as a set of three
-    // WindingVertex entries, each of which contains the position and winding count (which is the
-    // same for all three vertices of a triangle). The 'verts' out parameter is set to point to the
-    // resultant vertex array. CALLER IS RESPONSIBLE for deleting this buffer to avoid a memory
-    // leak!
-    static int PathToVertices(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
-                              WindingVertex** verts);
-
     // Enums used by GrTriangulator internals.
     typedef enum { kLeft_Side, kRight_Side } Side;
     enum class EdgeType { kInner, kOuter, kConnector };
diff --git a/src/gpu/ccpr/GrAutoMapVertexBuffer.h b/src/gpu/ccpr/GrAutoMapVertexBuffer.h
deleted file mode 100644
index ff2b831..0000000
--- a/src/gpu/ccpr/GrAutoMapVertexBuffer.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2020 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrAutoMapVertexBuffer_DEFINED
-#define GrAutoMapVertexBuffer_DEFINED
-
-#include "include/private/SkNoncopyable.h"
-#include "src/gpu/GrGpuBuffer.h"
-#include "src/gpu/GrOnFlushResourceProvider.h"
-
-// This class automatically allocates and maps a GPU vertex buffer, and polyfills the mapping
-// functionality with a mirror buffer on CPU if it is not supported.
-class GrAutoMapVertexBuffer : SkNoncopyable {
-public:
-    ~GrAutoMapVertexBuffer() {
-        if (this->isMapped()) {
-            this->unmapBuffer();
-        }
-    }
-
-    bool hasGpuBuffer() const { return SkToBool(fGpuBuffer.get()); }
-    sk_sp<const GrGpuBuffer> gpuBuffer() const { return fGpuBuffer; }
-    bool isMapped() const { return SkToBool(fData); }
-    void* data() const { SkASSERT(this->isMapped()); return fData; }
-
-    void resetAndMapBuffer(GrOnFlushResourceProvider* onFlushRP, size_t sizeInBytes) {
-        if (this->isMapped()) {
-            this->unmapBuffer();
-        }
-        fGpuBuffer = onFlushRP->makeBuffer(GrGpuBufferType::kVertex, sizeInBytes);
-        if (!fGpuBuffer) {
-            fSizeInBytes = 0;
-            fData = nullptr;
-            return;
-        }
-        fSizeInBytes = sizeInBytes;
-        fData = fGpuBuffer->map();
-        if (!fData) {
-            // Mapping failed. Allocate a mirror buffer on CPU.
-            fData = sk_malloc_throw(fSizeInBytes);
-        }
-    }
-
-    void unmapBuffer() {
-        SkASSERT(this->isMapped());
-        if (fGpuBuffer->isMapped()) {
-            fGpuBuffer->unmap();
-        } else {
-            // fData is a mirror buffer on CPU.
-            fGpuBuffer->updateData(fData, fSizeInBytes);
-            sk_free(fData);
-        }
-        fData = nullptr;
-    }
-
-protected:
-    sk_sp<GrGpuBuffer> fGpuBuffer;
-    size_t fSizeInBytes = 0;
-    void* fData = nullptr;
-};
-
-template<typename T> class GrTAutoMapVertexBuffer : public GrAutoMapVertexBuffer {
-public:
-    T& operator[](int idx) {
-        SkASSERT(this->isMapped());
-        SkASSERT(idx >= 0 && (size_t)idx < fSizeInBytes / sizeof(T));
-        return ((T*)fData)[idx];
-    }
-};
-
-#endif
diff --git a/src/gpu/ccpr/GrCCClipPath.cpp b/src/gpu/ccpr/GrCCClipPath.cpp
index eeeae99..510d3da 100644
--- a/src/gpu/ccpr/GrCCClipPath.cpp
+++ b/src/gpu/ccpr/GrCCClipPath.cpp
@@ -47,15 +47,12 @@
     fAccessRect = accessRect;
 }
 
-void GrCCClipPath::accountForOwnPath(GrCCPerFlushResourceSpecs* specs) const {
+void GrCCClipPath::accountForOwnPath(GrCCAtlas::Specs* specs) const {
     SkASSERT(this->isInitialized());
 
-    ++specs->fNumClipPaths;
-    specs->fRenderedPathStats.statPath(fDeviceSpacePath);
-
     SkIRect ibounds;
     if (ibounds.intersect(fAccessRect, fPathDevIBounds)) {
-        specs->fRenderedAtlasSpecs.accountForSpace(ibounds.width(), ibounds.height());
+        specs->accountForSpace(ibounds.width(), ibounds.height());
     }
 }
 
@@ -64,7 +61,7 @@
     SkASSERT(this->isInitialized());
     SkASSERT(!fHasAtlas);
     fAtlas = resources->renderDeviceSpacePathInAtlas(
-            fAccessRect, fDeviceSpacePath, fPathDevIBounds, GrFillRuleForSkPath(fDeviceSpacePath),
-            &fDevToAtlasOffset);
+            onFlushRP, fAccessRect, fDeviceSpacePath, fPathDevIBounds,
+            GrFillRuleForSkPath(fDeviceSpacePath), &fDevToAtlasOffset);
     SkDEBUGCODE(fHasAtlas = true);
 }
diff --git a/src/gpu/ccpr/GrCCClipPath.h b/src/gpu/ccpr/GrCCClipPath.h
index 7128fc7..49e7071 100644
--- a/src/gpu/ccpr/GrCCClipPath.h
+++ b/src/gpu/ccpr/GrCCClipPath.h
@@ -58,7 +58,7 @@
         return fPathDevIBounds;
     }
 
-    void accountForOwnPath(GrCCPerFlushResourceSpecs*) const;
+    void accountForOwnPath(GrCCAtlas::Specs*) const;
     void renderPathInAtlas(GrCCPerFlushResources*, GrOnFlushResourceProvider*);
 
     const SkIVector& atlasTranslate() const {
diff --git a/src/gpu/ccpr/GrCCConicShader.cpp b/src/gpu/ccpr/GrCCConicShader.cpp
deleted file mode 100644
index b22e848..0000000
--- a/src/gpu/ccpr/GrCCConicShader.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/ccpr/GrCCConicShader.h"
-
-#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
-#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
-
-void GrCCConicShader::emitSetupCode(
-        GrGLSLVertexGeoBuilder* s, const char* pts, const char** outHull4) const {
-    // K is distance from the line P2 -> P0. L is distance from the line P0 -> P1, scaled by 2w.
-    // M is distance from the line P1 -> P2, scaled by 2w. We do this in a space where P1=0.
-    s->declareGlobal(fKLMMatrix);
-    s->codeAppendf("float x0 = %s[0].x - %s[1].x, x2 = %s[2].x - %s[1].x;", pts, pts, pts, pts);
-    s->codeAppendf("float y0 = %s[0].y - %s[1].y, y2 = %s[2].y - %s[1].y;", pts, pts, pts, pts);
-    s->codeAppendf("float w = %s[3].x;", pts);
-    s->codeAppendf("%s = float3x3(y2 - y0, x0 - x2, x2*y0 - x0*y2, "
-                                 "2*w * float2(+y0, -x0), 0, "
-                                 "2*w * float2(-y2, +x2), 0);", fKLMMatrix.c_str());
-
-    s->declareGlobal(fControlPoint);
-    s->codeAppendf("%s = %s[1];", fControlPoint.c_str(), pts);
-
-    // Scale KLM by the inverse Manhattan width of K, and make sure K is positive. This allows K to
-    // double as the flat opposite edge AA. kwidth will not be 0 because we cull degenerate conics
-    // on the CPU.
-    s->codeAppendf("float kwidth = 2*bloat * (abs(%s[0].x) + abs(%s[0].y)) * sign(%s[0].z);",
-                   fKLMMatrix.c_str(), fKLMMatrix.c_str(), fKLMMatrix.c_str());
-    s->codeAppendf("%s *= 1/kwidth;", fKLMMatrix.c_str());
-
-    if (outHull4) {
-        // Clip the conic triangle by the tangent line at maximum height. Conics have the nice
-        // property that maximum height always occurs at T=.5. This is a simple application for
-        // De Casteljau's algorithm.
-        s->codeAppendf("float2 p1w = %s[1]*w;", pts);
-        s->codeAppend ("float r = 1 / (1 + w);");
-        s->codeAppend ("float2 conic_hull[4];");
-        s->codeAppendf("conic_hull[0] = %s[0];", pts);
-        s->codeAppendf("conic_hull[1] = (%s[0] + p1w) * r;", pts);
-        s->codeAppendf("conic_hull[2] = (p1w + %s[2]) * r;", pts);
-        s->codeAppendf("conic_hull[3] = %s[2];", pts);
-        *outHull4 = "conic_hull";
-    }
-}
-
-void GrCCConicShader::onEmitVaryings(
-        GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, SkString* code,
-        const char* position, const char* coverage, const char* cornerCoverage, const char* wind) {
-    code->appendf("float3 klm = float3(%s - %s, 1) * %s;",
-                  position, fControlPoint.c_str(), fKLMMatrix.c_str());
-    fKLM_fWind.reset(kFloat3_GrSLType, scope);
-    varyingHandler->addVarying("klm", &fKLM_fWind);
-    code->appendf("%s.xyz = klm;", OutName(fKLM_fWind));
-
-    fGrad_fCorner.reset(cornerCoverage ? kFloat4_GrSLType : kFloat2_GrSLType, scope);
-    varyingHandler->addVarying((cornerCoverage) ? "grad_and_corner" : "grad", &fGrad_fCorner);
-    code->appendf("%s.xy = 2*bloat * (float3x2(%s) * float3(2*klm[0], -klm[2], -klm[1]));",
-                  OutName(fGrad_fCorner), fKLMMatrix.c_str());
-
-}
-
-void GrCCConicShader::emitSampleMaskCode(GrGLSLFPFragmentBuilder* f) const {
-    f->codeAppendf("float k = %s.x, l = %s.y, m = %s.z;",
-                   fKLM_fWind.fsIn(), fKLM_fWind.fsIn(), fKLM_fWind.fsIn());
-    f->codeAppendf("float f = k*k - l*m;");
-    f->codeAppendf("float2 grad = %s;", fGrad_fCorner.fsIn());
-    f->applyFnToMultisampleMask("f", "grad", GrGLSLFPFragmentBuilder::ScopeFlags::kTopLevel);
-}
diff --git a/src/gpu/ccpr/GrCCConicShader.h b/src/gpu/ccpr/GrCCConicShader.h
deleted file mode 100644
index 1c07127..0000000
--- a/src/gpu/ccpr/GrCCConicShader.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrCCConicShader_DEFINED
-#define GrCCConicShader_DEFINED
-
-#include "src/gpu/ccpr/GrCCCoverageProcessor.h"
-
-/**
- * This class renders the coverage of closed conic curves using the techniques outlined in
- * "Resolution Independent Curve Rendering using Programmable Graphics Hardware" by Charles Loop and
- * Jim Blinn:
- *
- * https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
- *
- * The provided curves must be monotonic with respect to the vector of their closing edge [P2 - P0].
- * (Use GrCCGeometry::conicTo().)
- */
-class GrCCConicShader : public GrCCCoverageProcessor::Shader {
-public:
-    bool calculatesOwnEdgeCoverage() const override { return true; }
-
-    void emitSetupCode(
-            GrGLSLVertexGeoBuilder*, const char* pts, const char** outHull4) const override;
-
-    void onEmitVaryings(
-            GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, const char* position,
-            const char* coverage, const char* cornerCoverage, const char* wind) override;
-
-    void emitSampleMaskCode(GrGLSLFPFragmentBuilder*) const override;
-
-private:
-    const GrShaderVar fKLMMatrix{"klm_matrix", kFloat3x3_GrSLType};
-    const GrShaderVar fControlPoint{"control_point", kFloat2_GrSLType};
-    GrGLSLVarying fKLM_fWind;
-    GrGLSLVarying fGrad_fCorner;
-};
-
-#endif
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.cpp b/src/gpu/ccpr/GrCCCoverageProcessor.cpp
deleted file mode 100644
index 3ecf5a7..0000000
--- a/src/gpu/ccpr/GrCCCoverageProcessor.cpp
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/ccpr/GrCCCoverageProcessor.h"
-
-#include "src/gpu/GrOpFlushState.h"
-#include "src/gpu/GrOpsRenderPass.h"
-#include "src/gpu/GrProgramInfo.h"
-#include "src/gpu/ccpr/GrCCConicShader.h"
-#include "src/gpu/ccpr/GrCCCubicShader.h"
-#include "src/gpu/ccpr/GrCCQuadraticShader.h"
-#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
-#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
-#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
-
-class GrCCCoverageProcessor::TriangleShader : public GrCCCoverageProcessor::Shader {
-    void onEmitVaryings(
-            GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, SkString* code,
-            const char* position, const char* coverage, const char* cornerCoverage,
-            const char* /*wind*/) override {
-        fCoverages.reset(kHalf_GrSLType, scope);
-        varyingHandler->addVarying("coverage", &fCoverages);
-        code->appendf("%s = %s;", OutName(fCoverages), coverage);
-    }
-
-    void emitSampleMaskCode(GrGLSLFPFragmentBuilder*) const override { return; }
-
-    GrGLSLVarying fCoverages;
-};
-
-void GrCCCoverageProcessor::Shader::CalcWind(const GrCCCoverageProcessor& proc,
-                                             GrGLSLVertexGeoBuilder* s, const char* pts,
-                                             const char* outputWind) {
-    if (3 == proc.numInputPoints()) {
-        s->codeAppendf("float2 a = %s[0] - %s[1], "
-                              "b = %s[0] - %s[2];", pts, pts, pts, pts);
-    } else {
-        // All inputs are convex, so it's sufficient to just average the middle two input points.
-        SkASSERT(4 == proc.numInputPoints());
-        s->codeAppendf("float2 p12 = (%s[1] + %s[2]) * .5;", pts, pts);
-        s->codeAppendf("float2 a = %s[0] - p12, "
-                              "b = %s[0] - %s[3];", pts, pts, pts);
-    }
-
-    s->codeAppend ("float area_x2 = determinant(float2x2(a, b));");
-    if (proc.isTriangles()) {
-        // We cull extremely thin triangles by zeroing wind. When a triangle gets too thin it's
-        // possible for FP round-off error to actually give us the wrong winding direction, causing
-        // rendering artifacts. The criteria we choose is "height <~ 1/1024". So we drop a triangle
-        // if the max effect it can have on any single pixel is <~ 1/1024, or 1/4 of a bit in 8888.
-        s->codeAppend ("float2 bbox_size = max(abs(a), abs(b));");
-        s->codeAppend ("float basewidth = max(bbox_size.x + bbox_size.y, 1);");
-        s->codeAppendf("%s = (abs(area_x2 * 1024) > basewidth) ? sign(half(area_x2)) : 0;",
-                       outputWind);
-    } else {
-        // We already converted nearly-flat curves to lines on the CPU, so no need to worry about
-        // thin curve hulls at this point.
-        s->codeAppendf("%s = sign(half(area_x2));", outputWind);
-    }
-}
-
-void GrCCCoverageProcessor::Shader::CalcEdgeCoverageAtBloatVertex(GrGLSLVertexGeoBuilder* s,
-                                                                  const char* leftPt,
-                                                                  const char* rightPt,
-                                                                  const char* rasterVertexDir,
-                                                                  const char* outputCoverage) {
-    // Here we find an edge's coverage at one corner of a conservative raster bloat box whose center
-    // falls on the edge in question. (A bloat box is axis-aligned and the size of one pixel.) We
-    // always set up coverage so it is -1 at the outermost corner, 0 at the innermost, and -.5 at
-    // the center. Interpolated, these coverage values convert jagged conservative raster edges into
-    // smooth antialiased edges.
-    //
-    // d1 == (P + sign(n) * bloat) dot n                   (Distance at the bloat box vertex whose
-    //    == P dot n + (abs(n.x) + abs(n.y)) * bloatSize    coverage=-1, where the bloat box is
-    //                                                      centered on P.)
-    //
-    // d0 == (P - sign(n) * bloat) dot n                   (Distance at the bloat box vertex whose
-    //    == P dot n - (abs(n.x) + abs(n.y)) * bloatSize    coverage=0, where the bloat box is
-    //                                                      centered on P.)
-    //
-    // d == (P + rasterVertexDir * bloatSize) dot n        (Distance at the bloat box vertex whose
-    //   == P dot n + (rasterVertexDir dot n) * bloatSize   coverage we wish to calculate.)
-    //
-    // coverage == -(d - d0) / (d1 - d0)                   (coverage=-1 at d=d1; coverage=0 at d=d0)
-    //
-    //          == (rasterVertexDir dot n) / (abs(n.x) + abs(n.y)) * -.5 - .5
-    //
-    s->codeAppendf("float2 n = float2(%s.y - %s.y, %s.x - %s.x);",
-                   rightPt, leftPt, leftPt, rightPt);
-    s->codeAppend ("float nwidth = abs(n.x) + abs(n.y);");
-    s->codeAppendf("float t = dot(%s, n);", rasterVertexDir);
-    // The below conditional guarantees we get exactly 1 on the divide when nwidth=t (in case the
-    // GPU divides by multiplying by the reciprocal?) It also guards against NaN when nwidth=0.
-    s->codeAppendf("%s = half(abs(t) != nwidth ? t / nwidth : sign(t)) * -.5 - .5;",
-                   outputCoverage);
-}
-
-void GrCCCoverageProcessor::Shader::CalcEdgeCoveragesAtBloatVertices(GrGLSLVertexGeoBuilder* s,
-                                                                     const char* leftPt,
-                                                                     const char* rightPt,
-                                                                     const char* bloatDir1,
-                                                                     const char* bloatDir2,
-                                                                     const char* outputCoverages) {
-    // See comments in CalcEdgeCoverageAtBloatVertex.
-    s->codeAppendf("float2 n = float2(%s.y - %s.y, %s.x - %s.x);",
-                   rightPt, leftPt, leftPt, rightPt);
-    s->codeAppend ("float nwidth = abs(n.x) + abs(n.y);");
-    s->codeAppendf("float2 t = n * float2x2(%s, %s);", bloatDir1, bloatDir2);
-    s->codeAppendf("for (int i = 0; i < 2; ++i) {");
-    s->codeAppendf(    "%s[i] = half(abs(t[i]) != nwidth ? t[i] / nwidth : sign(t[i])) * -.5 - .5;",
-                       outputCoverages);
-    s->codeAppendf("}");
-}
-
-void GrCCCoverageProcessor::Shader::CalcCornerAttenuation(GrGLSLVertexGeoBuilder* s,
-                                                          const char* leftDir, const char* rightDir,
-                                                          const char* outputAttenuation) {
-    // obtuseness = cos(corner_angle)  if corner_angle > 90 degrees
-    //                              0  if corner_angle <= 90 degrees
-    //
-    // NOTE: leftDir and rightDir are normalized and point in the same direction the path was
-    // defined with, i.e., leftDir points into the corner and rightDir points away from the corner.
-    s->codeAppendf("half obtuseness = max(half(dot(%s, %s)), 0);", leftDir, rightDir);
-
-    // axis_alignedness = 1 - tan(angle_to_nearest_axis_from_corner_bisector)
-    //                    (i.e.,  1  when the corner bisector is aligned with the x- or y-axis
-    //                            0  when the corner bisector falls on a 45 degree angle
-    //                         0..1  when the corner bisector falls somewhere in between
-    s->codeAppendf("half2 abs_bisect_maybe_transpose = abs((0 == obtuseness) ? half2(%s - %s) : "
-                                                                              "half2(%s + %s));",
-                   leftDir, rightDir, leftDir, rightDir);
-    s->codeAppend ("half axis_alignedness = "
-                           "1 - min(abs_bisect_maybe_transpose.y, abs_bisect_maybe_transpose.x) / "
-                               "max(abs_bisect_maybe_transpose.x, abs_bisect_maybe_transpose.y);");
-
-    // ninety_degreesness = sin^2(corner_angle)
-    // sin^2 just because... it's always positive and the results looked better than plain sine... ?
-    s->codeAppendf("half ninety_degreesness = determinant(half2x2(%s, %s));", leftDir, rightDir);
-    s->codeAppend ("ninety_degreesness = ninety_degreesness * ninety_degreesness;");
-
-    // The below formula is not smart. It was just arrived at by considering the following
-    // observations:
-    //
-    // 1. 90-degree, axis-aligned corners have full attenuation along the bisector.
-    //    (i.e. coverage = 1 - distance_to_corner^2)
-    //    (i.e. outputAttenuation = 0)
-    //
-    // 2. 180-degree corners always have zero attenuation.
-    //    (i.e. coverage = 1 - distance_to_corner)
-    //    (i.e. outputAttenuation = 1)
-    //
-    // 3. 90-degree corners whose bisector falls on a 45 degree angle also do not attenuate.
-    //    (i.e. outputAttenuation = 1)
-    s->codeAppendf("%s = max(obtuseness, axis_alignedness * ninety_degreesness);",
-                   outputAttenuation);
-}
-
-GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const {
-    std::unique_ptr<Shader> shader;
-    switch (fPrimitiveType) {
-        case PrimitiveType::kTriangles:
-        case PrimitiveType::kWeightedTriangles:
-            shader = std::make_unique<TriangleShader>();
-            break;
-        case PrimitiveType::kQuadratics:
-            shader = std::make_unique<GrCCQuadraticShader>();
-            break;
-        case PrimitiveType::kCubics:
-            shader = std::make_unique<GrCCCubicShader>();
-            break;
-        case PrimitiveType::kConics:
-            shader = std::make_unique<GrCCConicShader>();
-            break;
-    }
-    return this->onCreateGLSLInstance(std::move(shader));
-}
-
-void GrCCCoverageProcessor::bindPipeline(GrOpFlushState* flushState, const GrPipeline& pipeline,
-                                         const SkRect& drawBounds,
-                                         const GrUserStencilSettings* stencil) const {
-    GrProgramInfo programInfo(flushState->writeView(), &pipeline, stencil, this,
-                              this->primType(), 0, flushState->renderPassBarriers(),
-                              flushState->colorLoadOp());
-    flushState->bindPipeline(programInfo, drawBounds);
-}
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.h b/src/gpu/ccpr/GrCCCoverageProcessor.h
deleted file mode 100644
index ed32261..0000000
--- a/src/gpu/ccpr/GrCCCoverageProcessor.h
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrCCCoverageProcessor_DEFINED
-#define GrCCCoverageProcessor_DEFINED
-
-#include "include/private/SkNx.h"
-#include "src/gpu/GrCaps.h"
-#include "src/gpu/GrGeometryProcessor.h"
-#include "src/gpu/GrPipeline.h"
-#include "src/gpu/GrShaderCaps.h"
-#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
-#include "src/gpu/glsl/GrGLSLShaderBuilder.h"
-#include "src/gpu/glsl/GrGLSLVarying.h"
-
-class GrGLSLFPFragmentBuilder;
-class GrGLSLVertexGeoBuilder;
-class GrOpFlushState;
-
-/**
- * This is the geometry processor for the simple convex primitive shapes (triangles and closed,
- * convex bezier curves) from which ccpr paths are composed. The output is a single-channel alpha
- * value, positive for clockwise shapes and negative for counter-clockwise, that indicates coverage.
- *
- * The caller is responsible to draw all primitives as produced by GrCCGeometry into a cleared,
- * floating point, alpha-only render target using SkBlendMode::kPlus. Once all of a path's
- * primitives have been drawn, the render target contains a composite coverage count that can then
- * be used to draw the path (see GrCCPathProcessor).
- *
- * To draw primitives, use appendMesh() and draw() (defined below).
- */
-class GrCCCoverageProcessor : public GrGeometryProcessor {
-public:
-    enum class PrimitiveType {
-        kTriangles,
-        kWeightedTriangles,  // Triangles (from the tessellator) whose winding magnitude > 1.
-        kQuadratics,
-        kCubics,
-        kConics
-    };
-    static const char* PrimitiveTypeName(PrimitiveType);
-
-    // Defines a single primitive shape with 3 input points (i.e. Triangles and Quadratics).
-    // X,Y point values are transposed.
-    struct TriPointInstance {
-        float fValues[6];
-
-        enum class Ordering : bool {
-            kXYTransposed,
-            kXYInterleaved,
-        };
-
-        void set(const SkPoint[3], const Sk2f& translate, Ordering);
-        void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& translate, Ordering);
-        void set(const Sk2f& P0, const Sk2f& P1, const Sk2f& P2, const Sk2f& translate, Ordering);
-    };
-
-    // Defines a single primitive shape with 4 input points, or 3 input points plus a "weight"
-    // parameter duplicated in both lanes of the 4th input (i.e. Cubics, Conics, and Triangles with
-    // a weighted winding number). X,Y point values are transposed.
-    struct QuadPointInstance {
-        float fX[4];
-        float fY[4];
-
-        void set(const SkPoint[4], float dx, float dy);
-        void setW(const SkPoint[3], const Sk2f& trans, float w);
-        void setW(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans, float w);
-        void setW(const Sk2f& P0, const Sk2f& P1, const Sk2f& P2, const Sk2f& trans, float w);
-    };
-
-    PrimitiveType primitiveType() const { return fPrimitiveType; }
-
-    // Number of bezier points for curves, or 3 for triangles.
-    int numInputPoints() const { return PrimitiveType::kCubics == fPrimitiveType ? 4 : 3; }
-
-    bool isTriangles() const {
-        return PrimitiveType::kTriangles == fPrimitiveType ||
-               PrimitiveType::kWeightedTriangles == fPrimitiveType;
-    }
-
-    int hasInputWeight() const {
-        return PrimitiveType::kWeightedTriangles == fPrimitiveType ||
-               PrimitiveType::kConics == fPrimitiveType;
-    }
-
-    // GrPrimitiveProcessor overrides.
-    const char* name() const override { return PrimitiveTypeName(fPrimitiveType); }
-    void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
-        SkDEBUGCODE(this->getDebugBloatKey(b));
-        b->add32((int)fPrimitiveType);
-    }
-    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
-
-#ifdef SK_DEBUG
-    // Increases the 1/2 pixel AA bloat by a factor of debugBloat.
-    void enableDebugBloat(float debugBloat) { fDebugBloat = debugBloat; }
-    bool debugBloatEnabled() const { return fDebugBloat > 0; }
-    float debugBloat() const { SkASSERT(this->debugBloatEnabled()); return fDebugBloat; }
-    void getDebugBloatKey(GrProcessorKeyBuilder* b) const {
-        uint32_t bloatBits;
-        memcpy(&bloatBits, &fDebugBloat, 4);
-        b->add32(bloatBits);
-    }
-#endif
-
-    // The caller uses these methods to actualy draw the coverage PrimitiveTypes. For each
-    // subpassIdx of each PrimitiveType, it calls reset/bind*/drawInstances.
-    virtual int numSubpasses() const = 0;
-    virtual void reset(PrimitiveType, int subpassIdx, GrResourceProvider*) = 0;
-    void bindPipeline(GrOpFlushState*, const GrPipeline&, const SkRect& drawBounds,
-                      const GrUserStencilSettings* = &GrUserStencilSettings::kUnused) const;
-    virtual void bindBuffers(GrOpsRenderPass*, sk_sp<const GrBuffer> instanceBuffer) const = 0;
-    virtual void drawInstances(GrOpsRenderPass*, int instanceCount, int baseInstance) const = 0;
-
-    // The Shader provides code to calculate each pixel's coverage in a RenderPass. It also
-    // provides details about shape-specific geometry.
-    class Shader {
-    public:
-        // Returns true if the Impl should not calculate the coverage argument for emitVaryings().
-        // If true, then "coverage" will have a signed magnitude of 1.
-        virtual bool calculatesOwnEdgeCoverage() const { return false; }
-
-        // Called before generating geometry. Subclasses may set up internal member variables during
-        // this time that will be needed during onEmitVaryings (e.g. transformation matrices).
-        //
-        // If the 'outHull4' parameter is provided, and there are not 4 input points, the subclass
-        // is required to fill it with the name of a 4-point hull around which the Impl can generate
-        // its geometry. If it is left unchanged, the Impl will use the regular input points.
-        virtual void emitSetupCode(
-                GrGLSLVertexGeoBuilder*, const char* pts, const char** outHull4 = nullptr) const {
-            SkASSERT(!outHull4);
-        }
-
-        void emitVaryings(
-                GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, SkString* code,
-                const char* position, const char* coverage, const char* cornerCoverage,
-                const char* wind) {
-            SkASSERT(GrGLSLVarying::Scope::kVertToGeo != scope);
-            this->onEmitVaryings(
-                    varyingHandler, scope, code, position, coverage, cornerCoverage, wind);
-        }
-
-        // Assigns the built-in sample mask at the current pixel.
-        virtual void emitSampleMaskCode(GrGLSLFPFragmentBuilder*) const = 0;
-
-        // Calculates the winding direction of the input points (+1, -1, or 0). Wind for extremely
-        // thin triangles gets rounded to zero.
-        static void CalcWind(const GrCCCoverageProcessor&, GrGLSLVertexGeoBuilder*, const char* pts,
-                             const char* outputWind);
-
-        // Calculates an edge's coverage at a conservative raster vertex. The edge is defined by two
-        // clockwise-ordered points, 'leftPt' and 'rightPt'. 'rasterVertexDir' is a pair of +/-1
-        // values that point in the direction of conservative raster bloat, starting from an
-        // endpoint.
-        //
-        // Coverage values ramp from -1 (completely outside the edge) to 0 (completely inside).
-        static void CalcEdgeCoverageAtBloatVertex(GrGLSLVertexGeoBuilder*, const char* leftPt,
-                                                  const char* rightPt, const char* rasterVertexDir,
-                                                  const char* outputCoverage);
-
-        // Calculates an edge's coverage at two conservative raster vertices.
-        // (See CalcEdgeCoverageAtBloatVertex).
-        static void CalcEdgeCoveragesAtBloatVertices(GrGLSLVertexGeoBuilder*, const char* leftPt,
-                                                     const char* rightPt, const char* bloatDir1,
-                                                     const char* bloatDir2,
-                                                     const char* outputCoverages);
-
-        // Corner boxes require an additional "attenuation" varying that is multiplied by the
-        // regular (linearly-interpolated) coverage. This function calculates the attenuation value
-        // to use in the single, outermost vertex. The remaining three vertices of the corner box
-        // all use an attenuation value of 1.
-        static void CalcCornerAttenuation(GrGLSLVertexGeoBuilder*, const char* leftDir,
-                                          const char* rightDir, const char* outputAttenuation);
-
-        virtual ~Shader() {}
-
-    protected:
-        // Here the subclass adds its internal varyings to the handler and produces code to
-        // initialize those varyings from a given position and coverage values.
-        //
-        // NOTE: the coverage values are signed appropriately for wind.
-        //       'coverage' will only be +1 or -1 on curves.
-        virtual void onEmitVaryings(
-                GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, const char* position,
-                const char* coverage, const char* cornerCoverage, const char* wind) = 0;
-
-        // Returns the name of a Shader's internal varying at the point where where its value is
-        // assigned. This is intended to work whether called for a vertex or a geometry shader.
-        const char* OutName(const GrGLSLVarying& varying) const {
-            using Scope = GrGLSLVarying::Scope;
-            SkASSERT(Scope::kVertToGeo != varying.scope());
-            return Scope::kGeoToFrag == varying.scope() ? varying.gsOut() : varying.vsOut();
-        }
-
-        // Our friendship with GrGLSLShaderBuilder does not propagate to subclasses.
-        inline static SkString& AccessCodeString(GrGLSLShaderBuilder* s) { return s->code(); }
-    };
-
-protected:
-    // Slightly undershoot a bloat radius of 0.5 so vertices that fall on integer boundaries don't
-    // accidentally bleed into neighbor pixels.
-    static constexpr float kAABloatRadius = 0.491111f;
-
-    GrCCCoverageProcessor(ClassID classID) : INHERITED(classID) {}
-
-    virtual GrPrimitiveType primType() const = 0;
-
-    virtual GrGLSLPrimitiveProcessor* onCreateGLSLInstance(std::unique_ptr<Shader>) const = 0;
-
-    // Our friendship with GrGLSLShaderBuilder does not propagate to subclasses.
-    inline static SkString& AccessCodeString(GrGLSLShaderBuilder* s) { return s->code(); }
-
-    PrimitiveType fPrimitiveType;
-    SkDEBUGCODE(float fDebugBloat = 0);
-
-    class TriangleShader;
-
-    using INHERITED = GrGeometryProcessor;
-};
-
-inline const char* GrCCCoverageProcessor::PrimitiveTypeName(PrimitiveType type) {
-    switch (type) {
-        case PrimitiveType::kTriangles: return "kTriangles";
-        case PrimitiveType::kWeightedTriangles: return "kWeightedTriangles";
-        case PrimitiveType::kQuadratics: return "kQuadratics";
-        case PrimitiveType::kCubics: return "kCubics";
-        case PrimitiveType::kConics: return "kConics";
-    }
-    SK_ABORT("Invalid PrimitiveType");
-}
-
-inline void GrCCCoverageProcessor::TriPointInstance::set(
-        const SkPoint p[3], const Sk2f& translate, Ordering ordering) {
-    this->set(p[0], p[1], p[2], translate, ordering);
-}
-
-inline void GrCCCoverageProcessor::TriPointInstance::set(
-        const SkPoint& p0, const SkPoint& p1, const SkPoint& p2, const Sk2f& translate,
-        Ordering ordering) {
-    Sk2f P0 = Sk2f::Load(&p0);
-    Sk2f P1 = Sk2f::Load(&p1);
-    Sk2f P2 = Sk2f::Load(&p2);
-    this->set(P0, P1, P2, translate, ordering);
-}
-
-inline void GrCCCoverageProcessor::TriPointInstance::set(
-        const Sk2f& P0, const Sk2f& P1, const Sk2f& P2, const Sk2f& translate, Ordering ordering) {
-    if (Ordering::kXYTransposed == ordering) {
-        Sk2f::Store3(fValues, P0 + translate, P1 + translate, P2 + translate);
-    } else {
-        (P0 + translate).store(fValues);
-        (P1 + translate).store(fValues + 2);
-        (P2 + translate).store(fValues + 4);
-    }
-}
-
-inline void GrCCCoverageProcessor::QuadPointInstance::set(const SkPoint p[4], float dx, float dy) {
-    Sk4f X,Y;
-    Sk4f::Load2(p, &X, &Y);
-    (X + dx).store(&fX);
-    (Y + dy).store(&fY);
-}
-
-inline void GrCCCoverageProcessor::QuadPointInstance::setW(const SkPoint p[3], const Sk2f& trans,
-                                                           float w) {
-    this->setW(p[0], p[1], p[2], trans, w);
-}
-
-inline void GrCCCoverageProcessor::QuadPointInstance::setW(const SkPoint& p0, const SkPoint& p1,
-                                                           const SkPoint& p2, const Sk2f& trans,
-                                                           float w) {
-    Sk2f P0 = Sk2f::Load(&p0);
-    Sk2f P1 = Sk2f::Load(&p1);
-    Sk2f P2 = Sk2f::Load(&p2);
-    this->setW(P0, P1, P2, trans, w);
-}
-
-inline void GrCCCoverageProcessor::QuadPointInstance::setW(const Sk2f& P0, const Sk2f& P1,
-                                                           const Sk2f& P2, const Sk2f& trans,
-                                                           float w) {
-    Sk2f W = Sk2f(w);
-    Sk2f::Store4(this, P0 + trans, P1 + trans, P2 + trans, W);
-}
-
-#endif
diff --git a/src/gpu/ccpr/GrCCCubicShader.cpp b/src/gpu/ccpr/GrCCCubicShader.cpp
deleted file mode 100644
index 67201ed..0000000
--- a/src/gpu/ccpr/GrCCCubicShader.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/ccpr/GrCCCubicShader.h"
-
-#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
-#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
-#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
-
-using Shader = GrCCCoverageProcessor::Shader;
-
-void GrCCCubicShader::emitSetupCode(
-        GrGLSLVertexGeoBuilder* s, const char* pts, const char** /*outHull4*/) const {
-    // Find the cubic's power basis coefficients.
-    s->codeAppendf("float2x4 C = float4x4(-1,  3, -3,  1, "
-                                         " 3, -6,  3,  0, "
-                                         "-3,  3,  0,  0, "
-                                         " 1,  0,  0,  0) * transpose(%s);", pts);
-
-    // Find the cubic's inflection function.
-    s->codeAppend ("float D3 = +determinant(float2x2(C[0].yz, C[1].yz));");
-    s->codeAppend ("float D2 = -determinant(float2x2(C[0].xz, C[1].xz));");
-    s->codeAppend ("float D1 = +determinant(float2x2(C));");
-
-    // Shift the exponents in D so the largest magnitude falls somewhere in 1..2. This protects us
-    // from overflow while solving for roots and KLM functionals.
-    s->codeAppend ("float Dmax = max(max(abs(D1), abs(D2)), abs(D3));");
-    s->codeAppend ("float norm;");
-    if (s->getProgramBuilder()->shaderCaps()->fpManipulationSupport()) {
-        s->codeAppend ("int exp;");
-        s->codeAppend ("frexp(Dmax, exp);");
-        s->codeAppend ("norm = ldexp(1, 1 - exp);");
-    } else {
-        s->codeAppend ("norm = 1/Dmax;"); // Dmax will not be 0 because we cull line cubics on CPU.
-    }
-    s->codeAppend ("D3 *= norm;");
-    s->codeAppend ("D2 *= norm;");
-    s->codeAppend ("D1 *= norm;");
-
-    // Calculate the KLM matrix.
-    s->declareGlobal(fKLMMatrix);
-    s->codeAppend ("float discr = 3*D2*D2 - 4*D1*D3;");
-    s->codeAppend ("float x = discr >= 0 ? 3 : 1;");
-    s->codeAppend ("float q = sqrt(x * abs(discr));");
-    s->codeAppend ("q = x*D2 + (D2 >= 0 ? q : -q);");
-
-    s->codeAppend ("float2 l, m;");
-    s->codeAppend ("l.ts = float2(q, 2*x * D1);");
-    s->codeAppend ("m.ts = float2(2, q) * (discr >= 0 ? float2(D3, 1) "
-                                                     ": float2(D2*D2 - D3*D1, D1));");
-
-    s->codeAppend ("float4 K;");
-    s->codeAppend ("float4 lm = l.sstt * m.stst;");
-    s->codeAppend ("K = float4(0, lm.x, -lm.y - lm.z, lm.w);");
-
-    s->codeAppend ("float4 L, M;");
-    s->codeAppend ("lm.yz += 2*lm.zy;");
-    s->codeAppend ("L = float4(-1,x,-x,1) * l.sstt * (discr >= 0 ? l.ssst * l.sttt : lm);");
-    s->codeAppend ("M = float4(-1,x,-x,1) * m.sstt * (discr >= 0 ? m.ssst * m.sttt : lm.xzyw);");
-
-    s->codeAppend ("int middlerow = abs(D2) > abs(D1) ? 2 : 1;");
-    s->codeAppend ("float3x3 CI = inverse(float3x3(C[0][0], C[0][middlerow], C[0][3], "
-                                                  "C[1][0], C[1][middlerow], C[1][3], "
-                                                  "      0,               0,       1));");
-    s->codeAppendf("%s = CI * float3x3(K[0], K[middlerow], K[3], "
-                                      "L[0], L[middlerow], L[3], "
-                                      "M[0], M[middlerow], M[3]);", fKLMMatrix.c_str());
-
-    // Evaluate the cubic at T=.5 for a mid-ish point.
-    s->codeAppendf("float2 midpoint = %s * float4(.125, .375, .375, .125);", pts);
-
-    // Orient the KLM matrix so L & M are both positive on the side of the curve we wish to fill.
-    s->codeAppendf("float2 orientation = sign(float3(midpoint, 1) * float2x3(%s[1], %s[2]));",
-                   fKLMMatrix.c_str(), fKLMMatrix.c_str());
-    s->codeAppendf("%s *= float3x3(orientation[0] * orientation[1], 0, 0, "
-                                  "0, orientation[0], 0, "
-                                  "0, 0, orientation[1]);", fKLMMatrix.c_str());
-}
-
-void GrCCCubicShader::onEmitVaryings(
-        GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, SkString* code,
-        const char* position, const char* coverage, const char* cornerCoverage, const char* wind) {
-    code->appendf("float3 klm = float3(%s, 1) * %s;", position, fKLMMatrix.c_str());
-    fKLM_fEdge.reset(kFloat3_GrSLType, scope);
-    varyingHandler->addVarying("klm", &fKLM_fEdge);
-    code->appendf("%s = klm;", OutName(fKLM_fEdge));
-
-    fGradMatrix.reset(kFloat4_GrSLType, scope);
-    varyingHandler->addVarying("grad_matrix", &fGradMatrix);
-    code->appendf("%s.xy = 2*bloat * 3 * klm[0] * %s[0].xy;",
-                  OutName(fGradMatrix), fKLMMatrix.c_str());
-    code->appendf("%s.zw = -2*bloat * (klm[1] * %s[2].xy + klm[2] * %s[1].xy);",
-                    OutName(fGradMatrix), fKLMMatrix.c_str(), fKLMMatrix.c_str());
-}
-
-void GrCCCubicShader::emitSampleMaskCode(GrGLSLFPFragmentBuilder* f) const {
-    f->codeAppendf("float k = %s.x, l = %s.y, m = %s.z;",
-                   fKLM_fEdge.fsIn(), fKLM_fEdge.fsIn(), fKLM_fEdge.fsIn());
-    f->codeAppendf("float f = k*k*k - l*m;");
-    f->codeAppendf("float2x2 grad_matrix = float2x2(%s);", fGradMatrix.fsIn());
-    f->codeAppendf("float2 grad = grad_matrix * float2(k, 1);");
-    f->applyFnToMultisampleMask("f", "grad", GrGLSLFPFragmentBuilder::ScopeFlags::kTopLevel);
-}
diff --git a/src/gpu/ccpr/GrCCCubicShader.h b/src/gpu/ccpr/GrCCCubicShader.h
deleted file mode 100644
index 76c14de..0000000
--- a/src/gpu/ccpr/GrCCCubicShader.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrCCCubicShader_DEFINED
-#define GrCCCubicShader_DEFINED
-
-#include "src/gpu/ccpr/GrCCCoverageProcessor.h"
-
-/**
- * This class renders the coverage of convex closed cubic segments using the techniques outlined in
- * "Resolution Independent Curve Rendering using Programmable Graphics Hardware" by Charles Loop and
- * Jim Blinn:
- *
- * https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
- *
- * The provided curve segments must be convex, monotonic with respect to the vector of their closing
- * edge [P3 - P0], and must not contain or be near any inflection points or loop intersections.
- * (Use GrCCGeometry::cubicTo().)
- */
-class GrCCCubicShader : public GrCCCoverageProcessor::Shader {
-public:
-    void emitSetupCode(
-            GrGLSLVertexGeoBuilder*, const char* pts, const char** outHull4) const override;
-
-    void onEmitVaryings(
-            GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, const char* position,
-            const char* coverage, const char* cornerCoverage, const char* wind) override;
-
-    void emitSampleMaskCode(GrGLSLFPFragmentBuilder*) const override;
-
-private:
-    const GrShaderVar fKLMMatrix{"klm_matrix", kFloat3x3_GrSLType};
-    GrGLSLVarying fKLM_fEdge;
-    GrGLSLVarying fGradMatrix;
-    GrGLSLVarying fCornerCoverage;
-};
-
-#endif
diff --git a/src/gpu/ccpr/GrCCFillGeometry.cpp b/src/gpu/ccpr/GrCCFillGeometry.cpp
deleted file mode 100644
index 68ad448..0000000
--- a/src/gpu/ccpr/GrCCFillGeometry.cpp
+++ /dev/null
@@ -1,802 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/ccpr/GrCCFillGeometry.h"
-
-#include "include/gpu/GrTypes.h"
-#include "src/core/SkGeometry.h"
-#include <algorithm>
-#include <cmath>
-#include <cstdlib>
-
-static constexpr float kFlatnessThreshold = 1/16.f; // 1/16 of a pixel.
-
-void GrCCFillGeometry::beginPath() {
-    SkASSERT(!fBuildingContour);
-    fVerbs.push_back(Verb::kBeginPath);
-}
-
-void GrCCFillGeometry::beginContour(const SkPoint& pt) {
-    SkASSERT(!fBuildingContour);
-    // Store the current verb count in the fTriangles field for now. When we close the contour we
-    // will use this value to calculate the actual number of triangles in its fan.
-    fCurrContourTallies = {fVerbs.count(), 0, 0, 0, 0};
-
-    fPoints.push_back(pt);
-    fVerbs.push_back(Verb::kBeginContour);
-    fCurrAnchorPoint = pt;
-
-    SkDEBUGCODE(fBuildingContour = true);
-}
-
-void GrCCFillGeometry::lineTo(const SkPoint P[2]) {
-    SkASSERT(fBuildingContour);
-    SkASSERT(P[0] == fPoints.back());
-    Sk2f p0 = Sk2f::Load(P);
-    Sk2f p1 = Sk2f::Load(P+1);
-    this->appendLine(p0, p1);
-}
-
-inline void GrCCFillGeometry::appendLine(const Sk2f& p0, const Sk2f& p1) {
-    SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1]));
-    if ((p0 == p1).allTrue()) {
-        return;
-    }
-    p1.store(&fPoints.push_back());
-    fVerbs.push_back(Verb::kLineTo);
-}
-
-static inline Sk2f normalize(const Sk2f& n) {
-    Sk2f nn = n*n;
-    return n * (nn + SkNx_shuffle<1,0>(nn)).rsqrt();
-}
-
-static inline float dot(const Sk2f& a, const Sk2f& b) {
-    float product[2];
-    (a * b).store(product);
-    return product[0] + product[1];
-}
-
-static inline bool are_collinear(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
-                                 float tolerance = kFlatnessThreshold) {
-    Sk2f l = p2 - p0; // Line from p0 -> p2.
-
-    // lwidth = Manhattan width of l.
-    Sk2f labs = l.abs();
-    float lwidth = labs[0] + labs[1];
-
-    // d = |p1 - p0| dot | l.y|
-    //                   |-l.x| = distance from p1 to l.
-    Sk2f dd = (p1 - p0) * SkNx_shuffle<1,0>(l);
-    float d = dd[0] - dd[1];
-
-    // We are collinear if a box with radius "tolerance", centered on p1, touches the line l.
-    // To decide this, we check if the distance from p1 to the line is less than the distance from
-    // p1 to the far corner of this imaginary box, along that same normal vector.
-    // The far corner of the box can be found at "p1 + sign(n) * tolerance", where n is normal to l:
-    //
-    //   abs(dot(p1 - p0, n)) <= dot(sign(n) * tolerance, n)
-    //
-    // Which reduces to:
-    //
-    //   abs(d) <= (n.x * sign(n.x) + n.y * sign(n.y)) * tolerance
-    //   abs(d) <= (abs(n.x) + abs(n.y)) * tolerance
-    //
-    // Use "<=" in case l == 0.
-    return std::abs(d) <= lwidth * tolerance;
-}
-
-static inline bool are_collinear(const SkPoint P[4], float tolerance = kFlatnessThreshold) {
-    Sk4f Px, Py;               // |Px  Py|   |p0 - p3|
-    Sk4f::Load2(P, &Px, &Py);  // |.   . | = |p1 - p3|
-    Px -= Px[3];               // |.   . |   |p2 - p3|
-    Py -= Py[3];               // |.   . |   |   0   |
-
-    // Find [lx, ly] = the line from p3 to the furthest-away point from p3.
-    Sk4f Pwidth = Px.abs() + Py.abs(); // Pwidth = Manhattan width of each point.
-    int lidx = Pwidth[0] > Pwidth[1] ? 0 : 1;
-    lidx = Pwidth[lidx] > Pwidth[2] ? lidx : 2;
-    float lx = Px[lidx], ly = Py[lidx];
-    float lwidth = Pwidth[lidx]; // lwidth = Manhattan width of [lx, ly].
-
-    //     |Px  Py|
-    // d = |.   . | * | ly| = distances from each point to l (two of the distances will be zero).
-    //     |.   . |   |-lx|
-    //     |.   . |
-    Sk4f d = Px*ly - Py*lx;
-
-    // We are collinear if boxes with radius "tolerance", centered on all 4 points all touch line l.
-    // (See the rationale for this formula in the above, 3-point version of this function.)
-    // Use "<=" in case l == 0.
-    return (d.abs() <= lwidth * tolerance).allTrue();
-}
-
-// Returns whether the (convex) curve segment is monotonic with respect to [endPt - startPt].
-static inline bool is_convex_curve_monotonic(const Sk2f& startPt, const Sk2f& tan0,
-                                             const Sk2f& endPt, const Sk2f& tan1) {
-    Sk2f v = endPt - startPt;
-    float dot0 = dot(tan0, v);
-    float dot1 = dot(tan1, v);
-
-    // A small, negative tolerance handles floating-point error in the case when one tangent
-    // approaches 0 length, meaning the (convex) curve segment is effectively a flat line.
-    float tolerance = -std::max(std::abs(dot0), std::abs(dot1)) * SK_ScalarNearlyZero;
-    return dot0 >= tolerance && dot1 >= tolerance;
-}
-
-template<int N> static inline SkNx<N,float> lerp(const SkNx<N,float>& a, const SkNx<N,float>& b,
-                                                 const SkNx<N,float>& t) {
-    return SkNx_fma(t, b - a, a);
-}
-
-void GrCCFillGeometry::quadraticTo(const SkPoint P[3]) {
-    SkASSERT(fBuildingContour);
-    SkASSERT(P[0] == fPoints.back());
-    Sk2f p0 = Sk2f::Load(P);
-    Sk2f p1 = Sk2f::Load(P+1);
-    Sk2f p2 = Sk2f::Load(P+2);
-
-    // Don't crunch on the curve if it is nearly flat (or just very small). Flat curves can break
-    // The monotonic chopping math.
-    if (are_collinear(p0, p1, p2)) {
-        this->appendLine(p0, p2);
-        return;
-    }
-
-    this->appendQuadratics(p0, p1, p2);
-}
-
-inline void GrCCFillGeometry::appendQuadratics(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2) {
-    Sk2f tan0 = p1 - p0;
-    Sk2f tan1 = p2 - p1;
-
-    // This should almost always be this case for well-behaved curves in the real world.
-    if (is_convex_curve_monotonic(p0, tan0, p2, tan1)) {
-        this->appendMonotonicQuadratic(p0, p1, p2);
-        return;
-    }
-
-    // Chop the curve into two segments with equal curvature. To do this we find the T value whose
-    // tangent angle is halfway between tan0 and tan1.
-    Sk2f n = normalize(tan0) - normalize(tan1);
-
-    // The midtangent can be found where (dQ(t) dot n) = 0:
-    //
-    //   0 = (dQ(t) dot n) = | 2*t  1 | * | p0 - 2*p1 + p2 | * | n |
-    //                                    | -2*p0 + 2*p1   |   | . |
-    //
-    //                     = | 2*t  1 | * | tan1 - tan0 | * | n |
-    //                                    | 2*tan0      |   | . |
-    //
-    //                     = 2*t * ((tan1 - tan0) dot n) + (2*tan0 dot n)
-    //
-    //   t = (tan0 dot n) / ((tan0 - tan1) dot n)
-    Sk2f dQ1n = (tan0 - tan1) * n;
-    Sk2f dQ0n = tan0 * n;
-    Sk2f t = (dQ0n + SkNx_shuffle<1,0>(dQ0n)) / (dQ1n + SkNx_shuffle<1,0>(dQ1n));
-    t = Sk2f::Min(Sk2f::Max(t, 0), 1); // Clamp for FP error.
-
-    Sk2f p01 = SkNx_fma(t, tan0, p0);
-    Sk2f p12 = SkNx_fma(t, tan1, p1);
-    Sk2f p012 = lerp(p01, p12, t);
-
-    this->appendMonotonicQuadratic(p0, p01, p012);
-    this->appendMonotonicQuadratic(p012, p12, p2);
-}
-
-inline void GrCCFillGeometry::appendMonotonicQuadratic(const Sk2f& p0, const Sk2f& p1,
-                                                       const Sk2f& p2) {
-    // Don't send curves to the GPU if we know they are nearly flat (or just very small).
-    if (are_collinear(p0, p1, p2)) {
-        this->appendLine(p0, p2);
-        return;
-    }
-
-    SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1]));
-    SkASSERT((p0 != p2).anyTrue());
-    p1.store(&fPoints.push_back());
-    p2.store(&fPoints.push_back());
-    fVerbs.push_back(Verb::kMonotonicQuadraticTo);
-    ++fCurrContourTallies.fQuadratics;
-}
-
-static inline Sk2f first_unless_nearly_zero(const Sk2f& a, const Sk2f& b) {
-    Sk2f aa = a*a;
-    aa += SkNx_shuffle<1,0>(aa);
-    SkASSERT(aa[0] == aa[1]);
-
-    Sk2f bb = b*b;
-    bb += SkNx_shuffle<1,0>(bb);
-    SkASSERT(bb[0] == bb[1]);
-
-    return (aa > bb * SK_ScalarNearlyZero).thenElse(a, b);
-}
-
-static inline void get_cubic_tangents(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
-                                      const Sk2f& p3, Sk2f* tan0, Sk2f* tan1) {
-    *tan0 = first_unless_nearly_zero(p1 - p0, p2 - p0);
-    *tan1 = first_unless_nearly_zero(p3 - p2, p3 - p1);
-}
-
-static inline bool is_cubic_nearly_quadratic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
-                                             const Sk2f& p3, const Sk2f& tan0, const Sk2f& tan1,
-                                             Sk2f* c) {
-    Sk2f c1 = SkNx_fma(Sk2f(1.5f), tan0, p0);
-    Sk2f c2 = SkNx_fma(Sk2f(-1.5f), tan1, p3);
-    *c = (c1 + c2) * .5f; // Hopefully optimized out if not used?
-    return ((c1 - c2).abs() <= 1).allTrue();
-}
-
-enum class ExcludedTerm : bool {
-    kQuadraticTerm,
-    kLinearTerm
-};
-
-// Finds where to chop a non-loop around its inflection points. The resulting cubic segments will be
-// chopped such that a box of radius 'padRadius', centered at any point along the curve segment, is
-// guaranteed to not cross the tangent lines at the inflection points (a.k.a lines L & M).
-//
-// 'chops' will be filled with 0, 2, or 4 T values. The segments between T0..T1 and T2..T3 must be
-// drawn with flat lines instead of cubics.
-//
-// A serpentine cubic has two inflection points, so this method takes Sk2f and computes the padding
-// for both in SIMD.
-static inline void find_chops_around_inflection_points(float padRadius, Sk2f tl, Sk2f sl,
-                                                       const Sk2f& C0, const Sk2f& C1,
-                                                       ExcludedTerm skipTerm, float Cdet,
-                                                       SkSTArray<4, float>* chops) {
-    SkASSERT(chops->empty());
-    SkASSERT(padRadius >= 0);
-
-    padRadius /= std::abs(Cdet); // Scale this single value rather than all of C^-1 later on.
-
-    // The homogeneous parametric functions for distance from lines L & M are:
-    //
-    //     l(t,s) = (t*sl - s*tl)^3
-    //     m(t,s) = (t*sm - s*tm)^3
-    //
-    // See "Resolution Independent Curve Rendering using Programmable Graphics Hardware",
-    // 4.3 Finding klmn:
-    //
-    // https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
-    //
-    // From here on we use Sk2f with "L" names, but the second lane will be for line M.
-    tl = (sl > 0).thenElse(tl, -tl); // Tl=tl/sl is the triple root of l(t,s). Normalize so s >= 0.
-    sl = sl.abs();
-
-    // Convert l(t,s), m(t,s) to power-basis form:
-    //
-    //                                                  | l3  m3 |
-    //    |l(t,s)  m(t,s)| = |t^3  t^2*s  t*s^2  s^3| * | l2  m2 |
-    //                                                  | l1  m1 |
-    //                                                  | l0  m0 |
-    //
-    Sk2f l3 = sl*sl*sl;
-    Sk2f l2or1 = (ExcludedTerm::kLinearTerm == skipTerm) ? sl*sl*tl*-3 : sl*tl*tl*3;
-
-    // The equation for line L can be found as follows:
-    //
-    //     L = C^-1 * (l excluding skipTerm)
-    //
-    // (See comments for GrPathUtils::calcCubicInverseTransposePowerBasisMatrix.)
-    // We are only interested in the normal to L, so only need the upper 2x2 of C^-1. And rather
-    // than divide by determinant(C) here, we have already performed this divide on padRadius.
-    Sk2f Lx =  C1[1]*l3 - C0[1]*l2or1;
-    Sk2f Ly = -C1[0]*l3 + C0[0]*l2or1;
-
-    // A box of radius "padRadius" is touching line L if "center dot L" is less than the Manhattan
-    // with of L. (See rationale in are_collinear.)
-    Sk2f Lwidth = Lx.abs() + Ly.abs();
-    Sk2f pad = Lwidth * padRadius;
-
-    // Will T=(t + cbrt(pad))/s be greater than 0? No need to solve roots outside T=0..1.
-    Sk2f insideLeftPad = pad + tl*tl*tl;
-
-    // Will T=(t - cbrt(pad))/s be less than 1? No need to solve roots outside T=0..1.
-    Sk2f tms = tl - sl;
-    Sk2f insideRightPad = pad - tms*tms*tms;
-
-    // Solve for the T values where abs(l(T)) = pad.
-    if (insideLeftPad[0] > 0 && insideRightPad[0] > 0) {
-        float padT = cbrtf(pad[0]);
-        Sk2f pts = (tl[0] + Sk2f(-padT, +padT)) / sl[0];
-        pts.store(chops->push_back_n(2));
-    }
-
-    // Solve for the T values where abs(m(T)) = pad.
-    if (insideLeftPad[1] > 0 && insideRightPad[1] > 0) {
-        float padT = cbrtf(pad[1]);
-        Sk2f pts = (tl[1] + Sk2f(-padT, +padT)) / sl[1];
-        pts.store(chops->push_back_n(2));
-    }
-}
-
-static inline void swap_if_greater(float& a, float& b) {
-    if (a > b) {
-        std::swap(a, b);
-    }
-}
-
-// Finds where to chop a non-loop around its intersection point. The resulting cubic segments will
-// be chopped such that a box of radius 'padRadius', centered at any point along the curve segment,
-// is guaranteed to not cross the tangent lines at the intersection point (a.k.a lines L & M).
-//
-// 'chops' will be filled with 0, 2, or 4 T values. The segments between T0..T1 and T2..T3 must be
-// drawn with quadratic splines instead of cubics.
-//
-// A loop intersection falls at two different T values, so this method takes Sk2f and computes the
-// padding for both in SIMD.
-static inline void find_chops_around_loop_intersection(float padRadius, Sk2f t2, Sk2f s2,
-                                                       const Sk2f& C0, const Sk2f& C1,
-                                                       ExcludedTerm skipTerm, float Cdet,
-                                                       SkSTArray<4, float>* chops) {
-    SkASSERT(chops->empty());
-    SkASSERT(padRadius >= 0);
-
-    padRadius /= std::abs(Cdet); // Scale this single value rather than all of C^-1 later on.
-
-    // The parametric functions for distance from lines L & M are:
-    //
-    //     l(T) = (T - Td)^2 * (T - Te)
-    //     m(T) = (T - Td) * (T - Te)^2
-    //
-    // See "Resolution Independent Curve Rendering using Programmable Graphics Hardware",
-    // 4.3 Finding klmn:
-    //
-    // https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
-    Sk2f T2 = t2/s2; // T2 is the double root of l(T).
-    Sk2f T1 = SkNx_shuffle<1,0>(T2); // T1 is the other root of l(T).
-
-    // Convert l(T), m(T) to power-basis form:
-    //
-    //                                      |  1   1 |
-    //    |l(T)  m(T)| = |T^3  T^2  T  1| * | l2  m2 |
-    //                                      | l1  m1 |
-    //                                      | l0  m0 |
-    //
-    // From here on we use Sk2f with "L" names, but the second lane will be for line M.
-    Sk2f l2 = SkNx_fma(Sk2f(-2), T2, -T1);
-    Sk2f l1 = T2 * SkNx_fma(Sk2f(2), T1, T2);
-    Sk2f l0 = -T2*T2*T1;
-
-    // The equation for line L can be found as follows:
-    //
-    //     L = C^-1 * (l excluding skipTerm)
-    //
-    // (See comments for GrPathUtils::calcCubicInverseTransposePowerBasisMatrix.)
-    // We are only interested in the normal to L, so only need the upper 2x2 of C^-1. And rather
-    // than divide by determinant(C) here, we have already performed this divide on padRadius.
-    Sk2f l2or1 = (ExcludedTerm::kLinearTerm == skipTerm) ? l2 : l1;
-    Sk2f Lx = -C0[1]*l2or1 + C1[1]; // l3 is always 1.
-    Sk2f Ly =  C0[0]*l2or1 - C1[0];
-
-    // A box of radius "padRadius" is touching line L if "center dot L" is less than the Manhattan
-    // with of L. (See rationale in are_collinear.)
-    Sk2f Lwidth = Lx.abs() + Ly.abs();
-    Sk2f pad = Lwidth * padRadius;
-
-    // Is l(T=0) outside the padding around line L?
-    Sk2f lT0 = l0; // l(T=0) = |0  0  0  1| dot |1  l2  l1  l0| = l0
-    Sk2f outsideT0 = lT0.abs() - pad;
-
-    // Is l(T=1) outside the padding around line L?
-    Sk2f lT1 = (Sk2f(1) + l2 + l1 + l0).abs(); // l(T=1) = |1  1  1  1| dot |1  l2  l1  l0|
-    Sk2f outsideT1 = lT1.abs() - pad;
-
-    // Values for solving the cubic.
-    Sk2f p, q, qqq, discr, numRoots, D;
-    bool hasDiscr = false;
-
-    // Values for calculating one root (rarely needed).
-    Sk2f R, QQ;
-    bool hasOneRootVals = false;
-
-    // Values for calculating three roots.
-    Sk2f P, cosTheta3;
-    bool hasThreeRootVals = false;
-
-    // Solve for the T values where l(T) = +pad and m(T) = -pad.
-    for (int i = 0; i < 2; ++i) {
-        float T = T2[i]; // T is the point we are chopping around.
-        if ((T < 0 && outsideT0[i] >= 0) || (T > 1 && outsideT1[i] >= 0)) {
-            // The padding around T is completely out of range. No point solving for it.
-            continue;
-        }
-
-        if (!hasDiscr) {
-            p = Sk2f(+.5f, -.5f) * pad;
-            q = (1.f/3) * (T2 - T1);
-            qqq = q*q*q;
-            discr = qqq*p*2 + p*p;
-            numRoots = (discr < 0).thenElse(3, 1);
-            D = T2 - q;
-            hasDiscr = true;
-        }
-
-        if (1 == numRoots[i]) {
-            if (!hasOneRootVals) {
-                Sk2f r = qqq + p;
-                Sk2f s = r.abs() + discr.sqrt();
-                R = (r > 0).thenElse(-s, s);
-                QQ = q*q;
-                hasOneRootVals = true;
-            }
-
-            float A = cbrtf(R[i]);
-            float B = A != 0 ? QQ[i]/A : 0;
-            // When there is only one root, ine L chops from root..1, line M chops from 0..root.
-            if (1 == i) {
-                chops->push_back(0);
-            }
-            chops->push_back(A + B + D[i]);
-            if (0 == i) {
-                chops->push_back(1);
-            }
-            continue;
-        }
-
-        if (!hasThreeRootVals) {
-            P = q.abs() * -2;
-            cosTheta3 = (q >= 0).thenElse(1, -1) + p / qqq.abs();
-            hasThreeRootVals = true;
-        }
-
-        static constexpr float k2PiOver3 = 2 * SK_ScalarPI / 3;
-        float theta = std::acos(cosTheta3[i]) * (1.f/3);
-        float roots[3] = {P[i] * std::cos(theta) + D[i],
-                          P[i] * std::cos(theta + k2PiOver3) + D[i],
-                          P[i] * std::cos(theta - k2PiOver3) + D[i]};
-
-        // Sort the three roots.
-        swap_if_greater(roots[0], roots[1]);
-        swap_if_greater(roots[1], roots[2]);
-        swap_if_greater(roots[0], roots[1]);
-
-        // Line L chops around the first 2 roots, line M chops around the second 2.
-        chops->push_back_n(2, &roots[i]);
-    }
-}
-
-void GrCCFillGeometry::cubicTo(const SkPoint P[4], float inflectPad, float loopIntersectPad) {
-    SkASSERT(fBuildingContour);
-    SkASSERT(P[0] == fPoints.back());
-
-    // Don't crunch on the curve or inflate geometry if it is nearly flat (or just very small).
-    // Flat curves can break the math below.
-    if (are_collinear(P)) {
-        Sk2f p0 = Sk2f::Load(P);
-        Sk2f p3 = Sk2f::Load(P+3);
-        this->appendLine(p0, p3);
-        return;
-    }
-
-    Sk2f p0 = Sk2f::Load(P);
-    Sk2f p1 = Sk2f::Load(P+1);
-    Sk2f p2 = Sk2f::Load(P+2);
-    Sk2f p3 = Sk2f::Load(P+3);
-
-    // Also detect near-quadratics ahead of time.
-    Sk2f tan0, tan1, c;
-    get_cubic_tangents(p0, p1, p2, p3, &tan0, &tan1);
-    if (is_cubic_nearly_quadratic(p0, p1, p2, p3, tan0, tan1, &c)) {
-        this->appendQuadratics(p0, c, p3);
-        return;
-    }
-
-    double tt[2], ss[2], D[4];
-    fCurrCubicType = SkClassifyCubic(P, tt, ss, D);
-    SkASSERT(!SkCubicIsDegenerate(fCurrCubicType));
-    Sk2f t = Sk2f(static_cast<float>(tt[0]), static_cast<float>(tt[1]));
-    Sk2f s = Sk2f(static_cast<float>(ss[0]), static_cast<float>(ss[1]));
-
-    ExcludedTerm skipTerm = (std::abs(D[2]) > std::abs(D[1]))
-                                    ? ExcludedTerm::kQuadraticTerm
-                                    : ExcludedTerm::kLinearTerm;
-    Sk2f C0 = SkNx_fma(Sk2f(3), p1 - p2, p3 - p0);
-    Sk2f C1 = (ExcludedTerm::kLinearTerm == skipTerm
-                       ? SkNx_fma(Sk2f(-2), p1, p0 + p2)
-                       : p1 - p0) * 3;
-    Sk2f C0x1 = C0 * SkNx_shuffle<1,0>(C1);
-    float Cdet = C0x1[0] - C0x1[1];
-
-    SkSTArray<4, float> chops;
-    if (SkCubicType::kLoop != fCurrCubicType) {
-        find_chops_around_inflection_points(inflectPad, t, s, C0, C1, skipTerm, Cdet, &chops);
-    } else {
-        find_chops_around_loop_intersection(loopIntersectPad, t, s, C0, C1, skipTerm, Cdet, &chops);
-    }
-    if (4 == chops.count() && chops[1] >= chops[2]) {
-        // This just the means the KLM roots are so close that their paddings overlap. We will
-        // approximate the entire middle section, but still have it chopped midway. For loops this
-        // chop guarantees the append code only sees convex segments. Otherwise, it means we are (at
-        // least almost) a cusp and the chop makes sure we get a sharp point.
-        Sk2f ts = t * SkNx_shuffle<1,0>(s);
-        chops[1] = chops[2] = (ts[0] + ts[1]) / (2*s[0]*s[1]);
-    }
-
-#ifdef SK_DEBUG
-    for (int i = 1; i < chops.count(); ++i) {
-        SkASSERT(chops[i] >= chops[i - 1]);
-    }
-#endif
-    this->appendCubics(AppendCubicMode::kLiteral, p0, p1, p2, p3, chops.begin(), chops.count());
-}
-
-static inline void chop_cubic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2, const Sk2f& p3,
-                              float T, Sk2f* ab, Sk2f* abc, Sk2f* abcd, Sk2f* bcd, Sk2f* cd) {
-    Sk2f TT = T;
-    *ab = lerp(p0, p1, TT);
-    Sk2f bc = lerp(p1, p2, TT);
-    *cd = lerp(p2, p3, TT);
-    *abc = lerp(*ab, bc, TT);
-    *bcd = lerp(bc, *cd, TT);
-    *abcd = lerp(*abc, *bcd, TT);
-}
-
-void GrCCFillGeometry::appendCubics(AppendCubicMode mode, const Sk2f& p0, const Sk2f& p1,
-                                    const Sk2f& p2, const Sk2f& p3, const float chops[],
-                                    int numChops, float localT0, float localT1) {
-    if (numChops) {
-        SkASSERT(numChops > 0);
-        int midChopIdx = numChops/2;
-        float T = chops[midChopIdx];
-        // Chops alternate between literal and approximate mode.
-        AppendCubicMode rightMode = (AppendCubicMode)((bool)mode ^ (midChopIdx & 1) ^ 1);
-
-        if (T <= localT0) {
-            // T is outside 0..1. Append the right side only.
-            this->appendCubics(rightMode, p0, p1, p2, p3, &chops[midChopIdx + 1],
-                               numChops - midChopIdx - 1, localT0, localT1);
-            return;
-        }
-
-        if (T >= localT1) {
-            // T is outside 0..1. Append the left side only.
-            this->appendCubics(mode, p0, p1, p2, p3, chops, midChopIdx, localT0, localT1);
-            return;
-        }
-
-        float localT = (T - localT0) / (localT1 - localT0);
-        Sk2f p01, p02, pT, p11, p12;
-        chop_cubic(p0, p1, p2, p3, localT, &p01, &p02, &pT, &p11, &p12);
-        this->appendCubics(mode, p0, p01, p02, pT, chops, midChopIdx, localT0, T);
-        this->appendCubics(rightMode, pT, p11, p12, p3, &chops[midChopIdx + 1],
-                           numChops - midChopIdx - 1, T, localT1);
-        return;
-    }
-
-    this->appendCubics(mode, p0, p1, p2, p3);
-}
-
-void GrCCFillGeometry::appendCubics(AppendCubicMode mode, const Sk2f& p0, const Sk2f& p1,
-                                    const Sk2f& p2, const Sk2f& p3, int maxSubdivisions) {
-    if (SkCubicType::kLoop != fCurrCubicType) {
-        // Serpentines and cusps are always monotonic after chopping around inflection points.
-        SkASSERT(!SkCubicIsDegenerate(fCurrCubicType));
-
-        if (AppendCubicMode::kApproximate == mode) {
-            // This section passes through an inflection point, so we can get away with a flat line.
-            // This can cause some curves to feel slightly more flat when inspected rigorously back
-            // and forth against another renderer, but for now this seems acceptable given the
-            // simplicity.
-            this->appendLine(p0, p3);
-            return;
-        }
-    } else {
-        Sk2f tan0, tan1;
-        get_cubic_tangents(p0, p1, p2, p3, &tan0, &tan1);
-
-        if (maxSubdivisions && !is_convex_curve_monotonic(p0, tan0, p3, tan1)) {
-            this->chopAndAppendCubicAtMidTangent(mode, p0, p1, p2, p3, tan0, tan1,
-                                                 maxSubdivisions - 1);
-            return;
-        }
-
-        if (AppendCubicMode::kApproximate == mode) {
-            Sk2f c;
-            if (!is_cubic_nearly_quadratic(p0, p1, p2, p3, tan0, tan1, &c) && maxSubdivisions) {
-                this->chopAndAppendCubicAtMidTangent(mode, p0, p1, p2, p3, tan0, tan1,
-                                                     maxSubdivisions - 1);
-                return;
-            }
-
-            this->appendMonotonicQuadratic(p0, c, p3);
-            return;
-        }
-    }
-
-    // Don't send curves to the GPU if we know they are nearly flat (or just very small).
-    // Since the cubic segment is known to be convex at this point, our flatness check is simple.
-    if (are_collinear(p0, (p1 + p2) * .5f, p3)) {
-        this->appendLine(p0, p3);
-        return;
-    }
-
-    SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1]));
-    SkASSERT((p0 != p3).anyTrue());
-    p1.store(&fPoints.push_back());
-    p2.store(&fPoints.push_back());
-    p3.store(&fPoints.push_back());
-    fVerbs.push_back(Verb::kMonotonicCubicTo);
-    ++fCurrContourTallies.fCubics;
-}
-
-// Given a convex curve segment with the following order-2 tangent function:
-//
-//                                                       |C2x  C2y|
-//     tan = some_scale * |dx/dt  dy/dt| = |t^2  t  1| * |C1x  C1y|
-//                                                       |C0x  C0y|
-//
-// This function finds the T value whose tangent angle is halfway between the tangents at T=0 and
-// T=1 (tan0 and tan1).
-static inline float find_midtangent(const Sk2f& tan0, const Sk2f& tan1,
-                                    const Sk2f& C2, const Sk2f& C1, const Sk2f& C0) {
-    // Tangents point in the direction of increasing T, so tan0 and -tan1 both point toward the
-    // midtangent. 'n' will therefore bisect tan0 and -tan1, giving us the normal to the midtangent.
-    //
-    //     n dot midtangent = 0
-    //
-    Sk2f n = normalize(tan0) - normalize(tan1);
-
-    // Find the T value at the midtangent. This is a simple quadratic equation:
-    //
-    //     midtangent dot n = 0
-    //
-    //     (|t^2  t  1| * C) dot n = 0
-    //
-    //     |t^2  t  1| dot C*n = 0
-    //
-    // First find coeffs = C*n.
-    Sk4f C[2];
-    Sk2f::Store4(C, C2, C1, C0, 0);
-    Sk4f coeffs = C[0]*n[0] + C[1]*n[1];
-
-    // Now solve the quadratic.
-    float a = coeffs[0], b = coeffs[1], c = coeffs[2];
-    float discr = b*b - 4*a*c;
-    if (discr < 0) {
-        return 0; // This will only happen if the curve is a line.
-    }
-
-    // The roots are q/a and c/q. Pick the one closer to T=.5.
-    float q = -.5f * (b + copysignf(std::sqrt(discr), b));
-    float r = .5f*q*a;
-    return std::abs(q*q - r) < std::abs(a*c - r) ? q/a : c/q;
-}
-
-inline void GrCCFillGeometry::chopAndAppendCubicAtMidTangent(AppendCubicMode mode, const Sk2f& p0,
-                                                             const Sk2f& p1, const Sk2f& p2,
-                                                             const Sk2f& p3, const Sk2f& tan0,
-                                                             const Sk2f& tan1,
-                                                             int maxFutureSubdivisions) {
-    float midT = find_midtangent(tan0, tan1, p3 + (p1 - p2)*3 - p0,
-                                             (p0 - p1*2 + p2)*2,
-                                             p1 - p0);
-    // Use positive logic since NaN fails comparisons. (However midT should not be NaN since we cull
-    // near-flat cubics in cubicTo().)
-    if (!(midT > 0 && midT < 1)) {
-        // The cubic is flat. Otherwise there would be a real midtangent inside T=0..1.
-        this->appendLine(p0, p3);
-        return;
-    }
-
-    Sk2f p01, p02, pT, p11, p12;
-    chop_cubic(p0, p1, p2, p3, midT, &p01, &p02, &pT, &p11, &p12);
-    this->appendCubics(mode, p0, p01, p02, pT, maxFutureSubdivisions);
-    this->appendCubics(mode, pT, p11, p12, p3, maxFutureSubdivisions);
-}
-
-void GrCCFillGeometry::conicTo(const SkPoint P[3], float w) {
-    SkASSERT(fBuildingContour);
-    SkASSERT(P[0] == fPoints.back());
-    Sk2f p0 = Sk2f::Load(P);
-    Sk2f p1 = Sk2f::Load(P+1);
-    Sk2f p2 = Sk2f::Load(P+2);
-
-    Sk2f tan0 = p1 - p0;
-    Sk2f tan1 = p2 - p1;
-
-    if (!is_convex_curve_monotonic(p0, tan0, p2, tan1)) {
-        // The derivative of a conic has a cumbersome order-4 denominator. However, this isn't
-        // necessary if we are only interested in a vector in the same *direction* as a given
-        // tangent line. Since the denominator scales dx and dy uniformly, we can throw it out
-        // completely after evaluating the derivative with the standard quotient rule. This leaves
-        // us with a simpler quadratic function that we use to find the midtangent.
-        float midT = find_midtangent(tan0, tan1, (w - 1) * (p2 - p0),
-                                                 (p2 - p0) - 2*w*(p1 - p0),
-                                                 w*(p1 - p0));
-        // Use positive logic since NaN fails comparisons. (However midT should not be NaN since we
-        // cull near-linear conics above. And while w=0 is flat, it's not a line and has valid
-        // midtangents.)
-        if (!(midT > 0 && midT < 1)) {
-            // The conic is flat. Otherwise there would be a real midtangent inside T=0..1.
-            this->appendLine(p0, p2);
-            return;
-        }
-
-        // Chop the conic at midtangent to produce two monotonic segments.
-        Sk4f p3d0 = Sk4f(p0[0], p0[1], 1, 0);
-        Sk4f p3d1 = Sk4f(p1[0], p1[1], 1, 0) * w;
-        Sk4f p3d2 = Sk4f(p2[0], p2[1], 1, 0);
-        Sk4f midT4 = midT;
-
-        Sk4f p3d01 = lerp(p3d0, p3d1, midT4);
-        Sk4f p3d12 = lerp(p3d1, p3d2, midT4);
-        Sk4f p3d012 = lerp(p3d01, p3d12, midT4);
-
-        Sk2f midpoint = Sk2f(p3d012[0], p3d012[1]) / p3d012[2];
-        Sk2f ww = Sk2f(p3d01[2], p3d12[2]) * Sk2f(p3d012[2]).rsqrt();
-
-        this->appendMonotonicConic(p0, Sk2f(p3d01[0], p3d01[1]) / p3d01[2], midpoint, ww[0]);
-        this->appendMonotonicConic(midpoint, Sk2f(p3d12[0], p3d12[1]) / p3d12[2], p2, ww[1]);
-        return;
-    }
-
-    this->appendMonotonicConic(p0, p1, p2, w);
-}
-
-void GrCCFillGeometry::appendMonotonicConic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
-                                            float w) {
-    SkASSERT(w >= 0);
-
-    Sk2f base = p2 - p0;
-    Sk2f baseAbs = base.abs();
-    float baseWidth = baseAbs[0] + baseAbs[1];
-
-    // Find the height of the curve. Max height always occurs at T=.5 for conics.
-    Sk2f d = (p1 - p0) * SkNx_shuffle<1,0>(base);
-    float h1 = std::abs(d[1] - d[0]); // Height of p1 above the base.
-    float ht = h1*w, hs = 1 + w; // Height of the conic = ht/hs.
-
-    // i.e. (ht/hs <= baseWidth * kFlatnessThreshold). Use "<=" in case base == 0.
-    if (ht <= (baseWidth*hs) * kFlatnessThreshold) {
-        // We are flat. (See rationale in are_collinear.)
-        this->appendLine(p0, p2);
-        return;
-    }
-
-    // i.e. (w > 1 && h1 - ht/hs < baseWidth).
-    if (w > 1 && h1*hs - ht < baseWidth*hs) {
-        // If we get within 1px of p1 when w > 1, we will pick up artifacts from the implicit
-        // function's reflection. Chop at max height (T=.5) and draw a triangle instead.
-        Sk2f p1w = p1*w;
-        Sk2f ab = p0 + p1w;
-        Sk2f bc = p1w + p2;
-        Sk2f highpoint = (ab + bc) / (2*(1 + w));
-        this->appendLine(p0, highpoint);
-        this->appendLine(highpoint, p2);
-        return;
-    }
-
-    SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1]));
-    SkASSERT((p0 != p2).anyTrue());
-    p1.store(&fPoints.push_back());
-    p2.store(&fPoints.push_back());
-    fConicWeights.push_back(w);
-    fVerbs.push_back(Verb::kMonotonicConicTo);
-    ++fCurrContourTallies.fConics;
-}
-
-GrCCFillGeometry::PrimitiveTallies GrCCFillGeometry::endContour() {
-    SkASSERT(fBuildingContour);
-    SkASSERT(fVerbs.count() >= fCurrContourTallies.fTriangles);
-
-    // The fTriangles field currently contains this contour's starting verb index. We can now
-    // use it to calculate the size of the contour's fan.
-    int fanSize = fVerbs.count() - fCurrContourTallies.fTriangles;
-    if (fPoints.back() == fCurrAnchorPoint) {
-        --fanSize;
-        fVerbs.push_back(Verb::kEndClosedContour);
-    } else {
-        fVerbs.push_back(Verb::kEndOpenContour);
-    }
-
-    fCurrContourTallies.fTriangles = std::max(fanSize - 2, 0);
-
-    SkDEBUGCODE(fBuildingContour = false);
-    return fCurrContourTallies;
-}
diff --git a/src/gpu/ccpr/GrCCFillGeometry.h b/src/gpu/ccpr/GrCCFillGeometry.h
deleted file mode 100644
index d0a3c1e..0000000
--- a/src/gpu/ccpr/GrCCFillGeometry.h
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrGrCCFillGeometry_DEFINED
-#define GrGrCCFillGeometry_DEFINED
-
-#include "include/core/SkPoint.h"
-#include "include/private/SkNx.h"
-#include "include/private/SkTArray.h"
-#include "src/core/SkGeometry.h"
-
-/**
- * This class chops device-space contours up into a series of segments that CCPR knows how to
- * fill. (See GrCCFillGeometry::Verb.)
- *
- * NOTE: This must be done in device space, since an affine transformation can change whether a
- * curve is monotonic.
- */
-class GrCCFillGeometry {
-public:
-    // These are the verbs that CCPR knows how to fill. If a path has any segments that don't map to
-    // this list, then they are chopped into smaller ones that do. A list of these comprise a
-    // compact representation of what can later be expanded into GPU instance data.
-    enum class Verb : uint8_t {
-        kBeginPath, // Included only for caller convenience.
-        kBeginContour,
-        kLineTo,
-        kMonotonicQuadraticTo, // Monotonic relative to the vector between its endpoints [P2 - P0].
-        kMonotonicCubicTo,
-        kMonotonicConicTo,
-        kEndClosedContour, // endPt == startPt.
-        kEndOpenContour // endPt != startPt.
-    };
-
-    // These tallies track numbers of CCPR primitives that are required to draw a contour.
-    struct PrimitiveTallies {
-        int fTriangles; // Number of triangles in the contour's fan.
-        int fWeightedTriangles; // Triangles (from the tessellator) whose winding magnitude > 1.
-        int fQuadratics;
-        int fCubics;
-        int fConics;
-
-        void operator+=(const PrimitiveTallies&);
-        PrimitiveTallies operator-(const PrimitiveTallies&) const;
-        bool operator==(const PrimitiveTallies&);
-    };
-
-    GrCCFillGeometry(int numSkPoints = 0, int numSkVerbs = 0, int numConicWeights = 0)
-            : fPoints(numSkPoints * 3) // Reserve for a 3x expansion in points and verbs.
-            , fVerbs(numSkVerbs * 3)
-            , fConicWeights(numConicWeights * 3/2) {}
-
-    const SkTArray<SkPoint, true>& points() const { SkASSERT(!fBuildingContour); return fPoints; }
-    const SkTArray<Verb, true>& verbs() const { SkASSERT(!fBuildingContour); return fVerbs; }
-    float getConicWeight(int idx) const { SkASSERT(!fBuildingContour); return fConicWeights[idx]; }
-
-    void reset() {
-        SkASSERT(!fBuildingContour);
-        fPoints.reset();
-        fVerbs.reset();
-    }
-
-    void beginPath();
-    void beginContour(const SkPoint&);
-    void lineTo(const SkPoint P[2]);
-    void quadraticTo(const SkPoint[3]);
-
-    // We pass through inflection points and loop intersections using a line and quadratic(s)
-    // respectively. 'inflectPad' and 'loopIntersectPad' specify how close (in pixels) cubic
-    // segments are allowed to get to these points. For normal rendering you will want to use the
-    // default values, but these can be overridden for testing purposes.
-    //
-    // NOTE: loops do appear to require two full pixels of padding around the intersection point.
-    //       With just one pixel-width of pad, we start to see bad pixels. Ultimately this has a
-    //       minimal effect on the total amount of segments produced. Most sections that pass
-    //       through the loop intersection can be approximated with a single quadratic anyway,
-    //       regardless of whether we are use one pixel of pad or two (1.622 avg. quads per loop
-    //       intersection vs. 1.489 on the tiger).
-    void cubicTo(const SkPoint[4], float inflectPad = 0.55f, float loopIntersectPad = 2);
-
-    void conicTo(const SkPoint[3], float w);
-
-    PrimitiveTallies endContour(); // Returns the numbers of primitives needed to draw the contour.
-
-private:
-    inline void appendLine(const Sk2f& p0, const Sk2f& p1);
-
-    inline void appendQuadratics(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2);
-    inline void appendMonotonicQuadratic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2);
-
-    enum class AppendCubicMode : bool {
-        kLiteral,
-        kApproximate
-    };
-    void appendCubics(AppendCubicMode, const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
-                      const Sk2f& p3, const float chops[], int numChops, float localT0 = 0,
-                     float localT1 = 1);
-    void appendCubics(AppendCubicMode, const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
-                      const Sk2f& p3, int maxSubdivisions = 2);
-    void chopAndAppendCubicAtMidTangent(AppendCubicMode, const Sk2f& p0, const Sk2f& p1,
-                                        const Sk2f& p2, const Sk2f& p3, const Sk2f& tan0,
-                                        const Sk2f& tan1, int maxFutureSubdivisions);
-
-    void appendMonotonicConic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2, float w);
-
-    // Transient state used while building a contour.
-    SkPoint fCurrAnchorPoint;
-    PrimitiveTallies fCurrContourTallies;
-    SkCubicType fCurrCubicType;
-    SkDEBUGCODE(bool fBuildingContour = false);
-
-    SkSTArray<128, SkPoint, true> fPoints;
-    SkSTArray<128, Verb, true> fVerbs;
-    SkSTArray<32, float, true> fConicWeights;
-};
-
-inline void GrCCFillGeometry::PrimitiveTallies::operator+=(const PrimitiveTallies& b) {
-    fTriangles += b.fTriangles;
-    fWeightedTriangles += b.fWeightedTriangles;
-    fQuadratics += b.fQuadratics;
-    fCubics += b.fCubics;
-    fConics += b.fConics;
-}
-
-GrCCFillGeometry::PrimitiveTallies
-inline GrCCFillGeometry::PrimitiveTallies::operator-(const PrimitiveTallies& b) const {
-    return {fTriangles - b.fTriangles,
-            fWeightedTriangles - b.fWeightedTriangles,
-            fQuadratics - b.fQuadratics,
-            fCubics - b.fCubics,
-            fConics - b.fConics};
-}
-
-inline bool GrCCFillGeometry::PrimitiveTallies::operator==(const PrimitiveTallies& b) {
-    return fTriangles == b.fTriangles && fWeightedTriangles == b.fWeightedTriangles &&
-           fQuadratics == b.fQuadratics && fCubics == b.fCubics && fConics == b.fConics;
-}
-
-#endif
diff --git a/src/gpu/ccpr/GrCCFiller.cpp b/src/gpu/ccpr/GrCCFiller.cpp
deleted file mode 100644
index d7035ae..0000000
--- a/src/gpu/ccpr/GrCCFiller.cpp
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/ccpr/GrCCFiller.h"
-
-#include "include/core/SkPath.h"
-#include "include/core/SkPoint.h"
-#include "src/core/SkMathPriv.h"
-#include "src/core/SkPathPriv.h"
-#include "src/gpu/GrCaps.h"
-#include "src/gpu/GrOnFlushResourceProvider.h"
-#include "src/gpu/GrOpFlushState.h"
-#include "src/gpu/GrProgramInfo.h"
-#include <cstdlib>
-
-using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
-using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance;
-
-GrCCFiller::GrCCFiller(Algorithm algorithm, int numPaths, int numSkPoints, int numSkVerbs,
-                       int numConicWeights)
-        : fAlgorithm(algorithm)
-        , fGeometry(numSkPoints, numSkVerbs, numConicWeights)
-        , fPathInfos(numPaths)
-        , fScissorSubBatches(numPaths)
-        , fTotalPrimitiveCounts{PrimitiveTallies(), PrimitiveTallies()} {
-    // Batches decide what to draw by looking where the previous one ended. Define initial batches
-    // that "end" at the beginning of the data. These will not be drawn, but will only be be read by
-    // the first actual batch.
-    fScissorSubBatches.push_back() = {PrimitiveTallies(), SkIRect::MakeEmpty()};
-    fBatches.push_back() = {PrimitiveTallies(), fScissorSubBatches.count(), PrimitiveTallies()};
-}
-
-void GrCCFiller::parseDeviceSpaceFill(const SkPath& path, const SkPoint* deviceSpacePts,
-                                      GrScissorTest scissorTest, const SkIRect& clippedDevIBounds,
-                                      const SkIVector& devToAtlasOffset) {
-    SkASSERT(!fInstanceBuffer.hasGpuBuffer());  // Can't call after prepareToDraw().
-    SkASSERT(!path.isEmpty());
-
-    int currPathPointsIdx = fGeometry.points().count();
-    int currPathVerbsIdx = fGeometry.verbs().count();
-    PrimitiveTallies currPathPrimitiveCounts = PrimitiveTallies();
-
-    fGeometry.beginPath();
-
-    const float* conicWeights = SkPathPriv::ConicWeightData(path);
-    int ptsIdx = 0;
-    int conicWeightsIdx = 0;
-    bool insideContour = false;
-
-    for (SkPath::Verb verb : SkPathPriv::Verbs(path)) {
-        switch (verb) {
-            case SkPath::kMove_Verb:
-                if (insideContour) {
-                    currPathPrimitiveCounts += fGeometry.endContour();
-                }
-                fGeometry.beginContour(deviceSpacePts[ptsIdx]);
-                ++ptsIdx;
-                insideContour = true;
-                continue;
-            case SkPath::kClose_Verb:
-                if (insideContour) {
-                    currPathPrimitiveCounts += fGeometry.endContour();
-                }
-                insideContour = false;
-                continue;
-            case SkPath::kLine_Verb:
-                fGeometry.lineTo(&deviceSpacePts[ptsIdx - 1]);
-                ++ptsIdx;
-                continue;
-            case SkPath::kQuad_Verb:
-                fGeometry.quadraticTo(&deviceSpacePts[ptsIdx - 1]);
-                ptsIdx += 2;
-                continue;
-            case SkPath::kCubic_Verb:
-                fGeometry.cubicTo(&deviceSpacePts[ptsIdx - 1]);
-                ptsIdx += 3;
-                continue;
-            case SkPath::kConic_Verb:
-                fGeometry.conicTo(&deviceSpacePts[ptsIdx - 1], conicWeights[conicWeightsIdx]);
-                ptsIdx += 2;
-                ++conicWeightsIdx;
-                continue;
-            default:
-                SK_ABORT("Unexpected path verb.");
-        }
-    }
-    SkASSERT(ptsIdx == path.countPoints());
-    SkASSERT(conicWeightsIdx == SkPathPriv::ConicWeightCnt(path));
-
-    if (insideContour) {
-        currPathPrimitiveCounts += fGeometry.endContour();
-    }
-
-    fPathInfos.emplace_back(scissorTest, devToAtlasOffset);
-
-    // Tessellate fans from very large and/or simple paths, in order to reduce overdraw.
-    int numVerbs = fGeometry.verbs().count() - currPathVerbsIdx - 1;
-    int64_t tessellationWork = (int64_t)numVerbs * (32 - SkCLZ(numVerbs)); // N log N.
-    int64_t fanningWork = (int64_t)clippedDevIBounds.height() * clippedDevIBounds.width();
-    if (tessellationWork * (50*50) + (100*100) < fanningWork) { // Don't tessellate under 100x100.
-        fPathInfos.back().tessellateFan(
-                fAlgorithm, path, fGeometry, currPathVerbsIdx, currPathPointsIdx, clippedDevIBounds,
-                &currPathPrimitiveCounts);
-    }
-
-    fTotalPrimitiveCounts[(int)scissorTest] += currPathPrimitiveCounts;
-
-    if (GrScissorTest::kEnabled == scissorTest) {
-        fScissorSubBatches.push_back() = {fTotalPrimitiveCounts[(int)GrScissorTest::kEnabled],
-                                          clippedDevIBounds.makeOffset(devToAtlasOffset)};
-    }
-}
-
-void GrCCFiller::PathInfo::tessellateFan(
-        Algorithm algorithm, const SkPath& originalPath, const GrCCFillGeometry& geometry,
-        int verbsIdx, int ptsIdx, const SkIRect& clippedDevIBounds,
-        PrimitiveTallies* newTriangleCounts) {
-    using Verb = GrCCFillGeometry::Verb;
-    SkASSERT(-1 == fFanTessellationCount);
-    SkASSERT(!fFanTessellation);
-
-    const SkTArray<Verb, true>& verbs = geometry.verbs();
-    const SkTArray<SkPoint, true>& pts = geometry.points();
-
-    newTriangleCounts->fTriangles =
-            newTriangleCounts->fWeightedTriangles = 0;
-
-    // Build an SkPath of the Redbook fan.
-    SkPath fan;
-    // When counting winding numbers in the stencil buffer, it works to use even/odd for the fan
-    // tessellation (where applicable). But we need to strip out inverse fill info because
-    // inverse-ness gets accounted for later on.
-    fan.setFillType(SkPathFillType_ConvertToNonInverse(originalPath.getFillType()));
-    SkASSERT(Verb::kBeginPath == verbs[verbsIdx]);
-    for (int i = verbsIdx + 1; i < verbs.count(); ++i) {
-        switch (verbs[i]) {
-            case Verb::kBeginPath:
-                SK_ABORT("Invalid GrCCFillGeometry");
-                continue;
-
-            case Verb::kBeginContour:
-                fan.moveTo(pts[ptsIdx++]);
-                continue;
-
-            case Verb::kLineTo:
-                fan.lineTo(pts[ptsIdx++]);
-                continue;
-
-            case Verb::kMonotonicQuadraticTo:
-            case Verb::kMonotonicConicTo:
-                fan.lineTo(pts[ptsIdx + 1]);
-                ptsIdx += 2;
-                continue;
-
-            case Verb::kMonotonicCubicTo:
-                fan.lineTo(pts[ptsIdx + 2]);
-                ptsIdx += 3;
-                continue;
-
-            case Verb::kEndClosedContour:
-            case Verb::kEndOpenContour:
-                fan.close();
-                continue;
-        }
-    }
-
-    GrTriangulator::WindingVertex* vertices = nullptr;
-    SkASSERT(!fan.isInverseFillType());
-    fFanTessellationCount = GrTriangulator::PathToVertices(
-            fan, std::numeric_limits<float>::infinity(), SkRect::Make(clippedDevIBounds),
-            &vertices);
-    if (fFanTessellationCount <= 0) {
-        SkASSERT(0 == fFanTessellationCount);
-        SkASSERT(nullptr == vertices);
-        return;
-    }
-
-    SkASSERT(0 == fFanTessellationCount % 3);
-    for (int i = 0; i < fFanTessellationCount; i += 3) {
-        int weight = abs(vertices[i].fWinding);
-        if (SkPathFillType::kEvenOdd == fan.getFillType()) {
-            // The tessellator doesn't wrap weights modulo 2 when we request even/odd fill type.
-            SkASSERT(weight & 1);
-            weight = 1;
-        }
-        newTriangleCounts->fTriangles += weight;
-        vertices[i].fWinding = weight;
-    }
-
-    fFanTessellation.reset(vertices);
-}
-
-GrCCFiller::BatchID GrCCFiller::closeCurrentBatch() {
-    SkASSERT(!fInstanceBuffer.hasGpuBuffer());
-    SkASSERT(!fBatches.empty());
-
-    const auto& lastBatch = fBatches.back();
-    int maxMeshes = 1 + fScissorSubBatches.count() - lastBatch.fEndScissorSubBatchIdx;
-    fMaxMeshesPerDraw = std::max(fMaxMeshesPerDraw, maxMeshes);
-
-    const auto& lastScissorSubBatch = fScissorSubBatches[lastBatch.fEndScissorSubBatchIdx - 1];
-    PrimitiveTallies batchTotalCounts = fTotalPrimitiveCounts[(int)GrScissorTest::kDisabled] -
-                                        lastBatch.fEndNonScissorIndices;
-    batchTotalCounts += fTotalPrimitiveCounts[(int)GrScissorTest::kEnabled] -
-                        lastScissorSubBatch.fEndPrimitiveIndices;
-
-    // This will invalidate lastBatch.
-    fBatches.push_back() = {
-        fTotalPrimitiveCounts[(int)GrScissorTest::kDisabled],
-        fScissorSubBatches.count(),
-        batchTotalCounts
-    };
-    return fBatches.count() - 1;
-}
-
-// Emits a contour's triangle fan.
-//
-// Classic Redbook fanning would be the triangles: [0  1  2], [0  2  3], ..., [0  n-2  n-1].
-//
-// This function emits the triangle: [0  n/3  n*2/3], and then recurses on all three sides. The
-// advantage to this approach is that for a convex-ish contour, it generates larger triangles.
-// Classic fanning tends to generate long, skinny triangles, which are expensive to draw since they
-// have a longer perimeter to rasterize and antialias.
-//
-// The indices array indexes the fan's points (think: glDrawElements), and must have at least log3
-// elements past the end for this method to use as scratch space.
-//
-// Returns the next triangle instance after the final one emitted.
-static TriPointInstance* emit_recursive_fan(
-        const SkTArray<SkPoint, true>& pts, SkTArray<int32_t, true>& indices, int firstIndex,
-        int indexCount, const Sk2f& devToAtlasOffset, TriPointInstance::Ordering ordering,
-        TriPointInstance out[]) {
-    if (indexCount < 3) {
-        return out;
-    }
-
-    int32_t oneThirdCount = indexCount / 3;
-    int32_t twoThirdsCount = (2 * indexCount) / 3;
-    out++->set(pts[indices[firstIndex]], pts[indices[firstIndex + oneThirdCount]],
-               pts[indices[firstIndex + twoThirdsCount]], devToAtlasOffset, ordering);
-
-    out = emit_recursive_fan(
-            pts, indices, firstIndex, oneThirdCount + 1, devToAtlasOffset, ordering, out);
-    out = emit_recursive_fan(
-            pts, indices, firstIndex + oneThirdCount, twoThirdsCount - oneThirdCount + 1,
-            devToAtlasOffset, ordering, out);
-
-    int endIndex = firstIndex + indexCount;
-    int32_t oldValue = indices[endIndex];
-    indices[endIndex] = indices[firstIndex];
-    out = emit_recursive_fan(
-            pts, indices, firstIndex + twoThirdsCount, indexCount - twoThirdsCount + 1,
-            devToAtlasOffset, ordering, out);
-    indices[endIndex] = oldValue;
-
-    return out;
-}
-
-void GrCCFiller::emitTessellatedFan(
-        const GrTriangulator::WindingVertex* vertices, int numVertices,
-        const Sk2f& devToAtlasOffset, TriPointInstance::Ordering ordering,
-        TriPointInstance* triPointInstanceData, QuadPointInstance* quadPointInstanceData,
-        GrCCFillGeometry::PrimitiveTallies* indices) {
-    for (int i = 0; i < numVertices; i += 3) {
-        int weight = vertices[i].fWinding;
-        SkASSERT(weight >= 1);
-        for (int j = 0; j < weight; ++j) {
-            // Unfortunately, there is not a way to increment stencil values by an amount larger
-            // than 1. Instead we draw the triangle 'weight' times.
-            triPointInstanceData[indices->fTriangles++].set(
-                    vertices[i].fPos, vertices[i + 1].fPos, vertices[i + 2].fPos, devToAtlasOffset,
-                    ordering);
-        }
-    }
-}
-
-bool GrCCFiller::prepareToDraw(GrOnFlushResourceProvider* onFlushRP) {
-    using Verb = GrCCFillGeometry::Verb;
-    SkASSERT(!fInstanceBuffer.hasGpuBuffer());
-    SkASSERT(fBatches.back().fEndNonScissorIndices == // Call closeCurrentBatch().
-             fTotalPrimitiveCounts[(int)GrScissorTest::kDisabled]);
-    SkASSERT(fBatches.back().fEndScissorSubBatchIdx == fScissorSubBatches.count());
-
-    auto triangleOrdering = TriPointInstance::Ordering::kXYInterleaved;
-
-    // Here we build a single instance buffer to share with every internal batch.
-    //
-    // CCPR processs 3 different types of primitives: triangles, quadratics, cubics. Each primitive
-    // type is further divided into instances that require a scissor and those that don't. This
-    // leaves us with 3*2 = 6 independent instance arrays to build for the GPU.
-    //
-    // Rather than place each instance array in its own GPU buffer, we allocate a single
-    // megabuffer and lay them all out side-by-side. We can offset the "baseInstance" parameter in
-    // our draw calls to direct the GPU to the applicable elements within a given array.
-    //
-    // We already know how big to make each of the 6 arrays from fTotalPrimitiveCounts, so layout is
-    // straightforward. Start with triangles and quadratics. They both view the instance buffer as
-    // an array of TriPointInstance[], so we can begin at zero and lay them out one after the other.
-    fBaseInstances[0].fTriangles = 0;
-    fBaseInstances[1].fTriangles = fBaseInstances[0].fTriangles +
-                                   fTotalPrimitiveCounts[0].fTriangles;
-    fBaseInstances[0].fQuadratics = fBaseInstances[1].fTriangles +
-                                    fTotalPrimitiveCounts[1].fTriangles;
-    fBaseInstances[1].fQuadratics = fBaseInstances[0].fQuadratics +
-                                    fTotalPrimitiveCounts[0].fQuadratics;
-    int triEndIdx = fBaseInstances[1].fQuadratics + fTotalPrimitiveCounts[1].fQuadratics;
-
-    // Wound triangles and cubics both view the same instance buffer as an array of
-    // QuadPointInstance[]. So, reinterpreting the instance data as QuadPointInstance[], we start
-    // them on the first index that will not overwrite previous TriPointInstance data.
-    int quadBaseIdx =
-            GrSizeDivRoundUp(triEndIdx * sizeof(TriPointInstance), sizeof(QuadPointInstance));
-    fBaseInstances[0].fWeightedTriangles = quadBaseIdx;
-    fBaseInstances[1].fWeightedTriangles = fBaseInstances[0].fWeightedTriangles +
-                                        fTotalPrimitiveCounts[0].fWeightedTriangles;
-    fBaseInstances[0].fCubics = fBaseInstances[1].fWeightedTriangles +
-                                fTotalPrimitiveCounts[1].fWeightedTriangles;
-    fBaseInstances[1].fCubics = fBaseInstances[0].fCubics + fTotalPrimitiveCounts[0].fCubics;
-    fBaseInstances[0].fConics = fBaseInstances[1].fCubics + fTotalPrimitiveCounts[1].fCubics;
-    fBaseInstances[1].fConics = fBaseInstances[0].fConics + fTotalPrimitiveCounts[0].fConics;
-    int quadEndIdx = fBaseInstances[1].fConics + fTotalPrimitiveCounts[1].fConics;
-
-    fInstanceBuffer.resetAndMapBuffer(onFlushRP, quadEndIdx * sizeof(QuadPointInstance));
-    if (!fInstanceBuffer.hasGpuBuffer()) {
-        SkDebugf("WARNING: failed to allocate CCPR fill instance buffer.\n");
-        return false;
-    }
-
-    auto triPointInstanceData = reinterpret_cast<TriPointInstance*>(fInstanceBuffer.data());
-    auto quadPointInstanceData = reinterpret_cast<QuadPointInstance*>(fInstanceBuffer.data());
-    SkASSERT(quadPointInstanceData);
-
-    PathInfo* nextPathInfo = fPathInfos.begin();
-    Sk2f devToAtlasOffset;
-    PrimitiveTallies instanceIndices[2] = {fBaseInstances[0], fBaseInstances[1]};
-    PrimitiveTallies* currIndices = nullptr;
-    SkSTArray<256, int32_t, true> currFan;
-    bool currFanIsTessellated = false;
-
-    const SkTArray<SkPoint, true>& pts = fGeometry.points();
-    int ptsIdx = -1;
-    int nextConicWeightIdx = 0;
-
-    // Expand the ccpr verbs into GPU instance buffers.
-    for (Verb verb : fGeometry.verbs()) {
-        switch (verb) {
-            case Verb::kBeginPath:
-                SkASSERT(currFan.empty());
-                currIndices = &instanceIndices[(int)nextPathInfo->scissorTest()];
-                devToAtlasOffset = Sk2f(static_cast<float>(nextPathInfo->devToAtlasOffset().fX),
-                                        static_cast<float>(nextPathInfo->devToAtlasOffset().fY));
-                currFanIsTessellated = nextPathInfo->hasFanTessellation();
-                if (currFanIsTessellated) {
-                    this->emitTessellatedFan(
-                            nextPathInfo->fanTessellation(), nextPathInfo->fanTessellationCount(),
-                            devToAtlasOffset, triangleOrdering, triPointInstanceData,
-                            quadPointInstanceData, currIndices);
-                }
-                ++nextPathInfo;
-                continue;
-
-            case Verb::kBeginContour:
-                SkASSERT(currFan.empty());
-                ++ptsIdx;
-                if (!currFanIsTessellated) {
-                    currFan.push_back(ptsIdx);
-                }
-                continue;
-
-            case Verb::kLineTo:
-                ++ptsIdx;
-                if (!currFanIsTessellated) {
-                    SkASSERT(!currFan.empty());
-                    currFan.push_back(ptsIdx);
-                }
-                continue;
-
-            case Verb::kMonotonicQuadraticTo:
-                triPointInstanceData[currIndices->fQuadratics++].set(
-                        &pts[ptsIdx], devToAtlasOffset, TriPointInstance::Ordering::kXYTransposed);
-                ptsIdx += 2;
-                if (!currFanIsTessellated) {
-                    SkASSERT(!currFan.empty());
-                    currFan.push_back(ptsIdx);
-                }
-                continue;
-
-            case Verb::kMonotonicCubicTo:
-                quadPointInstanceData[currIndices->fCubics++].set(
-                        &pts[ptsIdx], devToAtlasOffset[0], devToAtlasOffset[1]);
-                ptsIdx += 3;
-                if (!currFanIsTessellated) {
-                    SkASSERT(!currFan.empty());
-                    currFan.push_back(ptsIdx);
-                }
-                continue;
-
-            case Verb::kMonotonicConicTo:
-                quadPointInstanceData[currIndices->fConics++].setW(
-                        &pts[ptsIdx], devToAtlasOffset,
-                        fGeometry.getConicWeight(nextConicWeightIdx));
-                ptsIdx += 2;
-                ++nextConicWeightIdx;
-                if (!currFanIsTessellated) {
-                    SkASSERT(!currFan.empty());
-                    currFan.push_back(ptsIdx);
-                }
-                continue;
-
-            case Verb::kEndClosedContour:  // endPt == startPt.
-                if (!currFanIsTessellated) {
-                    SkASSERT(!currFan.empty());
-                    currFan.pop_back();
-                }
-                [[fallthrough]];
-            case Verb::kEndOpenContour:  // endPt != startPt.
-                SkASSERT(!currFanIsTessellated || currFan.empty());
-                if (!currFanIsTessellated && currFan.count() >= 3) {
-                    int fanSize = currFan.count();
-                    // Reserve space for emit_recursive_fan. Technically this can grow to
-                    // fanSize + log3(fanSize), but we approximate with log2.
-                    currFan.push_back_n(SkNextLog2(fanSize));
-                    SkDEBUGCODE(TriPointInstance* end =) emit_recursive_fan(
-                            pts, currFan, 0, fanSize, devToAtlasOffset, triangleOrdering,
-                            triPointInstanceData + currIndices->fTriangles);
-                    currIndices->fTriangles += fanSize - 2;
-                    SkASSERT(triPointInstanceData + currIndices->fTriangles == end);
-                }
-                currFan.reset();
-                continue;
-        }
-    }
-
-    fInstanceBuffer.unmapBuffer();
-
-    SkASSERT(nextPathInfo == fPathInfos.end());
-    SkASSERT(ptsIdx == pts.count() - 1);
-    SkASSERT(instanceIndices[0].fTriangles == fBaseInstances[1].fTriangles);
-    SkASSERT(instanceIndices[1].fTriangles == fBaseInstances[0].fQuadratics);
-    SkASSERT(instanceIndices[0].fQuadratics == fBaseInstances[1].fQuadratics);
-    SkASSERT(instanceIndices[1].fQuadratics == triEndIdx);
-    SkASSERT(instanceIndices[0].fWeightedTriangles == fBaseInstances[1].fWeightedTriangles);
-    SkASSERT(instanceIndices[1].fWeightedTriangles == fBaseInstances[0].fCubics);
-    SkASSERT(instanceIndices[0].fCubics == fBaseInstances[1].fCubics);
-    SkASSERT(instanceIndices[1].fCubics == fBaseInstances[0].fConics);
-    SkASSERT(instanceIndices[0].fConics == fBaseInstances[1].fConics);
-    SkASSERT(instanceIndices[1].fConics == quadEndIdx);
-
-    return true;
-}
-
-void GrCCFiller::drawFills(
-        GrOpFlushState* flushState, GrCCCoverageProcessor* proc, const GrPipeline& pipeline,
-        BatchID batchID, const SkIRect& drawBounds, const GrUserStencilSettings* stencil) const {
-    using PrimitiveType = GrCCCoverageProcessor::PrimitiveType;
-
-    SkASSERT(fInstanceBuffer.hasGpuBuffer());
-
-    GrResourceProvider* rp = flushState->resourceProvider();
-    const PrimitiveTallies& batchTotalCounts = fBatches[batchID].fTotalPrimitiveCounts;
-
-    int numSubpasses = proc->numSubpasses();
-
-    if (batchTotalCounts.fTriangles) {
-        for (int i = 0; i < numSubpasses; ++i) {
-            proc->reset(PrimitiveType::kTriangles, i, rp);
-            this->drawPrimitives(flushState, *proc, pipeline, stencil, batchID,
-                                 &PrimitiveTallies::fTriangles, drawBounds);
-        }
-    }
-
-    if (batchTotalCounts.fWeightedTriangles) {
-        SkASSERT(Algorithm::kStencilWindingCount != fAlgorithm);
-        for (int i = 0; i < numSubpasses; ++i) {
-            proc->reset(PrimitiveType::kWeightedTriangles, i, rp);
-            this->drawPrimitives(flushState, *proc, pipeline, stencil, batchID,
-                                 &PrimitiveTallies::fWeightedTriangles, drawBounds);
-        }
-    }
-
-    if (batchTotalCounts.fQuadratics) {
-        for (int i = 0; i < numSubpasses; ++i) {
-            proc->reset(PrimitiveType::kQuadratics, i, rp);
-            this->drawPrimitives(flushState, *proc, pipeline, stencil, batchID,
-                                 &PrimitiveTallies::fQuadratics, drawBounds);
-        }
-    }
-
-    if (batchTotalCounts.fCubics) {
-        for (int i = 0; i < numSubpasses; ++i) {
-            proc->reset(PrimitiveType::kCubics, i, rp);
-            this->drawPrimitives(flushState, *proc, pipeline, stencil, batchID,
-                                 &PrimitiveTallies::fCubics, drawBounds);
-        }
-    }
-
-    if (batchTotalCounts.fConics) {
-        for (int i = 0; i < numSubpasses; ++i) {
-            proc->reset(PrimitiveType::kConics, i, rp);
-            this->drawPrimitives(flushState, *proc, pipeline, stencil, batchID,
-                                 &PrimitiveTallies::fConics, drawBounds);
-        }
-    }
-}
-
-void GrCCFiller::drawPrimitives(
-        GrOpFlushState* flushState, const GrCCCoverageProcessor& proc, const GrPipeline& pipeline,
-        const GrUserStencilSettings* stencil, BatchID batchID, int PrimitiveTallies::*instanceType,
-        const SkIRect& drawBounds) const {
-    SkASSERT(pipeline.isScissorTestEnabled());
-
-    GrOpsRenderPass* renderPass = flushState->opsRenderPass();
-    proc.bindPipeline(flushState, pipeline, SkRect::Make(drawBounds), stencil);
-    proc.bindBuffers(renderPass, fInstanceBuffer.gpuBuffer());
-
-    SkASSERT(batchID > 0);
-    SkASSERT(batchID < fBatches.count());
-    const Batch& previousBatch = fBatches[batchID - 1];
-    const Batch& batch = fBatches[batchID];
-    SkDEBUGCODE(int totalInstanceCount = 0);
-
-    if (int instanceCount = batch.fEndNonScissorIndices.*instanceType -
-                            previousBatch.fEndNonScissorIndices.*instanceType) {
-        SkASSERT(instanceCount > 0);
-        int baseInstance = fBaseInstances[(int)GrScissorTest::kDisabled].*instanceType +
-                           previousBatch.fEndNonScissorIndices.*instanceType;
-        renderPass->setScissorRect(SkIRect::MakeXYWH(0, 0, drawBounds.width(),
-                                                     drawBounds.height()));
-        proc.drawInstances(renderPass, instanceCount, baseInstance);
-        SkDEBUGCODE(totalInstanceCount += instanceCount);
-    }
-
-    SkASSERT(previousBatch.fEndScissorSubBatchIdx > 0);
-    SkASSERT(batch.fEndScissorSubBatchIdx <= fScissorSubBatches.count());
-    int baseScissorInstance = fBaseInstances[(int)GrScissorTest::kEnabled].*instanceType;
-    for (int i = previousBatch.fEndScissorSubBatchIdx; i < batch.fEndScissorSubBatchIdx; ++i) {
-        const ScissorSubBatch& previousSubBatch = fScissorSubBatches[i - 1];
-        const ScissorSubBatch& scissorSubBatch = fScissorSubBatches[i];
-        int startIndex = previousSubBatch.fEndPrimitiveIndices.*instanceType;
-        int instanceCount = scissorSubBatch.fEndPrimitiveIndices.*instanceType - startIndex;
-        if (!instanceCount) {
-            continue;
-        }
-        SkASSERT(instanceCount > 0);
-        renderPass->setScissorRect(scissorSubBatch.fScissor);
-        proc.drawInstances(renderPass, instanceCount, baseScissorInstance + startIndex);
-        SkDEBUGCODE(totalInstanceCount += instanceCount);
-    }
-
-    SkASSERT(totalInstanceCount == batch.fTotalPrimitiveCounts.*instanceType);
-}
diff --git a/src/gpu/ccpr/GrCCFiller.h b/src/gpu/ccpr/GrCCFiller.h
deleted file mode 100644
index 50ace24..0000000
--- a/src/gpu/ccpr/GrCCFiller.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrCCPathParser_DEFINED
-#define GrCCPathParser_DEFINED
-
-#include "include/core/SkRect.h"
-#include "include/core/SkRefCnt.h"
-#include "src/core/SkPathPriv.h"
-#include "src/gpu/GrTriangulator.h"
-#include "src/gpu/ccpr/GrAutoMapVertexBuffer.h"
-#include "src/gpu/ccpr/GrCCCoverageProcessor.h"
-#include "src/gpu/ccpr/GrCCFillGeometry.h"
-#include "src/gpu/ops/GrDrawOp.h"
-
-class GrOnFlushResourceProvider;
-class SkMatrix;
-class SkPath;
-
-/**
- * This class parses SkPaths into CCPR primitives in GPU buffers, then issues calls to draw their
- * coverage counts.
- */
-class GrCCFiller {
-public:
-    enum class Algorithm : bool {
-        kStencilWindingCount
-    };
-
-    GrCCFiller(Algorithm, int numPaths, int numSkPoints, int numSkVerbs, int numConicWeights);
-
-    // Parses a device-space SkPath into the current batch, using the SkPath's original verbs and
-    // 'deviceSpacePts'. Accepts an optional post-device-space translate for placement in an atlas.
-    void parseDeviceSpaceFill(const SkPath&, const SkPoint* deviceSpacePts, GrScissorTest,
-                              const SkIRect& clippedDevIBounds, const SkIVector& devToAtlasOffset);
-
-    using BatchID = int;
-
-    // Compiles the outstanding parsed paths into a batch, and returns an ID that can be used to
-    // draw their fills in the future.
-    BatchID closeCurrentBatch();
-
-    // Builds internal GPU buffers and prepares for calls to drawFills(). Caller must close the
-    // current batch before calling this method, and cannot parse new paths afer.
-    bool prepareToDraw(GrOnFlushResourceProvider*);
-
-    // Called after prepareToDraw(). Draws the given batch of path fills.
-    void drawFills(GrOpFlushState*, GrCCCoverageProcessor*, const GrPipeline&, BatchID,
-                   const SkIRect& drawBounds,
-                   const GrUserStencilSettings* = &GrUserStencilSettings::kUnused) const;
-
-private:
-    static constexpr int kNumScissorModes = 2;
-    using PrimitiveTallies = GrCCFillGeometry::PrimitiveTallies;
-
-    // Every kBeginPath verb has a corresponding PathInfo entry.
-    class PathInfo {
-    public:
-        PathInfo(GrScissorTest scissorTest, const SkIVector& devToAtlasOffset)
-                : fScissorTest(scissorTest), fDevToAtlasOffset(devToAtlasOffset) {}
-
-        GrScissorTest scissorTest() const { return fScissorTest; }
-        const SkIVector& devToAtlasOffset() const { return fDevToAtlasOffset; }
-
-        // An empty tessellation fan is also valid; we use negative count to denote not tessellated.
-        bool hasFanTessellation() const { return fFanTessellationCount >= 0; }
-        int fanTessellationCount() const {
-            SkASSERT(this->hasFanTessellation());
-            return fFanTessellationCount;
-        }
-        const GrTriangulator::WindingVertex* fanTessellation() const {
-            SkASSERT(this->hasFanTessellation());
-            return fFanTessellation.get();
-        }
-        void tessellateFan(
-                Algorithm, const SkPath& originalPath, const GrCCFillGeometry&, int verbsIdx,
-                int ptsIdx, const SkIRect& clippedDevIBounds, PrimitiveTallies* newTriangleCounts);
-
-    private:
-        GrScissorTest fScissorTest;
-        SkIVector fDevToAtlasOffset;  // Translation from device space to location in atlas.
-        int fFanTessellationCount = -1;
-        std::unique_ptr<const GrTriangulator::WindingVertex[]> fFanTessellation;
-    };
-
-    // Defines a batch of CCPR primitives. Start indices are deduced by looking at the previous
-    // Batch in the list.
-    struct Batch {
-        PrimitiveTallies fEndNonScissorIndices;
-        int fEndScissorSubBatchIdx;
-        PrimitiveTallies fTotalPrimitiveCounts;
-    };
-
-    // Defines a sub-batch that will be drawn with the given scissor rect. Start indices are deduced
-    // by looking at the previous ScissorSubBatch in the list.
-    struct ScissorSubBatch {
-        PrimitiveTallies fEndPrimitiveIndices;
-        SkIRect fScissor;
-    };
-
-    void emitTessellatedFan(
-            const GrTriangulator::WindingVertex*, int numVertices, const Sk2f& devToAtlasOffset,
-            GrCCCoverageProcessor::TriPointInstance::Ordering,
-            GrCCCoverageProcessor::TriPointInstance*, GrCCCoverageProcessor::QuadPointInstance*,
-            GrCCFillGeometry::PrimitiveTallies*);
-    void drawPrimitives(GrOpFlushState*, const GrCCCoverageProcessor&, const GrPipeline&,
-                        const GrUserStencilSettings*, BatchID,
-                        int PrimitiveTallies::*instanceType, const SkIRect& drawBounds) const;
-
-    const Algorithm fAlgorithm;
-    GrCCFillGeometry fGeometry;
-    SkSTArray<32, PathInfo, true> fPathInfos;
-    SkSTArray<32, Batch, true> fBatches;
-    SkSTArray<32, ScissorSubBatch, true> fScissorSubBatches;
-    PrimitiveTallies fTotalPrimitiveCounts[kNumScissorModes];
-    int fMaxMeshesPerDraw = 0;
-
-    GrAutoMapVertexBuffer fInstanceBuffer;
-    PrimitiveTallies fBaseInstances[kNumScissorModes];
-};
-
-#endif
diff --git a/src/gpu/ccpr/GrCCPerFlushResources.cpp b/src/gpu/ccpr/GrCCPerFlushResources.cpp
index a48c3fc..674f620 100644
--- a/src/gpu/ccpr/GrCCPerFlushResources.cpp
+++ b/src/gpu/ccpr/GrCCPerFlushResources.cpp
@@ -8,118 +8,23 @@
 #include "src/gpu/ccpr/GrCCPerFlushResources.h"
 
 #include "include/gpu/GrRecordingContext.h"
+#include "src/gpu/GrFixedClip.h"
 #include "src/gpu/GrMemoryPool.h"
 #include "src/gpu/GrOnFlushResourceProvider.h"
 #include "src/gpu/GrRecordingContextPriv.h"
 #include "src/gpu/GrSurfaceDrawContext.h"
-#include "src/gpu/ccpr/GrSampleMaskProcessor.h"
 #include "src/gpu/geometry/GrStyledShape.h"
-
-#include <algorithm>
-
-using FillBatchID = GrCCFiller::BatchID;
-
-namespace {
-
-// Base class for an Op that renders a CCPR atlas.
-class AtlasOp : public GrDrawOp {
-public:
-    FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
-    GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
-                                      bool hasMixedSampledCoverage, GrClampType) override {
-        return GrProcessorSet::EmptySetAnalysis();
-    }
-    CombineResult onCombineIfPossible(GrOp* other, SkArenaAlloc*, const GrCaps&) override {
-        // We will only make multiple copy ops if they have different source proxies.
-        // TODO: make use of texture chaining.
-        return CombineResult::kCannotCombine;
-    }
-
-protected:
-    AtlasOp(uint32_t classID, sk_sp<const GrCCPerFlushResources> resources,
-            const SkISize& drawBounds)
-            : GrDrawOp(classID)
-            , fResources(std::move(resources)) {
-        this->setBounds(SkRect::MakeIWH(drawBounds.width(), drawBounds.height()),
-                        GrOp::HasAABloat::kNo, GrOp::IsHairline::kNo);
-    }
-
-    const sk_sp<const GrCCPerFlushResources> fResources;
-
-private:
-    void onPrePrepare(GrRecordingContext*,
-                      const GrSurfaceProxyView& writeView,
-                      GrAppliedClip*,
-                      const GrXferProcessor::DstProxyView&,
-                      GrXferBarrierFlags renderPassXferBarriers,
-                      GrLoadOp colorLoadOp) final {}
-    void onPrepare(GrOpFlushState*) final {}
-};
-
-// Renders coverage counts to a CCPR atlas using the resources' pre-filled GrCCPathParser.
-template<typename ProcessorType> class RenderAtlasOp : public AtlasOp {
-public:
-    DEFINE_OP_CLASS_ID
-
-    static GrOp::Owner Make(
-            GrRecordingContext* context, sk_sp<const GrCCPerFlushResources> resources,
-            FillBatchID fillBatchID, const SkISize& drawBounds) {
-        return GrOp::Make<RenderAtlasOp>(context,
-                std::move(resources), fillBatchID, drawBounds);
-    }
-
-    // GrDrawOp interface.
-    const char* name() const override { return "RenderAtlasOp (CCPR)"; }
-
-    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
-        ProcessorType proc;
-        GrPipeline pipeline(GrScissorTest::kEnabled, SkBlendMode::kPlus,
-                            flushState->drawOpArgs().writeView().swizzle());
-        fResources->filler().drawFills(flushState, &proc, pipeline, fFillBatchID, fDrawBounds);
-    }
-
-private:
-    friend class ::GrOp; // for ctor
-
-    RenderAtlasOp(sk_sp<const GrCCPerFlushResources> resources, FillBatchID fillBatchID,
-                  const SkISize& drawBounds)
-            : AtlasOp(ClassID(), std::move(resources), drawBounds)
-            , fFillBatchID(fillBatchID)
-            , fDrawBounds(SkIRect::MakeWH(drawBounds.width(), drawBounds.height())) {
-    }
-
-    const FillBatchID fFillBatchID;
-    const SkIRect fDrawBounds;
-};
-
-}  // namespace
+#include "src/gpu/ops/GrFillRectOp.h"
 
 GrCCPerFlushResources::GrCCPerFlushResources(GrOnFlushResourceProvider* onFlushRP,
-                                             const GrCCPerFlushResourceSpecs& specs)
-        : fFiller(GrCCFiller::Algorithm::kStencilWindingCount,
-                  specs.fNumClipPaths,
-                  specs.fRenderedPathStats.fNumTotalSkPoints,
-                  specs.fRenderedPathStats.fNumTotalSkVerbs,
-                  specs.fRenderedPathStats.fNumTotalConicWeights)
-        , fRenderedAtlasStack(specs.fRenderedAtlasSpecs, onFlushRP->caps()) {
-    int numRenderedPaths = specs.fNumClipPaths;
-    fStencilResolveBuffer.resetAndMapBuffer(
-            onFlushRP, numRenderedPaths * sizeof(GrStencilAtlasOp::ResolveRectInstance));
-    if (!fStencilResolveBuffer.hasGpuBuffer()) {
-        SkDebugf("WARNING: failed to allocate CCPR stencil resolve buffer. "
-                 "No paths will be drawn.\n");
-        return;
-    }
-    SkDEBUGCODE(fEndStencilResolveInstance = numRenderedPaths);
+                                             const GrCCAtlas::Specs& specs)
+        : fRenderedAtlasStack(specs, onFlushRP->caps()) {
 }
 
 const GrCCAtlas* GrCCPerFlushResources::renderDeviceSpacePathInAtlas(
-        const SkIRect& clipIBounds, const SkPath& devPath, const SkIRect& devPathIBounds,
-        GrFillRule fillRule, SkIVector* devToAtlasOffset) {
-    SkASSERT(this->isMapped());
-
+        GrOnFlushResourceProvider* onFlushRP, const SkIRect& clipIBounds, const SkPath& devPath,
+        const SkIRect& devPathIBounds, GrFillRule fillRule, SkIVector* devToAtlasOffset) {
     if (devPath.isEmpty()) {
-        SkDEBUGCODE(--fEndStencilResolveInstance);
         return nullptr;
     }
 
@@ -132,87 +37,155 @@
         enableScissorInAtlas = GrScissorTest::kEnabled;
     } else {
         // The clip and path bounds do not intersect. Draw nothing.
-        SkDEBUGCODE(--fEndStencilResolveInstance);
         return nullptr;
     }
 
-    this->placeRenderedPathInAtlas(clippedPathIBounds, enableScissorInAtlas, devToAtlasOffset);
-    fFiller.parseDeviceSpaceFill(devPath, SkPathPriv::PointData(devPath), enableScissorInAtlas,
-                                 clippedPathIBounds, *devToAtlasOffset);
+    this->placeRenderedPathInAtlas(onFlushRP, clippedPathIBounds, enableScissorInAtlas,
+                                   devToAtlasOffset);
 
-    // In MSAA mode we also record an internal draw instance that will be used to resolve stencil
-    // winding values to coverage when the atlas is generated.
-    this->recordStencilResolveInstance(clippedPathIBounds, *devToAtlasOffset, fillRule);
+    SkMatrix atlasMatrix = SkMatrix::Translate(devToAtlasOffset->fX, devToAtlasOffset->fY);
+    this->enqueueRenderedPath(devPath, fillRule, clippedPathIBounds, atlasMatrix,
+                              enableScissorInAtlas, *devToAtlasOffset);
 
     return &fRenderedAtlasStack.current();
 }
 
 void GrCCPerFlushResources::placeRenderedPathInAtlas(
-        const SkIRect& clippedPathIBounds, GrScissorTest scissorTest, SkIVector* devToAtlasOffset) {
+        GrOnFlushResourceProvider* onFlushRP, const SkIRect& clippedPathIBounds,
+        GrScissorTest scissorTest, SkIVector* devToAtlasOffset) {
     if (GrCCAtlas* retiredAtlas =
                 fRenderedAtlasStack.addRect(clippedPathIBounds, devToAtlasOffset)) {
-        // We did not fit in the previous coverage count atlas and it was retired. Close the path
-        // parser's current batch (which does not yet include the path we just parsed). We will
-        // render this batch into the retired atlas during finalize().
-        retiredAtlas->setFillBatchID(fFiller.closeCurrentBatch());
-        retiredAtlas->setEndStencilResolveInstance(fNextStencilResolveInstanceIdx);
+        // We did not fit in the previous coverage count atlas and it was retired. Render the
+        // retired atlas.
+        this->flushRenderedPaths(onFlushRP, retiredAtlas);
     }
 }
 
-void GrCCPerFlushResources::recordStencilResolveInstance(
-        const SkIRect& clippedPathIBounds, const SkIVector& devToAtlasOffset, GrFillRule fillRule) {
-    SkASSERT(fNextStencilResolveInstanceIdx < fEndStencilResolveInstance);
-
-    SkIRect atlasIBounds = clippedPathIBounds.makeOffset(devToAtlasOffset);
-    if (GrFillRule::kEvenOdd == fillRule) {
-        // Make even/odd fills counterclockwise. The resolve draw uses two-sided stencil, with
-        // "nonzero" settings in front and "even/odd" settings in back.
-        std::swap(atlasIBounds.fLeft, atlasIBounds.fRight);
+void GrCCPerFlushResources::enqueueRenderedPath(const SkPath& path, GrFillRule fillRule,
+                                                const SkIRect& clippedDevIBounds,
+                                                const SkMatrix& pathToAtlasMatrix,
+                                                GrScissorTest enableScissorInAtlas,
+                                                SkIVector devToAtlasOffset) {
+    SkPath* atlasPath;
+    if (enableScissorInAtlas == GrScissorTest::kDisabled) {
+        atlasPath = &fAtlasPaths[(int)fillRule].fUberPath;
+    } else {
+        auto& [scissoredPath, scissor] = fAtlasPaths[(int)fillRule].fScissoredPaths.push_back();
+        scissor = clippedDevIBounds.makeOffset(devToAtlasOffset);
+        atlasPath = &scissoredPath;
     }
-    fStencilResolveBuffer[fNextStencilResolveInstanceIdx++] = {
-            (int16_t)atlasIBounds.left(), (int16_t)atlasIBounds.top(),
-            (int16_t)atlasIBounds.right(), (int16_t)atlasIBounds.bottom()};
+    auto origin = clippedDevIBounds.topLeft() + devToAtlasOffset;
+    atlasPath->moveTo(origin.fX, origin.fY);  // Implicit moveTo(0,0).
+    atlasPath->addPath(path, pathToAtlasMatrix);
+}
+
+static void draw_stencil_to_coverage(GrOnFlushResourceProvider* onFlushRP,
+                                     GrSurfaceDrawContext* surfaceDrawContext, SkRect&& rect) {
+    auto aaType = GrAAType::kMSAA;
+    auto fillRectFlags = GrSimpleMeshDrawOpHelper::InputFlags::kNone;
+
+    // This will be the final op in the surfaceDrawContext. So if Ganesh is planning to discard the
+    // stencil values anyway, then we might not actually need to reset the stencil values back to 0.
+    bool mustResetStencil = !onFlushRP->caps()->discardStencilValuesAfterRenderPass();
+
+    if (surfaceDrawContext->numSamples() == 1) {
+        // We are mixed sampled. We need to either enable conservative raster (preferred) or disable
+        // MSAA in order to avoid double blend artifacts. (Even if we disable MSAA for the cover
+        // geometry, the stencil test is still multisampled and will still produce smooth results.)
+        if (onFlushRP->caps()->conservativeRasterSupport()) {
+            fillRectFlags |= GrSimpleMeshDrawOpHelper::InputFlags::kConservativeRaster;
+        } else {
+            aaType = GrAAType::kNone;
+        }
+        mustResetStencil = true;
+    }
+
+    const GrUserStencilSettings* stencil;
+    if (mustResetStencil) {
+        constexpr static GrUserStencilSettings kTestAndResetStencil(
+            GrUserStencilSettings::StaticInit<
+                0x0000,
+                GrUserStencilTest::kNotEqual,
+                0xffff,
+                GrUserStencilOp::kZero,
+                GrUserStencilOp::kKeep,
+                0xffff>());
+
+        // Outset the cover rect in case there are T-junctions in the path bounds.
+        rect.outset(1, 1);
+        stencil = &kTestAndResetStencil;
+    } else {
+        constexpr static GrUserStencilSettings kTestStencil(
+            GrUserStencilSettings::StaticInit<
+                0x0000,
+                GrUserStencilTest::kNotEqual,
+                0xffff,
+                GrUserStencilOp::kKeep,
+                GrUserStencilOp::kKeep,
+                0xffff>());
+
+        stencil = &kTestStencil;
+    }
+
+    GrPaint paint;
+    paint.setColor4f(SK_PMColor4fWHITE);
+    GrQuad coverQuad(rect);
+    DrawQuad drawQuad{coverQuad, coverQuad, GrQuadAAFlags::kAll};
+    auto coverOp = GrFillRectOp::Make(surfaceDrawContext->recordingContext(), std::move(paint),
+                                      aaType, &drawQuad, stencil, fillRectFlags);
+    surfaceDrawContext->addDrawOp(nullptr, std::move(coverOp));
+}
+
+void GrCCPerFlushResources::flushRenderedPaths(GrOnFlushResourceProvider* onFlushRP,
+                                               GrCCAtlas* atlas) {
+    auto surfaceDrawContext = atlas->instantiate(onFlushRP);
+    if (!surfaceDrawContext) {
+        for (int i = 0; i < (int)SK_ARRAY_COUNT(fAtlasPaths); ++i) {
+            fAtlasPaths[i].fUberPath.reset();
+            fAtlasPaths[i].fScissoredPaths.reset();
+        }
+        return;
+    }
+
+    for (int i = 0; i < (int)SK_ARRAY_COUNT(fAtlasPaths); ++i) {
+        SkPathFillType fillType = (i == (int)GrFillRule::kNonzero) ? SkPathFillType::kWinding
+                                                                   : SkPathFillType::kEvenOdd;
+        SkPath& uberPath = fAtlasPaths[i].fUberPath;
+        if (!uberPath.isEmpty()) {
+            uberPath.setIsVolatile(true);
+            uberPath.setFillType(fillType);
+            surfaceDrawContext->stencilPath(nullptr, GrAA::kYes, SkMatrix::I(), uberPath);
+            uberPath.reset();
+        }
+        for (auto& [scissoredPath, scissor] : fAtlasPaths[i].fScissoredPaths) {
+            GrFixedClip fixedClip(
+                    surfaceDrawContext->asRenderTargetProxy()->backingStoreDimensions(), scissor);
+            scissoredPath.setIsVolatile(true);
+            scissoredPath.setFillType(fillType);
+            surfaceDrawContext->stencilPath(&fixedClip, GrAA::kYes, SkMatrix::I(), scissoredPath);
+        }
+        fAtlasPaths[i].fScissoredPaths.reset();
+    }
+
+    draw_stencil_to_coverage(onFlushRP, surfaceDrawContext.get(),
+                             SkRect::MakeSize(SkSize::Make(atlas->drawBounds())));
+
+    if (surfaceDrawContext->asSurfaceProxy()->requiresManualMSAAResolve()) {
+        onFlushRP->addTextureResolveTask(sk_ref_sp(surfaceDrawContext->asTextureProxy()),
+                                         GrSurfaceProxy::ResolveFlags::kMSAA);
+    }
 }
 
 bool GrCCPerFlushResources::finalize(GrOnFlushResourceProvider* onFlushRP) {
-    SkASSERT(this->isMapped());
-    SkASSERT(fNextStencilResolveInstanceIdx == fEndStencilResolveInstance);
-
-    if (fStencilResolveBuffer.hasGpuBuffer()) {
-        fStencilResolveBuffer.unmapBuffer();
-    }
-
     if (!fRenderedAtlasStack.empty()) {
-        fRenderedAtlasStack.current().setFillBatchID(fFiller.closeCurrentBatch());
-        fRenderedAtlasStack.current().setEndStencilResolveInstance(fNextStencilResolveInstanceIdx);
+        this->flushRenderedPaths(onFlushRP, &fRenderedAtlasStack.current());
     }
-
-    // Build the GPU buffers to render path coverage counts. (This must not happen until after the
-    // final calls to fFiller/fStroker.closeCurrentBatch().)
-    if (!fFiller.prepareToDraw(onFlushRP)) {
-        return false;
+#ifdef SK_DEBUG
+    // These paths should have been rendered and reset to empty by this point.
+    for (const auto& [uberPath, scissoredPaths] : fAtlasPaths) {
+        SkASSERT(uberPath.isEmpty());
+        SkASSERT(scissoredPaths.empty());
     }
-
-    // Render the coverage count atlas(es).
-    int baseStencilResolveInstance = 0;
-    for (GrCCAtlas& atlas : fRenderedAtlasStack.atlases()) {
-        if (auto rtc = atlas.instantiate(onFlushRP)) {
-            GrOp::Owner op;
-            op = GrStencilAtlasOp::Make(
-                    rtc->recordingContext(), sk_ref_sp(this), atlas.getFillBatchID(),
-                    baseStencilResolveInstance,
-                    atlas.getEndStencilResolveInstance(), atlas.drawBounds());
-            rtc->addDrawOp(nullptr, std::move(op));
-            if (rtc->asSurfaceProxy()->requiresManualMSAAResolve()) {
-                onFlushRP->addTextureResolveTask(sk_ref_sp(rtc->asTextureProxy()),
-                                                 GrSurfaceProxy::ResolveFlags::kMSAA);
-            }
-        }
-
-        SkASSERT(atlas.getEndStencilResolveInstance() >= baseStencilResolveInstance);
-        baseStencilResolveInstance = atlas.getEndStencilResolveInstance();
-    }
-    SkASSERT(baseStencilResolveInstance == fEndStencilResolveInstance);
-
+#endif
     return true;
 }
diff --git a/src/gpu/ccpr/GrCCPerFlushResources.h b/src/gpu/ccpr/GrCCPerFlushResources.h
index 81c602f..6c67401 100644
--- a/src/gpu/ccpr/GrCCPerFlushResources.h
+++ b/src/gpu/ccpr/GrCCPerFlushResources.h
@@ -9,42 +9,9 @@
 #define GrCCPerFlushResources_DEFINED
 
 #include "src/gpu/GrNonAtomicRef.h"
-#include "src/gpu/ccpr/GrAutoMapVertexBuffer.h"
 #include "src/gpu/ccpr/GrCCAtlas.h"
-#include "src/gpu/ccpr/GrCCFiller.h"
-#include "src/gpu/ccpr/GrStencilAtlasOp.h"
 
-class GrCCPathCache;
-class GrCCPathCacheEntry;
-class GrOctoBounds;
 class GrOnFlushResourceProvider;
-class GrStyledShape;
-
-/**
- * This struct counts values that help us preallocate buffers for rendered path geometry.
- */
-struct GrCCRenderedPathStats {
-    int fMaxPointsPerPath = 0;
-    int fNumTotalSkPoints = 0;
-    int fNumTotalSkVerbs = 0;
-    int fNumTotalConicWeights = 0;
-
-    void statPath(const SkPath&);
-};
-
-/**
- * This struct encapsulates the minimum and desired requirements for the GPU resources required by
- * CCPR in a given flush.
- */
-struct GrCCPerFlushResourceSpecs {
-    int fNumClipPaths = 0;
-    GrCCRenderedPathStats fRenderedPathStats;
-    GrCCAtlas::Specs fRenderedAtlasSpecs;
-
-    bool isEmpty() const {
-        return 0 == fNumClipPaths;
-    }
-};
 
 /**
  * This class wraps all the GPU resources that CCPR builds at flush time. It is allocated in CCPR's
@@ -53,57 +20,39 @@
  */
 class GrCCPerFlushResources : public GrNonAtomicRef<GrCCPerFlushResources> {
 public:
-    GrCCPerFlushResources(
-            GrOnFlushResourceProvider*,const GrCCPerFlushResourceSpecs&);
-
-    bool isMapped() const { return fStencilResolveBuffer.isMapped(); }
+    GrCCPerFlushResources(GrOnFlushResourceProvider*, const GrCCAtlas::Specs&);
 
     // Renders a path into an atlas.
     const GrCCAtlas* renderDeviceSpacePathInAtlas(
-            const SkIRect& clipIBounds, const SkPath& devPath, const SkIRect& devPathIBounds,
-            GrFillRule fillRule, SkIVector* devToAtlasOffset);
+            GrOnFlushResourceProvider*, const SkIRect& clipIBounds, const SkPath& devPath,
+            const SkIRect& devPathIBounds, GrFillRule fillRule, SkIVector* devToAtlasOffset);
 
     // Finishes off the GPU buffers and renders the atlas(es).
     bool finalize(GrOnFlushResourceProvider*);
 
     // Accessors used by draw calls, once the resources have been finalized.
-    const GrCCFiller& filler() const { SkASSERT(!this->isMapped()); return fFiller; }
-    sk_sp<const GrGpuBuffer> stencilResolveBuffer() const {
-        SkASSERT(!this->isMapped());
-        return fStencilResolveBuffer.gpuBuffer();
-    }
-
-private:
     void placeRenderedPathInAtlas(
-            const SkIRect& clippedPathIBounds, GrScissorTest, SkIVector* devToAtlasOffset);
+            GrOnFlushResourceProvider*, const SkIRect& clippedPathIBounds, GrScissorTest,
+            SkIVector* devToAtlasOffset);
 
-    // In MSAA mode we record an additional instance per path that draws a rectangle on top of its
-    // corresponding path in the atlas and resolves stencil winding values to coverage.
-    void recordStencilResolveInstance(
-            const SkIRect& clippedPathIBounds, const SkIVector& devToAtlasOffset, GrFillRule);
+    // Enqueues the given path to be rendered during the next call to flushRenderedPaths().
+    void enqueueRenderedPath(const SkPath&, GrFillRule, const SkIRect& clippedDevIBounds,
+                             const SkMatrix& pathToAtlasMatrix, GrScissorTest enableScissorInAtlas,
+                             SkIVector devToAtlasOffset);
 
-    GrCCFiller fFiller;
+    // Renders all enqueued paths into the given atlas and clears our path queue.
+    void flushRenderedPaths(GrOnFlushResourceProvider*, GrCCAtlas*);
+
     GrCCAtlasStack fRenderedAtlasStack;
 
-    // Used in MSAA mode make an intermediate draw that resolves stencil winding values to coverage.
-    GrTAutoMapVertexBuffer<GrStencilAtlasOp::ResolveRectInstance> fStencilResolveBuffer;
-    int fNextStencilResolveInstanceIdx = 0;
-    SkDEBUGCODE(int fEndStencilResolveInstance);
-
-public:
-#ifdef SK_DEBUG
-    void debugOnly_didReuseRenderedPath() {
-        --fEndStencilResolveInstance;
-    }
-#endif
-    const GrTexture* testingOnly_frontRenderedAtlasTexture() const;
+    // Paths to be rendered in the atlas we are currently building.
+    struct AtlasPaths {
+        SkPath fUberPath;  // Contains all contours from all non-scissored paths.
+        SkSTArray<32, std::tuple<SkPath, SkIRect>> fScissoredPaths;
+    };
+    static_assert((int)GrFillRule::kNonzero == 0);
+    static_assert((int)GrFillRule::kEvenOdd == 1);
+    AtlasPaths fAtlasPaths[2];  // One for "nonzero" fill rule and one for "even-odd".
 };
 
-inline void GrCCRenderedPathStats::statPath(const SkPath& path) {
-    fMaxPointsPerPath = std::max(fMaxPointsPerPath, path.countPoints());
-    fNumTotalSkPoints += path.countPoints();
-    fNumTotalSkVerbs += path.countVerbs();
-    fNumTotalConicWeights += SkPathPriv::ConicWeightCnt(path);
-}
-
 #endif
diff --git a/src/gpu/ccpr/GrCCPerOpsTaskPaths.h b/src/gpu/ccpr/GrCCPerOpsTaskPaths.h
index 4692dc9..d2d2965 100644
--- a/src/gpu/ccpr/GrCCPerOpsTaskPaths.h
+++ b/src/gpu/ccpr/GrCCPerOpsTaskPaths.h
@@ -23,8 +23,6 @@
 // DDL TODO: given the usage pattern in DDL mode, this could probably be non-atomic refcounting.
 struct GrCCPerOpsTaskPaths : public SkRefCnt {
     std::map<uint32_t, GrCCClipPath> fClipPaths;
-    SkSTArenaAlloc<10 * 1024> fAllocator{10 * 1024 * 2};
-    sk_sp<const GrCCPerFlushResources> fFlushResources;
 };
 
 #endif
diff --git a/src/gpu/ccpr/GrCCQuadraticShader.cpp b/src/gpu/ccpr/GrCCQuadraticShader.cpp
deleted file mode 100644
index 135cb77..0000000
--- a/src/gpu/ccpr/GrCCQuadraticShader.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/ccpr/GrCCQuadraticShader.h"
-
-#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
-#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
-
-void GrCCQuadraticShader::emitSetupCode(
-        GrGLSLVertexGeoBuilder* s, const char* pts, const char** outHull4) const {
-    s->declareGlobal(fQCoordMatrix);
-    s->codeAppendf("%s = float2x2(1, 1, .5, 0) * inverse(float2x2(%s[2] - %s[0], %s[1] - %s[0]));",
-                   fQCoordMatrix.c_str(), pts, pts, pts, pts);
-
-    s->declareGlobal(fQCoord0);
-    s->codeAppendf("%s = %s[0];", fQCoord0.c_str(), pts);
-
-    if (outHull4) {
-        // Clip the bezier triangle by the tangent line at maximum height. Quadratics have the nice
-        // property that maximum height always occurs at T=.5. This is a simple application for
-        // De Casteljau's algorithm.
-        s->codeAppend ("float2 quadratic_hull[4];");
-        s->codeAppendf("quadratic_hull[0] = %s[0];", pts);
-        s->codeAppendf("quadratic_hull[1] = (%s[0] + %s[1]) * .5;", pts, pts);
-        s->codeAppendf("quadratic_hull[2] = (%s[1] + %s[2]) * .5;", pts, pts);
-        s->codeAppendf("quadratic_hull[3] = %s[2];", pts);
-        *outHull4 = "quadratic_hull";
-    }
-}
-
-void GrCCQuadraticShader::onEmitVaryings(
-        GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, SkString* code,
-        const char* position, const char* coverage, const char* cornerCoverage, const char* wind) {
-    fCoord_fGrad.reset(kFloat4_GrSLType, scope);
-    varyingHandler->addVarying("coord_and_grad", &fCoord_fGrad);
-    code->appendf("%s.xy = %s * (%s - %s);",  // Quadratic coords.
-                  OutName(fCoord_fGrad), fQCoordMatrix.c_str(), position, fQCoord0.c_str());
-    code->appendf("%s.zw = 2*bloat * float2(2 * %s.x, -1) * %s;",  // Gradient.
-                  OutName(fCoord_fGrad), OutName(fCoord_fGrad), fQCoordMatrix.c_str());
-}
-
-void GrCCQuadraticShader::emitSampleMaskCode(GrGLSLFPFragmentBuilder* f) const {
-    f->codeAppendf("float x = %s.x, y = %s.y;", fCoord_fGrad.fsIn(), fCoord_fGrad.fsIn());
-    f->codeAppendf("float f = x*x - y;");
-    f->codeAppendf("float2 grad = %s.zw;", fCoord_fGrad.fsIn());
-    f->applyFnToMultisampleMask("f", "grad", GrGLSLFPFragmentBuilder::ScopeFlags::kTopLevel);
-}
diff --git a/src/gpu/ccpr/GrCCQuadraticShader.h b/src/gpu/ccpr/GrCCQuadraticShader.h
deleted file mode 100644
index 055b771..0000000
--- a/src/gpu/ccpr/GrCCQuadraticShader.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrCCQuadraticShader_DEFINED
-#define GrCCQuadraticShader_DEFINED
-
-#include "src/gpu/ccpr/GrCCCoverageProcessor.h"
-
-/**
- * This class renders the coverage of closed quadratic curves using the techniques outlined in
- * "Resolution Independent Curve Rendering using Programmable Graphics Hardware" by Charles Loop and
- * Jim Blinn:
- *
- * https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
- *
- * The provided curves must be monotonic with respect to the vector of their closing edge [P2 - P0].
- * (Use GrCCGeometry::quadraticTo().)
- */
-class GrCCQuadraticShader : public GrCCCoverageProcessor::Shader {
-public:
-    void emitSetupCode(
-            GrGLSLVertexGeoBuilder*, const char* pts, const char** outHull4) const override;
-
-    void onEmitVaryings(
-            GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, const char* position,
-            const char* coverage, const char* cornerCoverage, const char* wind) override;
-
-    void emitSampleMaskCode(GrGLSLFPFragmentBuilder*) const override;
-
-private:
-    const GrShaderVar fQCoordMatrix{"qcoord_matrix", kFloat2x2_GrSLType};
-    const GrShaderVar fQCoord0{"qcoord0", kFloat2_GrSLType};
-    GrGLSLVarying fCoord_fGrad;
-    GrGLSLVarying fEdge_fWind_fCorner;
-};
-
-#endif
diff --git a/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp b/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
index d587f50..a93ca9f 100644
--- a/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
+++ b/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
@@ -101,10 +101,10 @@
         return;  // Nothing to draw.
     }
 
-    GrCCPerFlushResourceSpecs specs;
+    GrCCAtlas::Specs specs;
     int maxPreferredRTSize = onFlushRP->caps()->maxPreferredRenderTargetSize();
-    specs.fRenderedAtlasSpecs.fMaxPreferredTextureSize = maxPreferredRTSize;
-    specs.fRenderedAtlasSpecs.fMinTextureSize = std::min(512, maxPreferredRTSize);
+    specs.fMaxPreferredTextureSize = maxPreferredRTSize;
+    specs.fMinTextureSize = std::min(512, maxPreferredRTSize);
 
     // Move the per-opsTask paths that are about to be flushed from fPendingPaths to fFlushingPaths,
     // and count them up so we can preallocate buffers.
@@ -123,45 +123,26 @@
         }
     }
 
-    if (specs.isEmpty()) {
-        return;  // Nothing to draw.
-    }
+    fPerFlushResources = std::make_unique<GrCCPerFlushResources>(onFlushRP, specs);
 
-    auto resources = sk_make_sp<GrCCPerFlushResources>(onFlushRP, specs);
-    if (!resources->isMapped()) {
-        return;  // Some allocation failed.
-    }
-
-    // Layout the atlas(es) and parse paths.
+    // Layout the atlas(es) and render paths.
     for (const auto& flushingPaths : fFlushingPaths) {
         for (auto& clipsIter : flushingPaths->fClipPaths) {
-            clipsIter.second.renderPathInAtlas(resources.get(), onFlushRP);
+            clipsIter.second.renderPathInAtlas(fPerFlushResources.get(), onFlushRP);
         }
     }
 
     // Allocate resources and then render the atlas(es).
-    if (!resources->finalize(onFlushRP)) {
-        return;
-    }
-
-    // Commit flushing paths to the resources once they are successfully completed.
-    for (auto& flushingPaths : fFlushingPaths) {
-        SkASSERT(!flushingPaths->fFlushResources);
-        flushingPaths->fFlushResources = resources;
-    }
+    fPerFlushResources->finalize(onFlushRP);
 }
 
 void GrCoverageCountingPathRenderer::postFlush(GrDeferredUploadToken,
                                                SkSpan<const uint32_t> /* taskIDs */) {
     SkASSERT(fFlushing);
 
-    if (!fFlushingPaths.empty()) {
-        // In DDL mode these aren't guaranteed to be deleted so we must clear out the perFlush
-        // resources manually.
-        for (auto& flushingPaths : fFlushingPaths) {
-            flushingPaths->fFlushResources = nullptr;
-        }
+    fPerFlushResources.reset();
 
+    if (!fFlushingPaths.empty()) {
         // We wait to erase these until after flush, once Ops and FPs are done accessing their data.
         fFlushingPaths.reset();
     }
diff --git a/src/gpu/ccpr/GrCoverageCountingPathRenderer.h b/src/gpu/ccpr/GrCoverageCountingPathRenderer.h
index 09d06e0..12795dd 100644
--- a/src/gpu/ccpr/GrCoverageCountingPathRenderer.h
+++ b/src/gpu/ccpr/GrCoverageCountingPathRenderer.h
@@ -83,10 +83,9 @@
     // (It will only contain elements when fFlushing is true.)
     SkSTArray<4, sk_sp<GrCCPerOpsTaskPaths>> fFlushingPaths;
 
-    SkDEBUGCODE(bool fFlushing = false);
+    std::unique_ptr<GrCCPerFlushResources> fPerFlushResources;
 
-public:
-    const GrCCPerFlushResources* testingOnly_getCurrentFlushResources();
+    SkDEBUGCODE(bool fFlushing = false);
 };
 
 #endif
diff --git a/src/gpu/ccpr/GrSampleMaskProcessor.cpp b/src/gpu/ccpr/GrSampleMaskProcessor.cpp
deleted file mode 100644
index 01613ae..0000000
--- a/src/gpu/ccpr/GrSampleMaskProcessor.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright 2019 Google LLC.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/ccpr/GrSampleMaskProcessor.h"
-
-#include "src/gpu/GrOpsRenderPass.h"
-#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
-
-class GrSampleMaskProcessor::Impl : public GrGLSLGeometryProcessor {
-public:
-    Impl(std::unique_ptr<Shader> shader) : fShader(std::move(shader)) {}
-
-private:
-    void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&) override {}
-
-    void onEmitCode(EmitArgs&, GrGPArgs*) override;
-
-    const std::unique_ptr<Shader> fShader;
-};
-
-void GrSampleMaskProcessor::Impl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
-    const GrSampleMaskProcessor& proc = args.fGP.cast<GrSampleMaskProcessor>();
-    GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
-    GrGLSLVertexBuilder* v = args.fVertBuilder;
-    int numInputPoints = proc.numInputPoints();
-    int inputWidth = (4 == numInputPoints || proc.hasInputWeight()) ? 4 : 3;
-
-    varyingHandler->emitAttributes(proc);
-    SkASSERT(!*args.fFPCoordTransformHandler);
-
-    if (PrimitiveType::kTriangles == proc.fPrimitiveType) {
-        SkASSERT(!proc.hasInstanceAttributes());  // Triangles are drawn with vertex arrays.
-        gpArgs->fPositionVar = proc.fInputAttribs.front().asShaderVar();
-    } else {
-        SkASSERT(!proc.hasVertexAttributes());  // Curves are drawn with instanced rendering.
-
-        // Shaders expect a global "bloat" variable when calculating gradients.
-        v->defineConstant("half", "bloat", ".5");
-
-        const char* swizzle = (4 == numInputPoints || proc.hasInputWeight()) ? "xyzw" : "xyz";
-        v->codeAppendf("float%ix2 pts = transpose(float2x%i(X.%s, Y.%s));",
-                       inputWidth, inputWidth, swizzle, swizzle);
-
-        const char* hullPts = "pts";
-        fShader->emitSetupCode(v, "pts", &hullPts);
-        v->codeAppendf("float2 vertexpos = %s[sk_VertexID ^ (sk_VertexID >> 1)];", hullPts);
-        gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
-
-        fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kVertToFrag,
-                              &AccessCodeString(v), "vertexpos", nullptr, nullptr, nullptr);
-    }
-
-    // Fragment shader.
-    fShader->emitSampleMaskCode(args.fFragBuilder);
-}
-
-void GrSampleMaskProcessor::reset(PrimitiveType primitiveType, int subpassIdx,
-                                  GrResourceProvider* rp) {
-    SkASSERT(subpassIdx == 0);
-    fPrimitiveType = primitiveType;  // This will affect the return values for numInputPoints, etc.
-    SkASSERT(PrimitiveType::kWeightedTriangles != fPrimitiveType);
-
-    this->resetCustomFeatures();
-    fInputAttribs.reset();
-
-    switch (fPrimitiveType) {
-        case PrimitiveType::kTriangles:
-        case PrimitiveType::kWeightedTriangles:
-            fInputAttribs.emplace_back("point", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
-            this->setVertexAttributes(fInputAttribs.begin(), 1);
-            this->setInstanceAttributes(nullptr, 0);
-            break;
-        case PrimitiveType::kQuadratics:
-        case PrimitiveType::kCubics:
-        case PrimitiveType::kConics: {
-            auto instanceAttribType = (PrimitiveType::kQuadratics == fPrimitiveType)
-                    ? kFloat3_GrVertexAttribType : kFloat4_GrVertexAttribType;
-            auto shaderVarType = (PrimitiveType::kQuadratics == fPrimitiveType)
-                    ? kFloat3_GrSLType : kFloat4_GrSLType;
-            fInputAttribs.emplace_back("X", instanceAttribType, shaderVarType);
-            fInputAttribs.emplace_back("Y", instanceAttribType, shaderVarType);
-            this->setVertexAttributes(nullptr, 0);
-            this->setInstanceAttributes(fInputAttribs.begin(), fInputAttribs.count());
-            this->setWillUseCustomFeature(CustomFeatures::kSampleLocations);
-            break;
-        }
-    }
-}
-
-GrPrimitiveType GrSampleMaskProcessor::primType() const {
-    SkASSERT(PrimitiveType::kWeightedTriangles != fPrimitiveType);
-
-    switch (fPrimitiveType) {
-        case PrimitiveType::kTriangles:
-        case PrimitiveType::kWeightedTriangles:
-            return GrPrimitiveType::kTriangles;
-        case PrimitiveType::kQuadratics:
-        case PrimitiveType::kCubics:
-        case PrimitiveType::kConics:
-            return GrPrimitiveType::kTriangleStrip;
-        default:
-            return GrPrimitiveType::kTriangleStrip;
-    }
-}
-
-void GrSampleMaskProcessor::bindBuffers(GrOpsRenderPass* renderPass,
-                                        sk_sp<const GrBuffer> instanceBuffer) const {
-    SkASSERT(PrimitiveType::kWeightedTriangles != fPrimitiveType);
-
-    switch (fPrimitiveType) {
-        case PrimitiveType::kTriangles:
-        case PrimitiveType::kWeightedTriangles: {
-            renderPass->bindBuffers(nullptr, nullptr, std::move(instanceBuffer));
-            break;
-        }
-        case PrimitiveType::kQuadratics:
-        case PrimitiveType::kCubics:
-        case PrimitiveType::kConics: {
-            renderPass->bindBuffers(nullptr, std::move(instanceBuffer), nullptr);
-            break;
-        }
-    }
-}
-
-void GrSampleMaskProcessor::drawInstances(GrOpsRenderPass* renderPass, int instanceCount,
-                                          int baseInstance) const {
-    SkASSERT(PrimitiveType::kWeightedTriangles != fPrimitiveType);
-
-    switch (fPrimitiveType) {
-        case PrimitiveType::kTriangles:
-        case PrimitiveType::kWeightedTriangles: {
-            renderPass->draw(instanceCount * 3, baseInstance * 3);
-            break;
-        }
-        case PrimitiveType::kQuadratics:
-        case PrimitiveType::kCubics:
-        case PrimitiveType::kConics: {
-            renderPass->drawInstanced(instanceCount, baseInstance, 4, 0);
-            break;
-        }
-    }
-}
-
-GrGLSLPrimitiveProcessor* GrSampleMaskProcessor::onCreateGLSLInstance(
-        std::unique_ptr<Shader> shader) const {
-    return new Impl(std::move(shader));
-}
diff --git a/src/gpu/ccpr/GrSampleMaskProcessor.h b/src/gpu/ccpr/GrSampleMaskProcessor.h
deleted file mode 100644
index f3a6796..0000000
--- a/src/gpu/ccpr/GrSampleMaskProcessor.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2019 Google LLC.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrSampleMaskProcessor_DEFINED
-#define GrSampleMaskProcessor_DEFINED
-
-#include "src/gpu/ccpr/GrCCCoverageProcessor.h"
-
-/**
- * This class implements GrCCCoverageProcessor with MSAA using the sample mask.
- */
-class GrSampleMaskProcessor : public GrCCCoverageProcessor {
-public:
-    GrSampleMaskProcessor() : GrCCCoverageProcessor(kGrSampleMaskProcessor_ClassID) {}
-
-private:
-    GrPrimitiveType primType() const final;
-    int numSubpasses() const override { return 1; }
-    void reset(PrimitiveType, int subpassIdx, GrResourceProvider*) override;
-    void bindBuffers(GrOpsRenderPass*, sk_sp<const GrBuffer> instanceBuffer) const override;
-    void drawInstances(GrOpsRenderPass*, int instanceCount, int baseInstance) const override;
-
-    GrGLSLPrimitiveProcessor* onCreateGLSLInstance(std::unique_ptr<Shader>) const override;
-
-    SkSTArray<2, Attribute> fInputAttribs;
-
-    class Impl;
-};
-
-#endif
diff --git a/src/gpu/ccpr/GrStencilAtlasOp.cpp b/src/gpu/ccpr/GrStencilAtlasOp.cpp
deleted file mode 100644
index 0787805..0000000
--- a/src/gpu/ccpr/GrStencilAtlasOp.cpp
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright 2019 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/ccpr/GrStencilAtlasOp.h"
-
-#include "include/gpu/GrRecordingContext.h"
-#include "src/gpu/GrOpFlushState.h"
-#include "src/gpu/GrOpsRenderPass.h"
-#include "src/gpu/GrProgramInfo.h"
-#include "src/gpu/GrRecordingContextPriv.h"
-#include "src/gpu/ccpr/GrCCPerFlushResources.h"
-#include "src/gpu/ccpr/GrSampleMaskProcessor.h"
-#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
-#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
-
-namespace {
-
-class StencilResolveProcessor : public GrGeometryProcessor {
-public:
-    StencilResolveProcessor() : INHERITED(kStencilResolveProcessor_ClassID) {
-        static constexpr Attribute kIBounds = {
-                "ibounds", kShort4_GrVertexAttribType, kShort4_GrSLType};
-        this->setInstanceAttributes(&kIBounds, 1);
-        SkASSERT(this->instanceStride() == sizeof(GrStencilAtlasOp::ResolveRectInstance));
-    }
-
-private:
-    const char* name() const final { return "StencilResolveProcessor"; }
-    void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
-    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
-    class Impl;
-
-    using INHERITED = GrGeometryProcessor;
-};
-
-// This processor draws pixel-aligned rectangles directly on top of every path in the atlas.
-// The caller should have set up the instance data such that "Nonzero" paths get clockwise
-// rectangles (l < r) and "even/odd" paths get counter-clockwise (r < l). Its purpose
-// is to convert winding counts in the stencil buffer to A8 coverage in the color buffer.
-class StencilResolveProcessor::Impl : public GrGLSLGeometryProcessor {
-    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
-        args.fVaryingHandler->emitAttributes(args.fGP.cast<StencilResolveProcessor>());
-
-        GrGLSLVertexBuilder* v = args.fVertBuilder;
-        v->codeAppendf("short2 devcoord;");
-        v->codeAppendf("devcoord.x = (0 == (sk_VertexID & 1)) ? ibounds.x : ibounds.z;");
-        v->codeAppendf("devcoord.y = (sk_VertexID < 2) ? ibounds.y : ibounds.w;");
-
-        v->codeAppendf("float2 atlascoord = float2(devcoord);");
-        gpArgs->fPositionVar.set(kFloat2_GrSLType, "atlascoord");
-
-        // Just output "1" for coverage. This will be modulated by the MSAA stencil test.
-        GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
-        f->codeAppendf("const half4 %s = half4(1), %s = half4(1);",
-                       args.fOutputColor, args.fOutputCoverage);
-    }
-
-    void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&) override {}
-};
-
-GrGLSLPrimitiveProcessor* StencilResolveProcessor::createGLSLInstance(const GrShaderCaps&) const {
-    return new Impl();
-}
-
-}  // namespace
-
-GrOp::Owner GrStencilAtlasOp::Make(
-        GrRecordingContext* context, sk_sp<const GrCCPerFlushResources> resources,
-        FillBatchID fillBatchID, int baseStencilResolveInstance,
-        int endStencilResolveInstance, const SkISize& drawBounds) {
-
-    return GrOp::Make<GrStencilAtlasOp>(
-            context, std::move(resources), fillBatchID, baseStencilResolveInstance,
-            endStencilResolveInstance, drawBounds);
-}
-
-// Increments clockwise triangles and decrements counterclockwise. We use the same incr/decr
-// settings regardless of fill rule; fill rule is accounted for during the resolve step.
-static constexpr GrUserStencilSettings kIncrDecrStencil(
-    GrUserStencilSettings::StaticInitSeparate<
-        0x0000,                        0x0000,
-        GrUserStencilTest::kNever,     GrUserStencilTest::kNever,
-        0xffff,                        0xffff,
-        GrUserStencilOp::kIncWrap,     GrUserStencilOp::kDecWrap,
-        GrUserStencilOp::kIncWrap,     GrUserStencilOp::kDecWrap,
-        0xffff,                        0xffff>()
-);
-
-// Resolves stencil winding counts to A8 coverage. Leaves stencil values untouched.
-// NOTE: For the CCW face we intentionally use "1 == (stencil & 1)" because the contrapositive logic
-// (i.e. 0 != ...) causes bugs on Adreno Vulkan. http://skbug.com/9643
-static constexpr GrUserStencilSettings kResolveStencilCoverage(
-    GrUserStencilSettings::StaticInitSeparate<
-        0x0000,                           0x0001,
-        GrUserStencilTest::kNotEqual,     GrUserStencilTest::kEqual,
-        0xffff,                           0x0001,
-        GrUserStencilOp::kKeep,           GrUserStencilOp::kKeep,
-        GrUserStencilOp::kKeep,           GrUserStencilOp::kKeep,
-        0xffff,                           0xffff>()
-);
-
-// Same as above, but also resets stencil values to zero. This is better for non-tilers
-// where we prefer to not clear the stencil buffer at the beginning of every render pass.
-static constexpr GrUserStencilSettings kResolveStencilCoverageAndReset(
-    GrUserStencilSettings::StaticInitSeparate<
-        0x0000,                           0x0000,
-        GrUserStencilTest::kNotEqual,     GrUserStencilTest::kNotEqual,
-        0xffff,                           0x0001,
-        GrUserStencilOp::kZero,           GrUserStencilOp::kZero,
-        GrUserStencilOp::kKeep,           GrUserStencilOp::kZero,
-        0xffff,                           0xffff>()
-);
-
-// Same as above, but done in two passes for D3D, which doesn't support mismatched refs or masks on
-// dual sided stencil settings.
-static constexpr GrUserStencilSettings kResolveWindingCoverageAndReset(
-    GrUserStencilSettings::StaticInitSeparate<
-        0x0000,                           0x0000,
-        GrUserStencilTest::kNotEqual,     GrUserStencilTest::kNever,
-        0xffff,                           0xffff,
-        GrUserStencilOp::kZero,           GrUserStencilOp::kKeep,
-        GrUserStencilOp::kKeep,           GrUserStencilOp::kKeep,
-        0xffff,                           0xffff>()
-);
-static constexpr GrUserStencilSettings kResolveEvenOddCoverageAndReset(
-    GrUserStencilSettings::StaticInitSeparate<
-        0x0000,                           0x0000,
-        GrUserStencilTest::kNever,        GrUserStencilTest::kNotEqual,
-        0x0001,                           0x0001,
-        GrUserStencilOp::kKeep,           GrUserStencilOp::kZero,
-        GrUserStencilOp::kKeep,           GrUserStencilOp::kZero,
-        0xffff,                           0xffff>()
-);
-
-
-void GrStencilAtlasOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
-    SkIRect drawBoundsRect = SkIRect::MakeWH(fDrawBounds.width(), fDrawBounds.height());
-
-    GrPipeline pipeline(GrScissorTest::kEnabled, GrDisableColorXPFactory::MakeXferProcessor(),
-                        flushState->drawOpArgs().writeView().swizzle(),
-                        GrPipeline::InputFlags::kHWAntialias);
-
-    GrSampleMaskProcessor sampleMaskProc;
-
-    fResources->filler().drawFills(
-            flushState, &sampleMaskProc, pipeline, fFillBatchID, drawBoundsRect, &kIncrDecrStencil);
-
-    // We resolve the stencil coverage to alpha by drawing pixel-aligned boxes. Fine raster is
-    // not necessary, and will even cause artifacts if using mixed samples.
-    constexpr auto noHWAA = GrPipeline::InputFlags::kNone;
-
-    GrPipeline resolvePipeline(GrScissorTest::kEnabled, SkBlendMode::kSrc,
-                               flushState->drawOpArgs().writeView().swizzle(), noHWAA);
-    StencilResolveProcessor primProc;
-
-    if (!flushState->caps().twoSidedStencilRefsAndMasksMustMatch()) {
-        const GrUserStencilSettings* stencil =
-                (flushState->caps().discardStencilValuesAfterRenderPass()) ?
-                &kResolveStencilCoverage : &kResolveStencilCoverageAndReset;
-        this->drawResolve(flushState, resolvePipeline, stencil, primProc, drawBoundsRect);
-        return;
-    }
-
-    // If this ever becomes true then we should add new per-fill-type stencil settings that also
-    // don't reset back to zero.
-    SkASSERT(!flushState->caps().discardStencilValuesAfterRenderPass());
-
-    this->drawResolve(flushState, resolvePipeline, &kResolveWindingCoverageAndReset, primProc,
-                      drawBoundsRect);
-    this->drawResolve(flushState, resolvePipeline, &kResolveEvenOddCoverageAndReset, primProc,
-                      drawBoundsRect);
-}
-
-void GrStencilAtlasOp::drawResolve(GrOpFlushState* flushState, const GrPipeline& resolvePipeline,
-                                   const GrUserStencilSettings* stencil,
-                                   const GrPrimitiveProcessor& primProc,
-                                   const SkIRect& drawBounds) const {
-    GrProgramInfo programInfo(flushState->writeView(), &resolvePipeline, stencil,
-                              &primProc, GrPrimitiveType::kTriangleStrip, 0,
-                              flushState->renderPassBarriers(),
-                              flushState->colorLoadOp());
-    flushState->bindPipeline(programInfo, SkRect::Make(drawBounds));
-    flushState->setScissorRect(drawBounds);
-    flushState->bindBuffers(nullptr, fResources->stencilResolveBuffer(), nullptr);
-    flushState->drawInstanced(fEndStencilResolveInstance - fBaseStencilResolveInstance,
-                              fBaseStencilResolveInstance, 4, 0);
-}
diff --git a/src/gpu/ccpr/GrStencilAtlasOp.h b/src/gpu/ccpr/GrStencilAtlasOp.h
deleted file mode 100644
index da21dc5..0000000
--- a/src/gpu/ccpr/GrStencilAtlasOp.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2019 Google LLC.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrStencilAtlasOp_DEFINED
-#define GrStencilAtlasOp_DEFINED
-
-#include "src/gpu/GrMemoryPool.h"
-#include "src/gpu/ccpr/GrCCFiller.h"
-#include "src/gpu/ops/GrDrawOp.h"
-
-class GrCCPerFlushResources;
-
-// Renders literal A8 coverage to a CCPR atlas using an intermediate MSAA stencil buffer.
-class GrStencilAtlasOp : public GrDrawOp {
-public:
-    DEFINE_OP_CLASS_ID
-
-    using FillBatchID = GrCCFiller::BatchID;
-
-    // Once all the paths in an atlas have been drawn to the stencil buffer, we make a final pass
-    // where we draw "resolve" rects over each path whose purpose is to convert winding counts to A8
-    // coverage.
-    struct ResolveRectInstance {
-        int16_t l, t, r, b;
-    };
-
-    // GrDrawOp interface.
-    const char* name() const override { return "StencilAtlasOp (CCPR)"; }
-    FixedFunctionFlags fixedFunctionFlags() const override {
-        return FixedFunctionFlags::kUsesHWAA | FixedFunctionFlags::kUsesStencil;
-    }
-
-    GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
-                                      bool hasMixedSampledCoverage, GrClampType) override {
-        return GrProcessorSet::EmptySetAnalysis();
-    }
-    CombineResult onCombineIfPossible(GrOp* other, SkArenaAlloc*, const GrCaps&) override {
-        // We will only make multiple copy ops if they have different source proxies.
-        // TODO: make use of texture chaining.
-        return CombineResult::kCannotCombine;
-    }
-
-    static GrOp::Owner Make(
-            GrRecordingContext*, sk_sp<const GrCCPerFlushResources>, FillBatchID,
-            int baseStencilResolveInstance, int endStencilResolveInstance,
-            const SkISize& drawBounds);
-
-private:
-    void onPrePrepare(GrRecordingContext*,
-                      const GrSurfaceProxyView& writeView,
-                      GrAppliedClip*,
-                      const GrXferProcessor::DstProxyView&,
-                      GrXferBarrierFlags renderPassXferBarriers,
-                      GrLoadOp colorLoadOp) override {}
-    void onPrepare(GrOpFlushState*) override {}
-    void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
-    void drawResolve(GrOpFlushState*, const GrPipeline&, const GrUserStencilSettings*,
-                     const GrPrimitiveProcessor&, const SkIRect& drawBounds) const;
-
-    friend class ::GrOp; // for ctor
-
-    GrStencilAtlasOp(sk_sp<const GrCCPerFlushResources> resources, FillBatchID fillBatchID,
-                     int baseStencilResolveInstance,
-                     int endStencilResolveInstance, const SkISize& drawBounds)
-            : GrDrawOp(ClassID())
-            , fResources(std::move(resources))
-            , fFillBatchID(fillBatchID)
-            , fBaseStencilResolveInstance(baseStencilResolveInstance)
-            , fEndStencilResolveInstance(endStencilResolveInstance)
-            , fDrawBounds(drawBounds) {
-        this->setBounds(SkRect::MakeIWH(fDrawBounds.width(), fDrawBounds.height()),
-                        GrOp::HasAABloat::kNo, GrOp::IsHairline::kNo);
-    }
-
-    const sk_sp<const GrCCPerFlushResources> fResources;
-    const FillBatchID fFillBatchID;
-    const int fBaseStencilResolveInstance;
-    const int fEndStencilResolveInstance;
-    const SkISize fDrawBounds;
-    int fResolveBaseVertex;
-};
-
-
-#endif