ccpr: Tessellate fans for very large and/or simple paths

This increases CPU work, but reduces overdraw on the GPU as compared to
Redbook fanning.

TBR=bsalomon@google.com

Change-Id: I396b887075d4422531908c2361ee1e26f076d5c3
Reviewed-on: https://skia-review.googlesource.com/107141
Reviewed-by: Chris Dalton <csmartdalton@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/samplecode/SampleCCPRGeometry.cpp b/samplecode/SampleCCPRGeometry.cpp
index 5a2da7f..a0c5d7b 100644
--- a/samplecode/SampleCCPRGeometry.cpp
+++ b/samplecode/SampleCCPRGeometry.cpp
@@ -26,8 +26,8 @@
 #include "gl/GrGLGpu.cpp"
 #include "ops/GrDrawOp.h"
 
-using TriangleInstance = GrCCCoverageProcessor::TriangleInstance;
-using CubicInstance = GrCCCoverageProcessor::CubicInstance;
+using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
+using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance;
 using RenderPass = GrCCCoverageProcessor::RenderPass;
 
 static constexpr float kDebugBloat = 40;
@@ -66,8 +66,8 @@
     SkPoint fPoints[4] = {
             {100.05f, 100.05f}, {400.75f, 100.05f}, {400.75f, 300.95f}, {100.05f, 300.95f}};
 
-    SkTArray<TriangleInstance> fTriangleInstances;
-    SkTArray<CubicInstance> fCubicInstances;
+    SkTArray<TriPointInstance> fTriPointInstances;
+    SkTArray<QuadPointInstance> fQuadPointInstances;
 
     typedef SampleView INHERITED;
 };
@@ -190,8 +190,8 @@
 }
 
 void CCPRGeometryView::updateGpuData() {
-    fTriangleInstances.reset();
-    fCubicInstances.reset();
+    fTriPointInstances.reset();
+    fQuadPointInstances.reset();
 
     if (GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass)) {
         double t[2], s[2];
@@ -210,7 +210,7 @@
                     ptsIdx += 2;
                     continue;
                 case GrCCGeometry::Verb::kMonotonicCubicTo:
-                    fCubicInstances.push_back().set(&geometry.points()[ptsIdx], 0, 0);
+                    fQuadPointInstances.push_back().set(&geometry.points()[ptsIdx], 0, 0);
                     ptsIdx += 3;
                     continue;
                 default:
@@ -234,11 +234,11 @@
                 continue;
             }
             SkASSERT(GrCCGeometry::Verb::kMonotonicQuadraticTo == verb);
-            fTriangleInstances.push_back().set(&geometry.points()[ptsIdx], Sk2f(0, 0));
+            fTriPointInstances.push_back().set(&geometry.points()[ptsIdx], Sk2f(0, 0));
             ptsIdx += 2;
         }
     } else {
-        fTriangleInstances.push_back().set(fPoints[0], fPoints[1], fPoints[3], Sk2f(0, 0));
+        fTriPointInstances.push_back().set(fPoints[0], fPoints[1], fPoints[3], Sk2f(0, 0));
     }
 }
 
@@ -253,27 +253,28 @@
         return;
     }
 
-    GrCCCoverageProcessor proc(rp, fView->fRenderPass, state->caps());
+    GrCCCoverageProcessor proc(rp, fView->fRenderPass,
+                               GrCCCoverageProcessor::WindMethod::kCrossProduct);
     SkDEBUGCODE(proc.enableDebugVisualizations(kDebugBloat));
 
     SkSTArray<1, GrMesh> mesh;
     if (GrCCCoverageProcessor::RenderPassIsCubic(fView->fRenderPass)) {
         sk_sp<GrBuffer> instBuff(rp->createBuffer(
-                fView->fCubicInstances.count() * sizeof(CubicInstance), kVertex_GrBufferType,
-                kDynamic_GrAccessPattern,
+                fView->fQuadPointInstances.count() * sizeof(QuadPointInstance),
+                kVertex_GrBufferType, kDynamic_GrAccessPattern,
                 GrResourceProvider::kNoPendingIO_Flag | GrResourceProvider::kRequireGpuMemory_Flag,
-                fView->fCubicInstances.begin()));
-        if (!fView->fCubicInstances.empty() && instBuff) {
-            proc.appendMesh(instBuff.get(), fView->fCubicInstances.count(), 0, &mesh);
+                fView->fQuadPointInstances.begin()));
+        if (!fView->fQuadPointInstances.empty() && instBuff) {
+            proc.appendMesh(instBuff.get(), fView->fQuadPointInstances.count(), 0, &mesh);
         }
     } else {
         sk_sp<GrBuffer> instBuff(rp->createBuffer(
-                fView->fTriangleInstances.count() * sizeof(TriangleInstance), kVertex_GrBufferType,
+                fView->fTriPointInstances.count() * sizeof(TriPointInstance), kVertex_GrBufferType,
                 kDynamic_GrAccessPattern,
                 GrResourceProvider::kNoPendingIO_Flag | GrResourceProvider::kRequireGpuMemory_Flag,
-                fView->fTriangleInstances.begin()));
-        if (!fView->fTriangleInstances.empty() && instBuff) {
-            proc.appendMesh(instBuff.get(), fView->fTriangleInstances.count(), 0, &mesh);
+                fView->fTriPointInstances.begin()));
+        if (!fView->fTriPointInstances.empty() && instBuff) {
+            proc.appendMesh(instBuff.get(), fView->fTriPointInstances.count(), 0, &mesh);
         }
     }
 
