CCPR: Combine loops and serpentines back into a single shader

Bug: skia:
Change-Id: I945471ccd2580a2d39afd9c80acb8d9e5e196435
Reviewed-on: https://skia-review.googlesource.com/82460
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/core/SkGeometry.h b/src/core/SkGeometry.h
index 176ba4e..96b617f 100644
--- a/src/core/SkGeometry.h
+++ b/src/core/SkGeometry.h
@@ -174,13 +174,25 @@
         case SkCubicType::kLocalCusp:
         case SkCubicType::kCuspAtInfinity:
             return false;
-        default:
-            SK_ABORT("Invalid SkCubicType");
-            // fallthru
         case SkCubicType::kQuadratic:
         case SkCubicType::kLineOrPoint:
             return true;
     }
+    SK_ABORT("Invalid SkCubicType");
+    return true;
+}
+
+static inline const char* SkCubicTypeName(SkCubicType type) {
+    switch (type) {
+        case SkCubicType::kSerpentine: return "kSerpentine";
+        case SkCubicType::kLoop: return "kLoop";
+        case SkCubicType::kLocalCusp: return "kLocalCusp";
+        case SkCubicType::kCuspAtInfinity: return "kCuspAtInfinity";
+        case SkCubicType::kQuadratic: return "kQuadratic";
+        case SkCubicType::kLineOrPoint: return "kLineOrPoint";
+    }
+    SK_ABORT("Invalid SkCubicType");
+    return "";
 }
 
 /** Returns the cubic classification.
diff --git a/src/gpu/ccpr/GrCCPRCoverageOp.cpp b/src/gpu/ccpr/GrCCPRCoverageOp.cpp
index 8786ed4..5775c5a 100644
--- a/src/gpu/ccpr/GrCCPRCoverageOp.cpp
+++ b/src/gpu/ccpr/GrCCPRCoverageOp.cpp
@@ -253,11 +253,9 @@
     // that will not overwrite previous TriangleInstance data.
     int cubicBaseIdx = GR_CT_DIV_ROUND_UP(triEndIdx * sizeof(TriangleInstance),
                                           sizeof(CubicInstance));
-    baseInstances[0].fSerpentines = cubicBaseIdx;
-    baseInstances[1].fSerpentines = baseInstances[0].fSerpentines + fTallies[0].fSerpentines;
-    baseInstances[0].fLoops = baseInstances[1].fSerpentines + fTallies[1].fSerpentines;
-    baseInstances[1].fLoops = baseInstances[0].fLoops + fTallies[0].fLoops;
-    int cubicEndIdx = baseInstances[1].fLoops + fTallies[1].fLoops;
+    baseInstances[0].fCubics = cubicBaseIdx;
+    baseInstances[1].fCubics = baseInstances[0].fCubics + fTallies[0].fCubics;
+    int cubicEndIdx = baseInstances[1].fCubics + fTallies[1].fCubics;
 
     sk_sp<GrBuffer> instanceBuffer = onFlushRP->makeBuffer(kVertex_GrBufferType,
                                                            cubicEndIdx * sizeof(CubicInstance));
@@ -325,17 +323,10 @@
                 currFan.push_back(ptsIdx += 2);
                 continue;
 
-            case GrCCPRGeometry::Verb::kMonotonicSerpentineTo:
+            case GrCCPRGeometry::Verb::kMonotonicCubicTo:
                 SkASSERT(!currFan.empty());
-                cubicInstanceData[currIndices->fSerpentines++].set(&pts[ptsIdx],
-                                                                   atlasOffsetX, atlasOffsetY);
-                currFan.push_back(ptsIdx += 3);
-                continue;
-
-            case GrCCPRGeometry::Verb::kMonotonicLoopTo:
-                SkASSERT(!currFan.empty());
-                cubicInstanceData[currIndices->fLoops++].set(&pts[ptsIdx],
-                                                             atlasOffsetX, atlasOffsetY);
+                cubicInstanceData[currIndices->fCubics++].set(&pts[ptsIdx],
+                                                              atlasOffsetX, atlasOffsetY);
                 currFan.push_back(ptsIdx += 3);
                 continue;
 
@@ -375,10 +366,8 @@
     SkASSERT(instanceIndices[1].fTriangles == initialBaseInstances[0].fQuadratics);
     SkASSERT(instanceIndices[0].fQuadratics == initialBaseInstances[1].fQuadratics);
     SkASSERT(instanceIndices[1].fQuadratics == triEndIdx);
-    SkASSERT(instanceIndices[0].fSerpentines == initialBaseInstances[1].fSerpentines);
-    SkASSERT(instanceIndices[1].fSerpentines == initialBaseInstances[0].fLoops);
-    SkASSERT(instanceIndices[0].fLoops == initialBaseInstances[1].fLoops);
-    SkASSERT(instanceIndices[1].fLoops == cubicEndIdx);
+    SkASSERT(instanceIndices[0].fCubics == initialBaseInstances[1].fCubics);
+    SkASSERT(instanceIndices[1].fCubics == cubicEndIdx);
     return true;
 }
 
@@ -421,14 +410,10 @@
 
     // Cubics.
     auto constexpr kCubicsGrPrimitiveType = GrCCPRCoverageProcessor::kCubicsGrPrimitiveType;
-    this->drawMaskPrimitives(flushState, pipeline, RenderPass::kSerpentineHulls,
-                             kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fSerpentines);
-    this->drawMaskPrimitives(flushState, pipeline, RenderPass::kLoopHulls,
-                             kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fLoops);
-    this->drawMaskPrimitives(flushState, pipeline, RenderPass::kSerpentineCorners,
-                             kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fSerpentines);
-    this->drawMaskPrimitives(flushState, pipeline, RenderPass::kLoopCorners,
-                             kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fLoops);
+    this->drawMaskPrimitives(flushState, pipeline, RenderPass::kCubicHulls,
+                             kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fCubics);
+    this->drawMaskPrimitives(flushState, pipeline, RenderPass::kCubicCorners,
+                             kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fCubics);
 }
 
 void GrCCPRCoverageOp::drawMaskPrimitives(GrOpFlushState* flushState, const GrPipeline& pipeline,
diff --git a/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp b/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp
index d77324d..61e431a 100644
--- a/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp
+++ b/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp
@@ -111,7 +111,6 @@
 GrGLSLPrimitiveProcessor* GrCCPRCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const {
     std::unique_ptr<Shader> shader;
     switch (fRenderPass) {
-        using CubicType = GrCCPRCubicShader::CubicType;
         case RenderPass::kTriangleHulls:
             shader = skstd::make_unique<GrCCPRTriangleHullShader>();
             break;
@@ -127,43 +126,12 @@
         case RenderPass::kQuadraticCorners:
             shader = skstd::make_unique<GrCCPRQuadraticCornerShader>();
             break;
-        case RenderPass::kSerpentineHulls:
-            shader = skstd::make_unique<GrCCPRCubicHullShader>(CubicType::kSerpentine);
+        case RenderPass::kCubicHulls:
+            shader = skstd::make_unique<GrCCPRCubicHullShader>();
             break;
-        case RenderPass::kLoopHulls:
-            shader = skstd::make_unique<GrCCPRCubicHullShader>(CubicType::kLoop);
-            break;
-        case RenderPass::kSerpentineCorners:
-            shader = skstd::make_unique<GrCCPRCubicCornerShader>(CubicType::kSerpentine);
-            break;
-        case RenderPass::kLoopCorners:
-            shader = skstd::make_unique<GrCCPRCubicCornerShader>(CubicType::kLoop);
+        case RenderPass::kCubicCorners:
+            shader = skstd::make_unique<GrCCPRCubicCornerShader>();
             break;
     }
     return CreateGSImpl(std::move(shader));
 }
-
-const char* GrCCPRCoverageProcessor::GetRenderPassName(RenderPass renderPass) {
-    switch (renderPass) {
-        case RenderPass::kTriangleHulls:
-            return "RenderPass::kTriangleHulls";
-        case RenderPass::kTriangleEdges:
-            return "RenderPass::kTriangleEdges";
-        case RenderPass::kTriangleCorners:
-            return "RenderPass::kTriangleCorners";
-        case RenderPass::kQuadraticHulls:
-            return "RenderPass::kQuadraticHulls";
-        case RenderPass::kQuadraticCorners:
-            return "RenderPass::kQuadraticCorners";
-        case RenderPass::kSerpentineHulls:
-            return "RenderPass::kSerpentineHulls";
-        case RenderPass::kLoopHulls:
-            return "RenderPass::kLoopHulls";
-        case RenderPass::kSerpentineCorners:
-            return "RenderPass::kSerpentineCorners";
-        case RenderPass::kLoopCorners:
-            return "RenderPass::kLoopCorners";
-    }
-    SK_ABORT("Unexpected GrCCPRCoverageProcessor::RenderPass.");
-    return nullptr;
-}
diff --git a/src/gpu/ccpr/GrCCPRCoverageProcessor.h b/src/gpu/ccpr/GrCCPRCoverageProcessor.h
index 75cc45b..30aa2ac 100644
--- a/src/gpu/ccpr/GrCCPRCoverageProcessor.h
+++ b/src/gpu/ccpr/GrCCPRCoverageProcessor.h
@@ -72,17 +72,39 @@
         kQuadraticCorners,
 
         // Cubics.
-        kSerpentineHulls,
-        kLoopHulls,
-        kSerpentineCorners,
-        kLoopCorners
+        kCubicHulls,
+        kCubicCorners
     };
 
-    static constexpr bool RenderPassIsCubic(RenderPass pass) {
-        return pass >= RenderPass::kSerpentineHulls && pass <= RenderPass::kLoopCorners;
+    static inline bool RenderPassIsCubic(RenderPass pass) {
+        switch (pass) {
+            case RenderPass::kTriangleHulls:
+            case RenderPass::kTriangleEdges:
+            case RenderPass::kTriangleCorners:
+            case RenderPass::kQuadraticHulls:
+            case RenderPass::kQuadraticCorners:
+                return false;
+            case RenderPass::kCubicHulls:
+            case RenderPass::kCubicCorners:
+                return true;
+        }
+        SK_ABORT("Invalid GrCCPRCoverageProcessor::RenderPass");
+        return false;
     }
 
-    static const char* GetRenderPassName(RenderPass);
+    static inline const char* RenderPassName(RenderPass pass) {
+        switch (pass) {
+            case RenderPass::kTriangleHulls: return "kTriangleHulls";
+            case RenderPass::kTriangleEdges: return "kTriangleEdges";
+            case RenderPass::kTriangleCorners: return "kTriangleCorners";
+            case RenderPass::kQuadraticHulls: return "kQuadraticHulls";
+            case RenderPass::kQuadraticCorners: return "kQuadraticCorners";
+            case RenderPass::kCubicHulls: return "kCubicHulls";
+            case RenderPass::kCubicCorners: return "kCubicCorners";
+        }
+        SK_ABORT("Invalid GrCCPRCoverageProcessor::RenderPass");
+        return "";
+    }
 
     /**
      * This serves as the base class for each RenderPass's Shader. It indicates what type of
@@ -191,7 +213,7 @@
 
     GrCCPRCoverageProcessor(RenderPass);
 
-    const char* name() const override { return GetRenderPassName(fRenderPass); }
+    const char* name() const override { return RenderPassName(fRenderPass); }
     SkString dumpInfo() const override {
         return SkStringPrintf("%s\n%s", this->name(), this->INHERITED::dumpInfo().c_str());
     }
diff --git a/src/gpu/ccpr/GrCCPRCubicShader.cpp b/src/gpu/ccpr/GrCCPRCubicShader.cpp
index 1c29862..4d63e98 100644
--- a/src/gpu/ccpr/GrCCPRCubicShader.cpp
+++ b/src/gpu/ccpr/GrCCPRCubicShader.cpp
@@ -28,28 +28,25 @@
 
     // Calculate the KLM matrix.
     s->declareGlobal(fKLMMatrix);
-    s->codeAppend ("float4 K, L, M;");
-    s->codeAppend ("float2 l, m;");
     s->codeAppend ("float discr = 3*D2*D2 - 4*D1*D3;");
-    if (CubicType::kSerpentine == fCubicType) {
-        // This math also works out for the "cusp" and "cusp at infinity" cases.
-        s->codeAppend ("float q = sqrt(max(3*discr, 0));");
-        s->codeAppend ("q = 3*D2 + (D2 >= 0 ? q : -q);");
-        s->codeAppend ("l.ts = normalize(float2(q, 6*D1));");
-        s->codeAppend ("m.ts = discr <= 0 ? l.ts : normalize(float2(2*D3, q));");
-        s->codeAppend ("K = float4(0, l.s * m.s, -l.t * m.s - m.t * l.s, l.t * m.t);");
-        s->codeAppend ("L = float4(-1,3,-3,1) * l.ssst * l.sstt * l.sttt;");
-        s->codeAppend ("M = float4(-1,3,-3,1) * m.ssst * m.sstt * m.sttt;");
-    } else {
-        s->codeAppend ("float q = sqrt(max(-discr, 0));");
-        s->codeAppend ("q = D2 + (D2 >= 0 ? q : -q);");
-        s->codeAppend ("l.ts = normalize(float2(q, 2*D1));");
-        s->codeAppend ("m.ts = discr >= 0 ? l.ts : normalize(float2(2 * (D2*D2 - D3*D1), D1*q));");
-        s->codeAppend ("float4 lxm = float4(l.s * m.s, l.s * m.t, l.t * m.s, l.t * m.t);");
-        s->codeAppend ("K = float4(0, lxm.x, -lxm.y - lxm.z, lxm.w);");
-        s->codeAppend ("L = float4(-1,1,-1,1) * l.sstt * (lxm.xyzw + float4(0, 2*lxm.zy, 0));");
-        s->codeAppend ("M = float4(-1,1,-1,1) * m.sstt * (lxm.xzyw + float4(0, 2*lxm.yz, 0));");
-    }
+    s->codeAppend ("float x = discr >= 0 ? 3 : 1;");
+    s->codeAppend ("float q = sqrt(x * abs(discr));");
+    s->codeAppend ("q = x*D2 + (D2 >= 0 ? q : -q);");
+
+    s->codeAppend ("float2 l, m;");
+    s->codeAppend ("l.ts = normalize(float2(q, 2*x * D1));");
+    s->codeAppend ("m.ts = normalize(float2(2, q) * (discr >= 0 ? float2(D3, 1) "
+                                                               ": float2(D2*D2 - D3*D1, D1)));");
+
+    s->codeAppend ("float4 K;");
+    s->codeAppend ("float4 lm = l.sstt * m.stst;");
+    s->codeAppend ("K = float4(0, lm.x, -lm.y - lm.z, lm.w);");
+
+    s->codeAppend ("float4 L, M;");
+    s->codeAppend ("lm.yz += 2*lm.zy;");
+    s->codeAppend ("L = float4(-1,x,-x,1) * l.sstt * (discr >= 0 ? l.ssst * l.sttt : lm);");
+    s->codeAppend ("M = float4(-1,x,-x,1) * m.sstt * (discr >= 0 ? m.ssst * m.sttt : lm.xzyw);");
+
     s->codeAppend ("short middlerow = abs(D2) > abs(D1) ? 2 : 1;");
     s->codeAppend ("float3x3 CI = inverse(float3x3(C[0][0], C[0][middlerow], C[0][3], "
                                                   "C[1][0], C[1][middlerow], C[1][3], "
diff --git a/src/gpu/ccpr/GrCCPRCubicShader.h b/src/gpu/ccpr/GrCCPRCubicShader.h
index c0cb075..7b91a49 100644
--- a/src/gpu/ccpr/GrCCPRCubicShader.h
+++ b/src/gpu/ccpr/GrCCPRCubicShader.h
@@ -22,15 +22,7 @@
  * (Use GrCCPRGeometry.)
  */
 class GrCCPRCubicShader : public GrCCPRCoverageProcessor::Shader {
-public:
-    enum class CubicType {
-        kSerpentine,
-        kLoop
-    };
-
 protected:
-    GrCCPRCubicShader(CubicType cubicType) : fCubicType(cubicType) {}
-
     void emitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
                        const char* wind, GeometryVars*) const final;
 
