CCPR: Move flatness checks from geometry shaders to CPU

Vertex shaders can't discard geometry.

Bug: skia:
Change-Id: Iec22a103b7a4eda9a59fd99e48644183b0880f5f
Reviewed-on: https://skia-review.googlesource.com/82260
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/ccpr/GrCCPRCoverageProcessor.h b/src/gpu/ccpr/GrCCPRCoverageProcessor.h
index 3c4050a..75cc45b 100644
--- a/src/gpu/ccpr/GrCCPRCoverageProcessor.h
+++ b/src/gpu/ccpr/GrCCPRCoverageProcessor.h
@@ -117,17 +117,11 @@
         };
 
         virtual GeometryType getGeometryType() const = 0;
-        virtual int getNumInputPoints() const = 0;
 
         // Returns the number of independent geometric segments to generate for the render pass
         // (number of wedges for a hull, number of edges, or number of corners.)
         virtual int getNumSegments() const = 0;
 
-        // Determines the winding direction of the primitive. The subclass must write a value of
-        // either -1, 0, or +1 to "outputWind" (e.g. "sign(area)"). Fractional values are not valid.
-        virtual void emitWind(GrGLSLShaderBuilder*, const char* pts,
-                              const char* outputWind) const = 0;
-
         union GeometryVars {
             struct {
                 const char* fAlternatePoints; // floatNx2 (if left null, will use input points).
@@ -223,6 +217,10 @@
     // accidentally bleed into neighbor pixels.
     static constexpr float kAABloatRadius = 0.491111f;
 
+    int numInputPoints() const {
+        return RenderPassIsCubic(fRenderPass) ? 4 : 3;
+    }
+
     static GrGLSLPrimitiveProcessor* CreateGSImpl(std::unique_ptr<Shader>);
 
     const RenderPass fRenderPass;
diff --git a/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp b/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp
index 51791a1..5c9ba3b 100644
--- a/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp
+++ b/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp
@@ -49,11 +49,11 @@
         using InputType = GrGLSLGeometryBuilder::InputType;
         using OutputType = GrGLSLGeometryBuilder::OutputType;
 
-        int numPts = fShader->getNumInputPoints();
-        SkASSERT(3 == numPts || 4 == numPts);
+        int numInputPoints = proc.numInputPoints();
+        SkASSERT(3 == numInputPoints || 4 == numInputPoints);
 
-        g->codeAppendf("float%ix2 pts = float%ix2(", numPts, numPts);
-        for (int i = 0; i < numPts; ++i) {
+        g->codeAppendf("float%ix2 pts = float%ix2(", numInputPoints, numInputPoints);
+        for (int i = 0; i < numInputPoints; ++i) {
             g->codeAppend (i ? ", " : "");
             g->codeAppendf("sk_in[%i].sk_Position.xy", i);
         }
@@ -61,7 +61,11 @@
 
         GrShaderVar wind("wind", kHalf_GrSLType);
         g->declareGlobal(wind);
-        fShader->emitWind(g, "pts", wind.c_str());
+        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());
 
         SkString emitVertexFn;
         SkSTArray<2, GrShaderVar> emitArgs;
@@ -85,12 +89,8 @@
         Shader::GeometryVars vars;
         fShader->emitSetupCode(g, "pts", "sk_InvocationID", wind.c_str(), &vars);
         int maxPoints = this->onEmitGeometryShader(g, wind, emitVertexFn.c_str(), vars);
-
-        int numInputPoints = fShader->getNumInputPoints();
-        SkASSERT(3 == numInputPoints || 4 == numInputPoints);
         InputType inputType = (3 == numInputPoints) ? InputType::kTriangles
                                                     : InputType::kLinesAdjacency;
-
         g->configure(inputType, OutputType::kTriangleStrip, maxPoints, fShader->getNumSegments());
     }
 
@@ -117,7 +117,6 @@
 
         const char* hullPts = vars.fHullVars.fAlternatePoints;
         if (!hullPts) {
-            SkASSERT(fShader->getNumInputPoints() == numSides);
             hullPts = "pts";
         }
 
diff --git a/src/gpu/ccpr/GrCCPRCubicShader.cpp b/src/gpu/ccpr/GrCCPRCubicShader.cpp
index 59cd2a8..1c29862 100644
--- a/src/gpu/ccpr/GrCCPRCubicShader.cpp
+++ b/src/gpu/ccpr/GrCCPRCubicShader.cpp
@@ -9,25 +9,6 @@
 
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 
-void GrCCPRCubicShader::emitWind(GrGLSLShaderBuilder* s, const char* pts,
-                                 const char* outputWind) const {
-
-    s->codeAppendf("float area_times_2 = determinant(float3x3(1, %s[0], "
-                                                             "1, %s[2], "
-                                                             "0, %s[3] - %s[1]));",
-                                                             pts, pts, pts, pts);
-    // Drop curves that are nearly flat. The KLM  math becomes unstable in this case.
-    s->codeAppendf("if (2 * abs(area_times_2) < length(%s[3] - %s[0])) {", pts, pts);
-#ifndef SK_BUILD_FOR_MAC
-    s->codeAppend (    "return;");
-#else
-    // Returning from this geometry shader makes Mac very unhappy. Instead we make wind 0.
-    s->codeAppend (    "area_times_2 = 0;");
-#endif
-    s->codeAppend ("}");
-    s->codeAppendf("%s = sign(area_times_2);", outputWind);
-}
-
 void GrCCPRCubicShader::emitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
                                       const char* segmentId, const char* wind,
                                       GeometryVars* vars) const {
diff --git a/src/gpu/ccpr/GrCCPRCubicShader.h b/src/gpu/ccpr/GrCCPRCubicShader.h
index 8cfacd6..c0cb075 100644
--- a/src/gpu/ccpr/GrCCPRCubicShader.h
+++ b/src/gpu/ccpr/GrCCPRCubicShader.h
@@ -31,10 +31,6 @@
 protected:
     GrCCPRCubicShader(CubicType cubicType) : fCubicType(cubicType) {}
 
-    int getNumInputPoints() const final { return 4; }
-
-    void emitWind(GrGLSLShaderBuilder*, const char* pts, const char* outputWind) const final;
-
     void emitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
                        const char* wind, GeometryVars*) const final;
 
