ccpr: Various cleanups

Renames GrCCCoverageProcessor::RenderPass to PrimitiveType and handles
corners as subpasses instead. Various touchups to coverage processors
now that the overhaul is complete. This change should be strictly a
refactor.

Bug: skia:
Change-Id: I52852463330d5ec71fae7e19fadccd9ede8b2c16
Reviewed-on: https://skia-review.googlesource.com/116169
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.cpp b/src/gpu/ccpr/GrCCCoverageProcessor.cpp
index 8e7249e9..c44d464 100644
--- a/src/gpu/ccpr/GrCCCoverageProcessor.cpp
+++ b/src/gpu/ccpr/GrCCCoverageProcessor.cpp
@@ -7,6 +7,8 @@
 
 #include "GrCCCoverageProcessor.h"
 
+#include "GrGpuCommandBuffer.h"
+#include "GrOpFlushState.h"
 #include "SkMakeUnique.h"
 #include "ccpr/GrCCCubicShader.h"
 #include "ccpr/GrCCQuadraticShader.h"
@@ -129,7 +131,10 @@
 
 void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&,
                                                 GrProcessorKeyBuilder* b) const {
-    int key = (int)fRenderPass << 2;
+    int key = (int)fPrimitiveType << 3;
+    if (GSSubpass::kCorners == fGSSubpass) {
+        key |= 4;
+    }
     if (WindMethod::kInstanceData == fWindMethod) {
         key |= 2;
     }
@@ -146,20 +151,32 @@
 
 GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const {
     std::unique_ptr<Shader> shader;
-    switch (fRenderPass) {
-        case RenderPass::kTriangles:
-        case RenderPass::kTriangleCorners:
+    switch (fPrimitiveType) {
+        case PrimitiveType::kTriangles:
             shader = skstd::make_unique<GrCCTriangleShader>();
             break;
-        case RenderPass::kQuadratics:
-        case RenderPass::kQuadraticCorners:
+        case PrimitiveType::kQuadratics:
             shader = skstd::make_unique<GrCCQuadraticShader>();
             break;
-        case RenderPass::kCubics:
-        case RenderPass::kCubicCorners:
+        case PrimitiveType::kCubics:
             shader = skstd::make_unique<GrCCCubicShader>();
             break;
     }
     return Impl::kGeometryShader == fImpl ? this->createGSImpl(std::move(shader))
                                           : this->createVSImpl(std::move(shader));
 }
+
+void GrCCCoverageProcessor::draw(GrOpFlushState* flushState, const GrPipeline& pipeline,
+                                 const GrMesh meshes[],
+                                 const GrPipeline::DynamicState dynamicStates[], int meshCount,
+                                 const SkRect& drawBounds) const {
+    GrGpuRTCommandBuffer* cmdBuff = flushState->rtCommandBuffer();
+    cmdBuff->draw(pipeline, *this, meshes, dynamicStates, meshCount, drawBounds);
+
+    // Geometry shader backend draws primitives in two subpasses.
+    if (Impl::kGeometryShader == fImpl) {
+        SkASSERT(GSSubpass::kHulls == fGSSubpass);
+        GrCCCoverageProcessor cornerProc(*this, GSSubpass::kCorners);
+        cmdBuff->draw(pipeline, cornerProc, meshes, dynamicStates, meshCount, drawBounds);
+    }
+}
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.h b/src/gpu/ccpr/GrCCCoverageProcessor.h
index 8b2b003..8824646 100644
--- a/src/gpu/ccpr/GrCCCoverageProcessor.h
+++ b/src/gpu/ccpr/GrCCCoverageProcessor.h
@@ -10,6 +10,7 @@
 
 #include "GrCaps.h"
 #include "GrGeometryProcessor.h"
+#include "GrPipeline.h"
 #include "GrShaderCaps.h"
 #include "SkNx.h"
 #include "glsl/GrGLSLGeometryProcessor.h"
@@ -18,21 +19,29 @@
 class GrGLSLFPFragmentBuilder;
 class GrGLSLVertexGeoBuilder;
 class GrMesh;
+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 execute all render passes for all applicable primitives into a
- * cleared, floating point, alpha-only render target using SkBlendMode::kPlus (see RenderPass
- * below). 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).
+ * 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 a renderer pass, see appendMesh below.
+ * To draw primitives, use appendMesh() and draw() (defined below).
  */
 class GrCCCoverageProcessor : public GrGeometryProcessor {
 public:
+    enum class PrimitiveType {
+        kTriangles,
+        kQuadratics,
+        kCubics,
+    };
+    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 {
@@ -54,50 +63,18 @@
         void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans, float w);
     };
 
-    // All primitive shapes (triangles and closed, convex bezier curves) may require two render
-    // passes: One to draw a rough outline of the shape, and a second pass to touch up the corners.
-    // Check DoesRenderPass() before attempting to draw a given RenderPass. Here we enumerate every
-    // possible render pass needed in order to produce a complete coverage count mask. This is an
-    // exhaustive list of all ccpr coverage shaders.
-    enum class RenderPass {
-        kTriangles,
-        kTriangleCorners,
-        kQuadratics,
-        kQuadraticCorners,
-        kCubics,
-        kCubicCorners
-    };
-    static bool RenderPassIsCubic(RenderPass);
-    static const char* RenderPassName(RenderPass);
-
-    static bool DoesRenderPass(RenderPass renderPass, const GrCaps& caps) {
-        switch (renderPass) {
-            case RenderPass::kTriangles:
-            case RenderPass::kQuadratics:
-            case RenderPass::kCubics:
-                return true;
-            case RenderPass::kTriangleCorners:
-            case RenderPass::kQuadraticCorners:
-            case RenderPass::kCubicCorners:
-                return caps.shaderCaps()->geometryShaderSupport();
-        }
-        SK_ABORT("Invalid RenderPass");
-        return false;
-    }
-
     enum class WindMethod : bool {
         kCrossProduct, // Calculate wind = +/-1 by sign of the cross product.
         kInstanceData // Instance data provides custom, signed wind values of any magnitude.
                       // (For tightly-wound tessellated triangles.)
     };
 
-    GrCCCoverageProcessor(GrResourceProvider* rp, RenderPass pass, WindMethod windMethod)
+    GrCCCoverageProcessor(GrResourceProvider* rp, PrimitiveType type, WindMethod windMethod)
             : INHERITED(kGrCCCoverageProcessor_ClassID)
-            , fRenderPass(pass)
+            , fPrimitiveType(type)
             , fWindMethod(windMethod)
             , fImpl(rp->caps()->shaderCaps()->geometryShaderSupport() ? Impl::kGeometryShader
                                                                       : Impl::kVertexShader) {
-        SkASSERT(DoesRenderPass(pass, *rp->caps()));
         if (Impl::kGeometryShader == fImpl) {
             this->initGS();
         } else {
@@ -106,7 +83,7 @@
     }
 
     // GrPrimitiveProcessor overrides.
-    const char* name() const override { return RenderPassName(fRenderPass); }
+    const char* name() const override { return PrimitiveTypeName(fPrimitiveType); }
     SkString dumpInfo() const override {
         return SkStringPrintf("%s\n%s", this->name(), this->INHERITED::dumpInfo().c_str());
     }
@@ -132,6 +109,9 @@
         }
     }
 