@@ -42,17 +34,12 @@
 
     virtual void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) = 0;
 
-    const CubicType   fCubicType;
     GrShaderVar       fKLMMatrix{"klm_matrix", kFloat3x3_GrSLType};
     GrShaderVar       fEdgeDistanceEquation{"edge_distance_equation", kFloat3_GrSLType};
     GrGLSLGeoToFrag   fKLMD{kFloat4_GrSLType};
 };
 
 class GrCCPRCubicHullShader : public GrCCPRCubicShader {
-public:
-    GrCCPRCubicHullShader(CubicType cubicType) : GrCCPRCubicShader(cubicType) {}
-
-private:
     GeometryType getGeometryType() const override { return GeometryType::kHull; }
     int getNumSegments() const override { return 4; } // 4 wedges.
     void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* wedgeId,
@@ -64,10 +51,6 @@
 };
 
 class GrCCPRCubicCornerShader : public GrCCPRCubicShader {
-public:
-    GrCCPRCubicCornerShader(CubicType cubicType) : GrCCPRCubicShader(cubicType) {}
-
-private:
     GeometryType getGeometryType() const override { return GeometryType::kCorners; }
     int getNumSegments() const override { return 2; } // 2 corners.
     void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* cornerId,
diff --git a/src/gpu/ccpr/GrCCPRGeometry.cpp b/src/gpu/ccpr/GrCCPRGeometry.cpp
index d6423ef..f73cd55 100644
--- a/src/gpu/ccpr/GrCCPRGeometry.cpp
+++ b/src/gpu/ccpr/GrCCPRGeometry.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, 0};
+    fCurrContourTallies = {fVerbs.count(), 0, 0};
 
     fPoints.push_back(devPt);
     fVerbs.push_back(Verb::kBeginContour);