diff --git a/src/gpu/ccpr/GrCCPRGeometry.cpp b/src/gpu/ccpr/GrCCPRGeometry.cpp
index bbf5e4f..d6423ef 100644
--- a/src/gpu/ccpr/GrCCPRGeometry.cpp
+++ b/src/gpu/ccpr/GrCCPRGeometry.cpp
@@ -58,7 +58,7 @@
 }
 
 static inline bool are_collinear(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2) {
-    static constexpr float kFlatnessTolerance = 16; // 1/16 of a pixel.
+    static constexpr float kFlatnessTolerance = 4; // 1/4 of a pixel.
 
     // Area (times 2) of the triangle.
     Sk2f a = (p0 - p1) * SkNx_shuffle<1,0>(p1 - p2);
@@ -99,28 +99,17 @@
     Sk2f p2 = Sk2f::Load(&devP1);
     fCurrFanPoint = devP1;
 
-    // Don't send curves to the GPU if we know they are flat (or just very small).
-    if (are_collinear(p0, p1, p2)) {
-        p2.store(&fPoints.push_back());
-        fVerbs.push_back(Verb::kLineTo);
-        return;
-    }
-
     this->appendMonotonicQuadratics(p0, p1, p2);
 }
 
 inline void GrCCPRGeometry::appendMonotonicQuadratics(const Sk2f& p0, const Sk2f& p1,
-                                                      const Sk2f& p2, bool allowChop) {
-    SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1]));
+                                                      const Sk2f& p2) {
     Sk2f tan0 = p1 - p0;
     Sk2f tan1 = p2 - p1;
 
     // This should almost always be this case for well-behaved curves in the real world.
-    if (!allowChop || is_convex_curve_monotonic(p0, tan0, p2, tan1)) {
-        p1.store(&fPoints.push_back());
-        p2.store(&fPoints.push_back());
-        fVerbs.push_back(Verb::kMonotonicQuadraticTo);
-        ++fCurrContourTallies.fQuadratics;
+    if (is_convex_curve_monotonic(p0, tan0, p2, tan1)) {
+        this->appendSingleMonotonicQuadratic(p0, p1, p2);
         return;
     }
 
@@ -148,12 +137,25 @@
     Sk2f p12 = SkNx_fma(t, tan1, p1);
     Sk2f p012 = lerp(p01, p12, t);
 
-    p01.store(&fPoints.push_back());
-    p012.store(&fPoints.push_back());
-    p12.store(&fPoints.push_back());
+    this->appendSingleMonotonicQuadratic(p0, p01, p012);
+    this->appendSingleMonotonicQuadratic(p012, p12, p2);
+}
+
+inline void GrCCPRGeometry::appendSingleMonotonicQuadratic(const Sk2f& p0, const Sk2f& p1,
+                                                           const Sk2f& p2) {
+    SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1]));
+
+    // Don't send curves to the GPU if we know they are nearly flat (or just very small).
+    if (are_collinear(p0, p1, p2)) {
+        p2.store(&fPoints.push_back());
+        fVerbs.push_back(Verb::kLineTo);
+        return;
+    }
+
+    p1.store(&fPoints.push_back());
     p2.store(&fPoints.push_back());