+    void draw(GrOpFlushState*, const GrPipeline&, const GrMesh[], const GrPipeline::DynamicState[],
+              int meshCount, const SkRect& drawBounds) const;
+
     // The Shader provides code to calculate each pixel's coverage in a RenderPass. It also
     // provides details about shape-specific geometry.
     class Shader {
@@ -224,13 +204,30 @@
     static constexpr float kAABloatRadius = 0.491111f;
 
     // Number of bezier points for curves, or 3 for triangles.
-    int numInputPoints() const { return RenderPassIsCubic(fRenderPass) ? 4 : 3; }
+    int numInputPoints() const { return PrimitiveType::kCubics == fPrimitiveType ? 4 : 3; }
 
     enum class Impl : bool {
         kGeometryShader,
         kVertexShader
     };
 
+    // Geometry shader backend draws primitives in two subpasses.
+    enum class GSSubpass : bool {
+        kHulls,
+        kCorners
+    };
+
+    GrCCCoverageProcessor(const GrCCCoverageProcessor& proc, GSSubpass subpass)
+            : INHERITED(kGrCCCoverageProcessor_ClassID)
+            , fPrimitiveType(proc.fPrimitiveType)
+            , fWindMethod(proc.fWindMethod)
+            , fImpl(Impl::kGeometryShader)
+            SkDEBUGCODE(, fDebugBloat(proc.fDebugBloat))
+            , fGSSubpass(subpass) {
+        SkASSERT(Impl::kGeometryShader == proc.fImpl);
+        this->initGS();
+    }
+
     void initGS();
     void initVS(GrResourceProvider*);
 
@@ -242,20 +239,33 @@
     GrGLSLPrimitiveProcessor* createGSImpl(std::unique_ptr<Shader>) const;
     GrGLSLPrimitiveProcessor* createVSImpl(std::unique_ptr<Shader>) const;
 
-    const RenderPass fRenderPass;
+    const PrimitiveType fPrimitiveType;
     const WindMethod fWindMethod;
     const Impl fImpl;
     SkDEBUGCODE(float fDebugBloat = 0);
 
+    // Used by GSImpl.
+    const GSSubpass fGSSubpass = GSSubpass::kHulls;
+
     // Used by VSImpl.
-    sk_sp<const GrBuffer> fVertexBuffer;
-    sk_sp<const GrBuffer> fIndexBuffer;
-    int fNumIndicesPerInstance;
-    GrPrimitiveType fPrimitiveType;
+    sk_sp<const GrBuffer> fVSVertexBuffer;
+    sk_sp<const GrBuffer> fVSIndexBuffer;
+    int fVSNumIndicesPerInstance;
+    GrPrimitiveType fVSTriangleType;
 
     typedef GrGeometryProcessor INHERITED;
 };
 
+inline const char* GrCCCoverageProcessor::PrimitiveTypeName(PrimitiveType type) {
+    switch (type) {
+        case PrimitiveType::kTriangles: return "kTriangles";
+        case PrimitiveType::kQuadratics: return "kQuadratics";
+        case PrimitiveType::kCubics: return "kCubics";
+    }
+    SK_ABORT("Invalid PrimitiveType");
+    return "";
+}
+
 inline void GrCCCoverageProcessor::TriPointInstance::set(const SkPoint p[3], const Sk2f& trans) {
     this->set(p[0], p[1], p[2], trans);
 }
@@ -285,32 +295,4 @@
     Sk2f::Store4(this, P0, P1, P2, W);
 }
 
-inline bool GrCCCoverageProcessor::RenderPassIsCubic(RenderPass pass) {
-    switch (pass) {
-        case RenderPass::kTriangles:
-        case RenderPass::kTriangleCorners:
-        case RenderPass::kQuadratics:
-        case RenderPass::kQuadraticCorners:
-            return false;
-        case RenderPass::kCubics:
-        case RenderPass::kCubicCorners:
-            return true;
-    }
-    SK_ABORT("Invalid RenderPass");
-    return false;
-}
-
-inline const char* GrCCCoverageProcessor::RenderPassName(RenderPass pass) {
-    switch (pass) {
-        case RenderPass::kTriangles: return "kTriangles";
-        case RenderPass::kTriangleCorners: return "kTriangleCorners";
-        case RenderPass::kQuadratics: return "kQuadratics";
-        case RenderPass::kQuadraticCorners: return "kQuadraticCorners";
-        case RenderPass::kCubics: return "kCubics";
-        case RenderPass::kCubicCorners: return "kCubicCorners";
-    }
-    SK_ABORT("Invalid RenderPass");
-    return "";
-}
-
 #endif
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp
index 907de46..0bb34b1 100644
--- a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp
+++ b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp
@@ -229,6 +229,72 @@
 };
 
 /**
+ * Generates a conservative raster around a convex quadrilateral that encloses a cubic or quadratic.
+ */
+class GrCCCoverageProcessor::GSCurveHullImpl : public GrCCCoverageProcessor::GSImpl {
+public:
+    GSCurveHullImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
+
+    void onEmitGeometryShader(const GrCCCoverageProcessor&, GrGLSLGeometryBuilder* g,
+                              const GrShaderVar& wind, const char* emitVertexFn) const override {
+        const char* hullPts = "pts";
+        fShader->emitSetupCode(g, "pts", wind.c_str(), &hullPts);
+
+        // Visualize the input (convex) quadrilateral as a square. Paying special attention to wind,
+        // we can identify the points by their corresponding corner.
+        //
+        // NOTE: We split the square down the diagonal from top-right to bottom-left, and generate
+        // the hull in two independent invocations. Each invocation designates the corner it will
+        // begin with as top-left.
+        g->codeAppend ("int i = sk_InvocationID * 2;");
+        g->codeAppendf("float2 topleft = %s[i];", hullPts);
+        g->codeAppendf("float2 topright = %s[%s > 0 ? i + 1 : 3 - i];", hullPts, wind.c_str());
+        g->codeAppendf("float2 bottomleft = %s[%s > 0 ? 3 - i : i + 1];", hullPts, wind.c_str());
+        g->codeAppendf("float2 bottomright = %s[2 - i];", hullPts);
+
+        // Determine how much to outset the conservative raster hull from the relevant edges.
+        g->codeAppend ("float2 leftbloat = float2(topleft.y > bottomleft.y ? +bloat : -bloat, "
+                                                 "topleft.x > bottomleft.x ? -bloat : bloat);");
+        g->codeAppend ("float2 upbloat = float2(topright.y > topleft.y ? +bloat : -bloat, "
+                                               "topright.x > topleft.x ? -bloat : +bloat);");
+        g->codeAppend ("float2 rightbloat = float2(bottomright.y > topright.y ? +bloat : -bloat, "
+                                                  "bottomright.x > topright.x ? -bloat : +bloat);");
+
+        // Here we generate the conservative raster geometry. It is the convex hull of 4 pixel-size
+        // boxes centered on the input points, split evenly between two invocations. This translates
+        // to a polygon with either one, two, or three vertices at each input point, depending on
+        // how sharp the corner is. For more details on conservative raster, see:
+        // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html
+        g->codeAppendf("bool2 left_up_notequal = notEqual(leftbloat, upbloat);");
+        g->codeAppend ("if (all(left_up_notequal)) {");
+                           // The top-left corner will have three conservative raster vertices.
+                           // Emit the middle one first to the triangle strip.
+        g->codeAppendf(    "%s(topleft + float2(-leftbloat.y, leftbloat.x));", emitVertexFn);
+        g->codeAppend ("}");
+        g->codeAppend ("if (any(left_up_notequal)) {");
+                           // Second conservative raster vertex for the top-left corner.
+        g->codeAppendf(    "%s(topleft + leftbloat);", emitVertexFn);
+        g->codeAppend ("}");
+
+        // Main interior body of this invocation's half of the hull.
+        g->codeAppendf("%s(topleft + upbloat);", emitVertexFn);
+        g->codeAppendf("%s(bottomleft + leftbloat);", emitVertexFn);
+        g->codeAppendf("%s(topright + upbloat);", emitVertexFn);
+
+        // Remaining two conservative raster vertices for the top-right corner.
+        g->codeAppendf("bool2 up_right_notequal = notEqual(upbloat, rightbloat);");
+        g->codeAppend ("if (any(up_right_notequal)) {");
+        g->codeAppendf(    "%s(topright + rightbloat);", emitVertexFn);
+        g->codeAppend ("}");
+        g->codeAppend ("if (all(up_right_notequal)) {");
+        g->codeAppendf(    "%s(topright + float2(-upbloat.y, upbloat.x));", emitVertexFn);
+        g->codeAppend ("}");
+
+        g->configure(InputType::kLines, OutputType::kTriangleStrip, 7, 2);
+    }
+};
+
+/**
  * Generates conservative rasters around corners (aka pixel-size boxes) and calculates
  * coverage and attenuation ramps to fix up the coverage values written by the hulls.
  */
