michael@0: /* michael@0: * Copyright 2012 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "gl/GrGLShaderBuilder.h" michael@0: #include "gl/GrGLProgram.h" michael@0: #include "gl/GrGLUniformHandle.h" michael@0: #include "GrCoordTransform.h" michael@0: #include "GrDrawEffect.h" michael@0: #include "GrGpuGL.h" michael@0: #include "GrTexture.h" michael@0: #include "SkRTConf.h" michael@0: #include "SkTrace.h" michael@0: michael@0: #define GL_CALL(X) GR_GL_CALL(this->gpu()->glInterface(), X) michael@0: #define GL_CALL_RET(R, X) GR_GL_CALL_RET(this->gpu()->glInterface(), R, X) michael@0: michael@0: // number of each input/output type in a single allocation block michael@0: static const int kVarsPerBlock = 8; michael@0: michael@0: // except FS outputs where we expect 2 at most. michael@0: static const int kMaxFSOutputs = 2; michael@0: michael@0: // ES2 FS only guarantees mediump and lowp support michael@0: static const GrGLShaderVar::Precision kDefaultFragmentPrecision = GrGLShaderVar::kMedium_Precision; michael@0: michael@0: typedef GrGLUniformManager::UniformHandle UniformHandle; michael@0: michael@0: SK_CONF_DECLARE(bool, c_PrintShaders, "gpu.printShaders", false, michael@0: "Print the source code for all shaders generated."); michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: namespace { michael@0: michael@0: inline const char* color_attribute_name() { return "aColor"; } michael@0: inline const char* coverage_attribute_name() { return "aCoverage"; } michael@0: inline const char* declared_color_output_name() { return "fsColorOut"; } michael@0: inline const char* dual_source_output_name() { return "dualSourceOut"; } michael@0: inline const char* sample_function_name(GrSLType type, GrGLSLGeneration glslGen) { michael@0: if (kVec2f_GrSLType == type) { michael@0: return glslGen >= k130_GrGLSLGeneration ? "texture" : "texture2D"; michael@0: } else { michael@0: SkASSERT(kVec3f_GrSLType == type); michael@0: return glslGen >= k130_GrGLSLGeneration ? "textureProj" : "texture2DProj"; michael@0: } michael@0: } michael@0: michael@0: void append_texture_lookup(SkString* out, michael@0: GrGpuGL* gpu, michael@0: const char* samplerName, michael@0: const char* coordName, michael@0: uint32_t configComponentMask, michael@0: const char* swizzle, michael@0: GrSLType varyingType = kVec2f_GrSLType) { michael@0: SkASSERT(NULL != coordName); michael@0: michael@0: out->appendf("%s(%s, %s)", michael@0: sample_function_name(varyingType, gpu->glslGeneration()), michael@0: samplerName, michael@0: coordName); michael@0: michael@0: char mangledSwizzle[5]; michael@0: michael@0: // The swizzling occurs using texture params instead of shader-mangling if ARB_texture_swizzle michael@0: // is available. michael@0: if (!gpu->glCaps().textureSwizzleSupport() && michael@0: (kA_GrColorComponentFlag == configComponentMask)) { michael@0: char alphaChar = gpu->glCaps().textureRedSupport() ? 'r' : 'a'; michael@0: int i; michael@0: for (i = 0; '\0' != swizzle[i]; ++i) { michael@0: mangledSwizzle[i] = alphaChar; michael@0: } michael@0: mangledSwizzle[i] ='\0'; michael@0: swizzle = mangledSwizzle; michael@0: } michael@0: // For shader prettiness we omit the swizzle rather than appending ".rgba". michael@0: if (memcmp(swizzle, "rgba", 4)) { michael@0: out->appendf(".%s", swizzle); michael@0: } michael@0: } michael@0: michael@0: } michael@0: michael@0: static const char kDstCopyColorName[] = "_dstColor"; michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: GrGLShaderBuilder::GrGLShaderBuilder(GrGpuGL* gpu, michael@0: GrGLUniformManager& uniformManager, michael@0: const GrGLProgramDesc& desc) michael@0: : fGpu(gpu) michael@0: , fUniformManager(uniformManager) michael@0: , fFSFeaturesAddedMask(0) michael@0: , fFSInputs(kVarsPerBlock) michael@0: , fFSOutputs(kMaxFSOutputs) michael@0: , fUniforms(kVarsPerBlock) michael@0: , fSetupFragPosition(false) michael@0: , fHasCustomColorOutput(false) michael@0: , fHasSecondaryOutput(false) michael@0: , fTopLeftFragPosRead(kTopLeftFragPosRead_FragPosKey == desc.getHeader().fFragPosKey) { michael@0: michael@0: const GrGLProgramDesc::KeyHeader& header = desc.getHeader(); michael@0: michael@0: // Emit code to read the dst copy textue if necessary. michael@0: if (kNoDstRead_DstReadKey != header.fDstReadKey && michael@0: GrGLCaps::kNone_FBFetchType == fGpu->glCaps().fbFetchType()) { michael@0: bool topDown = SkToBool(kTopLeftOrigin_DstReadKeyBit & header.fDstReadKey); michael@0: const char* dstCopyTopLeftName; michael@0: const char* dstCopyCoordScaleName; michael@0: uint32_t configMask; michael@0: if (SkToBool(kUseAlphaConfig_DstReadKeyBit & header.fDstReadKey)) { michael@0: configMask = kA_GrColorComponentFlag; michael@0: } else { michael@0: configMask = kRGBA_GrColorComponentFlags; michael@0: } michael@0: fDstCopySamplerUniform = this->addUniform(kFragment_Visibility, michael@0: kSampler2D_GrSLType, michael@0: "DstCopySampler"); michael@0: fDstCopyTopLeftUniform = this->addUniform(kFragment_Visibility, michael@0: kVec2f_GrSLType, michael@0: "DstCopyUpperLeft", michael@0: &dstCopyTopLeftName); michael@0: fDstCopyScaleUniform = this->addUniform(kFragment_Visibility, michael@0: kVec2f_GrSLType, michael@0: "DstCopyCoordScale", michael@0: &dstCopyCoordScaleName); michael@0: const char* fragPos = this->fragmentPosition(); michael@0: this->fsCodeAppend("\t// Read color from copy of the destination.\n"); michael@0: this->fsCodeAppendf("\tvec2 _dstTexCoord = (%s.xy - %s) * %s;\n", michael@0: fragPos, dstCopyTopLeftName, dstCopyCoordScaleName); michael@0: if (!topDown) { michael@0: this->fsCodeAppend("\t_dstTexCoord.y = 1.0 - _dstTexCoord.y;\n"); michael@0: } michael@0: this->fsCodeAppendf("\tvec4 %s = ", kDstCopyColorName); michael@0: append_texture_lookup(&fFSCode, michael@0: fGpu, michael@0: this->getUniformCStr(fDstCopySamplerUniform), michael@0: "_dstTexCoord", michael@0: configMask, michael@0: "rgba"); michael@0: this->fsCodeAppend(";\n\n"); michael@0: } michael@0: michael@0: if (GrGLProgramDesc::kUniform_ColorInput == header.fColorInput) { michael@0: const char* name; michael@0: fColorUniform = this->addUniform(GrGLShaderBuilder::kFragment_Visibility, michael@0: kVec4f_GrSLType, "Color", &name); michael@0: fInputColor = GrGLSLExpr4(name); michael@0: } else if (GrGLProgramDesc::kSolidWhite_ColorInput == header.fColorInput) { michael@0: fInputColor = GrGLSLExpr4(1); michael@0: } else if (GrGLProgramDesc::kTransBlack_ColorInput == header.fColorInput) { michael@0: fInputColor = GrGLSLExpr4(0); michael@0: } michael@0: michael@0: if (GrGLProgramDesc::kUniform_ColorInput == header.fCoverageInput) { michael@0: const char* name; michael@0: fCoverageUniform = this->addUniform(GrGLShaderBuilder::kFragment_Visibility, michael@0: kVec4f_GrSLType, "Coverage", &name); michael@0: fInputCoverage = GrGLSLExpr4(name); michael@0: } else if (GrGLProgramDesc::kSolidWhite_ColorInput == header.fCoverageInput) { michael@0: fInputCoverage = GrGLSLExpr4(1); michael@0: } else if (GrGLProgramDesc::kTransBlack_ColorInput == header.fCoverageInput) { michael@0: fInputCoverage = GrGLSLExpr4(0); michael@0: } michael@0: michael@0: if (k110_GrGLSLGeneration != fGpu->glslGeneration()) { michael@0: fFSOutputs.push_back().set(kVec4f_GrSLType, michael@0: GrGLShaderVar::kOut_TypeModifier, michael@0: declared_color_output_name()); michael@0: fHasCustomColorOutput = true; michael@0: } michael@0: } michael@0: michael@0: bool GrGLShaderBuilder::enableFeature(GLSLFeature feature) { michael@0: switch (feature) { michael@0: case kStandardDerivatives_GLSLFeature: michael@0: if (!fGpu->glCaps().shaderDerivativeSupport()) { michael@0: return false; michael@0: } michael@0: if (kGLES_GrGLStandard == fGpu->glStandard()) { michael@0: this->addFSFeature(1 << kStandardDerivatives_GLSLFeature, michael@0: "GL_OES_standard_derivatives"); michael@0: } michael@0: return true; michael@0: default: michael@0: GrCrash("Unexpected GLSLFeature requested."); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: bool GrGLShaderBuilder::enablePrivateFeature(GLSLPrivateFeature feature) { michael@0: switch (feature) { michael@0: case kFragCoordConventions_GLSLPrivateFeature: michael@0: if (!fGpu->glCaps().fragCoordConventionsSupport()) { michael@0: return false; michael@0: } michael@0: if (fGpu->glslGeneration() < k150_GrGLSLGeneration) { michael@0: this->addFSFeature(1 << kFragCoordConventions_GLSLPrivateFeature, michael@0: "GL_ARB_fragment_coord_conventions"); michael@0: } michael@0: return true; michael@0: case kEXTShaderFramebufferFetch_GLSLPrivateFeature: michael@0: if (GrGLCaps::kEXT_FBFetchType != fGpu->glCaps().fbFetchType()) { michael@0: return false; michael@0: } michael@0: this->addFSFeature(1 << kEXTShaderFramebufferFetch_GLSLPrivateFeature, michael@0: "GL_EXT_shader_framebuffer_fetch"); michael@0: return true; michael@0: case kNVShaderFramebufferFetch_GLSLPrivateFeature: michael@0: if (GrGLCaps::kNV_FBFetchType != fGpu->glCaps().fbFetchType()) { michael@0: return false; michael@0: } michael@0: this->addFSFeature(1 << kNVShaderFramebufferFetch_GLSLPrivateFeature, michael@0: "GL_NV_shader_framebuffer_fetch"); michael@0: return true; michael@0: default: michael@0: GrCrash("Unexpected GLSLPrivateFeature requested."); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: void GrGLShaderBuilder::addFSFeature(uint32_t featureBit, const char* extensionName) { michael@0: if (!(featureBit & fFSFeaturesAddedMask)) { michael@0: fFSExtensions.appendf("#extension %s: require\n", extensionName); michael@0: fFSFeaturesAddedMask |= featureBit; michael@0: } michael@0: } michael@0: michael@0: void GrGLShaderBuilder::nameVariable(SkString* out, char prefix, const char* name) { michael@0: if ('\0' == prefix) { michael@0: *out = name; michael@0: } else { michael@0: out->printf("%c%s", prefix, name); michael@0: } michael@0: if (fCodeStage.inStageCode()) { michael@0: if (out->endsWith('_')) { michael@0: // Names containing "__" are reserved. michael@0: out->append("x"); michael@0: } michael@0: out->appendf("_Stage%d", fCodeStage.stageIndex()); michael@0: } michael@0: } michael@0: michael@0: const char* GrGLShaderBuilder::dstColor() { michael@0: if (fCodeStage.inStageCode()) { michael@0: const GrEffectRef& effect = *fCodeStage.effectStage()->getEffect(); michael@0: if (!effect->willReadDstColor()) { michael@0: GrDebugCrash("GrGLEffect asked for dst color but its generating GrEffect " michael@0: "did not request access."); michael@0: return ""; michael@0: } michael@0: } michael@0: static const char kFBFetchColorName[] = "gl_LastFragData[0]"; michael@0: GrGLCaps::FBFetchType fetchType = fGpu->glCaps().fbFetchType(); michael@0: if (GrGLCaps::kEXT_FBFetchType == fetchType) { michael@0: SkAssertResult(this->enablePrivateFeature(kEXTShaderFramebufferFetch_GLSLPrivateFeature)); michael@0: return kFBFetchColorName; michael@0: } else if (GrGLCaps::kNV_FBFetchType == fetchType) { michael@0: SkAssertResult(this->enablePrivateFeature(kNVShaderFramebufferFetch_GLSLPrivateFeature)); michael@0: return kFBFetchColorName; michael@0: } else if (fDstCopySamplerUniform.isValid()) { michael@0: return kDstCopyColorName; michael@0: } else { michael@0: return ""; michael@0: } michael@0: } michael@0: michael@0: void GrGLShaderBuilder::appendTextureLookup(SkString* out, michael@0: const GrGLShaderBuilder::TextureSampler& sampler, michael@0: const char* coordName, michael@0: GrSLType varyingType) const { michael@0: append_texture_lookup(out, michael@0: fGpu, michael@0: this->getUniformCStr(sampler.samplerUniform()), michael@0: coordName, michael@0: sampler.configComponentMask(), michael@0: sampler.swizzle(), michael@0: varyingType); michael@0: } michael@0: michael@0: void GrGLShaderBuilder::fsAppendTextureLookup(const GrGLShaderBuilder::TextureSampler& sampler, michael@0: const char* coordName, michael@0: GrSLType varyingType) { michael@0: this->appendTextureLookup(&fFSCode, sampler, coordName, varyingType); michael@0: } michael@0: michael@0: void GrGLShaderBuilder::fsAppendTextureLookupAndModulate( michael@0: const char* modulation, michael@0: const GrGLShaderBuilder::TextureSampler& sampler, michael@0: const char* coordName, michael@0: GrSLType varyingType) { michael@0: SkString lookup; michael@0: this->appendTextureLookup(&lookup, sampler, coordName, varyingType); michael@0: fFSCode.append((GrGLSLExpr4(modulation) * GrGLSLExpr4(lookup)).c_str()); michael@0: } michael@0: michael@0: GrGLShaderBuilder::DstReadKey GrGLShaderBuilder::KeyForDstRead(const GrTexture* dstCopy, michael@0: const GrGLCaps& caps) { michael@0: uint32_t key = kYesDstRead_DstReadKeyBit; michael@0: if (GrGLCaps::kNone_FBFetchType != caps.fbFetchType()) { michael@0: return key; michael@0: } michael@0: SkASSERT(NULL != dstCopy); michael@0: if (!caps.textureSwizzleSupport() && GrPixelConfigIsAlphaOnly(dstCopy->config())) { michael@0: // The fact that the config is alpha-only must be considered when generating code. michael@0: key |= kUseAlphaConfig_DstReadKeyBit; michael@0: } michael@0: if (kTopLeft_GrSurfaceOrigin == dstCopy->origin()) { michael@0: key |= kTopLeftOrigin_DstReadKeyBit; michael@0: } michael@0: SkASSERT(static_cast(key) == key); michael@0: return static_cast(key); michael@0: } michael@0: michael@0: GrGLShaderBuilder::FragPosKey GrGLShaderBuilder::KeyForFragmentPosition(const GrRenderTarget* dst, michael@0: const GrGLCaps&) { michael@0: if (kTopLeft_GrSurfaceOrigin == dst->origin()) { michael@0: return kTopLeftFragPosRead_FragPosKey; michael@0: } else { michael@0: return kBottomLeftFragPosRead_FragPosKey; michael@0: } michael@0: } michael@0: michael@0: michael@0: const GrGLenum* GrGLShaderBuilder::GetTexParamSwizzle(GrPixelConfig config, const GrGLCaps& caps) { michael@0: if (caps.textureSwizzleSupport() && GrPixelConfigIsAlphaOnly(config)) { michael@0: if (caps.textureRedSupport()) { michael@0: static const GrGLenum gRedSmear[] = { GR_GL_RED, GR_GL_RED, GR_GL_RED, GR_GL_RED }; michael@0: return gRedSmear; michael@0: } else { michael@0: static const GrGLenum gAlphaSmear[] = { GR_GL_ALPHA, GR_GL_ALPHA, michael@0: GR_GL_ALPHA, GR_GL_ALPHA }; michael@0: return gAlphaSmear; michael@0: } michael@0: } else { michael@0: static const GrGLenum gStraight[] = { GR_GL_RED, GR_GL_GREEN, GR_GL_BLUE, GR_GL_ALPHA }; michael@0: return gStraight; michael@0: } michael@0: } michael@0: michael@0: GrGLUniformManager::UniformHandle GrGLShaderBuilder::addUniformArray(uint32_t visibility, michael@0: GrSLType type, michael@0: const char* name, michael@0: int count, michael@0: const char** outName) { michael@0: SkASSERT(name && strlen(name)); michael@0: SkDEBUGCODE(static const uint32_t kVisibilityMask = kVertex_Visibility | kFragment_Visibility); michael@0: SkASSERT(0 == (~kVisibilityMask & visibility)); michael@0: SkASSERT(0 != visibility); michael@0: michael@0: BuilderUniform& uni = fUniforms.push_back(); michael@0: UniformHandle h = GrGLUniformManager::UniformHandle::CreateFromUniformIndex(fUniforms.count() - 1); michael@0: SkDEBUGCODE(UniformHandle h2 =) michael@0: fUniformManager.appendUniform(type, count); michael@0: // We expect the uniform manager to initially have no uniforms and that all uniforms are added michael@0: // by this function. Therefore, the handles should match. michael@0: SkASSERT(h2 == h); michael@0: uni.fVariable.setType(type); michael@0: uni.fVariable.setTypeModifier(GrGLShaderVar::kUniform_TypeModifier); michael@0: this->nameVariable(uni.fVariable.accessName(), 'u', name); michael@0: uni.fVariable.setArrayCount(count); michael@0: uni.fVisibility = visibility; michael@0: michael@0: // If it is visible in both the VS and FS, the precision must match. michael@0: // We declare a default FS precision, but not a default VS. So set the var michael@0: // to use the default FS precision. michael@0: if ((kVertex_Visibility | kFragment_Visibility) == visibility) { michael@0: // the fragment and vertex precisions must match michael@0: uni.fVariable.setPrecision(kDefaultFragmentPrecision); michael@0: } michael@0: michael@0: if (NULL != outName) { michael@0: *outName = uni.fVariable.c_str(); michael@0: } michael@0: michael@0: return h; michael@0: } michael@0: michael@0: SkString GrGLShaderBuilder::ensureFSCoords2D(const TransformedCoordsArray& coords, int index) { michael@0: if (kVec3f_GrSLType != coords[index].type()) { michael@0: SkASSERT(kVec2f_GrSLType == coords[index].type()); michael@0: return coords[index].getName(); michael@0: } michael@0: michael@0: SkString coords2D("coords2D"); michael@0: if (0 != index) { michael@0: coords2D.appendf("_%i", index); michael@0: } michael@0: this->fsCodeAppendf("\tvec2 %s = %s.xy / %s.z;", michael@0: coords2D.c_str(), coords[index].c_str(), coords[index].c_str()); michael@0: return coords2D; michael@0: } michael@0: michael@0: const char* GrGLShaderBuilder::fragmentPosition() { michael@0: if (fCodeStage.inStageCode()) { michael@0: const GrEffectRef& effect = *fCodeStage.effectStage()->getEffect(); michael@0: if (!effect->willReadFragmentPosition()) { michael@0: GrDebugCrash("GrGLEffect asked for frag position but its generating GrEffect " michael@0: "did not request access."); michael@0: return ""; michael@0: } michael@0: } michael@0: // We only declare "gl_FragCoord" when we're in the case where we want to use layout qualifiers michael@0: // to reverse y. Otherwise it isn't necessary and whether the "in" qualifier appears in the michael@0: // declaration varies in earlier GLSL specs. So it is simpler to omit it. michael@0: if (fTopLeftFragPosRead) { michael@0: fSetupFragPosition = true; michael@0: return "gl_FragCoord"; michael@0: } else if (fGpu->glCaps().fragCoordConventionsSupport()) { michael@0: if (!fSetupFragPosition) { michael@0: SkAssertResult(this->enablePrivateFeature(kFragCoordConventions_GLSLPrivateFeature)); michael@0: fFSInputs.push_back().set(kVec4f_GrSLType, michael@0: GrGLShaderVar::kIn_TypeModifier, michael@0: "gl_FragCoord", michael@0: GrGLShaderVar::kDefault_Precision, michael@0: GrGLShaderVar::kUpperLeft_Origin); michael@0: fSetupFragPosition = true; michael@0: } michael@0: return "gl_FragCoord"; michael@0: } else { michael@0: static const char* kCoordName = "fragCoordYDown"; michael@0: if (!fSetupFragPosition) { michael@0: // temporarily change the stage index because we're inserting non-stage code. michael@0: CodeStage::AutoStageRestore csar(&fCodeStage, NULL); michael@0: michael@0: SkASSERT(!fRTHeightUniform.isValid()); michael@0: const char* rtHeightName; michael@0: michael@0: fRTHeightUniform = this->addUniform(kFragment_Visibility, michael@0: kFloat_GrSLType, michael@0: "RTHeight", michael@0: &rtHeightName); michael@0: michael@0: this->fFSCode.prependf("\tvec4 %s = vec4(gl_FragCoord.x, %s - gl_FragCoord.y, gl_FragCoord.zw);\n", michael@0: kCoordName, rtHeightName); michael@0: fSetupFragPosition = true; michael@0: } michael@0: SkASSERT(fRTHeightUniform.isValid()); michael@0: return kCoordName; michael@0: } michael@0: } michael@0: michael@0: void GrGLShaderBuilder::fsEmitFunction(GrSLType returnType, michael@0: const char* name, michael@0: int argCnt, michael@0: const GrGLShaderVar* args, michael@0: const char* body, michael@0: SkString* outName) { michael@0: fFSFunctions.append(GrGLSLTypeString(returnType)); michael@0: this->nameVariable(outName, '\0', name); michael@0: fFSFunctions.appendf(" %s", outName->c_str()); michael@0: fFSFunctions.append("("); michael@0: for (int i = 0; i < argCnt; ++i) { michael@0: args[i].appendDecl(this->ctxInfo(), &fFSFunctions); michael@0: if (i < argCnt - 1) { michael@0: fFSFunctions.append(", "); michael@0: } michael@0: } michael@0: fFSFunctions.append(") {\n"); michael@0: fFSFunctions.append(body); michael@0: fFSFunctions.append("}\n\n"); michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: inline void append_default_precision_qualifier(GrGLShaderVar::Precision p, michael@0: GrGLStandard standard, michael@0: SkString* str) { michael@0: // Desktop GLSL has added precision qualifiers but they don't do anything. michael@0: if (kGLES_GrGLStandard == standard) { michael@0: switch (p) { michael@0: case GrGLShaderVar::kHigh_Precision: michael@0: str->append("precision highp float;\n"); michael@0: break; michael@0: case GrGLShaderVar::kMedium_Precision: michael@0: str->append("precision mediump float;\n"); michael@0: break; michael@0: case GrGLShaderVar::kLow_Precision: michael@0: str->append("precision lowp float;\n"); michael@0: break; michael@0: case GrGLShaderVar::kDefault_Precision: michael@0: GrCrash("Default precision now allowed."); michael@0: default: michael@0: GrCrash("Unknown precision value."); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void GrGLShaderBuilder::appendDecls(const VarArray& vars, SkString* out) const { michael@0: for (int i = 0; i < vars.count(); ++i) { michael@0: vars[i].appendDecl(this->ctxInfo(), out); michael@0: out->append(";\n"); michael@0: } michael@0: } michael@0: michael@0: void GrGLShaderBuilder::appendUniformDecls(ShaderVisibility visibility, michael@0: SkString* out) const { michael@0: for (int i = 0; i < fUniforms.count(); ++i) { michael@0: if (fUniforms[i].fVisibility & visibility) { michael@0: fUniforms[i].fVariable.appendDecl(this->ctxInfo(), out); michael@0: out->append(";\n"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void GrGLShaderBuilder::createAndEmitEffects(GrGLProgramEffectsBuilder* programEffectsBuilder, michael@0: const GrEffectStage* effectStages[], michael@0: const EffectKey effectKeys[], michael@0: int effectCnt, michael@0: GrGLSLExpr4* fsInOutColor) { michael@0: bool effectEmitted = false; michael@0: michael@0: GrGLSLExpr4 inColor = *fsInOutColor; michael@0: GrGLSLExpr4 outColor; michael@0: michael@0: for (int e = 0; e < effectCnt; ++e) { michael@0: SkASSERT(NULL != effectStages[e] && NULL != effectStages[e]->getEffect()); michael@0: const GrEffectStage& stage = *effectStages[e]; michael@0: michael@0: CodeStage::AutoStageRestore csar(&fCodeStage, &stage); michael@0: michael@0: if (inColor.isZeros()) { michael@0: SkString inColorName; michael@0: michael@0: // Effects have no way to communicate zeros, they treat an empty string as ones. michael@0: this->nameVariable(&inColorName, '\0', "input"); michael@0: this->fsCodeAppendf("\tvec4 %s = %s;\n", inColorName.c_str(), inColor.c_str()); michael@0: inColor = inColorName; michael@0: } michael@0: michael@0: // create var to hold stage result michael@0: SkString outColorName; michael@0: this->nameVariable(&outColorName, '\0', "output"); michael@0: this->fsCodeAppendf("\tvec4 %s;\n", outColorName.c_str()); michael@0: outColor = outColorName; michael@0: michael@0: michael@0: programEffectsBuilder->emitEffect(stage, michael@0: effectKeys[e], michael@0: outColor.c_str(), michael@0: inColor.isOnes() ? NULL : inColor.c_str(), michael@0: fCodeStage.stageIndex()); michael@0: michael@0: inColor = outColor; michael@0: effectEmitted = true; michael@0: } michael@0: michael@0: if (effectEmitted) { michael@0: *fsInOutColor = outColor; michael@0: } michael@0: } michael@0: michael@0: const char* GrGLShaderBuilder::getColorOutputName() const { michael@0: return fHasCustomColorOutput ? declared_color_output_name() : "gl_FragColor"; michael@0: } michael@0: michael@0: const char* GrGLShaderBuilder::enableSecondaryOutput() { michael@0: if (!fHasSecondaryOutput) { michael@0: fFSOutputs.push_back().set(kVec4f_GrSLType, michael@0: GrGLShaderVar::kOut_TypeModifier, michael@0: dual_source_output_name()); michael@0: fHasSecondaryOutput = true; michael@0: } michael@0: return dual_source_output_name(); michael@0: } michael@0: michael@0: bool GrGLShaderBuilder::finish(GrGLuint* outProgramId) { michael@0: SK_TRACE_EVENT0("GrGLShaderBuilder::finish"); michael@0: michael@0: GrGLuint programId = 0; michael@0: GL_CALL_RET(programId, CreateProgram()); michael@0: if (!programId) { michael@0: return false; michael@0: } michael@0: michael@0: SkTDArray shadersToDelete; michael@0: michael@0: if (!this->compileAndAttachShaders(programId, &shadersToDelete)) { michael@0: GL_CALL(DeleteProgram(programId)); michael@0: return false; michael@0: } michael@0: michael@0: this->bindProgramLocations(programId); michael@0: if (fUniformManager.isUsingBindUniform()) { michael@0: fUniformManager.getUniformLocations(programId, fUniforms); michael@0: } michael@0: michael@0: GL_CALL(LinkProgram(programId)); michael@0: michael@0: // Calling GetProgramiv is expensive in Chromium. Assume success in release builds. michael@0: bool checkLinked = !fGpu->ctxInfo().isChromium(); michael@0: #ifdef SK_DEBUG michael@0: checkLinked = true; michael@0: #endif michael@0: if (checkLinked) { michael@0: GrGLint linked = GR_GL_INIT_ZERO; michael@0: GL_CALL(GetProgramiv(programId, GR_GL_LINK_STATUS, &linked)); michael@0: if (!linked) { michael@0: GrGLint infoLen = GR_GL_INIT_ZERO; michael@0: GL_CALL(GetProgramiv(programId, GR_GL_INFO_LOG_LENGTH, &infoLen)); michael@0: SkAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger michael@0: if (infoLen > 0) { michael@0: // retrieve length even though we don't need it to workaround michael@0: // bug in chrome cmd buffer param validation. michael@0: GrGLsizei length = GR_GL_INIT_ZERO; michael@0: GL_CALL(GetProgramInfoLog(programId, michael@0: infoLen+1, michael@0: &length, michael@0: (char*)log.get())); michael@0: GrPrintf((char*)log.get()); michael@0: } michael@0: SkDEBUGFAIL("Error linking program"); michael@0: GL_CALL(DeleteProgram(programId)); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (!fUniformManager.isUsingBindUniform()) { michael@0: fUniformManager.getUniformLocations(programId, fUniforms); michael@0: } michael@0: michael@0: for (int i = 0; i < shadersToDelete.count(); ++i) { michael@0: GL_CALL(DeleteShader(shadersToDelete[i])); michael@0: } michael@0: michael@0: *outProgramId = programId; michael@0: return true; michael@0: } michael@0: michael@0: // Compiles a GL shader and attaches it to a program. Returns the shader ID if michael@0: // successful, or 0 if not. michael@0: static GrGLuint attach_shader(const GrGLContext& glCtx, michael@0: GrGLuint programId, michael@0: GrGLenum type, michael@0: const SkString& shaderSrc) { michael@0: const GrGLInterface* gli = glCtx.interface(); michael@0: michael@0: GrGLuint shaderId; michael@0: GR_GL_CALL_RET(gli, shaderId, CreateShader(type)); michael@0: if (0 == shaderId) { michael@0: return 0; michael@0: } michael@0: michael@0: const GrGLchar* sourceStr = shaderSrc.c_str(); michael@0: GrGLint sourceLength = static_cast(shaderSrc.size()); michael@0: GR_GL_CALL(gli, ShaderSource(shaderId, 1, &sourceStr, &sourceLength)); michael@0: GR_GL_CALL(gli, CompileShader(shaderId)); michael@0: michael@0: // Calling GetShaderiv in Chromium is quite expensive. Assume success in release builds. michael@0: bool checkCompiled = !glCtx.isChromium(); michael@0: #ifdef SK_DEBUG michael@0: checkCompiled = true; michael@0: #endif michael@0: if (checkCompiled) { michael@0: GrGLint compiled = GR_GL_INIT_ZERO; michael@0: GR_GL_CALL(gli, GetShaderiv(shaderId, GR_GL_COMPILE_STATUS, &compiled)); michael@0: michael@0: if (!compiled) { michael@0: GrGLint infoLen = GR_GL_INIT_ZERO; michael@0: GR_GL_CALL(gli, GetShaderiv(shaderId, GR_GL_INFO_LOG_LENGTH, &infoLen)); michael@0: SkAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger michael@0: if (infoLen > 0) { michael@0: // retrieve length even though we don't need it to workaround bug in Chromium cmd michael@0: // buffer param validation. michael@0: GrGLsizei length = GR_GL_INIT_ZERO; michael@0: GR_GL_CALL(gli, GetShaderInfoLog(shaderId, infoLen+1, michael@0: &length, (char*)log.get())); michael@0: GrPrintf(shaderSrc.c_str()); michael@0: GrPrintf("\n%s", log.get()); michael@0: } michael@0: SkDEBUGFAIL("Shader compilation failed!"); michael@0: GR_GL_CALL(gli, DeleteShader(shaderId)); michael@0: return 0; michael@0: } michael@0: } michael@0: if (c_PrintShaders) { michael@0: GrPrintf(shaderSrc.c_str()); michael@0: GrPrintf("\n"); michael@0: } michael@0: michael@0: // Attach the shader, but defer deletion until after we have linked the program. michael@0: // This works around a bug in the Android emulator's GLES2 wrapper which michael@0: // will immediately delete the shader object and free its memory even though it's michael@0: // attached to a program, which then causes glLinkProgram to fail. michael@0: GR_GL_CALL(gli, AttachShader(programId, shaderId)); michael@0: michael@0: return shaderId; michael@0: } michael@0: michael@0: bool GrGLShaderBuilder::compileAndAttachShaders(GrGLuint programId, SkTDArray* shaderIds) const { michael@0: SkString fragShaderSrc(GrGetGLSLVersionDecl(this->ctxInfo())); michael@0: fragShaderSrc.append(fFSExtensions); michael@0: append_default_precision_qualifier(kDefaultFragmentPrecision, michael@0: fGpu->glStandard(), michael@0: &fragShaderSrc); michael@0: this->appendUniformDecls(kFragment_Visibility, &fragShaderSrc); michael@0: this->appendDecls(fFSInputs, &fragShaderSrc); michael@0: // We shouldn't have declared outputs on 1.10 michael@0: SkASSERT(k110_GrGLSLGeneration != fGpu->glslGeneration() || fFSOutputs.empty()); michael@0: this->appendDecls(fFSOutputs, &fragShaderSrc); michael@0: fragShaderSrc.append(fFSFunctions); michael@0: fragShaderSrc.append("void main() {\n"); michael@0: fragShaderSrc.append(fFSCode); michael@0: fragShaderSrc.append("}\n"); michael@0: michael@0: GrGLuint fragShaderId = attach_shader(fGpu->glContext(), programId, GR_GL_FRAGMENT_SHADER, fragShaderSrc); michael@0: if (!fragShaderId) { michael@0: return false; michael@0: } michael@0: michael@0: *shaderIds->append() = fragShaderId; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void GrGLShaderBuilder::bindProgramLocations(GrGLuint programId) const { michael@0: if (fHasCustomColorOutput) { michael@0: GL_CALL(BindFragDataLocation(programId, 0, declared_color_output_name())); michael@0: } michael@0: if (fHasSecondaryOutput) { michael@0: GL_CALL(BindFragDataLocationIndexed(programId, 0, 1, dual_source_output_name())); michael@0: } michael@0: } michael@0: michael@0: const GrGLContextInfo& GrGLShaderBuilder::ctxInfo() const { michael@0: return fGpu->ctxInfo(); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: GrGLFullShaderBuilder::GrGLFullShaderBuilder(GrGpuGL* gpu, michael@0: GrGLUniformManager& uniformManager, michael@0: const GrGLProgramDesc& desc) michael@0: : INHERITED(gpu, uniformManager, desc) michael@0: , fDesc(desc) michael@0: , fVSAttrs(kVarsPerBlock) michael@0: , fVSOutputs(kVarsPerBlock) michael@0: , fGSInputs(kVarsPerBlock) michael@0: , fGSOutputs(kVarsPerBlock) { michael@0: michael@0: const GrGLProgramDesc::KeyHeader& header = fDesc.getHeader(); michael@0: michael@0: fPositionVar = &fVSAttrs.push_back(); michael@0: fPositionVar->set(kVec2f_GrSLType, GrGLShaderVar::kAttribute_TypeModifier, "aPosition"); michael@0: if (-1 != header.fLocalCoordAttributeIndex) { michael@0: fLocalCoordsVar = &fVSAttrs.push_back(); michael@0: fLocalCoordsVar->set(kVec2f_GrSLType, michael@0: GrGLShaderVar::kAttribute_TypeModifier, michael@0: "aLocalCoords"); michael@0: } else { michael@0: fLocalCoordsVar = fPositionVar; michael@0: } michael@0: michael@0: const char* viewMName; michael@0: fViewMatrixUniform = this->addUniform(GrGLShaderBuilder::kVertex_Visibility, michael@0: kMat33f_GrSLType, "ViewM", &viewMName); michael@0: michael@0: this->vsCodeAppendf("\tvec3 pos3 = %s * vec3(%s, 1);\n" michael@0: "\tgl_Position = vec4(pos3.xy, 0, pos3.z);\n", michael@0: viewMName, fPositionVar->c_str()); michael@0: michael@0: // we output point size in the GS if present michael@0: if (header.fEmitsPointSize michael@0: #if GR_GL_EXPERIMENTAL_GS michael@0: && !header.fExperimentalGS michael@0: #endif michael@0: ) { michael@0: this->vsCodeAppend("\tgl_PointSize = 1.0;\n"); michael@0: } michael@0: michael@0: if (GrGLProgramDesc::kAttribute_ColorInput == header.fColorInput) { michael@0: this->addAttribute(kVec4f_GrSLType, color_attribute_name()); michael@0: const char *vsName, *fsName; michael@0: this->addVarying(kVec4f_GrSLType, "Color", &vsName, &fsName); michael@0: this->vsCodeAppendf("\t%s = %s;\n", vsName, color_attribute_name()); michael@0: this->setInputColor(fsName); michael@0: } michael@0: michael@0: if (GrGLProgramDesc::kAttribute_ColorInput == header.fCoverageInput) { michael@0: this->addAttribute(kVec4f_GrSLType, coverage_attribute_name()); michael@0: const char *vsName, *fsName; michael@0: this->addVarying(kVec4f_GrSLType, "Coverage", &vsName, &fsName); michael@0: this->vsCodeAppendf("\t%s = %s;\n", vsName, coverage_attribute_name()); michael@0: this->setInputCoverage(fsName); michael@0: } michael@0: } michael@0: michael@0: bool GrGLFullShaderBuilder::addAttribute(GrSLType type, const char* name) { michael@0: for (int i = 0; i < fVSAttrs.count(); ++i) { michael@0: const GrGLShaderVar& attr = fVSAttrs[i]; michael@0: // if attribute already added, don't add it again michael@0: if (attr.getName().equals(name)) { michael@0: SkASSERT(attr.getType() == type); michael@0: return false; michael@0: } michael@0: } michael@0: fVSAttrs.push_back().set(type, michael@0: GrGLShaderVar::kAttribute_TypeModifier, michael@0: name); michael@0: return true; michael@0: } michael@0: michael@0: bool GrGLFullShaderBuilder::addEffectAttribute(int attributeIndex, michael@0: GrSLType type, michael@0: const SkString& name) { michael@0: if (!this->addAttribute(type, name.c_str())) { michael@0: return false; michael@0: } michael@0: michael@0: fEffectAttributes.push_back().set(attributeIndex, name); michael@0: return true; michael@0: } michael@0: michael@0: void GrGLFullShaderBuilder::addVarying(GrSLType type, michael@0: const char* name, michael@0: const char** vsOutName, michael@0: const char** fsInName) { michael@0: fVSOutputs.push_back(); michael@0: fVSOutputs.back().setType(type); michael@0: fVSOutputs.back().setTypeModifier(GrGLShaderVar::kVaryingOut_TypeModifier); michael@0: this->nameVariable(fVSOutputs.back().accessName(), 'v', name); michael@0: michael@0: if (vsOutName) { michael@0: *vsOutName = fVSOutputs.back().getName().c_str(); michael@0: } michael@0: // input to FS comes either from VS or GS michael@0: const SkString* fsName; michael@0: #if GR_GL_EXPERIMENTAL_GS michael@0: if (fDesc.getHeader().fExperimentalGS) { michael@0: // if we have a GS take each varying in as an array michael@0: // and output as non-array. michael@0: fGSInputs.push_back(); michael@0: fGSInputs.back().setType(type); michael@0: fGSInputs.back().setTypeModifier(GrGLShaderVar::kVaryingIn_TypeModifier); michael@0: fGSInputs.back().setUnsizedArray(); michael@0: *fGSInputs.back().accessName() = fVSOutputs.back().getName(); michael@0: fGSOutputs.push_back(); michael@0: fGSOutputs.back().setType(type); michael@0: fGSOutputs.back().setTypeModifier(GrGLShaderVar::kVaryingOut_TypeModifier); michael@0: this->nameVariable(fGSOutputs.back().accessName(), 'g', name); michael@0: fsName = fGSOutputs.back().accessName(); michael@0: } else michael@0: #endif michael@0: { michael@0: fsName = fVSOutputs.back().accessName(); michael@0: } michael@0: this->fsInputAppend().set(type, GrGLShaderVar::kVaryingIn_TypeModifier, *fsName); michael@0: if (fsInName) { michael@0: *fsInName = fsName->c_str(); michael@0: } michael@0: } michael@0: michael@0: const SkString* GrGLFullShaderBuilder::getEffectAttributeName(int attributeIndex) const { michael@0: const AttributePair* attribEnd = fEffectAttributes.end(); michael@0: for (const AttributePair* attrib = fEffectAttributes.begin(); attrib != attribEnd; ++attrib) { michael@0: if (attrib->fIndex == attributeIndex) { michael@0: return &attrib->fName; michael@0: } michael@0: } michael@0: michael@0: return NULL; michael@0: } michael@0: michael@0: GrGLProgramEffects* GrGLFullShaderBuilder::createAndEmitEffects( michael@0: const GrEffectStage* effectStages[], michael@0: const EffectKey effectKeys[], michael@0: int effectCnt, michael@0: GrGLSLExpr4* inOutFSColor) { michael@0: michael@0: GrGLVertexProgramEffectsBuilder programEffectsBuilder(this, effectCnt); michael@0: this->INHERITED::createAndEmitEffects(&programEffectsBuilder, michael@0: effectStages, michael@0: effectKeys, michael@0: effectCnt, michael@0: inOutFSColor); michael@0: return programEffectsBuilder.finish(); michael@0: } michael@0: michael@0: bool GrGLFullShaderBuilder::compileAndAttachShaders(GrGLuint programId, SkTDArray* shaderIds) const { michael@0: const GrGLContext& glCtx = this->gpu()->glContext(); michael@0: SkString vertShaderSrc(GrGetGLSLVersionDecl(this->ctxInfo())); michael@0: this->appendUniformDecls(kVertex_Visibility, &vertShaderSrc); michael@0: this->appendDecls(fVSAttrs, &vertShaderSrc); michael@0: this->appendDecls(fVSOutputs, &vertShaderSrc); michael@0: vertShaderSrc.append("void main() {\n"); michael@0: vertShaderSrc.append(fVSCode); michael@0: vertShaderSrc.append("}\n"); michael@0: GrGLuint vertShaderId = attach_shader(glCtx, programId, GR_GL_VERTEX_SHADER, vertShaderSrc); michael@0: if (!vertShaderId) { michael@0: return false; michael@0: } michael@0: *shaderIds->append() = vertShaderId; michael@0: michael@0: #if GR_GL_EXPERIMENTAL_GS michael@0: if (fDesc.getHeader().fExperimentalGS) { michael@0: SkASSERT(this->ctxInfo().glslGeneration() >= k150_GrGLSLGeneration); michael@0: SkString geomShaderSrc(GrGetGLSLVersionDecl(this->ctxInfo())); michael@0: geomShaderSrc.append("layout(triangles) in;\n" michael@0: "layout(triangle_strip, max_vertices = 6) out;\n"); michael@0: this->appendDecls(fGSInputs, &geomShaderSrc); michael@0: this->appendDecls(fGSOutputs, &geomShaderSrc); michael@0: geomShaderSrc.append("void main() {\n"); michael@0: geomShaderSrc.append("\tfor (int i = 0; i < 3; ++i) {\n" michael@0: "\t\tgl_Position = gl_in[i].gl_Position;\n"); michael@0: if (fDesc.getHeader().fEmitsPointSize) { michael@0: geomShaderSrc.append("\t\tgl_PointSize = 1.0;\n"); michael@0: } michael@0: SkASSERT(fGSInputs.count() == fGSOutputs.count()); michael@0: for (int i = 0; i < fGSInputs.count(); ++i) { michael@0: geomShaderSrc.appendf("\t\t%s = %s[i];\n", michael@0: fGSOutputs[i].getName().c_str(), michael@0: fGSInputs[i].getName().c_str()); michael@0: } michael@0: geomShaderSrc.append("\t\tEmitVertex();\n" michael@0: "\t}\n" michael@0: "\tEndPrimitive();\n"); michael@0: geomShaderSrc.append("}\n"); michael@0: GrGLuint geomShaderId = attach_shader(glCtx, programId, GR_GL_GEOMETRY_SHADER, geomShaderSrc); michael@0: if (!geomShaderId) { michael@0: return false; michael@0: } michael@0: *shaderIds->append() = geomShaderId; michael@0: } michael@0: #endif michael@0: michael@0: return this->INHERITED::compileAndAttachShaders(programId, shaderIds); michael@0: } michael@0: michael@0: void GrGLFullShaderBuilder::bindProgramLocations(GrGLuint programId) const { michael@0: this->INHERITED::bindProgramLocations(programId); michael@0: michael@0: const GrGLProgramDesc::KeyHeader& header = fDesc.getHeader(); michael@0: michael@0: // Bind the attrib locations to same values for all shaders michael@0: SkASSERT(-1 != header.fPositionAttributeIndex); michael@0: GL_CALL(BindAttribLocation(programId, michael@0: header.fPositionAttributeIndex, michael@0: fPositionVar->c_str())); michael@0: if (-1 != header.fLocalCoordAttributeIndex) { michael@0: GL_CALL(BindAttribLocation(programId, michael@0: header.fLocalCoordAttributeIndex, michael@0: fLocalCoordsVar->c_str())); michael@0: } michael@0: if (-1 != header.fColorAttributeIndex) { michael@0: GL_CALL(BindAttribLocation(programId, michael@0: header.fColorAttributeIndex, michael@0: color_attribute_name())); michael@0: } michael@0: if (-1 != header.fCoverageAttributeIndex) { michael@0: GL_CALL(BindAttribLocation(programId, michael@0: header.fCoverageAttributeIndex, michael@0: coverage_attribute_name())); michael@0: } michael@0: michael@0: const AttributePair* attribEnd = fEffectAttributes.end(); michael@0: for (const AttributePair* attrib = fEffectAttributes.begin(); attrib != attribEnd; ++attrib) { michael@0: GL_CALL(BindAttribLocation(programId, attrib->fIndex, attrib->fName.c_str())); michael@0: } michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: GrGLFragmentOnlyShaderBuilder::GrGLFragmentOnlyShaderBuilder(GrGpuGL* gpu, michael@0: GrGLUniformManager& uniformManager, michael@0: const GrGLProgramDesc& desc) michael@0: : INHERITED(gpu, uniformManager, desc) michael@0: , fNumTexCoordSets(0) { michael@0: michael@0: SkASSERT(!desc.getHeader().fHasVertexCode); michael@0: SkASSERT(gpu->glCaps().fixedFunctionSupport()); michael@0: SkASSERT(gpu->glCaps().pathRenderingSupport()); michael@0: SkASSERT(GrGLProgramDesc::kAttribute_ColorInput != desc.getHeader().fColorInput); michael@0: SkASSERT(GrGLProgramDesc::kAttribute_ColorInput != desc.getHeader().fCoverageInput); michael@0: } michael@0: michael@0: int GrGLFragmentOnlyShaderBuilder::addTexCoordSets(int count) { michael@0: int firstFreeCoordSet = fNumTexCoordSets; michael@0: fNumTexCoordSets += count; michael@0: SkASSERT(gpu()->glCaps().maxFixedFunctionTextureCoords() >= fNumTexCoordSets); michael@0: return firstFreeCoordSet; michael@0: } michael@0: michael@0: GrGLProgramEffects* GrGLFragmentOnlyShaderBuilder::createAndEmitEffects( michael@0: const GrEffectStage* effectStages[], michael@0: const EffectKey effectKeys[], michael@0: int effectCnt, michael@0: GrGLSLExpr4* inOutFSColor) { michael@0: michael@0: GrGLTexGenProgramEffectsBuilder texGenEffectsBuilder(this, effectCnt); michael@0: this->INHERITED::createAndEmitEffects(&texGenEffectsBuilder, michael@0: effectStages, michael@0: effectKeys, michael@0: effectCnt, michael@0: inOutFSColor); michael@0: return texGenEffectsBuilder.finish(); michael@0: }