diff --git a/src/gpu/GrTessellator.cpp b/src/gpu/GrTessellator.cpp
index 2974cf0..00e5c8a 100644
--- a/src/gpu/GrTessellator.cpp
+++ b/src/gpu/GrTessellator.cpp
@@ -2359,6 +2359,7 @@
                    GrTessellator::WindingVertex** verts) {
     int contourCnt = get_contour_count(path, tolerance);
     if (contourCnt <= 0) {
+        *verts = nullptr;
         return 0;
     }
     SkArenaAlloc alloc(kArenaChunkSize);
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.cpp b/src/gpu/ccpr/GrCCCoverageProcessor.cpp
index c875625..2c3166d 100644
--- a/src/gpu/ccpr/GrCCCoverageProcessor.cpp
+++ b/src/gpu/ccpr/GrCCCoverageProcessor.cpp
@@ -66,8 +66,11 @@
 
 void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&,
                                                 GrProcessorKeyBuilder* b) const {
-    int key = (int)fRenderPass << 1;
-    if (Impl::kGeometryShader == fImpl) {
+    int key = (int)fRenderPass << 2;
+    if (WindMethod::kInstanceData == fWindMethod) {
+        key |= 2;
+    }
+    if (Impl::kVertexShader == fImpl) {
         key |= 1;
     }
 #ifdef SK_DEBUG
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.h b/src/gpu/ccpr/GrCCCoverageProcessor.h
index 1296e08..e6adfff 100644
--- a/src/gpu/ccpr/GrCCCoverageProcessor.h
+++ b/src/gpu/ccpr/GrCCCoverageProcessor.h
@@ -33,8 +33,9 @@
  */
 class GrCCCoverageProcessor : public GrGeometryProcessor {
 public:
-    // Defines a single triangle or closed quadratic bezier, with transposed x,y point values.
-    struct TriangleInstance {
+    // Defines a single primitive shape with 3 input points (i.e. Triangles and Quadratics).
+    // X,Y point values are transposed.
+    struct TriPointInstance {
         float fX[3];
         float fY[3];
 
@@ -42,12 +43,15 @@
         void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans);
     };
 
-    // Defines a single closed cubic bezier, with transposed x,y point values.
-    struct CubicInstance {
+    // Defines a single primitive shape with 4 input points, or 3 input points plus a W parameter
+    // duplicated in both 4th components (i.e. Cubics or Triangles with a custom 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 set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans, float w);
     };
 
     // All primitive shapes (triangles and closed, convex bezier curves) require more than one
@@ -93,24 +97,29 @@
                caps.shaderCaps()->geometryShaderSupport();
     }
 
-    GrCCCoverageProcessor(GrResourceProvider* rp, RenderPass pass, const GrCaps& caps)
+    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)
             : INHERITED(kGrCCCoverageProcessor_ClassID)
             , fRenderPass(pass)
-            , fImpl(caps.shaderCaps()->geometryShaderSupport() ? Impl::kGeometryShader
-                                                               : Impl::kVertexShader) {
-        SkASSERT(DoesRenderPass(pass, caps));
+            , fWindMethod(windMethod)
+            , fImpl(rp->caps()->shaderCaps()->geometryShaderSupport() ? Impl::kGeometryShader
+                                                                      : Impl::kVertexShader) {
+        SkASSERT(DoesRenderPass(pass, *rp->caps()));
         if (Impl::kGeometryShader == fImpl) {
             this->initGS();
         } else {
-            this->initVS(rp, caps);
+            this->initVS(rp);
         }
     }
 
     // Appends a GrMesh that will draw the provided instances. The instanceBuffer must be an array
-    // of either TriangleInstance or CubicInstance, depending on this processor's RendererPass, with
-    // coordinates in the desired shape's final atlas-space position.
-    //
-    // NOTE: Quadratics use TriangleInstance since both have 3 points.
+    // of either TriPointInstance or QuadPointInstance, depending on this processor's RendererPass,
+    // with coordinates in the desired shape's final atlas-space position.
     void appendMesh(GrBuffer* instanceBuffer, int instanceCount, int baseInstance,
                     SkTArray<GrMesh>* out) {
         if (Impl::kGeometryShader == fImpl) {
@@ -227,7 +236,7 @@
     };
 
     void initGS();
-    void initVS(GrResourceProvider*, const GrCaps&);
+    void initVS(GrResourceProvider*);
 
     void appendGSMesh(GrBuffer* instanceBuffer, int instanceCount, int baseInstance,
                       SkTArray<GrMesh>* out) const;
@@ -238,6 +247,7 @@
     GrGLSLPrimitiveProcessor* createVSImpl(std::unique_ptr<Shader>) const;
 
     const RenderPass fRenderPass;
+    const WindMethod fWindMethod;
     const Impl fImpl;
     SkDEBUGCODE(float fDebugBloat = 0);
 
@@ -250,11 +260,11 @@
     typedef GrGeometryProcessor INHERITED;
 };
 
-inline void GrCCCoverageProcessor::TriangleInstance::set(const SkPoint p[3], const Sk2f& trans) {
+inline void GrCCCoverageProcessor::TriPointInstance::set(const SkPoint p[3], const Sk2f& trans) {
     this->set(p[0], p[1], p[2], trans);
 }
 
-inline void GrCCCoverageProcessor::TriangleInstance::set(const SkPoint& p0, const SkPoint& p1,
+inline void GrCCCoverageProcessor::TriPointInstance::set(const SkPoint& p0, const SkPoint& p1,
                                                          const SkPoint& p2, const Sk2f& trans) {
     Sk2f P0 = Sk2f::Load(&p0) + trans;
     Sk2f P1 = Sk2f::Load(&p1) + trans;
@@ -262,13 +272,23 @@
     Sk2f::Store3(this, P0, P1, P2);
 }
 
-inline void GrCCCoverageProcessor::CubicInstance::set(const SkPoint p[4], float dx, float dy) {
+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::set(const SkPoint& p0, const SkPoint& p1,
+                                                          const SkPoint& p2, const Sk2f& trans,
+                                                          float w) {
+    Sk2f P0 = Sk2f::Load(&p0) + trans;
+    Sk2f P1 = Sk2f::Load(&p1) + trans;
+    Sk2f P2 = Sk2f::Load(&p2) + trans;
+    Sk2f W = Sk2f(w);
+    Sk2f::Store4(this, P0, P1, P2, W);
+}
+
 inline bool GrCCCoverageProcessor::RenderPassIsCubic(RenderPass pass) {
     switch (pass) {
         case RenderPass::kTriangleHulls:
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp
index 0754389..224533b 100644
--- a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp
+++ b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp
@@ -31,7 +31,7 @@
 
         // The vertex shader simply forwards transposed x or y values to the geometry shader.
         SkASSERT(1 == proc.numAttribs());
-        gpArgs->fPositionVar.set(4 == proc.numInputPoints() ? kFloat4_GrSLType : kFloat3_GrSLType,
+        gpArgs->fPositionVar.set(GrVertexAttribTypeToSLType(proc.getAttrib(0).fType),
                                  proc.getAttrib(0).fName);
 
         // Geometry shader.
@@ -57,11 +57,20 @@
 
         GrShaderVar wind("wind", kHalf_GrSLType);
         g->declareGlobal(wind);
-        g->codeAppend ("float area_x2 = determinant(float2x2(pts[0] - pts[1], pts[0] - pts[2]));");
-        if (4 == numInputPoints) {
-            g->codeAppend ("area_x2 += determinant(float2x2(pts[0] - pts[2], pts[0] - pts[3]));");
+        if (WindMethod::kCrossProduct == proc.fWindMethod) {
+            g->codeAppend ("float area_x2 = determinant(float2x2(pts[0] - pts[1], "
+                                                                "pts[0] - pts[2]));");
+            if (4 == numInputPoints) {
+                g->codeAppend ("area_x2 += determinant(float2x2(pts[0] - pts[2], "
+                                                               "pts[0] - pts[3]));");
+            }
+            g->codeAppendf("%s = sign(area_x2);", wind.c_str());
+        } else {
+            SkASSERT(WindMethod::kInstanceData == proc.fWindMethod);
+            SkASSERT(3 == numInputPoints);
+            SkASSERT(kFloat4_GrVertexAttribType == proc.getAttrib(0).fType);
+            g->codeAppendf("%s = sk_in[0].sk_Position.w;", wind.c_str());
         }
-        g->codeAppendf("%s = sign(area_x2);", wind.c_str());
 
         SkString emitVertexFn;
         SkSTArray<2, GrShaderVar> emitArgs;
@@ -322,12 +331,17 @@
 
 void GrCCCoverageProcessor::initGS() {
     SkASSERT(Impl::kGeometryShader == fImpl);
-    if (RenderPassIsCubic(fRenderPass)) {
-        this->addVertexAttrib("x_or_y_values", kFloat4_GrVertexAttribType); // (See appendMesh.)
-        SkASSERT(sizeof(CubicInstance) == this->getVertexStride() * 2);
+    if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) {
+        SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints());
+        this->addVertexAttrib("x_or_y_values", kFloat4_GrVertexAttribType);
+        SkASSERT(sizeof(QuadPointInstance) == this->getVertexStride() * 2);
+        SkASSERT(offsetof(QuadPointInstance, fY) == this->getVertexStride());
+        GR_STATIC_ASSERT(0 == offsetof(QuadPointInstance, fX));
     } else {
-        this->addVertexAttrib("x_or_y_values", kFloat3_GrVertexAttribType); // (See appendMesh.)
-        SkASSERT(sizeof(TriangleInstance) == this->getVertexStride() * 2);
+        this->addVertexAttrib("x_or_y_values", kFloat3_GrVertexAttribType);
+        SkASSERT(sizeof(TriPointInstance) == this->getVertexStride() * 2);
+        SkASSERT(offsetof(TriPointInstance, fY) == this->getVertexStride());
+        GR_STATIC_ASSERT(0 == offsetof(TriPointInstance, fX));
     }
     this->setWillUseGeoShader();
 }
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp
index 8f2884a..4ac6443 100644
--- a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp
+++ b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp
@@ -35,15 +35,25 @@
         GrGLSLVertexBuilder* v = args.fVertBuilder;
         int numInputPoints = proc.numInputPoints();
 
-        v->codeAppendf("float%ix2 pts = transpose(float2x%i(%s, %s));",
-                       numInputPoints, numInputPoints, proc.getAttrib(kAttribIdx_X).fName,
-                       proc.getAttrib(kAttribIdx_Y).fName);
+        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);
 
-        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]));");
+        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);
         }
-        v->codeAppend ("half wind = sign(area_x2);");
 
         float bloat = kAABloatRadius;
 #ifdef SK_DEBUG
@@ -340,17 +350,9 @@
     }
 };
 