@@ -243,7 +309,7 @@
                               const GrShaderVar& wind, const char* emitVertexFn) const override {
         fShader->emitSetupCode(g, "pts", wind.c_str());
 
-        bool isTriangle = RenderPass::kTriangleCorners == proc.fRenderPass;
+        bool isTriangle = PrimitiveType::kTriangles == proc.fPrimitiveType;
         g->codeAppendf("int corneridx = sk_InvocationID;");
         if (!isTriangle) {
             g->codeAppendf("corneridx *= %i;", proc.numInputPoints() - 1);
@@ -314,75 +380,9 @@
     }
 };
 
-/**
- * Generates a conservative raster around a convex quadrilateral that encloses a cubic or quadratic.
- */
-class GrCCCoverageProcessor::GSCurveHullImpl : public GrCCCoverageProcessor::GSImpl {
-public:
-    GSCurveHullImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
-
-    void onEmitGeometryShader(const GrCCCoverageProcessor&, GrGLSLGeometryBuilder* g,
-                              const GrShaderVar& wind, const char* emitVertexFn) const override {
-        const char* hullPts = "pts";
-        fShader->emitSetupCode(g, "pts", wind.c_str(), &hullPts);
-
-        // Visualize the input (convex) quadrilateral as a square. Paying special attention to wind,
-        // we can identify the points by their corresponding corner.
-        //
-        // NOTE: We split the square down the diagonal from top-right to bottom-left, and generate
-        // the hull in two independent invocations. Each invocation designates the corner it will
-        // begin with as top-left.
-        g->codeAppend ("int i = sk_InvocationID * 2;");
-        g->codeAppendf("float2 topleft = %s[i];", hullPts);
-        g->codeAppendf("float2 topright = %s[%s > 0 ? i + 1 : 3 - i];", hullPts, wind.c_str());
-        g->codeAppendf("float2 bottomleft = %s[%s > 0 ? 3 - i : i + 1];", hullPts, wind.c_str());
-        g->codeAppendf("float2 bottomright = %s[2 - i];", hullPts);
-
-        // Determine how much to outset the conservative raster hull from the relevant edges.
-        g->codeAppend ("float2 leftbloat = float2(topleft.y > bottomleft.y ? +bloat : -bloat, "
-                                                 "topleft.x > bottomleft.x ? -bloat : bloat);");
-        g->codeAppend ("float2 upbloat = float2(topright.y > topleft.y ? +bloat : -bloat, "
-                                               "topright.x > topleft.x ? -bloat : +bloat);");
-        g->codeAppend ("float2 rightbloat = float2(bottomright.y > topright.y ? +bloat : -bloat, "
-                                                  "bottomright.x > topright.x ? -bloat : +bloat);");
-
-        // Here we generate the conservative raster geometry. It is the convex hull of 4 pixel-size
-        // boxes centered on the input points, split evenly between two invocations. This translates
-        // to a polygon with either one, two, or three vertices at each input point, depending on
-        // how sharp the corner is. For more details on conservative raster, see:
-        // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html
-        g->codeAppendf("bool2 left_up_notequal = notEqual(leftbloat, upbloat);");
-        g->codeAppend ("if (all(left_up_notequal)) {");
-                           // The top-left corner will have three conservative raster vertices.
-                           // Emit the middle one first to the triangle strip.
-        g->codeAppendf(    "%s(topleft + float2(-leftbloat.y, leftbloat.x));", emitVertexFn);
-        g->codeAppend ("}");
-        g->codeAppend ("if (any(left_up_notequal)) {");
-                           // Second conservative raster vertex for the top-left corner.
-        g->codeAppendf(    "%s(topleft + leftbloat);", emitVertexFn);
-        g->codeAppend ("}");
-
-        // Main interior body of this invocation's half of the hull.
-        g->codeAppendf("%s(topleft + upbloat);", emitVertexFn);
-        g->codeAppendf("%s(bottomleft + leftbloat);", emitVertexFn);
-        g->codeAppendf("%s(topright + upbloat);", emitVertexFn);
-
-        // Remaining two conservative raster vertices for the top-right corner.
-        g->codeAppendf("bool2 up_right_notequal = notEqual(upbloat, rightbloat);");
-        g->codeAppend ("if (any(up_right_notequal)) {");
-        g->codeAppendf(    "%s(topright + rightbloat);", emitVertexFn);
-        g->codeAppend ("}");
-        g->codeAppend ("if (all(up_right_notequal)) {");
-        g->codeAppendf(    "%s(topright + float2(-upbloat.y, upbloat.x));", emitVertexFn);
-        g->codeAppend ("}");
-
-        g->configure(InputType::kLines, OutputType::kTriangleStrip, 7, 2);
-    }
-};
-
 void GrCCCoverageProcessor::initGS() {
     SkASSERT(Impl::kGeometryShader == fImpl);
-    if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) {
+    if (PrimitiveType::kCubics == fPrimitiveType || WindMethod::kInstanceData == fWindMethod) {
         SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints());
         this->addVertexAttrib("x_or_y_values", kFloat4_GrVertexAttribType);
         SkASSERT(sizeof(QuadPointInstance) == this->getVertexStride() * 2);
@@ -408,17 +408,11 @@
 }
 
 GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGSImpl(std::unique_ptr<Shader> shadr) const {
-    switch (fRenderPass) {
-        case RenderPass::kTriangles:
-            return new GSTriangleHullImpl(std::move(shadr));
-        case RenderPass::kQuadratics:
-        case RenderPass::kCubics:
-            return new GSCurveHullImpl(std::move(shadr));
-        case RenderPass::kTriangleCorners:
-        case RenderPass::kQuadraticCorners:
-        case RenderPass::kCubicCorners:
-            return new GSCornerImpl(std::move(shadr));
+    if (GSSubpass::kHulls == fGSSubpass) {
+        return (PrimitiveType::kTriangles == fPrimitiveType)
+                   ? (GSImpl*) new GSTriangleHullImpl(std::move(shadr))
+                   : (GSImpl*) new GSCurveHullImpl(std::move(shadr));
     }
-    SK_ABORT("Invalid RenderPass");
-    return nullptr;
+    SkASSERT(GSSubpass::kCorners == fGSSubpass);
+    return new GSCornerImpl(std::move(shadr));
 }
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp
index d0fc4e0..7af1a73 100644
--- a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp
+++ b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp
@@ -10,92 +10,30 @@
 #include "GrMesh.h"
 #include "glsl/GrGLSLVertexGeoBuilder.h"
 
-using Shader = GrCCCoverageProcessor::Shader;
-
-static constexpr int kAttribIdx_X = 0;
-static constexpr int kAttribIdx_Y = 1;
-static constexpr int kAttribIdx_VertexData = 2;
-
-/**
- * This class and its subclasses implement the coverage processor with vertex shaders.
- */
+// This class implements the coverage processor with vertex shaders.
 class GrCCCoverageProcessor::VSImpl : public GrGLSLGeometryProcessor {
-protected:
-    VSImpl(std::unique_ptr<Shader> shader) : fShader(std::move(shader)) {}
+public:
+    VSImpl(std::unique_ptr<Shader> shader, int numSides)
+            : fShader(std::move(shader)), fNumSides(numSides) {}
 
+private:
     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
                  FPCoordTransformIter&& transformIter) final {
         this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
     }
 
-    struct Coverages {
-        const char* fCoverage = nullptr; // half
-        const char* fAttenuatedCoverage = nullptr; // half2
-    };
-
-    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
-        const GrCCCoverageProcessor& proc = args.fGP.cast<GrCCCoverageProcessor>();
-
-        // Vertex shader.
-        GrGLSLVertexBuilder* v = args.fVertBuilder;
-        int numInputPoints = proc.numInputPoints();
-
-        const char* swizzle = (4 == numInputPoints) ? "xyzw" : "xyz";
-        v->codeAppendf("float%ix2 pts = transpose(float2x%i(%s.%s, %s.%s));",
-                       numInputPoints, numInputPoints, proc.getAttrib(kAttribIdx_X).fName, swizzle,
-                       proc.getAttrib(kAttribIdx_Y).fName, swizzle);
-
-        if (WindMethod::kCrossProduct == proc.fWindMethod) {
-            v->codeAppend ("float area_x2 = determinant(float2x2(pts[0] - pts[1], "
-                                                                "pts[0] - pts[2]));");
-            if (4 == numInputPoints) {
-                v->codeAppend ("area_x2 += determinant(float2x2(pts[0] - pts[2], "
-                                                               "pts[0] - pts[3]));");
-            }
-            v->codeAppend ("half wind = sign(area_x2);");
-        } else {
-            SkASSERT(WindMethod::kInstanceData == proc.fWindMethod);
-            SkASSERT(3 == numInputPoints);
-            SkASSERT(kFloat4_GrVertexAttribType == proc.getAttrib(kAttribIdx_X).fType);
-            v->codeAppendf("half wind = %s.w;", proc.getAttrib(kAttribIdx_X).fName);
-        }
-
-        float bloat = kAABloatRadius;
-#ifdef SK_DEBUG
-        if (proc.debugBloatEnabled()) {
-            bloat *= proc.debugBloat();
-        }
-#endif
-        v->defineConstant("bloat", bloat);
-
-        Coverages coverages;
-        this->emitVertexPosition(proc, v, gpArgs, &coverages);
-        SkASSERT(kFloat2_GrSLType == gpArgs->fPositionVar.getType());
-
-        GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
-        SkString varyingCode;
-        fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kVertToFrag, &varyingCode,
-                              gpArgs->fPositionVar.c_str(), coverages.fCoverage,
-                              coverages.fAttenuatedCoverage);
-        v->codeAppend(varyingCode.c_str());
-
-        varyingHandler->emitAttributes(proc);
-        SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
-
-        // Fragment shader.
-        fShader->emitFragmentCode(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
-    }
-
-    virtual void emitVertexPosition(const GrCCCoverageProcessor&, GrGLSLVertexBuilder*, GrGPArgs*,
-                                    Coverages* outCoverages) const = 0;
-
-    virtual ~VSImpl() {}
+    void onEmitCode(EmitArgs&, GrGPArgs*) override;
 
     const std::unique_ptr<Shader> fShader;
