Initial refactor of shaderbuilder to prepare for geometry shaders

gitignore for eclipse

BUG=skia:
R=bsalomon@google.com, bsalomon@chromium.org

Author: joshualitt@chromium.org

Review URL: https://codereview.chromium.org/491673002
diff --git a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp
new file mode 100644
index 0000000..d216a18
--- /dev/null
+++ b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp
@@ -0,0 +1,351 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrGLFragmentShaderBuilder.h"
+#include "GrGLShaderStringBuilder.h"
+#include "GrGLProgramBuilder.h"
+#include "../GrGpuGL.h"
+
+namespace {
+#define GL_CALL(X) GR_GL_CALL(gpu->glInterface(), X)
+#define GL_CALL_RET(R, X) GR_GL_CALL_RET(gpu->glInterface(), R, X)
+// ES2 FS only guarantees mediump and lowp support
+static const GrGLShaderVar::Precision kDefaultFragmentPrecision = GrGLShaderVar::kMedium_Precision;
+static const char kDstCopyColorName[] = "_dstColor";
+inline const char* declared_color_output_name() { return "fsColorOut"; }
+inline const char* dual_source_output_name() { return "dualSourceOut"; }
+inline void append_default_precision_qualifier(GrGLShaderVar::Precision p,
+                                               GrGLStandard standard,
+                                               SkString* str) {
+    // Desktop GLSL has added precision qualifiers but they don't do anything.
+    if (kGLES_GrGLStandard == standard) {
+        switch (p) {
+            case GrGLShaderVar::kHigh_Precision:
+                str->append("precision highp float;\n");
+                break;
+            case GrGLShaderVar::kMedium_Precision:
+                str->append("precision mediump float;\n");
+                break;
+            case GrGLShaderVar::kLow_Precision:
+                str->append("precision lowp float;\n");
+                break;
+            case GrGLShaderVar::kDefault_Precision:
+                SkFAIL("Default precision now allowed.");
+            default:
+                SkFAIL("Unknown precision value.");
+        }
+    }
+}
+}
+
+GrGLFragmentShaderBuilder::DstReadKey GrGLFragmentShaderBuilder::KeyForDstRead(
+        const GrTexture* dstCopy, const GrGLCaps& caps) {
+    uint32_t key = kYesDstRead_DstReadKeyBit;
+    if (caps.fbFetchSupport()) {
+        return key;
+    }
+    SkASSERT(NULL != dstCopy);
+    if (!caps.textureSwizzleSupport() && GrPixelConfigIsAlphaOnly(dstCopy->config())) {
+        // The fact that the config is alpha-only must be considered when generating code.
+        key |= kUseAlphaConfig_DstReadKeyBit;
+    }
+    if (kTopLeft_GrSurfaceOrigin == dstCopy->origin()) {
+        key |= kTopLeftOrigin_DstReadKeyBit;
+    }
+    SkASSERT(static_cast<DstReadKey>(key) == key);
+    return static_cast<DstReadKey>(key);
+}
+
+GrGLFragmentShaderBuilder::FragPosKey GrGLFragmentShaderBuilder::KeyForFragmentPosition(
+        const GrRenderTarget* dst, const GrGLCaps&) {
+    if (kTopLeft_GrSurfaceOrigin == dst->origin()) {
+        return kTopLeftFragPosRead_FragPosKey;
+    } else {
+        return kBottomLeftFragPosRead_FragPosKey;
+    }
+}
+
+GrGLFragmentShaderBuilder::GrGLFragmentShaderBuilder(GrGLProgramBuilder* program,
+                                                     const GrGLProgramDesc& desc)
+    : INHERITED(program)
+    , fHasCustomColorOutput(false)
+    , fHasSecondaryOutput(false)
+    , fSetupFragPosition(false)
+    , fTopLeftFragPosRead(kTopLeftFragPosRead_FragPosKey == desc.getHeader().fFragPosKey){
+}
+
+const char* GrGLFragmentShaderBuilder::dstColor() {
+    if (fProgramBuilder->fCodeStage.inStageCode()) {
+        const GrEffect* effect = fProgramBuilder->fCodeStage.effectStage()->getEffect();
+        if (!effect->willReadDstColor()) {
+            SkDEBUGFAIL("GrGLEffect asked for dst color but its generating GrEffect "
+                        "did not request access.");
+            return "";
+        }
+    }
+
+    GrGpuGL* gpu = fProgramBuilder->gpu();
+    if (gpu->glCaps().fbFetchSupport()) {
+        this->addFeature(1 << (GrGLFragmentShaderBuilder::kLastGLSLPrivateFeature + 1),
+                         gpu->glCaps().fbFetchExtensionString());
+        return gpu->glCaps().fbFetchColorName();
+    } else if (fProgramBuilder->fUniformHandles.fDstCopySamplerUni.isValid()) {
+        return kDstCopyColorName;
+    } else {
+        return "";
+    }
+}
+
+bool GrGLFragmentShaderBuilder::enableFeature(GLSLFeature feature) {
+    switch (feature) {
+        case kStandardDerivatives_GLSLFeature: {
+            GrGpuGL* gpu = fProgramBuilder->gpu();
+            if (!gpu->glCaps().shaderDerivativeSupport()) {
+                return false;
+            }
+            if (kGLES_GrGLStandard == gpu->glStandard()) {
+                this->addFeature(1 << kStandardDerivatives_GLSLFeature,
+                                 "GL_OES_standard_derivatives");
+            }
+            return true;
+        }
+        default:
+            SkFAIL("Unexpected GLSLFeature requested.");
+            return false;
+    }
+}
+
+SkString GrGLFragmentShaderBuilder::ensureFSCoords2D(const TransformedCoordsArray& coords, int index) {
+    if (kVec3f_GrSLType != coords[index].type()) {
+        SkASSERT(kVec2f_GrSLType == coords[index].type());
+        return coords[index].getName();
+    }
+
+    SkString coords2D("coords2D");
+    if (0 != index) {
+        coords2D.appendf("_%i", index);
+    }
+    this->codeAppendf("\tvec2 %s = %s.xy / %s.z;",
+                      coords2D.c_str(), coords[index].c_str(), coords[index].c_str());
+    return coords2D;
+}
+
+const char* GrGLFragmentShaderBuilder::fragmentPosition() {
+    GrGLProgramBuilder::CodeStage* cs = &fProgramBuilder->fCodeStage;
+    if (cs->inStageCode()) {
+        const GrEffect* effect = cs->effectStage()->getEffect();
+        if (!effect->willReadFragmentPosition()) {
+            SkDEBUGFAIL("GrGLEffect asked for frag position but its generating GrEffect "
+                        "did not request access.");
+            return "";
+        }
+    }
+
+    GrGpuGL* gpu = fProgramBuilder->gpu();
+    // We only declare "gl_FragCoord" when we're in the case where we want to use layout qualifiers
+    // to reverse y. Otherwise it isn't necessary and whether the "in" qualifier appears in the
+    // declaration varies in earlier GLSL specs. So it is simpler to omit it.
+    if (fTopLeftFragPosRead) {
+        fSetupFragPosition = true;
+        return "gl_FragCoord";
+    } else if (gpu->glCaps().fragCoordConventionsSupport()) {
+        if (!fSetupFragPosition) {
+            if (gpu->glslGeneration() < k150_GrGLSLGeneration) {
+                this->addFeature(1 << kFragCoordConventions_GLSLPrivateFeature,
+                                 "GL_ARB_fragment_coord_conventions");
+            }
+            fInputs.push_back().set(kVec4f_GrSLType,
+                                    GrGLShaderVar::kIn_TypeModifier,
+                                    "gl_FragCoord",
+                                    GrGLShaderVar::kDefault_Precision,
+                                    GrGLShaderVar::kUpperLeft_Origin);
+            fSetupFragPosition = true;
+        }
+        return "gl_FragCoord";
+    } else {
+        static const char* kCoordName = "fragCoordYDown";
+        if (!fSetupFragPosition) {
+            // temporarily change the stage index because we're inserting non-stage code.
+            GrGLProgramBuilder::CodeStage::AutoStageRestore csar(cs, NULL);
+
+            SkASSERT(!fProgramBuilder->fUniformHandles.fRTHeightUni.isValid());
+            const char* rtHeightName;
+
+            fProgramBuilder->fUniformHandles.fRTHeightUni =
+                    fProgramBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
+                                         kFloat_GrSLType,
+                                         "RTHeight",
+                                         &rtHeightName);
+
+            // Using glFragCoord.zw for the last two components tickles an Adreno driver bug that
+            // causes programs to fail to link. Making this function return a vec2() didn't fix the
+            // problem but using 1.0 for the last two components does.
+            this->codePrependf("\tvec4 %s = vec4(gl_FragCoord.x, %s - gl_FragCoord.y, 1.0, "
+                               "1.0);\n", kCoordName, rtHeightName);
+            fSetupFragPosition = true;
+        }
+        SkASSERT(fProgramBuilder->fUniformHandles.fRTHeightUni.isValid());
+        return kCoordName;
+    }
+}
+
+void GrGLFragmentShaderBuilder::addVarying(GrSLType type,
+               const char* name,
+               const char** fsInName) {
+    fInputs.push_back().set(type, GrGLShaderVar::kVaryingIn_TypeModifier, name);
+    if (fsInName) {
+        *fsInName = name;
+    }
+}
+
+void GrGLFragmentShaderBuilder::bindProgramLocations(GrGLuint programId) {
+    GrGpuGL* gpu = fProgramBuilder->gpu();
+    if (fHasCustomColorOutput) {
+        GL_CALL(BindFragDataLocation(programId, 0, declared_color_output_name()));
+    }
+    if (fHasSecondaryOutput) {
+        GL_CALL(BindFragDataLocationIndexed(programId, 0, 1, dual_source_output_name()));
+    }
+}
+
+bool GrGLFragmentShaderBuilder::compileAndAttachShaders(GrGLuint programId,
+                                                        SkTDArray<GrGLuint>* shaderIds) const {
+    GrGpuGL* gpu = fProgramBuilder->gpu();
+    SkString fragShaderSrc(GrGetGLSLVersionDecl(gpu->ctxInfo()));
+    fragShaderSrc.append(fExtensions);
+    append_default_precision_qualifier(kDefaultFragmentPrecision,
+                                       gpu->glStandard(),
+                                       &fragShaderSrc);
+    fProgramBuilder->appendUniformDecls(GrGLProgramBuilder::kFragment_Visibility, &fragShaderSrc);
+    fProgramBuilder->appendDecls(fInputs, &fragShaderSrc);
+    // We shouldn't have declared outputs on 1.10
+    SkASSERT(k110_GrGLSLGeneration != gpu->glslGeneration() || fOutputs.empty());
+    fProgramBuilder->appendDecls(fOutputs, &fragShaderSrc);
+    fragShaderSrc.append(fFunctions);
+    fragShaderSrc.append("void main() {\n");
+    fragShaderSrc.append(fCode);
+    fragShaderSrc.append("}\n");
+
+    GrGLuint fragShaderId = GrGLCompileAndAttachShader(gpu->glContext(),
+            programId, GR_GL_FRAGMENT_SHADER, fragShaderSrc);
+    if (!fragShaderId) {
+        return false;
+    }
+
+    *shaderIds->append() = fragShaderId;
+
+    return true;
+}
+
+void GrGLFragmentShaderBuilder::emitCodeBeforeEffects() {
+    const GrGLProgramDesc::KeyHeader& header = fProgramBuilder->desc().getHeader();
+    GrGpuGL* gpu = fProgramBuilder->gpu();
+
+    ///////////////////////////////////////////////////////////////////////////
+    // emit code to read the dst copy texture, if necessary
+    if (kNoDstRead_DstReadKey != header.fDstReadKey && !gpu->glCaps().fbFetchSupport()) {
+        bool topDown = SkToBool(kTopLeftOrigin_DstReadKeyBit & header.fDstReadKey);
+        const char* dstCopyTopLeftName;
+        const char* dstCopyCoordScaleName;
+        const char* dstCopySamplerName;
+        uint32_t configMask;
+        if (SkToBool(kUseAlphaConfig_DstReadKeyBit & header.fDstReadKey)) {
+            configMask = kA_GrColorComponentFlag;
+        } else {
+            configMask = kRGBA_GrColorComponentFlags;
+        }
+        fProgramBuilder->fUniformHandles.fDstCopySamplerUni =
+            fProgramBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
+                                 kSampler2D_GrSLType,
+                                 "DstCopySampler",
+                                 &dstCopySamplerName);
+        fProgramBuilder->fUniformHandles.fDstCopyTopLeftUni =
+            fProgramBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
+                                 kVec2f_GrSLType,
+                                 "DstCopyUpperLeft",
+                                 &dstCopyTopLeftName);
+        fProgramBuilder->fUniformHandles.fDstCopyScaleUni =
+            fProgramBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
+                                 kVec2f_GrSLType,
+                                 "DstCopyCoordScale",
+                                 &dstCopyCoordScaleName);
+        const char* fragPos = fragmentPosition();
+
+        this->codeAppend("// Read color from copy of the destination.\n");
+        this->codeAppendf("vec2 _dstTexCoord = (%s.xy - %s) * %s;",
+                          fragPos, dstCopyTopLeftName, dstCopyCoordScaleName);
+        if (!topDown) {
+            this->codeAppend("_dstTexCoord.y = 1.0 - _dstTexCoord.y;");
+        }
+        this->codeAppendf("vec4 %s = ", kDstCopyColorName);
+        this->appendTextureLookup(dstCopySamplerName,
+                                  "_dstTexCoord",
+                                  configMask,
+                                  "rgba");
+        this->codeAppend(";");
+    }
+
+    if (k110_GrGLSLGeneration != gpu->glslGeneration()) {
+        fOutputs.push_back().set(kVec4f_GrSLType,
+                                 GrGLShaderVar::kOut_TypeModifier,
+                                 declared_color_output_name());
+        fHasCustomColorOutput = true;
+    }
+}
+
+void GrGLFragmentShaderBuilder::emitCodeAfterEffects(const GrGLSLExpr4& inputColor, const GrGLSLExpr4& inputCoverage) {
+    const GrGLProgramDesc::KeyHeader& header = fProgramBuilder->desc().getHeader();
+
+    ///////////////////////////////////////////////////////////////////////////
+    // write the secondary color output if necessary
+    if (GrGLProgramDesc::CoverageOutputUsesSecondaryOutput(header.fCoverageOutput)) {
+        const char* secondaryOutputName = this->enableSecondaryOutput();
+
+        // default coeff to ones for kCoverage_DualSrcOutput
+        GrGLSLExpr4 coeff(1);
+        if (GrGLProgramDesc::kSecondaryCoverageISA_CoverageOutput == header.fCoverageOutput) {
+            // Get (1-A) into coeff
+            coeff = GrGLSLExpr4::VectorCast(GrGLSLExpr1(1) - inputColor.a());
+        } else if (GrGLProgramDesc::kSecondaryCoverageISC_CoverageOutput ==
+                   header.fCoverageOutput){
+            // Get (1-RGBA) into coeff
+            coeff = GrGLSLExpr4(1) - inputColor;
+        }
+        // Get coeff * coverage into modulate and then write that to the dual source output.
+        codeAppendf("\t%s = %s;\n", secondaryOutputName, (coeff * inputCoverage).c_str());
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // combine color and coverage as frag color
+
+    // Get "color * coverage" into fragColor
+    GrGLSLExpr4 fragColor = inputColor * inputCoverage;
+    // Now tack on "+(1-coverage)dst onto the frag color if we were asked to do so.
+    if (GrGLProgramDesc::kCombineWithDst_CoverageOutput == header.fCoverageOutput) {
+        GrGLSLExpr4 dstCoeff = GrGLSLExpr4(1) - inputCoverage;
+
+        GrGLSLExpr4 dstContribution = dstCoeff * GrGLSLExpr4(dstColor());
+
+        fragColor = fragColor + dstContribution;
+    }
+    codeAppendf("\t%s = %s;\n", this->getColorOutputName(), fragColor.c_str());
+}
+
+const char* GrGLFragmentShaderBuilder::enableSecondaryOutput() {
+    if (!fHasSecondaryOutput) {
+        fOutputs.push_back().set(kVec4f_GrSLType,
+                                 GrGLShaderVar::kOut_TypeModifier,
+                                 dual_source_output_name());
+        fHasSecondaryOutput = true;
+    }
+    return dual_source_output_name();
+}
+
+const char* GrGLFragmentShaderBuilder::getColorOutputName() const {
+    return fHasCustomColorOutput ? declared_color_output_name() : "gl_FragColor";
+}
+