-    fVerbs.push_back_n(2, Verb::kMonotonicQuadraticTo);
-    fCurrContourTallies.fQuadratics += 2;
+    fVerbs.push_back(Verb::kMonotonicQuadraticTo);
+    ++fCurrContourTallies.fQuadratics;
 }
 
 using ExcludedTerm = GrPathUtils::ExcludedTerm;
@@ -295,7 +297,7 @@
     Sk2f p3 = Sk2f::Load(&devP3);
     fCurrFanPoint = devP3;
 
-    // Don't crunch on the curve and inflate geometry if it is already flat (or just very small).
+    // Don't crunch on the curve and inflate geometry if it is nearly flat (or just very small).
     if (are_collinear(p0, p1, p2) &&
         are_collinear(p1, p2, p3) &&
         are_collinear(p0, (p1 + p2) * .5f, p3)) {
@@ -524,6 +526,15 @@
     }
 
     SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1]));
+
+    // Don't send curves to the GPU if we know they are nearly flat (or just very small).
+    // Since the cubic segment is known to be convex at this point, our flatness check is simple.
+    if (are_collinear(p0, (p1 + p2) * .5f, p3)) {
+        p3.store(&fPoints.push_back());
+        fVerbs.push_back(Verb::kLineTo);
+        return;
+    }
+
     p1.store(&fPoints.push_back());
     p2.store(&fPoints.push_back());
     p3.store(&fPoints.push_back());
@@ -561,7 +572,11 @@
         return;
     }
 