-
-    typedef GrGLSLGeometryProcessor INHERITED;
+    const int fNumSides;
 };
 
+static constexpr int kAttribIdx_X = 0; // Transposed X values of all input points.
+static constexpr int kAttribIdx_Y = 1; // Transposed Y values of all input points.
+static constexpr int kAttribIdx_VertexData = 2;
+
+// Vertex data tells the shader how to offset vertices for conservative raster, as well as how to
+// calculate coverage values for corners and edges.
 static constexpr int kVertexData_LeftNeighborIdShift = 10;
 static constexpr int kVertexData_RightNeighborIdShift = 8;
 static constexpr int kVertexData_BloatIdxShift = 6;
@@ -104,10 +42,6 @@
 static constexpr int kVertexData_IsEdgeBit = 1 << 3;
 static constexpr int kVertexData_IsHullBit = 1 << 2;
 
-/**
- * Vertex data tells the shader how to offset vertices for conservative raster, and how/whether to
- * calculate initial coverage values for edges. See VSHullAndEdgeImpl.
- */
 static constexpr int32_t pack_vertex_data(int32_t leftNeighborID, int32_t rightNeighborID,
                                           int32_t bloatIdx, int32_t cornerID,
                                           int32_t extraData = 0) {
@@ -244,7 +178,8 @@
 
 GR_DECLARE_STATIC_UNIQUE_KEY(gTriangleIndexBufferKey);
 
-static constexpr int32_t kHull4Vertices[] = {
+// Curves, including quadratics, are drawn with a four-sided hull.
+static constexpr int32_t kCurveVertices[] = {
     hull_vertex_data(0, 0, 4),
     hull_vertex_data(0, 1, 4),
     hull_vertex_data(0, 2, 4),
@@ -269,16 +204,16 @@
     corner_vertex_data(2, 3, 0, 3),
 };
 
-GR_DECLARE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey);
+GR_DECLARE_STATIC_UNIQUE_KEY(gCurveVertexBufferKey);
 
-static constexpr uint16_t kHull4IndicesAsStrips[] =  {
+static constexpr uint16_t kCurveIndicesAsStrips[] =  {
     1, 0, 2, 11, 3, 5, 4, kRestartStrip, // First half of the hull (split diagonally).
     7, 6, 8, 5, 9, 11, 10, kRestartStrip, // Second half of the hull.
     13, 12, 14, 15, kRestartStrip, // First corner.
-    17, 16, 18, 19 // Second corner.
+    17, 16, 18, 19 // Final corner.
 };
 
-static constexpr uint16_t kHull4IndicesAsTris[] =  {
+static constexpr uint16_t kCurveIndicesAsTris[] =  {
     // First half of the hull (split diagonally).
      1,  0,  2,
      0, 11,  2,
@@ -297,243 +232,265 @@
     13, 12, 14,
     12, 15, 14,
 
-    // Second corner.
+    // Final corner.
     17, 16, 18,
     16, 19, 18,
 };
 
-GR_DECLARE_STATIC_UNIQUE_KEY(gHull4IndexBufferKey);
+GR_DECLARE_STATIC_UNIQUE_KEY(gCurveIndexBufferKey);
 
+// Generates a conservative raster hull around a triangle or curve. For triangles we generate
+// additional conservative rasters with coverage ramps around the edges and corners.
+//
+// Triangles are drawn in three steps: (1) Draw a conservative raster of the entire triangle, with a
+// coverage of +1. (2) Draw conservative rasters around each edge, with a coverage ramp from -1 to
+// 0. These edge coverage values convert jagged conservative raster edges into smooth, antialiased
+// ones. (3) Draw conservative rasters (aka pixel-size boxes) around each corner, replacing the
+// previous coverage values with ones that ramp to zero in the bloat vertices that fall outside the
+// triangle.
+//
+// Curves are drawn in two separate passes. Here we just draw a conservative raster around the input
+// points. The Shader takes care of everything else for now. The final curve corners get touched up
+// in a later step by VSCornerImpl.
+void GrCCCoverageProcessor::VSImpl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
+    const GrCCCoverageProcessor& proc = args.fGP.cast<GrCCCoverageProcessor>();
+    GrGLSLVertexBuilder* v = args.fVertBuilder;
+    int numInputPoints = proc.numInputPoints();
 
-/**
- * Generates a conservative raster hull around a triangle or curve. For triangles we generate
- * additional conservative rasters with coverage ramps around the edges and corners.
- *
- * Triangles are drawn in three steps: (1) Draw a conservative raster of the entire triangle, with a
- * coverage of +1. (2) Draw conservative rasters around each edge, with a coverage ramp from -1 to
- * 0. These edge coverage values convert jagged conservative raster edges into smooth, antialiased
- * ones. (3) Draw conservative rasters (aka pixel-size boxes) around each corner, replacing the
- * previous coverage values with ones that ramp to zero in the bloat vertices that fall outside the
- * triangle.
- *
- * Curves are drawn in two separate passes. Here we just draw a conservative raster around the input
- * points. The Shader takes care of everything else for now. The final curve corners get touched up
- * in a later step by VSCornerImpl.
- */
-class VSHullAndEdgeImpl : public GrCCCoverageProcessor::VSImpl {
-public:
-    VSHullAndEdgeImpl(std::unique_ptr<Shader> shader, int numSides)
-            : VSImpl(std::move(shader)), fNumSides(numSides) {}
+    const char* swizzle = (4 == numInputPoints) ? "xyzw" : "xyz";
+    v->codeAppendf("float%ix2 pts = transpose(float2x%i(%s.%s, %s.%s));",
+                   numInputPoints, numInputPoints, proc.getAttrib(kAttribIdx_X).fName, swizzle,
+                   proc.getAttrib(kAttribIdx_Y).fName, swizzle);
 
-    void emitVertexPosition(const GrCCCoverageProcessor& proc, GrGLSLVertexBuilder* v,
-                            GrGPArgs* gpArgs, Coverages* outCoverages) const override {
-        const char* hullPts = "pts";
-        fShader->emitSetupCode(v, "pts", "wind", &hullPts);
-
-        // Reverse all indices if the wind is counter-clockwise: [0, 1, 2] -> [2, 1, 0].
-        v->codeAppendf("int clockwise_indices = wind > 0 ? %s : 0x%x - %s;",
-                       proc.getAttrib(kAttribIdx_VertexData).fName,
-                       ((fNumSides - 1) << kVertexData_LeftNeighborIdShift) |
-                       ((fNumSides - 1) << kVertexData_RightNeighborIdShift) |
-                       (((1 << kVertexData_RightNeighborIdShift) - 1) ^ 3) |
-                       (fNumSides - 1),
-                       proc.getAttrib(kAttribIdx_VertexData).fName);
-
-        // Here we generate conservative raster geometry for the input polygon. It is the convex
-        // hull of N pixel-size boxes, one centered on each the input points. Each corner has three
-        // vertices, where one or two may cause degenerate triangles. The vertex data tells us how
-        // to offset each vertex. Triangle edges and corners are also handled here using the same
-        // concept. For more details on conservative raster, see:
-        // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html
-        v->codeAppendf("float2 corner = %s[clockwise_indices & 3];", hullPts);
-        v->codeAppendf("float2 left = %s[clockwise_indices >> %i];",
-                       hullPts, kVertexData_LeftNeighborIdShift);
-        v->codeAppendf("float2 right = %s[(clockwise_indices >> %i) & 3];",
-                       hullPts, kVertexData_RightNeighborIdShift);
-
-        v->codeAppend ("float2 leftbloat = sign(corner - left);");
-        v->codeAppend ("leftbloat = float2(0 != leftbloat.y ? leftbloat.y : leftbloat.x, "
-                                          "0 != leftbloat.x ? -leftbloat.x : -leftbloat.y);");
-
-        v->codeAppend ("float2 rightbloat = sign(right - corner);");
-        v->codeAppend ("rightbloat = float2(0 != rightbloat.y ? rightbloat.y : rightbloat.x, "
-                                           "0 != rightbloat.x ? -rightbloat.x : -rightbloat.y);");
-
-        v->codeAppend ("bool2 left_right_notequal = notEqual(leftbloat, rightbloat);");
-
-        v->codeAppend ("float2 bloatdir = leftbloat;");
-
-        v->codeAppend ("float2 leftdir = corner - left;");
-        v->codeAppend ("leftdir = (float2(0) != leftdir) ? normalize(leftdir) : float2(1, 0);");
-
-        v->codeAppend ("float2 rightdir = right - corner;");
-        v->codeAppend ("rightdir = (float2(0) != rightdir)"
-                               "? normalize(rightdir) : float2(1, 0);");
-
-        v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner?
-                       proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit);
-
-                           // In corner boxes, all 4 coverage values will not map linearly.
-                           // Therefore it is important to align the box so its diagonal shared
-                           // edge points out of the triangle, in the direction that ramps to 0.
-        v->codeAppend (    "bloatdir = float2(leftdir.x > rightdir.x ? +1 : -1, "
-                                             "leftdir.y > rightdir.y ? +1 : -1);");
-
-                           // For corner boxes, we hack left_right_notequal to always true. This
-                           // in turn causes the upcoming code to always rotate, generating all
-                           // 4 vertices of the corner box.
-        v->codeAppendf(    "left_right_notequal = bool2(true);");
-        v->codeAppend ("}");
-
-        // At each corner of the polygon, our hull will have either 1, 2, or 3 vertices (or 4 if
-        // it's a corner box). We begin with this corner's first raster vertex (leftbloat), then
-        // continue rotating 90 degrees clockwise until we reach the desired raster vertex for this
-        // invocation. Corners with less than 3 corresponding raster vertices will result in
-        // redundant vertices and degenerate triangles.
-        v->codeAppendf("int bloatidx = (%s >> %i) & 3;",
-                       proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_BloatIdxShift);
-        v->codeAppend ("switch (bloatidx) {");
-        v->codeAppend (    "case 3:");
-                                // Only corners will have bloatidx=3, and corners always rotate.
-        v->codeAppend (        "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW.
-                               // fallthru.
-        v->codeAppend (    "case 2:");
-        v->codeAppendf(        "if (all(left_right_notequal)) {");
-        v->codeAppend (            "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW.
-        v->codeAppend (        "}");
-                               // fallthru.
-        v->codeAppend (    "case 1:");
-        v->codeAppendf(        "if (any(left_right_notequal)) {");
-        v->codeAppend (            "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW.
-        v->codeAppend (        "}");
-                               // fallthru.
-        v->codeAppend ("}");
-
-        v->codeAppend ("float2 vertex = corner + bloatdir * bloat;");
-        gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex");
-
-        v->codeAppend ("half left_coverage; {");
-        Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "left_coverage");
-        v->codeAppend ("}");
-
-        v->codeAppend ("half right_coverage; {");
-        Shader::CalcEdgeCoverageAtBloatVertex(v, "corner", "right", "bloatdir", "right_coverage");
-        v->codeAppend ("}");
-
-        v->codeAppend ("half attenuation; {");
-        Shader::CalcCornerCoverageAttenuation(v, "leftdir", "rightdir", "attenuation");
-        v->codeAppend ("}");
-
-        // Hulls have a coverage of +1 all around.
-        v->codeAppend ("half coverage = +1;");
-
-        if (3 == fNumSides) {
-            v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge?
-                           proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsEdgeBit);
-            v->codeAppend (    "coverage = left_coverage;");
-            v->codeAppend ("}");
-
-            v->codeAppendf("if (0 != (%s & %i)) {", // Invert coverage?
-                           proc.getAttrib(kAttribIdx_VertexData).fName,
-                           kVertexData_InvertNegativeCoverageBit);
-            v->codeAppend (    "coverage = -1 - coverage;");
-            v->codeAppend ("}");
+    if (WindMethod::kCrossProduct == proc.fWindMethod) {
+        v->codeAppend ("float area_x2 = determinant(float2x2(pts[0] - pts[1], "
+                                                            "pts[0] - pts[2]));");
+        if (4 == numInputPoints) {
+            v->codeAppend ("area_x2 += determinant(float2x2(pts[0] - pts[2], "
+                                                           "pts[0] - pts[3]));");
         }
-
-        // Corner boxes require attenuation.
-        v->codeAppend ("half2 attenuated_coverage = half2(0, 1);");
-
-        v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner?
-                       proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit);
-                           // We use coverage=-1 to erase what the hull geometry wrote.
-        v->codeAppend (    "coverage = -1;");
-        if (3 == fNumSides) {
-                           // Triangle corners also have to erase what the edge geometry wrote.
-            v->codeAppend ("coverage -= left_coverage + right_coverage;");
-        }
-                           // The x and y components of "attenuated_coverage" are multiplied
-                           // together by the fragment shader. They ramp to 0 with attenuation in
-                           // the diagonal that points out of the corner, and linearly from
-                           // left-edge coverage to right in the opposite diagonal.
-                           // bloatidx=0 is the outermost vertex; the one that has attenuation.
-        v->codeAppend (    "attenuated_coverage = (0 == bloatidx)"
-                                   "? half2(0, attenuation) : half2(1);");
-        v->codeAppend (    "if (1 == bloatidx || 2 == bloatidx) {");
-        v->codeAppend (        "attenuated_coverage.x += right_coverage;");
-        v->codeAppend (    "}");
-        v->codeAppend (    "if (bloatidx >= 2) {");
-        v->codeAppend (        "attenuated_coverage.x += left_coverage;");
-        v->codeAppend (    "}");
-        v->codeAppend ("}");
-
-        v->codeAppend ("coverage *= wind;");
-        outCoverages->fCoverage = "coverage";
-
-        v->codeAppend ("attenuated_coverage.x *= wind;");
-        outCoverages->fAttenuatedCoverage = "attenuated_coverage";
+        v->codeAppend ("half wind = sign(area_x2);");
+    } else {
+        SkASSERT(WindMethod::kInstanceData == proc.fWindMethod);
+        SkASSERT(3 == numInputPoints);
+        SkASSERT(kFloat4_GrVertexAttribType == proc.getAttrib(kAttribIdx_X).fType);
+        v->codeAppendf("half wind = %s.w;", proc.getAttrib(kAttribIdx_X).fName);
     }
 
-private:
-    const int fNumSides;
-};
+    float bloat = kAABloatRadius;
+#ifdef SK_DEBUG
+    if (proc.debugBloatEnabled()) {
+        bloat *= proc.debugBloat();
+    }
+#endif
+    v->defineConstant("bloat", bloat);
+
+    const char* hullPts = "pts";
+    fShader->emitSetupCode(v, "pts", "wind", &hullPts);
+
+    // Reverse all indices if the wind is counter-clockwise: [0, 1, 2] -> [2, 1, 0].
+    v->codeAppendf("int clockwise_indices = wind > 0 ? %s : 0x%x - %s;",
+                   proc.getAttrib(kAttribIdx_VertexData).fName,
+                   ((fNumSides - 1) << kVertexData_LeftNeighborIdShift) |
+                   ((fNumSides - 1) << kVertexData_RightNeighborIdShift) |
+                   (((1 << kVertexData_RightNeighborIdShift) - 1) ^ 3) |
+                   (fNumSides - 1),
+                   proc.getAttrib(kAttribIdx_VertexData).fName);
+
+    // Here we generate conservative raster geometry for the input polygon. It is the convex
+    // hull of N pixel-size boxes, one centered on each the input points. Each corner has three
+    // vertices, where one or two may cause degenerate triangles. The vertex data tells us how
+    // to offset each vertex. Triangle edges and corners are also handled here using the same
+    // concept. For more details on conservative raster, see:
+    // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html
+    v->codeAppendf("float2 corner = %s[clockwise_indices & 3];", hullPts);
+    v->codeAppendf("float2 left = %s[clockwise_indices >> %i];",
+                   hullPts, kVertexData_LeftNeighborIdShift);
+    v->codeAppendf("float2 right = %s[(clockwise_indices >> %i) & 3];",
+                   hullPts, kVertexData_RightNeighborIdShift);
+
+    v->codeAppend ("float2 leftbloat = sign(corner - left);");
+    v->codeAppend ("leftbloat = float2(0 != leftbloat.y ? leftbloat.y : leftbloat.x, "
+                                      "0 != leftbloat.x ? -leftbloat.x : -leftbloat.y);");
+
+    v->codeAppend ("float2 rightbloat = sign(right - corner);");
+    v->codeAppend ("rightbloat = float2(0 != rightbloat.y ? rightbloat.y : rightbloat.x, "
+                                       "0 != rightbloat.x ? -rightbloat.x : -rightbloat.y);");
+
+    v->codeAppend ("bool2 left_right_notequal = notEqual(leftbloat, rightbloat);");
+
+    v->codeAppend ("float2 bloatdir = leftbloat;");
+
+    v->codeAppend ("float2 leftdir = corner - left;");
+    v->codeAppend ("leftdir = (float2(0) != leftdir) ? normalize(leftdir) : float2(1, 0);");
+
+    v->codeAppend ("float2 rightdir = right - corner;");
+    v->codeAppend ("rightdir = (float2(0) != rightdir) ? normalize(rightdir) : float2(1, 0);");
+
+    v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner?
+                   proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit);
+
+                       // In corner boxes, all 4 coverage values will not map linearly.
+                       // Therefore it is important to align the box so its diagonal shared
+                       // edge points out of the triangle, in the direction that ramps to 0.
+    v->codeAppend (    "bloatdir = float2(leftdir.x > rightdir.x ? +1 : -1, "
+                                         "leftdir.y > rightdir.y ? +1 : -1);");
+
+                       // For corner boxes, we hack left_right_notequal to always true. This
+                       // in turn causes the upcoming code to always rotate, generating all
+                       // 4 vertices of the corner box.
+    v->codeAppendf(    "left_right_notequal = bool2(true);");
+    v->codeAppend ("}");
+
+    // At each corner of the polygon, our hull will have either 1, 2, or 3 vertices (or 4 if
+    // it's a corner box). We begin with this corner's first raster vertex (leftbloat), then
+    // continue rotating 90 degrees clockwise until we reach the desired raster vertex for this
+    // invocation. Corners with less than 3 corresponding raster vertices will result in
+    // redundant vertices and degenerate triangles.
+    v->codeAppendf("int bloatidx = (%s >> %i) & 3;",
+                   proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_BloatIdxShift);
+    v->codeAppend ("switch (bloatidx) {");
+    v->codeAppend (    "case 3:");
+                            // Only corners will have bloatidx=3, and corners always rotate.
+    v->codeAppend (        "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW.
+                           // fallthru.
+    v->codeAppend (    "case 2:");
+    v->codeAppendf(        "if (all(left_right_notequal)) {");
+    v->codeAppend (            "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW.
+    v->codeAppend (        "}");
+                           // fallthru.
+    v->codeAppend (    "case 1:");
+    v->codeAppendf(        "if (any(left_right_notequal)) {");
+    v->codeAppend (            "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW.
+    v->codeAppend (        "}");
+                           // fallthru.
+    v->codeAppend ("}");
+
+    v->codeAppend ("float2 vertex = corner + bloatdir * bloat;");
+    gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex");
+
+    v->codeAppend ("half left_coverage; {");
+    Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "left_coverage");
+    v->codeAppend ("}");
+
+    v->codeAppend ("half right_coverage; {");
+    Shader::CalcEdgeCoverageAtBloatVertex(v, "corner", "right", "bloatdir", "right_coverage");
+    v->codeAppend ("}");
+
+    v->codeAppend ("half attenuation; {");
+    Shader::CalcCornerCoverageAttenuation(v, "leftdir", "rightdir", "attenuation");
+    v->codeAppend ("}");
+
+    // Hulls have a coverage of +1 all around.
+    v->codeAppend ("half coverage = +1;");
+
+    if (3 == fNumSides) {
+        v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge?
+                       proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsEdgeBit);
+        v->codeAppend (    "coverage = left_coverage;");
+        v->codeAppend ("}");
+
+        v->codeAppendf("if (0 != (%s & %i)) {", // Invert coverage?
+                       proc.getAttrib(kAttribIdx_VertexData).fName,
+                       kVertexData_InvertNegativeCoverageBit);
+        v->codeAppend (    "coverage = -1 - coverage;");
+        v->codeAppend ("}");
+    }
+
+    // Corner boxes require attenuation.
+    v->codeAppend ("half2 attenuated_coverage = half2(0, 1);");
+
+    v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner?
+                   proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit);
+                       // We use coverage=-1 to erase what the hull geometry wrote.
+    v->codeAppend (    "coverage = -1;");
+    if (3 == fNumSides) {
+                       // Triangle corners also have to erase what the edge geometry wrote.
+        v->codeAppend ("coverage -= left_coverage + right_coverage;");
+    }
+                       // The x and y components of "attenuated_coverage" are multiplied
+                       // together by the fragment shader. They ramp to 0 with attenuation in
+                       // the diagonal that points out of the corner, and linearly from
+                       // left-edge coverage to right in the opposite diagonal.
+                       // bloatidx=0 is the outermost vertex; the one that has attenuation.
+    v->codeAppend (    "attenuated_coverage = (0 == bloatidx)"
+                               "? half2(0, attenuation) : half2(1);");
+    v->codeAppend (    "if (1 == bloatidx || 2 == bloatidx) {");
+    v->codeAppend (        "attenuated_coverage.x += right_coverage;");
+    v->codeAppend (    "}");
+    v->codeAppend (    "if (bloatidx >= 2) {");
+    v->codeAppend (        "attenuated_coverage.x += left_coverage;");
+    v->codeAppend (    "}");
+    v->codeAppend ("}");
+
+    GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
+    SkString varyingCode;
+    v->codeAppend ("coverage *= wind;");
+    v->codeAppend ("attenuated_coverage.x *= wind;");
+    fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kVertToFrag, &varyingCode,
+                          gpArgs->fPositionVar.c_str(), "coverage", "attenuated_coverage");
+    v->codeAppend(varyingCode.c_str());
+
+    varyingHandler->emitAttributes(proc);
+    SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
+
+    // Fragment shader.
+    fShader->emitFragmentCode(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
+}
 
 void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) {
     SkASSERT(Impl::kVertexShader == fImpl);
     const GrCaps& caps = *rp->caps();
 
-    switch (fRenderPass) {
-        case RenderPass::kTriangles: {
+    switch (fPrimitiveType) {
+        case PrimitiveType::kTriangles: {
             GR_DEFINE_STATIC_UNIQUE_KEY(gTriangleVertexBufferKey);
-            fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType,
-                                                       sizeof(kTriangleVertices),
-                                                       kTriangleVertices,
-                                                       gTriangleVertexBufferKey);
+            fVSVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType,
+                                                         sizeof(kTriangleVertices),
+                                                         kTriangleVertices,
+                                                         gTriangleVertexBufferKey);
             GR_DEFINE_STATIC_UNIQUE_KEY(gTriangleIndexBufferKey);
             if (caps.usePrimitiveRestart()) {
-                fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
-                                                          sizeof(kTriangleIndicesAsStrips),
-                                                          kTriangleIndicesAsStrips,
-                                                          gTriangleIndexBufferKey);
-                fNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsStrips);
+                fVSIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
+                                                            sizeof(kTriangleIndicesAsStrips),
+                                                            kTriangleIndicesAsStrips,
+                                                            gTriangleIndexBufferKey);
+                fVSNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsStrips);
             } else {
-                fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
-                                                          sizeof(kTriangleIndicesAsTris),
-                                                          kTriangleIndicesAsTris,
-                                                          gTriangleIndexBufferKey);
-                fNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsTris);
+                fVSIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
+                                                            sizeof(kTriangleIndicesAsTris),
+                                                            kTriangleIndicesAsTris,
+                                                            gTriangleIndexBufferKey);
+                fVSNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsTris);
             }
             break;
         }
 