-void GrCCCoverageProcessor::initVS(GrResourceProvider* rp, const GrCaps& caps) {
+void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) {
     SkASSERT(Impl::kVertexShader == fImpl);
-
-    GrVertexAttribType inputPtsType = RenderPassIsCubic(fRenderPass) ?
-                                      kFloat4_GrVertexAttribType : kFloat3_GrVertexAttribType;
-
-    SkASSERT(kAttribIdx_X == this->numAttribs());
-    this->addInstanceAttrib("X", inputPtsType);
-
-    SkASSERT(kAttribIdx_Y == this->numAttribs());
-    this->addInstanceAttrib("Y", inputPtsType);
+    const GrCaps& caps = *rp->caps();
 
     switch (fRenderPass) {
         case RenderPass::kTriangleHulls: {
@@ -373,8 +375,6 @@
                                                           gHull3AndEdgeIndexBufferKey);
                 fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull3AndEdgeIndicesAsTris);
             }
-            SkASSERT(kAttribIdx_VertexData == this->numAttribs());
-            this->addVertexAttrib("vertexdata", kInt_GrVertexAttribType);
             break;
         }
         case RenderPass::kQuadraticHulls:
@@ -396,8 +396,6 @@
                                                           gHull4IndexBufferKey);
                 fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4IndicesAsTris);
             }
-            SkASSERT(kAttribIdx_VertexData == this->numAttribs());
-            this->addVertexAttrib("vertexdata", kInt_GrVertexAttribType);
             break;
         }
         case RenderPass::kTriangleEdges:
@@ -427,20 +425,36 @@
         }
     }
 
