text: /*
   * 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";
  }
  
  