| Index: src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp
|
| diff --git a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..d216a18c25821cb64b13160fc5a5e2bce83fa0b1
|
| --- /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";
|
| +}
|
| +
|
|
|