-#ifdef SK_DEBUG
-    if (RenderPassIsCubic(fRenderPass)) {
-        SkASSERT(offsetof(CubicInstance, fX) == this->getAttrib(kAttribIdx_X).fOffsetInRecord);
-        SkASSERT(offsetof(CubicInstance, fY) == this->getAttrib(kAttribIdx_Y).fOffsetInRecord);
-        SkASSERT(sizeof(CubicInstance) == this->getInstanceStride());
+    if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) {
+        SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints());
+
+        SkASSERT(kAttribIdx_X == this->numAttribs());
+        this->addInstanceAttrib("X", kFloat4_GrVertexAttribType);
+
+        SkASSERT(kAttribIdx_Y == this->numAttribs());
+        this->addInstanceAttrib("Y", kFloat4_GrVertexAttribType);
+
+        SkASSERT(offsetof(QuadPointInstance, fX) == this->getAttrib(kAttribIdx_X).fOffsetInRecord);
+        SkASSERT(offsetof(QuadPointInstance, fY) == this->getAttrib(kAttribIdx_Y).fOffsetInRecord);
+        SkASSERT(sizeof(QuadPointInstance) == this->getInstanceStride());
     } else {
-        SkASSERT(offsetof(TriangleInstance, fX) == this->getAttrib(kAttribIdx_X).fOffsetInRecord);
-        SkASSERT(offsetof(TriangleInstance, fY) == this->getAttrib(kAttribIdx_Y).fOffsetInRecord);
-        SkASSERT(sizeof(TriangleInstance) == this->getInstanceStride());
+        SkASSERT(kAttribIdx_X == this->numAttribs());
+        this->addInstanceAttrib("X", kFloat3_GrVertexAttribType);
+
+        SkASSERT(kAttribIdx_Y == this->numAttribs());
+        this->addInstanceAttrib("Y", kFloat3_GrVertexAttribType);
+
+        SkASSERT(offsetof(TriPointInstance, fX) == this->getAttrib(kAttribIdx_X).fOffsetInRecord);
+        SkASSERT(offsetof(TriPointInstance, fY) == this->getAttrib(kAttribIdx_Y).fOffsetInRecord);
+        SkASSERT(sizeof(TriPointInstance) == this->getInstanceStride());
     }
+
     if (fVertexBuffer) {
+        SkASSERT(kAttribIdx_VertexData == this->numAttribs());
+        this->addVertexAttrib("vertexdata", kInt_GrVertexAttribType);
+
         SkASSERT(sizeof(int32_t) == this->getVertexStride());
     }
-#endif
 
     if (caps.usePrimitiveRestart()) {
         this->setWillUsePrimitiveRestart();
diff --git a/src/gpu/ccpr/GrCCGeometry.cpp b/src/gpu/ccpr/GrCCGeometry.cpp
index 19bc874..481f4e4 100644
--- a/src/gpu/ccpr/GrCCGeometry.cpp
+++ b/src/gpu/ccpr/GrCCGeometry.cpp
@@ -30,7 +30,7 @@
 
     // 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};
+    fCurrContourTallies = {fVerbs.count(), 0, 0, 0};
 
     fPoints.push_back(devPt);
     fVerbs.push_back(Verb::kBeginContour);
diff --git a/src/gpu/ccpr/GrCCGeometry.h b/src/gpu/ccpr/GrCCGeometry.h
index b50dd01..47b57c1 100644
--- a/src/gpu/ccpr/GrCCGeometry.h
+++ b/src/gpu/ccpr/GrCCGeometry.h
@@ -35,9 +35,10 @@
         kEndOpenContour // endPt != startPt.
     };
 
-    // These tallies track numbers of CCPR primitives are required to draw a contour.
+    // 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 fWoundTriangles; // Triangles (from the tessellator) whose winding magnitude > 1.
         int fQuadratics;
         int fCubics;
 
@@ -115,11 +116,11 @@
                                   int maxSubdivisions = kMaxSubdivionsPerCubicSection);
 
     // Transient state used while building a contour.
-    SkPoint                         fCurrAnchorPoint;
-    SkPoint                         fCurrFanPoint;
-    PrimitiveTallies                fCurrContourTallies;
-    SkCubicType                     fCurrCubicType;
-    SkDEBUGCODE(bool                fBuildingContour = false);
+    SkPoint fCurrAnchorPoint;
+    SkPoint fCurrFanPoint;
+    PrimitiveTallies fCurrContourTallies;
+    SkCubicType fCurrCubicType;
+    SkDEBUGCODE(bool fBuildingContour = false);
 
     // TODO: These points could eventually be written directly to block-allocated GPU buffers.
     SkSTArray<128, SkPoint, true>   fPoints;
@@ -128,6 +129,7 @@
 
 inline void GrCCGeometry::PrimitiveTallies::operator+=(const PrimitiveTallies& b) {
     fTriangles += b.fTriangles;
+    fWoundTriangles += b.fWoundTriangles;
     fQuadratics += b.fQuadratics;
     fCubics += b.fCubics;
 }
@@ -135,12 +137,14 @@
 GrCCGeometry::PrimitiveTallies
 inline GrCCGeometry::PrimitiveTallies::operator-(const PrimitiveTallies& b) const {
     return {fTriangles - b.fTriangles,
+            fWoundTriangles - b.fWoundTriangles,
             fQuadratics - b.fQuadratics,
             fCubics - b.fCubics};
 }
 
 inline bool GrCCGeometry::PrimitiveTallies::operator==(const PrimitiveTallies& b) {
-    return fTriangles == b.fTriangles && fQuadratics == b.fQuadratics && fCubics == b.fCubics;
+    return fTriangles == b.fTriangles && fWoundTriangles == b.fWoundTriangles &&
+           fQuadratics == b.fQuadratics && fCubics == b.fCubics;
 }
 
 #endif
diff --git a/src/gpu/ccpr/GrCCPathParser.cpp b/src/gpu/ccpr/GrCCPathParser.cpp
index 2a632d3..b19ffdf 100644
--- a/src/gpu/ccpr/GrCCPathParser.cpp
+++ b/src/gpu/ccpr/GrCCPathParser.cpp
@@ -16,9 +16,10 @@
 #include "SkPathPriv.h"
 #include "SkPoint.h"
 #include "ccpr/GrCCGeometry.h"
