ccpr: Don't use flat interpolation when it is slow

Bug: skia:
Change-Id: I1bc087187541183fdbaa5f2b93e8b8d287ac8ef8
Reviewed-on: https://skia-review.googlesource.com/102100
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/ccpr/GrCCCubicShader.cpp b/src/gpu/ccpr/GrCCCubicShader.cpp
index fbaf7d9..3bf8479 100644
--- a/src/gpu/ccpr/GrCCCubicShader.cpp
+++ b/src/gpu/ccpr/GrCCCubicShader.cpp
@@ -133,14 +133,16 @@
 
 void GrCCCubicCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
                                            GrGLSLVarying::Scope scope, SkString* code) {
+    using Interpolation = GrGLSLVaryingHandler::Interpolation;
+
     fdKLMDdx.reset(kFloat4_GrSLType, scope);
-    varyingHandler->addFlatVarying("dklmddx", &fdKLMDdx);
+    varyingHandler->addVarying("dklmddx", &fdKLMDdx, Interpolation::kCanBeFlat);
     code->appendf("%s = float4(%s[0].x, %s[1].x, %s[2].x, %s.x);",
                   OutName(fdKLMDdx), fKLMMatrix.c_str(), fKLMMatrix.c_str(),
                   fKLMMatrix.c_str(), fEdgeDistanceEquation.c_str());
 
     fdKLMDdy.reset(kFloat4_GrSLType, scope);
-    varyingHandler->addFlatVarying("dklmddy", &fdKLMDdy);
+    varyingHandler->addVarying("dklmddy", &fdKLMDdy, Interpolation::kCanBeFlat);
     code->appendf("%s = float4(%s[0].y, %s[1].y, %s[2].y, %s.y);",
                   OutName(fdKLMDdy), fKLMMatrix.c_str(), fKLMMatrix.c_str(),
                   fKLMMatrix.c_str(), fEdgeDistanceEquation.c_str());
diff --git a/src/gpu/ccpr/GrCCPathProcessor.cpp b/src/gpu/ccpr/GrCCPathProcessor.cpp
index f93a0bd..986b587 100644
--- a/src/gpu/ccpr/GrCCPathProcessor.cpp
+++ b/src/gpu/ccpr/GrCCPathProcessor.cpp
@@ -146,6 +146,8 @@
 
 void GLSLPathProcessor::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
     using InstanceAttribs = GrCCPathProcessor::InstanceAttribs;
+    using Interpolation = GrGLSLVaryingHandler::Interpolation;
+
     const GrCCPathProcessor& proc = args.fGP.cast<GrCCPathProcessor>();
     GrGLSLUniformHandler* uniHandler = args.fUniformHandler;
     GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
@@ -160,8 +162,8 @@
     GrGLSLVarying texcoord(kFloat2_GrSLType);
     GrGLSLVarying color(kHalf4_GrSLType);
     varyingHandler->addVarying("texcoord", &texcoord);
-    varyingHandler->addFlatPassThroughAttribute(&proc.getInstanceAttrib(InstanceAttribs::kColor),
-                                                args.fOutputColor);
+    varyingHandler->addPassThroughAttribute(&proc.getInstanceAttrib(InstanceAttribs::kColor),
+                                            args.fOutputColor, Interpolation::kCanBeFlat);
 
     // The vertex shader bloats and intersects the devBounds and devBounds45 rectangles, in order to
     // find an octagon that circumscribes the (bloated) path.
diff --git a/src/gpu/ccpr/GrCCQuadraticShader.cpp b/src/gpu/ccpr/GrCCQuadraticShader.cpp
index 002fcfd..1122eaf 100644
--- a/src/gpu/ccpr/GrCCQuadraticShader.cpp
+++ b/src/gpu/ccpr/GrCCQuadraticShader.cpp
@@ -101,14 +101,16 @@
 
 void GrCCQuadraticCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
                                                GrGLSLVarying::Scope scope, SkString* code) {
+    using Interpolation = GrGLSLVaryingHandler::Interpolation;
+
     fdXYDdx.reset(kFloat3_GrSLType, scope);
-    varyingHandler->addFlatVarying("dXYDdx", &fdXYDdx);
+    varyingHandler->addVarying("dXYDdx", &fdXYDdx, Interpolation::kCanBeFlat);
     code->appendf("%s = float3(%s[0].x, %s[0].y, %s.x);",
                   OutName(fdXYDdx), fCanonicalMatrix.c_str(), fCanonicalMatrix.c_str(),
                   fEdgeDistanceEquation.c_str());
 
     fdXYDdy.reset(kFloat3_GrSLType, scope);
-    varyingHandler->addFlatVarying("dXYDdy", &fdXYDdy);
+    varyingHandler->addVarying("dXYDdy", &fdXYDdy, Interpolation::kCanBeFlat);
     code->appendf("%s = float3(%s[1].x, %s[1].y, %s.y);",
                   OutName(fdXYDdy), fCanonicalMatrix.c_str(), fCanonicalMatrix.c_str(),
                   fEdgeDistanceEquation.c_str());
diff --git a/src/gpu/ccpr/GrCCTriangleShader.cpp b/src/gpu/ccpr/GrCCTriangleShader.cpp
index 6e9b13e..6ed8758 100644
--- a/src/gpu/ccpr/GrCCTriangleShader.cpp
+++ b/src/gpu/ccpr/GrCCTriangleShader.cpp
@@ -18,7 +18,8 @@
                                         const char* wind) {
     fCoverageTimesWind.reset(kHalf_GrSLType, scope);
     if (!inputCoverage) {
-        varyingHandler->addFlatVarying("wind", &fCoverageTimesWind);
+        varyingHandler->addVarying("wind", &fCoverageTimesWind,
+                                   GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
         code->appendf("%s = %s;", OutName(fCoverageTimesWind), wind);
     } else {
         varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind);
@@ -89,13 +90,14 @@
                                               GrGLSLVarying::Scope scope, SkString* code,
                                               const char* position, const char* inputCoverage,
                                               const char* wind) {
+    using Interpolation = GrGLSLVaryingHandler::Interpolation;
     SkASSERT(!inputCoverage);
 
     fCornerLocationInAABoxes.reset(kFloat2x2_GrSLType, scope);
     varyingHandler->addVarying("corner_location_in_aa_boxes", &fCornerLocationInAABoxes);
 
     fBisectInAABoxes.reset(kFloat2x2_GrSLType, scope);
-    varyingHandler->addFlatVarying("bisect_in_aa_boxes", &fBisectInAABoxes);
+    varyingHandler->addVarying("bisect_in_aa_boxes", &fBisectInAABoxes, Interpolation::kCanBeFlat);
 
     code->appendf("for (int i = 0; i < 2; ++i) {");
     code->appendf(    "%s[i] = %s * %s[i] + %s[i];",
@@ -105,7 +107,7 @@
     code->appendf("}");
 
     fWindTimesHalf.reset(kHalf_GrSLType, scope);
-    varyingHandler->addFlatVarying("wind_times_half", &fWindTimesHalf);
+    varyingHandler->addVarying("wind_times_half", &fWindTimesHalf, Interpolation::kCanBeFlat);
     code->appendf("%s = %s * .5;", OutName(fWindTimesHalf), wind);
 }
 
diff --git a/src/gpu/effects/GrAtlasedShaderHelpers.h b/src/gpu/effects/GrAtlasedShaderHelpers.h
index 8c09291..a7d445a 100644
--- a/src/gpu/effects/GrAtlasedShaderHelpers.h
+++ b/src/gpu/effects/GrAtlasedShaderHelpers.h
@@ -20,6 +20,8 @@
                                      GrGLSLVarying *uv,
                                      GrGLSLVarying *texIdx,
                                      GrGLSLVarying *st) {
+    using Interpolation = GrGLSLVaryingHandler::Interpolation;
+
     // This extracts the texture index and texel coordinates from the same variable
     // Packing structure: texel coordinates are multiplied by 2 (or shifted left 1)
     //                    texture index is stored as lower bits of both x and y
@@ -40,11 +42,9 @@
     args.fVaryingHandler->addVarying("TextureCoords", uv);
     args.fVertBuilder->codeAppendf("%s = unormTexCoords * %s;", uv->vsOut(), atlasSizeInvName);
 
-    if (args.fShaderCaps->integerSupport()) {
-        args.fVaryingHandler->addFlatVarying("TexIndex", texIdx);
-    } else {
-        args.fVaryingHandler->addVarying("TexIndex", texIdx);
-    }
+    args.fVaryingHandler->addVarying("TexIndex", texIdx, args.fShaderCaps->integerSupport()
+                                                                 ? Interpolation::kMustBeFlat
+                                                                 : Interpolation::kCanBeFlat);
     args.fVertBuilder->codeAppendf("%s = texIdx;", texIdx->vsOut());
 
     if (st) {
diff --git a/src/gpu/glsl/GrGLSLVarying.cpp b/src/gpu/glsl/GrGLSLVarying.cpp
index 5a57613..c5cef34 100644
--- a/src/gpu/glsl/GrGLSLVarying.cpp
+++ b/src/gpu/glsl/GrGLSLVarying.cpp
@@ -10,36 +10,44 @@
 #include "glsl/GrGLSLProgramBuilder.h"
 
 void GrGLSLVaryingHandler::addPassThroughAttribute(const GrGeometryProcessor::Attribute* input,
-                                                   const char* output) {
-    GrSLType type = GrVertexAttribTypeToSLType(input->fType);
-    GrGLSLVarying v(type);
-    this->addVarying(input->fName, &v);
-    this->writePassThroughAttribute(input, output, v);
-}
-
-void GrGLSLVaryingHandler::addFlatPassThroughAttribute(const GrGeometryProcessor::Attribute* input,
-                                                       const char* output) {
-    GrSLType type = GrVertexAttribTypeToSLType(input->fType);
-    GrGLSLVarying v(type);
-    this->addFlatVarying(input->fName, &v);
-    this->writePassThroughAttribute(input, output, v);
-}
-
-void GrGLSLVaryingHandler::writePassThroughAttribute(const GrGeometryProcessor::Attribute* input,
-                                                     const char* output, const GrGLSLVarying& v) {
+                                                   const char* output,
+                                                   Interpolation interpolation) {
     SkASSERT(!fProgramBuilder->primitiveProcessor().willUseGeoShader());
+    GrSLType type = GrVertexAttribTypeToSLType(input->fType);
+    GrGLSLVarying v(type);
+    this->addVarying(input->fName, &v, interpolation);
     fProgramBuilder->fVS.codeAppendf("%s = %s;", v.vsOut(), input->fName);
     fProgramBuilder->fFS.codeAppendf("%s = %s;", output, v.fsIn());
 }
 
-void GrGLSLVaryingHandler::internalAddVarying(const char* name, GrGLSLVarying* varying, bool flat) {
+static bool use_flat_interpolation(GrGLSLVaryingHandler::Interpolation interpolation,
+                                   const GrShaderCaps& shaderCaps) {
+    switch (interpolation) {
+        using Interpolation = GrGLSLVaryingHandler::Interpolation;
+        case Interpolation::kInterpolated:
+            return false;
+        case Interpolation::kCanBeFlat:
+            SkASSERT(!shaderCaps.preferFlatInterpolation() ||
+                     shaderCaps.flatInterpolationSupport());
+            return shaderCaps.preferFlatInterpolation();
+        case Interpolation::kMustBeFlat:
+            SkASSERT(shaderCaps.flatInterpolationSupport());
+            return true;
+    }
+    SK_ABORT("Invalid interpolation");
+    return false;
+}
+
+void GrGLSLVaryingHandler::addVarying(const char* name, GrGLSLVarying* varying,
+                                      Interpolation interpolation) {
+    SkASSERT(GrSLTypeIsFloatType(varying->type()) || Interpolation::kMustBeFlat == interpolation);
     bool willUseGeoShader = fProgramBuilder->primitiveProcessor().willUseGeoShader();
     VaryingInfo& v = fVaryings.push_back();
 
     SkASSERT(varying);
     SkASSERT(kVoid_GrSLType != varying->fType);
     v.fType = varying->fType;
-    v.fIsFlat = flat;
+    v.fIsFlat = use_flat_interpolation(interpolation, *fProgramBuilder->shaderCaps());
     fProgramBuilder->nameVariable(&v.fVsOut, 'v', name);
     v.fVisibility = kNone_GrShaderFlags;
     if (varying->isInVertexShader()) {
diff --git a/src/gpu/glsl/GrGLSLVarying.h b/src/gpu/glsl/GrGLSLVarying.h
index dac9bb7..57704ad 100644
--- a/src/gpu/glsl/GrGLSLVarying.h
+++ b/src/gpu/glsl/GrGLSLVarying.h
@@ -79,6 +79,12 @@
      */
     void setNoPerspective();
 
+    enum class Interpolation {
+        kInterpolated,
+        kCanBeFlat, // Use "flat" if it will be faster.
+        kMustBeFlat // Use "flat" even if it is known to be slow.
+    };
+
     /*
      * addVarying allows fine grained control for setting up varyings between stages. Calling this
      * function will make sure all necessary decls are setup for the client. The client however is
@@ -87,20 +93,8 @@
      * addPassThroughAttribute.
      * TODO convert most uses of addVarying to addPassThroughAttribute
      */
-    void addVarying(const char* name, GrGLSLVarying* varying) {
-        SkASSERT(GrSLTypeIsFloatType(varying->type())); // Integers must use addFlatVarying.
-        this->internalAddVarying(name, varying, false /*flat*/);
-    }
-
-    /*
-     * addFlatVarying sets up a varying whose value is constant across every fragment. The graphics
-     * pipeline will pull its value from the final vertex of the draw primitive (provoking vertex).
-     * Flat interpolation is not always supported and the user must check the caps before using.
-     * TODO: Some platforms can change the provoking vertex. Should we be resetting this knob?
-     */
-    void addFlatVarying(const char* name, GrGLSLVarying* varying) {
-        this->internalAddVarying(name, varying, true /*flat*/);
-    }
+    void addVarying(const char* name, GrGLSLVarying* varying,
+                    Interpolation = Interpolation::kInterpolated);
 
     /*
      * The GP can use these calls to pass an attribute through all shaders directly to 'output' in
@@ -110,8 +104,8 @@
      * that will be set as the output varying for all emitted vertices.
      * TODO it might be nicer behavior to have a flag to declare output inside these calls
      */
-    void addPassThroughAttribute(const GrGeometryProcessor::Attribute*, const char* output);
-    void addFlatPassThroughAttribute(const GrGeometryProcessor::Attribute*, const char* output);
+    void addPassThroughAttribute(const GrGeometryProcessor::Attribute*, const char* output,
+                                 Interpolation = Interpolation::kInterpolated);
 
     void emitAttributes(const GrGeometryProcessor& gp);
 
@@ -148,10 +142,6 @@
     GrGLSLProgramBuilder* fProgramBuilder;
 
 private:
-    void internalAddVarying(const char* name, GrGLSLVarying*, bool flat);
-    void writePassThroughAttribute(const GrGeometryProcessor::Attribute*, const char* output,
-                                   const GrGLSLVarying&);
-
     void addAttribute(const GrShaderVar& var);
 
     virtual void onFinalize() = 0;
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 46d2630..565a658 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -116,6 +116,7 @@
 
         private:
             void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
+                using Interpolation = GrGLSLVaryingHandler::Interpolation;
                 const auto& textureGP = args.fGP.cast<TextureGeometryProcessor>();
                 fColorSpaceXformHelper.emitCode(
                         args.fUniformHandler, textureGP.fColorSpaceXform.get());
@@ -127,26 +128,19 @@
                                      args.fUniformHandler,
                                      textureGP.fTextureCoords.asShaderVar(),
                                      args.fFPCoordTransformHandler);
-                if (args.fShaderCaps->preferFlatInterpolation()) {
-                    args.fVaryingHandler->addFlatPassThroughAttribute(&textureGP.fColors,
-                                                                      args.fOutputColor);
-                } else {
-                    args.fVaryingHandler->addPassThroughAttribute(&textureGP.fColors,
-                                                                  args.fOutputColor);
-                }
+                args.fVaryingHandler->addPassThroughAttribute(&textureGP.fColors,
+                                                              args.fOutputColor,
+                                                              Interpolation::kCanBeFlat);
                 args.fFragBuilder->codeAppend("float2 texCoord;");
                 args.fVaryingHandler->addPassThroughAttribute(&textureGP.fTextureCoords,
                                                               "texCoord");
                 if (textureGP.numTextureSamplers() > 1) {
+                    // If this changes to float, reconsider Interpolation::kMustBeFlat.
+                    SkASSERT(kInt_GrVertexAttribType == textureGP.fTextureIdx.fType);
                     SkASSERT(args.fShaderCaps->integerSupport());
                     args.fFragBuilder->codeAppend("int texIdx;");
-                    if (args.fShaderCaps->flatInterpolationSupport()) {
-                        args.fVaryingHandler->addFlatPassThroughAttribute(&textureGP.fTextureIdx,
-                                                                          "texIdx");
-                    } else {
-                        args.fVaryingHandler->addPassThroughAttribute(&textureGP.fTextureIdx,
-                                                                      "texIdx");
-                    }
+                    args.fVaryingHandler->addPassThroughAttribute(&textureGP.fTextureIdx, "texIdx",
+                                                                  Interpolation::kMustBeFlat);
                     args.fFragBuilder->codeAppend("switch (texIdx) {");
                     for (int i = 0; i < textureGP.numTextureSamplers(); ++i) {
                         args.fFragBuilder->codeAppendf("case %d: %s = ", i, args.fOutputColor);