-        case RenderPass::kQuadratics:
-        case RenderPass::kCubics: {
-            GR_DEFINE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey);
-            fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType, sizeof(kHull4Vertices),
-                                                       kHull4Vertices, gHull4VertexBufferKey);
-            GR_DEFINE_STATIC_UNIQUE_KEY(gHull4IndexBufferKey);
+        case PrimitiveType::kQuadratics:
+        case PrimitiveType::kCubics: {
+            GR_DEFINE_STATIC_UNIQUE_KEY(gCurveVertexBufferKey);
+            fVSVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType,
+                                                         sizeof(kCurveVertices), kCurveVertices,
+                                                         gCurveVertexBufferKey);
+            GR_DEFINE_STATIC_UNIQUE_KEY(gCurveIndexBufferKey);
             if (caps.usePrimitiveRestart()) {
-                fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
-                                                          sizeof(kHull4IndicesAsStrips),
-                                                          kHull4IndicesAsStrips,
-                                                          gHull4IndexBufferKey);
-                fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4IndicesAsStrips);
+                fVSIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
+                                                            sizeof(kCurveIndicesAsStrips),
+                                                            kCurveIndicesAsStrips,
+                                                            gCurveIndexBufferKey);
+                fVSNumIndicesPerInstance = SK_ARRAY_COUNT(kCurveIndicesAsStrips);
             } else {
-                fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
-                                                          sizeof(kHull4IndicesAsTris),
-                                                          kHull4IndicesAsTris,
-                                                          gHull4IndexBufferKey);
-                fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4IndicesAsTris);
+                fVSIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
+                                                            sizeof(kCurveIndicesAsTris),
+                                                            kCurveIndicesAsTris,
+                                                            gCurveIndexBufferKey);
+                fVSNumIndicesPerInstance = SK_ARRAY_COUNT(kCurveIndicesAsTris);
             }
             break;
         }