+#include <stdlib.h>
 
-using TriangleInstance = GrCCCoverageProcessor::TriangleInstance;
-using CubicInstance = GrCCCoverageProcessor::CubicInstance;
+using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
+using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance;
 
 GrCCPathParser::GrCCPathParser(int maxTotalPaths, int maxPathPoints, int numSkPoints,
                                int numSkVerbs)
@@ -32,7 +33,8 @@
     // 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()};
-    fCoverageCountBatches.push_back() = {PrimitiveTallies(), fScissorSubBatches.count()};
+    fCoverageCountBatches.push_back() = {PrimitiveTallies(), fScissorSubBatches.count(),
+                                         PrimitiveTallies()};
 }
 
 void GrCCPathParser::parsePath(const SkMatrix& m, const SkPath& path, SkRect* devBounds,
@@ -105,6 +107,7 @@
     fCurrPathPointsIdx = fGeometry.points().count();
     fCurrPathVerbsIdx = fGeometry.verbs().count();
     fCurrPathPrimitiveCounts = PrimitiveTallies();
+    fCurrPathFillType = path.getFillType();
 
     fGeometry.beginPath();
 
@@ -160,7 +163,81 @@
                                     int16_t atlasOffsetX, int16_t atlasOffsetY) {
     SkASSERT(fParsingPath);
 
-    fPathsInfo.push_back() = {scissorMode, atlasOffsetX, atlasOffsetY};
+    fPathsInfo.emplace_back(scissorMode, atlasOffsetX, atlasOffsetY);
+
+    // Tessellate fans from very large and/or simple paths, in order to reduce overdraw.
+    int numVerbs = fGeometry.verbs().count() - fCurrPathVerbsIdx - 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.
+        fCurrPathPrimitiveCounts.fTriangles =
+                fCurrPathPrimitiveCounts.fWoundTriangles = 0;
+
+        const SkTArray<GrCCGeometry::Verb, true>& verbs = fGeometry.verbs();
+        const SkTArray<SkPoint, true>& pts = fGeometry.points();
+        int ptsIdx = fCurrPathPointsIdx;
+
+        // Build an SkPath of the Redbook fan.
+        SkPath fan;
+        fan.setFillType(fCurrPathFillType);
+        SkASSERT(GrCCGeometry::Verb::kBeginPath == verbs[fCurrPathVerbsIdx]);
+        for (int i = fCurrPathVerbsIdx + 1; i < fGeometry.verbs().count(); ++i) {
+            switch (verbs[i]) {
+                case GrCCGeometry::Verb::kBeginPath:
+                    SK_ABORT("Invalid GrCCGeometry");
+                    continue;
+
+                case GrCCGeometry::Verb::kBeginContour:
+                    fan.moveTo(pts[ptsIdx++]);
+                    continue;
+
+                case GrCCGeometry::Verb::kLineTo:
+                    fan.lineTo(pts[ptsIdx++]);
+                    continue;
+
+                case GrCCGeometry::Verb::kMonotonicQuadraticTo:
+                    fan.lineTo(pts[ptsIdx + 1]);
+                    ptsIdx += 2;
+                    continue;
+
+                case GrCCGeometry::Verb::kMonotonicCubicTo:
+                    fan.lineTo(pts[ptsIdx + 2]);
+                    ptsIdx += 3;
+                    continue;
+
+                case GrCCGeometry::Verb::kEndClosedContour:
+                case GrCCGeometry::Verb::kEndOpenContour:
+                    fan.close();
+                    continue;
+            }
+        }
+        GrTessellator::WindingVertex* vertices = nullptr;
+        int count = GrTessellator::PathToVertices(fan, std::numeric_limits<float>::infinity(),
+                                                  SkRect::Make(clippedDevIBounds), &vertices);
+        SkASSERT(0 == count % 3);
+        for (int i = 0; i < count; i += 3) {
+            SkASSERT(vertices[i].fWinding == vertices[i + 1].fWinding);
+            SkASSERT(vertices[i].fWinding == vertices[i + 2].fWinding);
+            if (1 == abs(vertices[i].fWinding)) {
+                // Ensure this triangle's points actually wind in the same direction as fWinding.
+                float ax = vertices[i].fPos.fX - vertices[i + 1].fPos.fX;
+                float ay = vertices[i].fPos.fY - vertices[i + 1].fPos.fY;
+                float bx = vertices[i].fPos.fX - vertices[i + 2].fPos.fX;
+                float by = vertices[i].fPos.fY - vertices[i + 2].fPos.fY;
+                float wind = ay*bx - ax*by;
+                if ((wind > 0) != (vertices[i].fWinding > 0)) {
+                    std::swap(vertices[i + 1].fPos, vertices[i + 2].fPos);
+                }
+                ++fCurrPathPrimitiveCounts.fTriangles;
+            } else {
+                ++fCurrPathPrimitiveCounts.fWoundTriangles;
+            }
+        }
+
+        fPathsInfo.back().fFanTessellation.reset(vertices);
+        fPathsInfo.back().fFanTessellationCount = count;
+    }
+
     fTotalPrimitiveCounts[(int)scissorMode] += fCurrPathPrimitiveCounts;
 
     if (ScissorMode::kScissored == scissorMode) {
@@ -180,15 +257,23 @@
 GrCCPathParser::CoverageCountBatchID GrCCPathParser::closeCurrentBatch() {
     SkASSERT(!fInstanceBuffer);
     SkASSERT(!fCoverageCountBatches.empty());
+    const auto& lastBatch = fCoverageCountBatches.back();
+    const auto& lastScissorSubBatch = fScissorSubBatches[lastBatch.fEndScissorSubBatchIdx - 1];
 
-    int maxMeshes = 1 + fScissorSubBatches.count() -
-                        fCoverageCountBatches.back().fEndScissorSubBatchIdx;
-    fMaxMeshesPerDraw = SkTMax(fMaxMeshesPerDraw, maxMeshes);
+    PrimitiveTallies batchTotalCounts = fTotalPrimitiveCounts[(int)ScissorMode::kNonScissored] -
+                                        lastBatch.fEndNonScissorIndices;
+    batchTotalCounts += fTotalPrimitiveCounts[(int)ScissorMode::kScissored] -
+                        lastScissorSubBatch.fEndPrimitiveIndices;
 
     fCoverageCountBatches.push_back() = {
         fTotalPrimitiveCounts[(int)ScissorMode::kNonScissored],
-        fScissorSubBatches.count()
+        fScissorSubBatches.count(),
+        batchTotalCounts
     };
+
+    int maxMeshes = 1 + fScissorSubBatches.count() - lastBatch.fEndScissorSubBatchIdx;
+    fMaxMeshesPerDraw = SkTMax(fMaxMeshesPerDraw, maxMeshes);
+
     return fCoverageCountBatches.count() - 1;
 }
 
@@ -205,10 +290,10 @@
 // elements past the end for this method to use as scratch space.
 //
 // Returns the next triangle instance after the final one emitted.
-static TriangleInstance* emit_recursive_fan(const SkTArray<SkPoint, true>& pts,
+static TriPointInstance* emit_recursive_fan(const SkTArray<SkPoint, true>& pts,
                                             SkTArray<int32_t, true>& indices, int firstIndex,
                                             int indexCount, const Sk2f& atlasOffset,
-                                            TriangleInstance out[]) {
+                                            TriPointInstance out[]) {
     if (indexCount < 3) {
         return out;
     }
@@ -232,6 +317,22 @@
     return out;
 }
 
+static void emit_tessellated_fan(const GrTessellator::WindingVertex* vertices, int numVertices,
+                                 const Sk2f& atlasOffset, TriPointInstance* triPointInstanceData,
+                                 QuadPointInstance* quadPointInstanceData,
+                                 GrCCGeometry::PrimitiveTallies* indices) {
+    for (int i = 0; i < numVertices; i += 3) {
+        if (1 == abs(vertices[i].fWinding)) {
+            triPointInstanceData[indices->fTriangles++].set(vertices[i].fPos, vertices[i + 1].fPos,
+                                                            vertices[i + 2].fPos, atlasOffset);
+        } else {
+            quadPointInstanceData[indices->fWoundTriangles++].set(
+                    vertices[i].fPos, vertices[i+1].fPos, vertices[i + 2].fPos, atlasOffset,
+                    static_cast<float>(vertices[i].fWinding));
+        }
+    }
+}
+
 bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
     SkASSERT(!fParsingPath); // Call saveParsedPath() or discardParsedPath().
     SkASSERT(fCoverageCountBatches.back().fEndNonScissorIndices == // Call closeCurrentBatch().
@@ -250,7 +351,7 @@
     //
     // 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 TriangleInstance[], so we can begin at zero and lay them out one after the other.
+    // 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;
@@ -260,85 +361,112 @@
                                     fTotalPrimitiveCounts[0].fQuadratics;
     int triEndIdx = fBaseInstances[1].fQuadratics + fTotalPrimitiveCounts[1].fQuadratics;
 
-    // Cubics view the same instance buffer as an array of CubicInstance[]. So, reinterpreting the
-    // instance data as CubicInstance[], we start them on the first index that will not overwrite
-    // previous TriangleInstance data.
-    int cubicBaseIdx =
-            GR_CT_DIV_ROUND_UP(triEndIdx * sizeof(TriangleInstance), sizeof(CubicInstance));
-    fBaseInstances[0].fCubics = cubicBaseIdx;
+    // 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 =
+            GR_CT_DIV_ROUND_UP(triEndIdx * sizeof(TriPointInstance), sizeof(QuadPointInstance));
+    fBaseInstances[0].fWoundTriangles = quadBaseIdx;
+    fBaseInstances[1].fWoundTriangles = fBaseInstances[0].fWoundTriangles +
+                                        fTotalPrimitiveCounts[0].fWoundTriangles;
+    fBaseInstances[0].fCubics = fBaseInstances[1].fWoundTriangles +
+                                fTotalPrimitiveCounts[1].fWoundTriangles;
     fBaseInstances[1].fCubics = fBaseInstances[0].fCubics + fTotalPrimitiveCounts[0].fCubics;
-    int cubicEndIdx = fBaseInstances[1].fCubics + fTotalPrimitiveCounts[1].fCubics;
+    int quadEndIdx = fBaseInstances[1].fCubics + fTotalPrimitiveCounts[1].fCubics;
 
     fInstanceBuffer = onFlushRP->makeBuffer(kVertex_GrBufferType,
-                                            cubicEndIdx * sizeof(CubicInstance));
+                                            quadEndIdx * sizeof(QuadPointInstance));
     if (!fInstanceBuffer) {
         return false;
     }
 
-    TriangleInstance* triangleInstanceData = static_cast<TriangleInstance*>(fInstanceBuffer->map());
-    CubicInstance* cubicInstanceData = reinterpret_cast<CubicInstance*>(triangleInstanceData);
-    SkASSERT(cubicInstanceData);
+    TriPointInstance* triPointInstanceData = static_cast<TriPointInstance*>(fInstanceBuffer->map());
+    QuadPointInstance* quadPointInstanceData =
+            reinterpret_cast<QuadPointInstance*>(triPointInstanceData);
+    SkASSERT(quadPointInstanceData);
 
-    PathInfo* currPathInfo = fPathsInfo.begin();
+    PathInfo* nextPathInfo = fPathsInfo.begin();
     float atlasOffsetX = 0.0, atlasOffsetY = 0.0;
     Sk2f atlasOffset;
-    int ptsIdx = -1;
     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;
 
     // Expand the ccpr verbs into GPU instance buffers.
     for (GrCCGeometry::Verb verb : fGeometry.verbs()) {
         switch (verb) {
             case GrCCGeometry::Verb::kBeginPath:
                 SkASSERT(currFan.empty());
-                currIndices = &instanceIndices[(int)currPathInfo->fScissorMode];
-                atlasOffsetX = static_cast<float>(currPathInfo->fAtlasOffsetX);
-                atlasOffsetY = static_cast<float>(currPathInfo->fAtlasOffsetY);
+                currIndices = &instanceIndices[(int)nextPathInfo->fScissorMode];
+                atlasOffsetX = static_cast<float>(nextPathInfo->fAtlasOffsetX);
+                atlasOffsetY = static_cast<float>(nextPathInfo->fAtlasOffsetY);
                 atlasOffset = {atlasOffsetX, atlasOffsetY};
-                ++currPathInfo;
+                currFanIsTessellated = nextPathInfo->fFanTessellation.get();
+                if (currFanIsTessellated) {
+                    emit_tessellated_fan(nextPathInfo->fFanTessellation.get(),
+                                         nextPathInfo->fFanTessellationCount, atlasOffset,
+                                         triPointInstanceData, quadPointInstanceData, currIndices);
+                }
+                ++nextPathInfo;
                 continue;
 
             case GrCCGeometry::Verb::kBeginContour:
                 SkASSERT(currFan.empty());
-                currFan.push_back(++ptsIdx);
+                ++ptsIdx;
+                if (!currFanIsTessellated) {
+                    currFan.push_back(ptsIdx);
+                }
                 continue;
 
             case GrCCGeometry::Verb::kLineTo:
-                SkASSERT(!currFan.empty());
-                currFan.push_back(++ptsIdx);
+                ++ptsIdx;
+                if (!currFanIsTessellated) {
+                    SkASSERT(!currFan.empty());
+                    currFan.push_back(ptsIdx);
+                }
                 continue;
 
             case GrCCGeometry::Verb::kMonotonicQuadraticTo:
-                SkASSERT(!currFan.empty());
-                triangleInstanceData[currIndices->fQuadratics++].set(&pts[ptsIdx], atlasOffset);
-                currFan.push_back(ptsIdx += 2);
+                triPointInstanceData[currIndices->fQuadratics++].set(&pts[ptsIdx], atlasOffset);
+                ptsIdx += 2;
+                if (!currFanIsTessellated) {
+                    SkASSERT(!currFan.empty());
+                    currFan.push_back(ptsIdx);
+                }
                 continue;
 
             case GrCCGeometry::Verb::kMonotonicCubicTo:
-                SkASSERT(!currFan.empty());
-                cubicInstanceData[currIndices->fCubics++].set(&pts[ptsIdx], atlasOffsetX,
-                                                              atlasOffsetY);
-                currFan.push_back(ptsIdx += 3);
+                quadPointInstanceData[currIndices->fCubics++].set(&pts[ptsIdx], atlasOffsetX,
+                                                                  atlasOffsetY);
+                ptsIdx += 3;
+                if (!currFanIsTessellated) {
+                    SkASSERT(!currFan.empty());
+                    currFan.push_back(ptsIdx);
+                }
                 continue;
 
             case GrCCGeometry::Verb::kEndClosedContour:  // endPt == startPt.
-                SkASSERT(!currFan.empty());
-                currFan.pop_back();
+                if (!currFanIsTessellated) {
+                    SkASSERT(!currFan.empty());
+                    currFan.pop_back();
+                }
             // fallthru.
             case GrCCGeometry::Verb::kEndOpenContour:  // endPt != startPt.
-                if (currFan.count() >= 3) {
+                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(TriangleInstance* end =)
+                    SkDEBUGCODE(TriPointInstance* end =)
                             emit_recursive_fan(pts, currFan, 0, fanSize, atlasOffset,
-                                               triangleInstanceData + currIndices->fTriangles);
+                                               triPointInstanceData + currIndices->fTriangles);
                     currIndices->fTriangles += fanSize - 2;
-                    SkASSERT(triangleInstanceData + currIndices->fTriangles == end);
+                    SkASSERT(triPointInstanceData + currIndices->fTriangles == end);
                 }
                 currFan.reset();
                 continue;
@@ -347,14 +475,16 @@
 
     fInstanceBuffer->unmap();
 
-    SkASSERT(currPathInfo == fPathsInfo.end());
+    SkASSERT(nextPathInfo == fPathsInfo.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].fWoundTriangles == fBaseInstances[1].fWoundTriangles);
+    SkASSERT(instanceIndices[1].fWoundTriangles == fBaseInstances[0].fCubics);
     SkASSERT(instanceIndices[0].fCubics == fBaseInstances[1].fCubics);
-    SkASSERT(instanceIndices[1].fCubics == cubicEndIdx);
+    SkASSERT(instanceIndices[1].fCubics == quadEndIdx);
 
     fMeshesScratchBuffer.reserve(fMaxMeshesPerDraw);
     fDynamicStatesScratchBuffer.reserve(fMaxMeshesPerDraw);
@@ -365,36 +495,56 @@
 void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCountBatchID batchID,
                                        const SkIRect& drawBounds) const {
     using RenderPass = GrCCCoverageProcessor::RenderPass;
+    using WindMethod = GrCCCoverageProcessor::WindMethod;
 
     SkASSERT(fInstanceBuffer);
 
+    const PrimitiveTallies& batchTotalCounts = fCoverageCountBatches[batchID].fTotalPrimitiveCounts;
+
     GrPipeline pipeline(flushState->drawOpArgs().fProxy, GrPipeline::ScissorState::kEnabled,
                         SkBlendMode::kPlus);
 
-    // Triangles.
-    this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleHulls,
-                         &PrimitiveTallies::fTriangles, drawBounds);
-    this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleEdges,
-                         &PrimitiveTallies::fTriangles, drawBounds);  // Might get skipped.
-    this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners,
-                         &PrimitiveTallies::fTriangles, drawBounds);
+    if (batchTotalCounts.fTriangles) {
+        this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleHulls,
+                             WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, drawBounds);
+        this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleEdges,
+                             WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles,
+                             drawBounds); // Might get skipped.
+        this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners,
+                             WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, drawBounds);
+    }
 
