diff options
Diffstat (limited to 'src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp')
-rw-r--r-- | src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp new file mode 100644 index 0000000000..d216a18c25 --- /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"; +} + |