-
-        case RenderPass::kTriangleCorners:
-        case RenderPass::kQuadraticCorners:
-        case RenderPass::kCubicCorners:
-            SK_ABORT("Corners are not used by VSImpl.");
     }
 
-    if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) {
+    if (PrimitiveType::kCubics == fPrimitiveType || WindMethod::kInstanceData == fWindMethod) {
         SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints());
 
         SkASSERT(kAttribIdx_X == this->numAttribs());
@@ -563,32 +520,28 @@
 
     if (caps.usePrimitiveRestart()) {
         this->setWillUsePrimitiveRestart();
-        fPrimitiveType = GrPrimitiveType::kTriangleStrip;
+        fVSTriangleType = GrPrimitiveType::kTriangleStrip;
     } else {
-        fPrimitiveType = GrPrimitiveType::kTriangles;
+        fVSTriangleType = GrPrimitiveType::kTriangles;
     }
 }
 
 void GrCCCoverageProcessor::appendVSMesh(GrBuffer* instanceBuffer, int instanceCount,
                                          int baseInstance, SkTArray<GrMesh>* out) const {
     SkASSERT(Impl::kVertexShader == fImpl);
-    GrMesh& mesh = out->emplace_back(fPrimitiveType);
-    mesh.setIndexedInstanced(fIndexBuffer.get(), fNumIndicesPerInstance, instanceBuffer,
+    GrMesh& mesh = out->emplace_back(fVSTriangleType);
+    mesh.setIndexedInstanced(fVSIndexBuffer.get(), fVSNumIndicesPerInstance, instanceBuffer,
                              instanceCount, baseInstance);
-    mesh.setVertexData(fVertexBuffer.get(), 0);
+    mesh.setVertexData(fVSVertexBuffer.get(), 0);
 }
 
 GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createVSImpl(std::unique_ptr<Shader> shadr) const {
-    switch (fRenderPass) {
-        case RenderPass::kTriangles:
-            return new VSHullAndEdgeImpl(std::move(shadr), 3);
-        case RenderPass::kQuadratics:
-        case RenderPass::kCubics:
-            return new VSHullAndEdgeImpl(std::move(shadr), 4);
-        case RenderPass::kTriangleCorners:
-        case RenderPass::kQuadraticCorners:
-        case RenderPass::kCubicCorners:
-            SK_ABORT("Corners are not used by VSImpl.");
+    switch (fPrimitiveType) {
+        case PrimitiveType::kTriangles:
+            return new VSImpl(std::move(shadr), 3);
+        case PrimitiveType::kQuadratics:
+        case PrimitiveType::kCubics:
+            return new VSImpl(std::move(shadr), 4);
     }
     SK_ABORT("Invalid RenderPass");
     return nullptr;
diff --git a/src/gpu/ccpr/GrCCPathParser.cpp b/src/gpu/ccpr/GrCCPathParser.cpp
index 1114367..abeb164 100644
--- a/src/gpu/ccpr/GrCCPathParser.cpp
+++ b/src/gpu/ccpr/GrCCPathParser.cpp
@@ -500,7 +500,7 @@
 
 void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCountBatchID batchID,
                                        const SkIRect& drawBounds) const {
-    using RenderPass = GrCCCoverageProcessor::RenderPass;
+    using PrimitiveType = GrCCCoverageProcessor::PrimitiveType;
     using WindMethod = GrCCCoverageProcessor::WindMethod;
 
     SkASSERT(fInstanceBuffer);
@@ -511,54 +511,40 @@
                         SkBlendMode::kPlus);
 
     if (batchTotalCounts.fTriangles) {
-        this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangles,
+        this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kTriangles,
                              WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, drawBounds);
-        this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners,
-                             WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles,
-                             drawBounds); // Might get skipped.
     }
 
     if (batchTotalCounts.fWoundTriangles) {
-        this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangles,
+        this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kTriangles,
                              WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles,
                              drawBounds);