-    // Quadratics.
-    this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadraticHulls,
-                         &PrimitiveTallies::fQuadratics, drawBounds);
-    this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadraticCorners,
-                         &PrimitiveTallies::fQuadratics, drawBounds);
+    if (batchTotalCounts.fWoundTriangles) {
+        this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleHulls,
+                             WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles,
+                             drawBounds);
+        this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleEdges,
+                             WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles,
+                             drawBounds); // Might get skipped.
+        this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners,
+                             WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles,
+                             drawBounds);
+    }
 
-    // Cubics.
-    this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicHulls,
-                         &PrimitiveTallies::fCubics, drawBounds);
-    this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicCorners,
-                         &PrimitiveTallies::fCubics, drawBounds);
+    if (batchTotalCounts.fQuadratics) {
+        this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadraticHulls,
+                             WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds);
+        this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadraticCorners,
+                             WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds);
+    }
+
+    if (batchTotalCounts.fCubics) {
+        this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicHulls,
+                             WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds);
+        this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicCorners,
+                             WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds);
+    }
 }
 
 void GrCCPathParser::drawRenderPass(GrOpFlushState* flushState, const GrPipeline& pipeline,
                                     CoverageCountBatchID batchID,
                                     GrCCCoverageProcessor::RenderPass renderPass,
+                                    GrCCCoverageProcessor::WindMethod windMethod,
                                     int PrimitiveTallies::*instanceType,
                                     const SkIRect& drawBounds) const {
     SkASSERT(pipeline.getScissorState().enabled());
@@ -407,12 +557,13 @@
     fMeshesScratchBuffer.pop_back_n(fMeshesScratchBuffer.count());
     fDynamicStatesScratchBuffer.pop_back_n(fDynamicStatesScratchBuffer.count());
 
-    GrCCCoverageProcessor proc(flushState->resourceProvider(), renderPass, flushState->caps());
+    GrCCCoverageProcessor proc(flushState->resourceProvider(), renderPass, windMethod);
 
     SkASSERT(batchID > 0);
     SkASSERT(batchID < fCoverageCountBatches.count());
     const CoverageCountBatch& previousBatch = fCoverageCountBatches[batchID - 1];
     const CoverageCountBatch& batch = fCoverageCountBatches[batchID];
+    SkDEBUGCODE(int totalInstanceCount = 0);
 
     if (int instanceCount = batch.fEndNonScissorIndices.*instanceType -
                             previousBatch.fEndNonScissorIndices.*instanceType) {
@@ -422,6 +573,7 @@
         proc.appendMesh(fInstanceBuffer.get(), instanceCount, baseInstance, &fMeshesScratchBuffer);
         fDynamicStatesScratchBuffer.push_back().fScissorRect.setXYWH(0, 0, drawBounds.width(),
                                                                      drawBounds.height());
+        SkDEBUGCODE(totalInstanceCount += instanceCount);
     }
 
     SkASSERT(previousBatch.fEndScissorSubBatchIdx > 0);
@@ -439,10 +591,12 @@
         proc.appendMesh(fInstanceBuffer.get(), instanceCount,
                         baseScissorInstance + startIndex, &fMeshesScratchBuffer);
         fDynamicStatesScratchBuffer.push_back().fScissorRect = scissorSubBatch.fScissor;
+        SkDEBUGCODE(totalInstanceCount += instanceCount);
     }
 
     SkASSERT(fMeshesScratchBuffer.count() == fDynamicStatesScratchBuffer.count());
     SkASSERT(fMeshesScratchBuffer.count() <= fMaxMeshesPerDraw);