@@ -538,13 +538,8 @@
     p1.store(&fPoints.push_back());
     p2.store(&fPoints.push_back());
     p3.store(&fPoints.push_back());
-    if (SkCubicType::kLoop != fCurrCubicType) {
-        fVerbs.push_back(Verb::kMonotonicSerpentineTo);
-        ++fCurrContourTallies.fSerpentines;
-    } else {
-        fVerbs.push_back(Verb::kMonotonicLoopTo);
-        ++fCurrContourTallies.fLoops;
-    }
+    fVerbs.push_back(Verb::kMonotonicCubicTo);
+    ++fCurrContourTallies.fCubics;
 }
 
 void GrCCPRGeometry::appendCubicApproximation(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
diff --git a/src/gpu/ccpr/GrCCPRGeometry.h b/src/gpu/ccpr/GrCCPRGeometry.h
index 407e655..9a3b255 100644
--- a/src/gpu/ccpr/GrCCPRGeometry.h
+++ b/src/gpu/ccpr/GrCCPRGeometry.h
@@ -30,8 +30,7 @@
         kBeginContour,
         kLineTo,
         kMonotonicQuadraticTo, // Monotonic relative to the vector between its endpoints [P2 - P0].
-        kMonotonicSerpentineTo,
-        kMonotonicLoopTo,
+        kMonotonicCubicTo,
         kEndClosedContour, // endPt == startPt.
         kEndOpenContour // endPt != startPt.
     };
@@ -40,8 +39,7 @@
     struct PrimitiveTallies {
         int fTriangles; // Number of triangles in the contour's fan.
         int fQuadratics;
-        int fSerpentines;
-        int fLoops;
+        int fCubics;
 
         void operator+=(const PrimitiveTallies&);
         PrimitiveTallies operator-(const PrimitiveTallies&) const;
@@ -130,16 +128,14 @@
 inline void GrCCPRGeometry::PrimitiveTallies::operator+=(const PrimitiveTallies& b) {
     fTriangles += b.fTriangles;
     fQuadratics += b.fQuadratics;
-    fSerpentines += b.fSerpentines;
-    fLoops += b.fLoops;
+    fCubics += b.fCubics;
 }
 
 GrCCPRGeometry::PrimitiveTallies
 inline GrCCPRGeometry::PrimitiveTallies::operator-(const PrimitiveTallies& b) const {
     return {fTriangles - b.fTriangles,
             fQuadratics - b.fQuadratics,
-            fSerpentines - b.fSerpentines,
-            fLoops - b.fLoops};
+            fCubics - b.fCubics};
 }
 
 #endif