-        this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners,
-                             WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles,
-                             drawBounds); // Might get skipped.
     }
 
     if (batchTotalCounts.fQuadratics) {
-        this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadratics,
-                             WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds);
-        this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadraticCorners,
+        this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kQuadratics,
                              WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds);
     }
 
     if (batchTotalCounts.fCubics) {
-        this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubics,
-                             WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds);
-        this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicCorners,
+        this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kCubics,
                              WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds);
     }
 }
 
-void GrCCPathParser::drawRenderPass(GrOpFlushState* flushState, const GrPipeline& pipeline,
+void GrCCPathParser::drawPrimitives(GrOpFlushState* flushState, const GrPipeline& pipeline,
                                     CoverageCountBatchID batchID,
-                                    GrCCCoverageProcessor::RenderPass renderPass,
+                                    GrCCCoverageProcessor::PrimitiveType primitiveType,
                                     GrCCCoverageProcessor::WindMethod windMethod,
                                     int PrimitiveTallies::*instanceType,
                                     const SkIRect& drawBounds) const {
     SkASSERT(pipeline.getScissorState().enabled());
 
-    if (!GrCCCoverageProcessor::DoesRenderPass(renderPass, flushState->caps())) {
-        return;
-    }
-
     // Don't call reset(), as that also resets the reserve count.
     fMeshesScratchBuffer.pop_back_n(fMeshesScratchBuffer.count());
     fDynamicStatesScratchBuffer.pop_back_n(fDynamicStatesScratchBuffer.count());
 
-    GrCCCoverageProcessor proc(flushState->resourceProvider(), renderPass, windMethod);
+    GrCCCoverageProcessor proc(flushState->resourceProvider(), primitiveType, windMethod);
 
     SkASSERT(batchID > 0);
     SkASSERT(batchID < fCoverageCountBatches.count());
@@ -600,9 +586,8 @@
     SkASSERT(totalInstanceCount == batch.fTotalPrimitiveCounts.*instanceType);
 
     if (!fMeshesScratchBuffer.empty()) {
-        SkASSERT(flushState->rtCommandBuffer());
-        flushState->rtCommandBuffer()->draw(pipeline, proc, fMeshesScratchBuffer.begin(),
-                                            fDynamicStatesScratchBuffer.begin(),
-                                            fMeshesScratchBuffer.count(), SkRect::Make(drawBounds));
+        proc.draw(flushState, pipeline, fMeshesScratchBuffer.begin(),
+                  fDynamicStatesScratchBuffer.begin(), fMeshesScratchBuffer.count(),
+                  SkRect::Make(drawBounds));
     }
 }
diff --git a/src/gpu/ccpr/GrCCPathParser.h b/src/gpu/ccpr/GrCCPathParser.h
index b1a1ee5..b28ab3b 100644
--- a/src/gpu/ccpr/GrCCPathParser.h
+++ b/src/gpu/ccpr/GrCCPathParser.h
@@ -127,8 +127,8 @@
     void parsePath(const SkPath&, const SkPoint* deviceSpacePts);
     void endContourIfNeeded(bool insideContour);
 
-    void drawRenderPass(GrOpFlushState*, const GrPipeline&, CoverageCountBatchID,
-                        GrCCCoverageProcessor::RenderPass, GrCCCoverageProcessor::WindMethod,
+    void drawPrimitives(GrOpFlushState*, const GrPipeline&, CoverageCountBatchID,
+                        GrCCCoverageProcessor::PrimitiveType, GrCCCoverageProcessor::WindMethod,
                         int PrimitiveTallies::*instanceType, const SkIRect& drawBounds) const;
 
     // Staging area for the path being parsed.