+    SkASSERT(totalInstanceCount == batch.fTotalPrimitiveCounts.*instanceType);
 
     if (!fMeshesScratchBuffer.empty()) {
         SkASSERT(flushState->rtCommandBuffer());
diff --git a/src/gpu/ccpr/GrCCPathParser.h b/src/gpu/ccpr/GrCCPathParser.h
index aff6b10..f041610 100644
--- a/src/gpu/ccpr/GrCCPathParser.h
+++ b/src/gpu/ccpr/GrCCPathParser.h
@@ -10,6 +10,7 @@
 
 #include "GrMesh.h"
 #include "GrNonAtomicRef.h"
+#include "GrTessellator.h"
 #include "SkRect.h"
 #include "SkRefCnt.h"
 #include "ccpr/GrCCCoverageProcessor.h"
@@ -76,8 +77,13 @@
 
     // Every kBeginPath verb has a corresponding PathInfo entry.
     struct PathInfo {
+        PathInfo(ScissorMode scissorMode, int16_t offsetX, int16_t offsetY)
+                : fScissorMode(scissorMode), fAtlasOffsetX(offsetX), fAtlasOffsetY(offsetY) {}
+
         ScissorMode fScissorMode;
         int16_t fAtlasOffsetX, fAtlasOffsetY;
+        std::unique_ptr<GrTessellator::WindingVertex[]> fFanTessellation;
+        int fFanTessellationCount = 0;
     };
 
     // Defines a batch of CCPR primitives. Start indices are deduced by looking at the previous
@@ -85,6 +91,7 @@
     struct CoverageCountBatch {
         PrimitiveTallies fEndNonScissorIndices;
         int fEndScissorSubBatchIdx;
+        PrimitiveTallies fTotalPrimitiveCounts;
     };
 
     // Defines a sub-batch from CoverageCountBatch that will be drawn with the given scissor rect.
@@ -98,8 +105,8 @@
     void endContourIfNeeded(bool insideContour);
 
     void drawRenderPass(GrOpFlushState*, const GrPipeline&, CoverageCountBatchID,
-                        GrCCCoverageProcessor::RenderPass, int PrimitiveTallies::*instanceType,
-                        const SkIRect& drawBounds) const;
+                        GrCCCoverageProcessor::RenderPass, GrCCCoverageProcessor::WindMethod,
+                        int PrimitiveTallies::*instanceType, const SkIRect& drawBounds) const;
 
     // Staging area for the path being parsed.
     SkDEBUGCODE(int fParsingPath = false);
@@ -107,6 +114,7 @@
     int fCurrPathPointsIdx;
     int fCurrPathVerbsIdx;
     PrimitiveTallies fCurrPathPrimitiveCounts;
+    SkPath::FillType fCurrPathFillType;
 
     GrCCGeometry fGeometry;
     SkSTArray<32, PathInfo, true> fPathsInfo;