Refactor middle-out tessellation shader for fixed count

Refactors the middle-out shader in a way that will allow the fixed
count tessellator to easily run Wang's formula and "throw away"
unnecessary triangles by making them empty.

Bug: skia:10419
Change-Id: I86b76cb24ef43ccdbc890f7dfffcda91f8b01a9c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/416542
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/tessellate/shaders/GrPathTessellationShader_MiddleOut.cpp b/src/gpu/tessellate/shaders/GrPathTessellationShader_MiddleOut.cpp
index 4bd2ad5..ee1d162 100644
--- a/src/gpu/tessellate/shaders/GrPathTessellationShader_MiddleOut.cpp
+++ b/src/gpu/tessellate/shaders/GrPathTessellationShader_MiddleOut.cpp
@@ -36,26 +36,24 @@
 
 private:
     const char* name() const final { return "tessellate_MiddleOutShader"; }
-    void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {}
+    void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
     GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
 };
 
 GrGLSLGeometryProcessor* MiddleOutShader::createGLSLInstance(const GrShaderCaps&) const {
     class Impl : public GrPathTessellationShader::Impl {
         void emitVertexCode(GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
-            v->insertFunction(kUnpackRationalCubicFn);
-            v->insertFunction(kEvalRationalCubicFn);
             if (v->getProgramBuilder()->shaderCaps()->bitManipulationSupport()) {
                 // Determines the T value at which to place the given vertex in a "middle-out"
                 // topology.
                 v->insertFunction(R"(
                 float find_middle_out_T() {
                     int totalTriangleIdx = sk_VertexID/3 + 1;
-                    int depth = findMSB(totalTriangleIdx);
-                    int firstTriangleAtDepth = (1 << depth);
+                    int resolveLevel = findMSB(totalTriangleIdx) + 1;
+                    int firstTriangleAtDepth = (1 << (resolveLevel - 1));
                     int triangleIdxWithinDepth = totalTriangleIdx - firstTriangleAtDepth;
                     int vertexIdxWithinDepth = triangleIdxWithinDepth * 2 + sk_VertexID % 3;
-                    return ldexp(float(vertexIdxWithinDepth), -1 - depth);
+                    return ldexp(float(vertexIdxWithinDepth), -resolveLevel);
                 })");
             } else {
                 // Determines the T value at which to place the given vertex in a "middle-out"
@@ -63,11 +61,11 @@
                 v->insertFunction(R"(
                 float find_middle_out_T() {
                     float totalTriangleIdx = float(sk_VertexID/3) + 1;
-                    float depth = floor(log2(totalTriangleIdx));
-                    float firstTriangleAtDepth = exp2(depth);
+                    float resolveLevel = floor(log2(totalTriangleIdx)) + 1;
+                    float firstTriangleAtDepth = exp2(resolveLevel - 1);
                     float triangleIdxWithinDepth = totalTriangleIdx - firstTriangleAtDepth;
                     float vertexIdxWithinDepth = triangleIdxWithinDepth*2 + float(sk_VertexID % 3);
-                    return vertexIdxWithinDepth * exp2(-1 - depth);
+                    return vertexIdxWithinDepth * exp2(-resolveLevel);
                 })");
             }
             v->codeAppend(R"(
@@ -78,10 +76,33 @@
                            : (sk_VertexID == 1) ? inputPoints_0_1.zw
                                                 : inputPoints_2_3.xy;
             } else {
-                float4x3 P = unpack_rational_cubic(inputPoints_0_1.xy, inputPoints_0_1.zw,
-                                                   inputPoints_2_3.xy, inputPoints_2_3.zw);
+                float w = -1;  // w < 0 tells us to treat the instance as an integral cubic.
+                float4x2 P = float4x2(inputPoints_0_1, inputPoints_2_3);
+                if (isinf(P[3].y)) {
+                    // The patch is a conic.
+                    w = P[3].x;
+                    P[3] = P[2];  // Duplicate the endpoint.
+                    P[1] *= w;  // Unproject p1.
+                }
                 float T = find_middle_out_T();
-                localcoord = eval_rational_cubic(P, T);
+                if (0 < T && T < 1) {
+                    // Evaluate at T. Use De Casteljau's for its accuracy and stability.
+                    float2 ab = mix(P[0], P[1], T);
+                    float2 bc = mix(P[1], P[2], T);
+                    float2 cd = mix(P[2], P[3], T);
+                    float2 abc = mix(ab, bc, T);
+                    float2 bcd = mix(bc, cd, T);
+                    float2 abcd = mix(abc, bcd, T);
+
+                    // Evaluate the conic weight at T.
+                    float u = mix(1.0, w, T);
+                    float v = w + 1 - u;  // == mix(w, 1, T)
+                    float uv = mix(u, v, T);
+
+                    localcoord = (w < 0) ? /*cubic*/ abcd : /*conic*/ abc/uv;
+                } else {
+                    localcoord = (T == 0) ? P[0].xy : P[3].xy;
+                }
             }
             float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
             gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");