-    this->appendMonotonicQuadratics(p0, c, p3, SkToBool(maxSubdivisions));
+    if (maxSubdivisions) {
+        this->appendMonotonicQuadratics(p0, c, p3);
+    } else {
+        this->appendSingleMonotonicQuadratic(p0, c, p3);
+    }
 }
 
 GrCCPRGeometry::PrimitiveTallies GrCCPRGeometry::endContour() {
diff --git a/src/gpu/ccpr/GrCCPRGeometry.h b/src/gpu/ccpr/GrCCPRGeometry.h
index 1cb4719..407e655 100644
--- a/src/gpu/ccpr/GrCCPRGeometry.h
+++ b/src/gpu/ccpr/GrCCPRGeometry.h
@@ -93,8 +93,8 @@
     PrimitiveTallies endContour(); // Returns the numbers of primitives needed to draw the contour.
 
 private:
-    inline void appendMonotonicQuadratics(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
-                                          bool allowChop = true);
+    inline void appendMonotonicQuadratics(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2);
+    inline void appendSingleMonotonicQuadratic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2);
 
     using AppendCubicFn = void(GrCCPRGeometry::*)(const Sk2f& p0, const Sk2f& p1,
                                                   const Sk2f& p2, const Sk2f& p3,
diff --git a/src/gpu/ccpr/GrCCPRQuadraticShader.cpp b/src/gpu/ccpr/GrCCPRQuadraticShader.cpp
index 6dd062b..01527d0 100644
--- a/src/gpu/ccpr/GrCCPRQuadraticShader.cpp
+++ b/src/gpu/ccpr/GrCCPRQuadraticShader.cpp
@@ -9,22 +9,6 @@
 
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 
-void GrCCPRQuadraticShader::emitWind(GrGLSLShaderBuilder* s, const char* pts,
-                                     const char* outputWind) const {
-    s->codeAppendf("float area_times_2 = determinant(float2x2(%s[1] - %s[0], %s[2] - %s[0]));",
-                                                     pts, pts, pts, pts);
-    // Drop curves that are nearly flat, in favor of the higher quality triangle antialiasing.
-    s->codeAppendf("if (2 * abs(area_times_2) < length(%s[2] - %s[0])) {", pts, pts);
-#ifndef SK_BUILD_FOR_MAC
-    s->codeAppend (    "return;");
-#else
-    // Returning from this geometry shader makes Mac very unhappy. Instead we make wind 0.
-    s->codeAppend (    "area_times_2 = 0;");
-#endif
-    s->codeAppend ("}");
-    s->codeAppendf("%s = sign(area_times_2);", outputWind);
-}
-
 void GrCCPRQuadraticShader::emitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
                                           const char* segmentId, const char* wind,
                                           GeometryVars* vars) const {
diff --git a/src/gpu/ccpr/GrCCPRQuadraticShader.h b/src/gpu/ccpr/GrCCPRQuadraticShader.h
index 4877ede..d04e82d 100644
--- a/src/gpu/ccpr/GrCCPRQuadraticShader.h
+++ b/src/gpu/ccpr/GrCCPRQuadraticShader.h
@@ -22,10 +22,6 @@
  */
 class GrCCPRQuadraticShader : public GrCCPRCoverageProcessor::Shader {
 protected:
-    int getNumInputPoints() const final { return 3; }
-
-    void emitWind(GrGLSLShaderBuilder*, const char* pts, const char* outputWind) const final;
-
     void emitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
                        const char* wind, GeometryVars*) const final;
 
diff --git a/src/gpu/ccpr/GrCCPRTriangleShader.cpp b/src/gpu/ccpr/GrCCPRTriangleShader.cpp
index c2c624b..836be31 100644
--- a/src/gpu/ccpr/GrCCPRTriangleShader.cpp
+++ b/src/gpu/ccpr/GrCCPRTriangleShader.cpp
@@ -10,12 +10,6 @@
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLVertexGeoBuilder.h"
 
-void GrCCPRTriangleShader::emitWind(GrGLSLShaderBuilder* s, const char* pts,
-                                    const char* outputWind) const {
-    s->codeAppendf("%s = sign(determinant(float2x2(%s[1] - %s[0], %s[2] - %s[0])));",
-                   outputWind, pts, pts, pts, pts);
-}
-
 GrCCPRTriangleHullShader::WindHandling
 GrCCPRTriangleHullShader::onEmitVaryings(GrGLSLVaryingHandler*, SkString* code,
                                          const char* /*position*/, const char* /*coverage*/,
diff --git a/src/gpu/ccpr/GrCCPRTriangleShader.h b/src/gpu/ccpr/GrCCPRTriangleShader.h
index a0bbb66..0d7fc8a 100644
--- a/src/gpu/ccpr/GrCCPRTriangleShader.h
+++ b/src/gpu/ccpr/GrCCPRTriangleShader.h
@@ -11,23 +11,13 @@
 #include "ccpr/GrCCPRCoverageProcessor.h"
 
 /**
- * This class renders the coverage of triangles. Triangles are rendered in three passes, as
- * described below.
- */
-class GrCCPRTriangleShader : public GrCCPRCoverageProcessor::Shader {
-public:
-    int getNumInputPoints() const final { return 3; }
-    int getNumSegments() const final { return 3; } // 3 wedges, 3 edges, 3 corners.
-    void emitWind(GrGLSLShaderBuilder* s, const char* pts, const char* outputWind) const final;
-};
-
-/**
  * Pass 1: Draw the triangle's conservative raster hull with a coverage of 1. (Conservative raster
  *         is drawn by considering 3 pixel size boxes, one centered at each vertex, and drawing the
  *         convex hull of those boxes.)
  */
-class GrCCPRTriangleHullShader : public GrCCPRTriangleShader {
+class GrCCPRTriangleHullShader : public GrCCPRCoverageProcessor::Shader {
     GeometryType getGeometryType() const override { return GeometryType::kHull; }
+    int getNumSegments() const final { return 3; }
 
     WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
                                 const char* coverage, const char* wind) override;
@@ -39,8 +29,9 @@
  *         each edge (i.e. convex hull of two pixel-size boxes at the endpoints), interpolating from
  *         coverage=-1 on the outside edge to coverage=0 on the inside edge.
  */
-class GrCCPRTriangleEdgeShader : public GrCCPRTriangleShader {
+class GrCCPRTriangleEdgeShader : public GrCCPRCoverageProcessor::Shader {
     GeometryType getGeometryType() const override { return GeometryType::kEdges; }
+    int getNumSegments() const final { return 3; }
 
     WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
                                 const char* coverage, const char* wind) override;
@@ -54,8 +45,9 @@
  *         done previously so that it takes into account the region that is outside both edges at
  *         the same time.
  */
-class GrCCPRTriangleCornerShader : public GrCCPRTriangleShader {
+class GrCCPRTriangleCornerShader : public GrCCPRCoverageProcessor::Shader {
     GeometryType getGeometryType() const override { return GeometryType::kCorners; }
+    int getNumSegments() const final { return 3; }
 
     void emitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* cornerId,
                        const char* wind, GeometryVars*) const override;