1 /* 2 Copyright 2011 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 #include "GrGLProgram.h" 18 19 #include "GrBinHashKey.h" 20 #include "GrGLConfig.h" 21 #include "GrMemory.h" 22 23 #include "SkXfermode.h" 24 25 namespace { 26 27 const char* GrPrecision() { 28 if (GR_GL_SUPPORT_ES2) { 29 return "mediump"; 30 } else { 31 return " "; 32 } 33 } 34 35 const char* GrShaderPrecision() { 36 if (GR_GL_SUPPORT_ES2) { 37 return "precision mediump float;\n"; 38 } else { 39 return ""; 40 } 41 } 42 43 } // namespace 44 45 #define PRINT_SHADERS 0 46 47 #if GR_GL_ATTRIBUTE_MATRICES 48 #define VIEW_MATRIX_NAME "aViewM" 49 #else 50 #define VIEW_MATRIX_NAME "uViewM" 51 #endif 52 53 #define POS_ATTR_NAME "aPosition" 54 #define COL_ATTR_NAME "aColor" 55 #define COL_UNI_NAME "uColor" 56 #define EDGES_UNI_NAME "uEdges" 57 #define COL_FILTER_UNI_NAME "uColorFilter" 58 59 static inline void tex_attr_name(int coordIdx, GrStringBuilder* s) { 60 *s = "aTexCoord"; 61 s->appendS32(coordIdx); 62 } 63 64 static inline const char* float_vector_type(int count) { 65 static const char* FLOAT_VECS[] = {"ERROR", "float", "vec2", "vec3", "vec4"}; 66 GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(FLOAT_VECS)); 67 return FLOAT_VECS[count]; 68 } 69 70 static inline const char* vector_homog_coord(int count) { 71 static const char* HOMOGS[] = {"ERROR", "", ".y", ".z", ".w"}; 72 GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(HOMOGS)); 73 return HOMOGS[count]; 74 } 75 76 static inline const char* vector_nonhomog_coords(int count) { 77 static const char* NONHOMOGS[] = {"ERROR", "", ".x", ".xy", ".xyz"}; 78 GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(NONHOMOGS)); 79 return NONHOMOGS[count]; 80 } 81 82 static inline const char* vector_all_coords(int count) { 83 static const char* ALL[] = {"ERROR", "", ".xy", ".xyz", ".xyzw"}; 84 GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ALL)); 85 return ALL[count]; 86 } 87 88 static inline const char* all_ones_vec(int count) { 89 static const char* ONESVEC[] = {"ERROR", "1.0", "vec2(1,1)", 90 "vec3(1,1,1)", "vec4(1,1,1,1)"}; 91 GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ONESVEC)); 92 return ONESVEC[count]; 93 } 94 95 static inline const char* all_zeros_vec(int count) { 96 static const char* ZEROSVEC[] = {"ERROR", "0.0", "vec2(0,0)", 97 "vec3(0,0,0)", "vec4(0,0,0,0)"}; 98 GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ZEROSVEC)); 99 return ZEROSVEC[count]; 100 } 101 102 static inline const char* declared_color_output_name() { return "fsColorOut"; } 103 static inline const char* dual_source_output_name() { return "dualSourceOut"; } 104 105 static void tex_matrix_name(int stage, GrStringBuilder* s) { 106 #if GR_GL_ATTRIBUTE_MATRICES 107 *s = "aTexM"; 108 #else 109 *s = "uTexM"; 110 #endif 111 s->appendS32(stage); 112 } 113 114 static void normalized_texel_size_name(int stage, GrStringBuilder* s) { 115 *s = "uTexelSize"; 116 s->appendS32(stage); 117 } 118 119 static void sampler_name(int stage, GrStringBuilder* s) { 120 *s = "uSampler"; 121 s->appendS32(stage); 122 } 123 124 static void stage_varying_name(int stage, GrStringBuilder* s) { 125 *s = "vStage"; 126 s->appendS32(stage); 127 } 128 129 static void radial2_param_name(int stage, GrStringBuilder* s) { 130 *s = "uRadial2Params"; 131 s->appendS32(stage); 132 } 133 134 static void radial2_varying_name(int stage, GrStringBuilder* s) { 135 *s = "vB"; 136 s->appendS32(stage); 137 } 138 139 static void tex_domain_name(int stage, GrStringBuilder* s) { 140 *s = "uTexDom"; 141 s->appendS32(stage); 142 } 143 144 GrGLProgram::GrGLProgram() { 145 } 146 147 GrGLProgram::~GrGLProgram() { 148 } 149 150 void GrGLProgram::overrideBlend(GrBlendCoeff* srcCoeff, 151 GrBlendCoeff* dstCoeff) const { 152 switch (fProgramDesc.fDualSrcOutput) { 153 case ProgramDesc::kNone_DualSrcOutput: 154 break; 155 // the prog will write a coverage value to the secondary 156 // output and the dst is blended by one minus that value. 157 case ProgramDesc::kCoverage_DualSrcOutput: 158 case ProgramDesc::kCoverageISA_DualSrcOutput: 159 case ProgramDesc::kCoverageISC_DualSrcOutput: 160 *dstCoeff = (GrBlendCoeff)GrGpu::kIS2C_BlendCoeff; 161 break; 162 default: 163 GrCrash("Unexpected dual source blend output"); 164 break; 165 } 166 } 167 168 void GrGLProgram::buildKey(GrBinHashKeyBuilder& key) const { 169 // Add stage configuration to the key 170 key.keyData(reinterpret_cast<const uint32_t*>(&fProgramDesc), sizeof(ProgramDesc)); 171 } 172 173 // assigns modulation of two vars to an output var 174 // vars can be vec4s or floats (or one of each) 175 // result is always vec4 176 // if either var is "" then assign to the other var 177 // if both are "" then assign all ones 178 static inline void modulate_helper(const char* outputVar, 179 const char* var0, 180 const char* var1, 181 GrStringBuilder* code) { 182 GrAssert(NULL != outputVar); 183 GrAssert(NULL != var0); 184 GrAssert(NULL != var1); 185 GrAssert(NULL != code); 186 187 bool has0 = '\0' != *var0; 188 bool has1 = '\0' != *var1; 189 190 if (!has0 && !has1) { 191 code->appendf("\t%s = %s;\n", outputVar, all_ones_vec(4)); 192 } else if (!has0) { 193 code->appendf("\t%s = vec4(%s);\n", outputVar, var1); 194 } else if (!has1) { 195 code->appendf("\t%s = vec4(%s);\n", outputVar, var0); 196 } else { 197 code->appendf("\t%s = vec4(%s * %s);\n", outputVar, var0, var1); 198 } 199 } 200 201 // assigns addition of two vars to an output var 202 // vars can be vec4s or floats (or one of each) 203 // result is always vec4 204 // if either var is "" then assign to the other var 205 // if both are "" then assign all zeros 206 static inline void add_helper(const char* outputVar, 207 const char* var0, 208 const char* var1, 209 GrStringBuilder* code) { 210 GrAssert(NULL != outputVar); 211 GrAssert(NULL != var0); 212 GrAssert(NULL != var1); 213 GrAssert(NULL != code); 214 215 bool has0 = '\0' != *var0; 216 bool has1 = '\0' != *var1; 217 218 if (!has0 && !has1) { 219 code->appendf("\t%s = %s;\n", outputVar, all_zeros_vec(4)); 220 } else if (!has0) { 221 code->appendf("\t%s = vec4(%s);\n", outputVar, var1); 222 } else if (!has1) { 223 code->appendf("\t%s = vec4(%s);\n", outputVar, var0); 224 } else { 225 code->appendf("\t%s = vec4(%s + %s);\n", outputVar, var0, var1); 226 } 227 } 228 229 // given two blend coeffecients determine whether the src 230 // and/or dst computation can be omitted. 231 static inline void needBlendInputs(SkXfermode::Coeff srcCoeff, 232 SkXfermode::Coeff dstCoeff, 233 bool* needSrcValue, 234 bool* needDstValue) { 235 if (SkXfermode::kZero_Coeff == srcCoeff) { 236 switch (dstCoeff) { 237 // these all read the src 238 case SkXfermode::kSC_Coeff: 239 case SkXfermode::kISC_Coeff: 240 case SkXfermode::kSA_Coeff: 241 case SkXfermode::kISA_Coeff: 242 *needSrcValue = true; 243 break; 244 default: 245 *needSrcValue = false; 246 break; 247 } 248 } else { 249 *needSrcValue = true; 250 } 251 if (SkXfermode::kZero_Coeff == dstCoeff) { 252 switch (srcCoeff) { 253 // these all read the dst 254 case SkXfermode::kDC_Coeff: 255 case SkXfermode::kIDC_Coeff: 256 case SkXfermode::kDA_Coeff: 257 case SkXfermode::kIDA_Coeff: 258 *needDstValue = true; 259 break; 260 default: 261 *needDstValue = false; 262 break; 263 } 264 } else { 265 *needDstValue = true; 266 } 267 } 268 269 /** 270 * Create a blend_coeff * value string to be used in shader code. Sets empty 271 * string if result is trivially zero. 272 */ 273 static void blendTermString(GrStringBuilder* str, SkXfermode::Coeff coeff, 274 const char* src, const char* dst, 275 const char* value) { 276 switch (coeff) { 277 case SkXfermode::kZero_Coeff: /** 0 */ 278 *str = ""; 279 break; 280 case SkXfermode::kOne_Coeff: /** 1 */ 281 *str = value; 282 break; 283 case SkXfermode::kSC_Coeff: 284 str->printf("(%s * %s)", src, value); 285 break; 286 case SkXfermode::kISC_Coeff: 287 str->printf("((%s - %s) * %s)", all_ones_vec(4), src, value); 288 break; 289 case SkXfermode::kDC_Coeff: 290 str->printf("(%s * %s)", dst, value); 291 break; 292 case SkXfermode::kIDC_Coeff: 293 str->printf("((%s - %s) * %s)", all_ones_vec(4), dst, value); 294 break; 295 case SkXfermode::kSA_Coeff: /** src alpha */ 296 str->printf("(%s.a * %s)", src, value); 297 break; 298 case SkXfermode::kISA_Coeff: /** inverse src alpha (i.e. 1 - sa) */ 299 str->printf("((1.0 - %s.a) * %s)", src, value); 300 break; 301 case SkXfermode::kDA_Coeff: /** dst alpha */ 302 str->printf("(%s.a * %s)", dst, value); 303 break; 304 case SkXfermode::kIDA_Coeff: /** inverse dst alpha (i.e. 1 - da) */ 305 str->printf("((1.0 - %s.a) * %s)", dst, value); 306 break; 307 default: 308 GrCrash("Unexpected xfer coeff."); 309 break; 310 } 311 } 312 /** 313 * Adds a line to the fragment shader code which modifies the color by 314 * the specified color filter. 315 */ 316 static void addColorFilter(GrStringBuilder* fsCode, const char * outputVar, 317 SkXfermode::Coeff uniformCoeff, 318 SkXfermode::Coeff colorCoeff, 319 const char* inColor) { 320 GrStringBuilder colorStr, constStr; 321 blendTermString(&colorStr, colorCoeff, COL_FILTER_UNI_NAME, 322 inColor, inColor); 323 blendTermString(&constStr, uniformCoeff, COL_FILTER_UNI_NAME, 324 inColor, COL_FILTER_UNI_NAME); 325 326 add_helper(outputVar, colorStr.c_str(), constStr.c_str(), fsCode); 327 } 328 329 bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const { 330 331 ShaderCodeSegments segments; 332 const uint32_t& layout = fProgramDesc.fVertexLayout; 333 334 programData->fUniLocations.reset(); 335 336 SkXfermode::Coeff colorCoeff, uniformCoeff; 337 // The rest of transfer mode color filters have not been implemented 338 if (fProgramDesc.fColorFilterXfermode < SkXfermode::kCoeffModesCnt) { 339 GR_DEBUGCODE(bool success =) 340 SkXfermode::ModeAsCoeff(static_cast<SkXfermode::Mode> 341 (fProgramDesc.fColorFilterXfermode), 342 &uniformCoeff, &colorCoeff); 343 GR_DEBUGASSERT(success); 344 } else { 345 colorCoeff = SkXfermode::kOne_Coeff; 346 uniformCoeff = SkXfermode::kZero_Coeff; 347 } 348 349 bool needColorFilterUniform; 350 bool needComputedColor; 351 needBlendInputs(uniformCoeff, colorCoeff, 352 &needColorFilterUniform, &needComputedColor); 353 354 // the dual source output has no canonical var name, have to 355 // declare an output, which is incompatible with gl_FragColor/gl_FragData. 356 const char* fsColorOutput; 357 bool dualSourceOutputWritten = false; 358 bool usingDeclaredOutputs = ProgramDesc::kNone_DualSrcOutput != 359 fProgramDesc.fDualSrcOutput; 360 if (usingDeclaredOutputs) { 361 GrAssert(0 == segments.fHeader.size()); 362 segments.fHeader.printf("#version 150\n"); 363 fsColorOutput = declared_color_output_name(); 364 segments.fFSOutputs.appendf("out vec4 %s;\n", fsColorOutput); 365 } else { 366 fsColorOutput = "gl_FragColor"; 367 } 368 369 #if GR_GL_ATTRIBUTE_MATRICES 370 segments.fVSAttrs += "attribute mat3 " VIEW_MATRIX_NAME ";\n"; 371 programData->fUniLocations.fViewMatrixUni = kSetAsAttribute; 372 #else 373 segments.fVSUnis += "uniform mat3 " VIEW_MATRIX_NAME ";\n"; 374 programData->fUniLocations.fViewMatrixUni = kUseUniform; 375 #endif 376 segments.fVSAttrs += "attribute vec2 " POS_ATTR_NAME ";\n"; 377 378 segments.fVSCode.append( 379 "void main() {\n" 380 "\tvec3 pos3 = " VIEW_MATRIX_NAME " * vec3("POS_ATTR_NAME", 1);\n" 381 "\tgl_Position = vec4(pos3.xy, 0, pos3.z);\n"); 382 383 // incoming color to current stage being processed. 384 GrStringBuilder inColor; 385 386 if (needComputedColor) { 387 switch (fProgramDesc.fColorType) { 388 case ProgramDesc::kAttribute_ColorType: 389 segments.fVSAttrs.append( "attribute vec4 " COL_ATTR_NAME ";\n"); 390 segments.fVaryings.append("varying vec4 vColor;\n"); 391 segments.fVSCode.append( "\tvColor = " COL_ATTR_NAME ";\n"); 392 inColor = "vColor"; 393 break; 394 case ProgramDesc::kUniform_ColorType: 395 segments.fFSUnis.append( "uniform vec4 " COL_UNI_NAME ";\n"); 396 programData->fUniLocations.fColorUni = kUseUniform; 397 inColor = COL_UNI_NAME; 398 break; 399 default: 400 GrAssert(ProgramDesc::kNone_ColorType == fProgramDesc.fColorType); 401 break; 402 } 403 } 404 405 if (fProgramDesc.fEmitsPointSize){ 406 segments.fVSCode.append("\tgl_PointSize = 1.0;\n"); 407 } 408 409 segments.fFSCode.append("void main() {\n"); 410 411 // add texture coordinates that are used to the list of vertex attr decls 412 GrStringBuilder texCoordAttrs[GrDrawTarget::kMaxTexCoords]; 413 for (int t = 0; t < GrDrawTarget::kMaxTexCoords; ++t) { 414 if (GrDrawTarget::VertexUsesTexCoordIdx(t, layout)) { 415 tex_attr_name(t, texCoordAttrs + t); 416 segments.fVSAttrs.appendf("attribute vec2 %s;\n", texCoordAttrs[t].c_str()); 417 } 418 } 419 420 /////////////////////////////////////////////////////////////////////////// 421 // compute the final color 422 423 // if we have color stages string them together, feeding the output color 424 // of each to the next and generating code for each stage. 425 if (needComputedColor) { 426 GrStringBuilder outColor; 427 for (int s = 0; s < fProgramDesc.fFirstCoverageStage; ++s) { 428 if (fProgramDesc.fStages[s].isEnabled()) { 429 // create var to hold stage result 430 outColor = "color"; 431 outColor.appendS32(s); 432 segments.fFSCode.appendf("\tvec4 %s;\n", outColor.c_str()); 433 434 const char* inCoords; 435 // figure out what our input coords are 436 if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) & 437 layout) { 438 inCoords = POS_ATTR_NAME; 439 } else { 440 int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout); 441 // we better have input tex coordinates if stage is enabled. 442 GrAssert(tcIdx >= 0); 443 GrAssert(texCoordAttrs[tcIdx].size()); 444 inCoords = texCoordAttrs[tcIdx].c_str(); 445 } 446 447 genStageCode(s, 448 fProgramDesc.fStages[s], 449 inColor.size() ? inColor.c_str() : NULL, 450 outColor.c_str(), 451 inCoords, 452 &segments, 453 &programData->fUniLocations.fStages[s]); 454 inColor = outColor; 455 } 456 } 457 } 458 459 // if have all ones for the "dst" input to the color filter then we can make 460 // additional optimizations. 461 if (needColorFilterUniform && !inColor.size() && 462 (SkXfermode::kIDC_Coeff == uniformCoeff || 463 SkXfermode::kIDA_Coeff == uniformCoeff)) { 464 uniformCoeff = SkXfermode::kZero_Coeff; 465 bool bogus; 466 needBlendInputs(SkXfermode::kZero_Coeff, colorCoeff, 467 &needColorFilterUniform, &bogus); 468 } 469 if (needColorFilterUniform) { 470 segments.fFSUnis.append( "uniform vec4 " COL_FILTER_UNI_NAME ";\n"); 471 programData->fUniLocations.fColorFilterUni = kUseUniform; 472 } 473 474 bool wroteFragColorZero = false; 475 if (SkXfermode::kZero_Coeff == uniformCoeff && 476 SkXfermode::kZero_Coeff == colorCoeff) { 477 segments.fFSCode.appendf("\t%s = %s;\n", 478 fsColorOutput, 479 all_zeros_vec(4)); 480 wroteFragColorZero = true; 481 } else if (SkXfermode::kDst_Mode != fProgramDesc.fColorFilterXfermode) { 482 segments.fFSCode.appendf("\tvec4 filteredColor;\n"); 483 const char* color = inColor.size() ? inColor.c_str() : all_ones_vec(4); 484 addColorFilter(&segments.fFSCode, "filteredColor", uniformCoeff, 485 colorCoeff, color); 486 inColor = "filteredColor"; 487 } 488 489 /////////////////////////////////////////////////////////////////////////// 490 // compute the partial coverage (coverage stages and edge aa) 491 492 GrStringBuilder inCoverage; 493 494 // we don't need to compute coverage at all if we know the final shader 495 // output will be zero and we don't have a dual src blend output. 496 if (!wroteFragColorZero || 497 ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) { 498 if (fProgramDesc.fEdgeAANumEdges > 0) { 499 segments.fFSUnis.append("uniform vec3 " EDGES_UNI_NAME "["); 500 segments.fFSUnis.appendS32(fProgramDesc.fEdgeAANumEdges); 501 segments.fFSUnis.append("];\n"); 502 programData->fUniLocations.fEdgesUni = kUseUniform; 503 int count = fProgramDesc.fEdgeAANumEdges; 504 segments.fFSCode.append( 505 "\tvec3 pos = vec3(gl_FragCoord.xy, 1);\n"); 506 for (int i = 0; i < count; i++) { 507 segments.fFSCode.append("\tfloat a"); 508 segments.fFSCode.appendS32(i); 509 segments.fFSCode.append(" = clamp(dot(" EDGES_UNI_NAME "["); 510 segments.fFSCode.appendS32(i); 511 segments.fFSCode.append("], pos), 0.0, 1.0);\n"); 512 } 513 segments.fFSCode.append("\tfloat edgeAlpha = "); 514 for (int i = 0; i < count - 1; i++) { 515 segments.fFSCode.append("min(a"); 516 segments.fFSCode.appendS32(i); 517 segments.fFSCode.append(" * a"); 518 segments.fFSCode.appendS32(i + 1); 519 segments.fFSCode.append(", "); 520 } 521 segments.fFSCode.append("a"); 522 segments.fFSCode.appendS32(count - 1); 523 segments.fFSCode.append(" * a0"); 524 for (int i = 0; i < count - 1; i++) { 525 segments.fFSCode.append(")"); 526 } 527 segments.fFSCode.append(";\n"); 528 inCoverage = "edgeAlpha"; 529 } 530 531 GrStringBuilder outCoverage; 532 const int& startStage = fProgramDesc.fFirstCoverageStage; 533 for (int s = startStage; s < GrDrawTarget::kNumStages; ++s) { 534 if (fProgramDesc.fStages[s].isEnabled()) { 535 // create var to hold stage output 536 outCoverage = "coverage"; 537 outCoverage.appendS32(s); 538 segments.fFSCode.appendf("\tvec4 %s;\n", outCoverage.c_str()); 539 540 const char* inCoords; 541 // figure out what our input coords are 542 if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) & layout) { 543 inCoords = POS_ATTR_NAME; 544 } else { 545 int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout); 546 // we better have input tex coordinates if stage is enabled. 547 GrAssert(tcIdx >= 0); 548 GrAssert(texCoordAttrs[tcIdx].size()); 549 inCoords = texCoordAttrs[tcIdx].c_str(); 550 } 551 552 genStageCode(s, 553 fProgramDesc.fStages[s], 554 inCoverage.size() ? inCoverage.c_str() : NULL, 555 outCoverage.c_str(), 556 inCoords, 557 &segments, 558 &programData->fUniLocations.fStages[s]); 559 inCoverage = outCoverage; 560 } 561 } 562 if (ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) { 563 segments.fFSOutputs.appendf("out vec4 %s;\n", 564 dual_source_output_name()); 565 bool outputIsZero = false; 566 GrStringBuilder coeff; 567 if (ProgramDesc::kCoverage_DualSrcOutput != 568 fProgramDesc.fDualSrcOutput && !wroteFragColorZero) { 569 if (!inColor.size()) { 570 outputIsZero = true; 571 } else { 572 if (fProgramDesc.fDualSrcOutput == 573 ProgramDesc::kCoverageISA_DualSrcOutput) { 574 coeff.printf("(1 - %s.a)", inColor.c_str()); 575 } else { 576 coeff.printf("(vec4(1,1,1,1) - %s)", inColor.c_str()); 577 } 578 } 579 } 580 if (outputIsZero) { 581 segments.fFSCode.appendf("\t%s = %s;\n", 582 dual_source_output_name(), 583 all_zeros_vec(4)); 584 } else { 585 modulate_helper(dual_source_output_name(), 586 coeff.c_str(), 587 inCoverage.c_str(), 588 &segments.fFSCode); 589 } 590 dualSourceOutputWritten = true; 591 } 592 } 593 594 /////////////////////////////////////////////////////////////////////////// 595 // combine color and coverage as frag color 596 597 if (!wroteFragColorZero) { 598 modulate_helper(fsColorOutput, 599 inColor.c_str(), 600 inCoverage.c_str(), 601 &segments.fFSCode); 602 } 603 604 segments.fVSCode.append("}\n"); 605 segments.fFSCode.append("}\n"); 606 607 /////////////////////////////////////////////////////////////////////////// 608 // compile and setup attribs and unis 609 610 if (!CompileFSAndVS(segments, programData)) { 611 return false; 612 } 613 614 if (!this->bindOutputsAttribsAndLinkProgram(texCoordAttrs, 615 usingDeclaredOutputs, 616 dualSourceOutputWritten, 617 programData)) { 618 return false; 619 } 620 621 this->getUniformLocationsAndInitCache(programData); 622 623 return true; 624 } 625 626 bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments, 627 CachedData* programData) { 628 629 static const int MAX_STRINGS = 6; 630 const char* strings[MAX_STRINGS]; 631 int lengths[MAX_STRINGS]; 632 int stringCnt = 0; 633 634 if (segments.fHeader.size()) { 635 strings[stringCnt] = segments.fHeader.c_str(); 636 lengths[stringCnt] = segments.fHeader.size(); 637 ++stringCnt; 638 } 639 if (segments.fVSUnis.size()) { 640 strings[stringCnt] = segments.fVSUnis.c_str(); 641 lengths[stringCnt] = segments.fVSUnis.size(); 642 ++stringCnt; 643 } 644 if (segments.fVSAttrs.size()) { 645 strings[stringCnt] = segments.fVSAttrs.c_str(); 646 lengths[stringCnt] = segments.fVSAttrs.size(); 647 ++stringCnt; 648 } 649 if (segments.fVaryings.size()) { 650 strings[stringCnt] = segments.fVaryings.c_str(); 651 lengths[stringCnt] = segments.fVaryings.size(); 652 ++stringCnt; 653 } 654 655 GrAssert(segments.fVSCode.size()); 656 strings[stringCnt] = segments.fVSCode.c_str(); 657 lengths[stringCnt] = segments.fVSCode.size(); 658 ++stringCnt; 659 660 #if PRINT_SHADERS 661 GrPrintf(segments.fHeader.c_str()); 662 GrPrintf(segments.fVSUnis.c_str()); 663 GrPrintf(segments.fVSAttrs.c_str()); 664 GrPrintf(segments.fVaryings.c_str()); 665 GrPrintf(segments.fVSCode.c_str()); 666 GrPrintf("\n"); 667 #endif 668 GrAssert(stringCnt <= MAX_STRINGS); 669 programData->fVShaderID = CompileShader(GR_GL_VERTEX_SHADER, 670 stringCnt, 671 strings, 672 lengths); 673 674 if (!programData->fVShaderID) { 675 return false; 676 } 677 678 stringCnt = 0; 679 680 if (segments.fHeader.size()) { 681 strings[stringCnt] = segments.fHeader.c_str(); 682 lengths[stringCnt] = segments.fHeader.size(); 683 ++stringCnt; 684 } 685 if (strlen(GrShaderPrecision()) > 1) { 686 strings[stringCnt] = GrShaderPrecision(); 687 lengths[stringCnt] = strlen(GrShaderPrecision()); 688 ++stringCnt; 689 } 690 if (segments.fFSUnis.size()) { 691 strings[stringCnt] = segments.fFSUnis.c_str(); 692 lengths[stringCnt] = segments.fFSUnis.size(); 693 ++stringCnt; 694 } 695 if (segments.fVaryings.size()) { 696 strings[stringCnt] = segments.fVaryings.c_str(); 697 lengths[stringCnt] = segments.fVaryings.size(); 698 ++stringCnt; 699 } 700 if (segments.fFSOutputs.size()) { 701 strings[stringCnt] = segments.fFSOutputs.c_str(); 702 lengths[stringCnt] = segments.fFSOutputs.size(); 703 ++stringCnt; 704 } 705 706 GrAssert(segments.fFSCode.size()); 707 strings[stringCnt] = segments.fFSCode.c_str(); 708 lengths[stringCnt] = segments.fFSCode.size(); 709 ++stringCnt; 710 711 #if PRINT_SHADERS 712 GrPrintf(segments.fHeader.c_str()); 713 GrPrintf(GrShaderPrecision()); 714 GrPrintf(segments.fFSUnis.c_str()); 715 GrPrintf(segments.fVaryings.c_str()); 716 GrPrintf(segments.fFSOutputs.c_str()); 717 GrPrintf(segments.fFSCode.c_str()); 718 GrPrintf("\n"); 719 #endif 720 GrAssert(stringCnt <= MAX_STRINGS); 721 programData->fFShaderID = CompileShader(GR_GL_FRAGMENT_SHADER, 722 stringCnt, 723 strings, 724 lengths); 725 726 if (!programData->fFShaderID) { 727 return false; 728 } 729 730 return true; 731 } 732 733 GrGLuint GrGLProgram::CompileShader(GrGLenum type, 734 int stringCnt, 735 const char** strings, 736 int* stringLengths) { 737 GrGLuint shader = GR_GL(CreateShader(type)); 738 if (0 == shader) { 739 return 0; 740 } 741 742 GrGLint compiled = GR_GL_INIT_ZERO; 743 GR_GL(ShaderSource(shader, stringCnt, strings, stringLengths)); 744 GR_GL(CompileShader(shader)); 745 GR_GL(GetShaderiv(shader, GR_GL_COMPILE_STATUS, &compiled)); 746 747 if (!compiled) { 748 GrGLint infoLen = GR_GL_INIT_ZERO; 749 GR_GL(GetShaderiv(shader, GR_GL_INFO_LOG_LENGTH, &infoLen)); 750 GrAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger 751 if (infoLen > 0) { 752 GR_GL(GetShaderInfoLog(shader, infoLen+1, NULL, (char*)log.get())); 753 for (int i = 0; i < stringCnt; ++i) { 754 if (NULL == stringLengths || stringLengths[i] < 0) { 755 GrPrintf(strings[i]); 756 } else { 757 GrPrintf("%.*s", stringLengths[i], strings[i]); 758 } 759 } 760 GrPrintf("\n%s", log.get()); 761 } 762 GrAssert(!"Shader compilation failed!"); 763 GR_GL(DeleteShader(shader)); 764 return 0; 765 } 766 return shader; 767 } 768 769 bool GrGLProgram::bindOutputsAttribsAndLinkProgram( 770 GrStringBuilder texCoordAttrNames[], 771 bool bindColorOut, 772 bool bindDualSrcOut, 773 CachedData* programData) const { 774 programData->fProgramID = GR_GL(CreateProgram()); 775 if (!programData->fProgramID) { 776 return false; 777 } 778 const GrGLint& progID = programData->fProgramID; 779 780 GR_GL(AttachShader(progID, programData->fVShaderID)); 781 GR_GL(AttachShader(progID, programData->fFShaderID)); 782 783 if (bindColorOut) { 784 GR_GL(BindFragDataLocationIndexed(programData->fProgramID, 785 0, 0, declared_color_output_name())); 786 } 787 if (bindDualSrcOut) { 788 GR_GL(BindFragDataLocationIndexed(programData->fProgramID, 789 0, 1, dual_source_output_name())); 790 } 791 792 // Bind the attrib locations to same values for all shaders 793 GR_GL(BindAttribLocation(progID, PositionAttributeIdx(), POS_ATTR_NAME)); 794 for (int t = 0; t < GrDrawTarget::kMaxTexCoords; ++t) { 795 if (texCoordAttrNames[t].size()) { 796 GR_GL(BindAttribLocation(progID, 797 TexCoordAttributeIdx(t), 798 texCoordAttrNames[t].c_str())); 799 } 800 } 801 802 if (kSetAsAttribute == programData->fUniLocations.fViewMatrixUni) { 803 GR_GL(BindAttribLocation(progID, 804 ViewMatrixAttributeIdx(), 805 VIEW_MATRIX_NAME)); 806 } 807 808 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) { 809 const StageUniLocations& unis = programData->fUniLocations.fStages[s]; 810 if (kSetAsAttribute == unis.fTextureMatrixUni) { 811 GrStringBuilder matName; 812 tex_matrix_name(s, &matName); 813 GR_GL(BindAttribLocation(progID, 814 TextureMatrixAttributeIdx(s), 815 matName.c_str())); 816 } 817 } 818 819 GR_GL(BindAttribLocation(progID, ColorAttributeIdx(), COL_ATTR_NAME)); 820 821 GR_GL(LinkProgram(progID)); 822 823 GrGLint linked = GR_GL_INIT_ZERO; 824 GR_GL(GetProgramiv(progID, GR_GL_LINK_STATUS, &linked)); 825 if (!linked) { 826 GrGLint infoLen = GR_GL_INIT_ZERO; 827 GR_GL(GetProgramiv(progID, GR_GL_INFO_LOG_LENGTH, &infoLen)); 828 GrAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger 829 if (infoLen > 0) { 830 GR_GL(GetProgramInfoLog(progID, 831 infoLen+1, 832 NULL, 833 (char*)log.get())); 834 GrPrintf((char*)log.get()); 835 } 836 GrAssert(!"Error linking program"); 837 GR_GL(DeleteProgram(progID)); 838 programData->fProgramID = 0; 839 return false; 840 } 841 return true; 842 } 843 844 void GrGLProgram::getUniformLocationsAndInitCache(CachedData* programData) const { 845 const GrGLint& progID = programData->fProgramID; 846 847 if (kUseUniform == programData->fUniLocations.fViewMatrixUni) { 848 programData->fUniLocations.fViewMatrixUni = 849 GR_GL(GetUniformLocation(progID, VIEW_MATRIX_NAME)); 850 GrAssert(kUnusedUniform != programData->fUniLocations.fViewMatrixUni); 851 } 852 if (kUseUniform == programData->fUniLocations.fColorUni) { 853 programData->fUniLocations.fColorUni = 854 GR_GL(GetUniformLocation(progID, COL_UNI_NAME)); 855 GrAssert(kUnusedUniform != programData->fUniLocations.fColorUni); 856 } 857 if (kUseUniform == programData->fUniLocations.fColorFilterUni) { 858 programData->fUniLocations.fColorFilterUni = 859 GR_GL(GetUniformLocation(progID, COL_FILTER_UNI_NAME)); 860 GrAssert(kUnusedUniform != programData->fUniLocations.fColorFilterUni); 861 } 862 863 if (kUseUniform == programData->fUniLocations.fEdgesUni) { 864 programData->fUniLocations.fEdgesUni = 865 GR_GL(GetUniformLocation(progID, EDGES_UNI_NAME)); 866 GrAssert(kUnusedUniform != programData->fUniLocations.fEdgesUni); 867 } else { 868 programData->fUniLocations.fEdgesUni = kUnusedUniform; 869 } 870 871 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) { 872 StageUniLocations& locations = programData->fUniLocations.fStages[s]; 873 if (fProgramDesc.fStages[s].isEnabled()) { 874 if (kUseUniform == locations.fTextureMatrixUni) { 875 GrStringBuilder texMName; 876 tex_matrix_name(s, &texMName); 877 locations.fTextureMatrixUni = GR_GL(GetUniformLocation( 878 progID, 879 texMName.c_str())); 880 GrAssert(kUnusedUniform != locations.fTextureMatrixUni); 881 } 882 883 if (kUseUniform == locations.fSamplerUni) { 884 GrStringBuilder samplerName; 885 sampler_name(s, &samplerName); 886 locations.fSamplerUni = GR_GL(GetUniformLocation( 887 progID, 888 samplerName.c_str())); 889 GrAssert(kUnusedUniform != locations.fSamplerUni); 890 } 891 892 if (kUseUniform == locations.fNormalizedTexelSizeUni) { 893 GrStringBuilder texelSizeName; 894 normalized_texel_size_name(s, &texelSizeName); 895 locations.fNormalizedTexelSizeUni = 896 GR_GL(GetUniformLocation(progID, texelSizeName.c_str())); 897 GrAssert(kUnusedUniform != locations.fNormalizedTexelSizeUni); 898 } 899 900 if (kUseUniform == locations.fRadial2Uni) { 901 GrStringBuilder radial2ParamName; 902 radial2_param_name(s, &radial2ParamName); 903 locations.fRadial2Uni = GR_GL(GetUniformLocation( 904 progID, 905 radial2ParamName.c_str())); 906 GrAssert(kUnusedUniform != locations.fRadial2Uni); 907 } 908 909 if (kUseUniform == locations.fTexDomUni) { 910 GrStringBuilder texDomName; 911 tex_domain_name(s, &texDomName); 912 locations.fTexDomUni = GR_GL(GetUniformLocation( 913 progID, 914 texDomName.c_str())); 915 GrAssert(kUnusedUniform != locations.fTexDomUni); 916 } 917 } 918 } 919 GR_GL(UseProgram(progID)); 920 921 // init sampler unis and set bogus values for state tracking 922 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) { 923 if (kUnusedUniform != programData->fUniLocations.fStages[s].fSamplerUni) { 924 GR_GL(Uniform1i(programData->fUniLocations.fStages[s].fSamplerUni, s)); 925 } 926 programData->fTextureMatrices[s] = GrMatrix::InvalidMatrix(); 927 programData->fRadial2CenterX1[s] = GR_ScalarMax; 928 programData->fRadial2Radius0[s] = -GR_ScalarMax; 929 programData->fTextureWidth[s] = -1; 930 programData->fTextureHeight[s] = -1; 931 } 932 programData->fViewMatrix = GrMatrix::InvalidMatrix(); 933 programData->fColor = GrColor_ILLEGAL; 934 programData->fColorFilterColor = GrColor_ILLEGAL; 935 } 936 937 //============================================================================ 938 // Stage code generation 939 //============================================================================ 940 941 void GrGLProgram::genStageCode(int stageNum, 942 const GrGLProgram::ProgramDesc::StageDesc& desc, 943 const char* fsInColor, // NULL means no incoming color 944 const char* fsOutColor, 945 const char* vsInCoord, 946 ShaderCodeSegments* segments, 947 StageUniLocations* locations) const { 948 949 GrAssert(stageNum >= 0 && stageNum <= 9); 950 951 GrStringBuilder varyingName; 952 stage_varying_name(stageNum, &varyingName); 953 954 // First decide how many coords are needed to access the texture 955 // Right now it's always 2 but we could start using 1D textures for 956 // gradients. 957 static const int coordDims = 2; 958 int varyingDims; 959 /// Vertex Shader Stuff 960 961 // decide whether we need a matrix to transform texture coords 962 // and whether the varying needs a perspective coord. 963 GrStringBuilder texMName; 964 tex_matrix_name(stageNum, &texMName); 965 if (desc.fOptFlags & ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit) { 966 varyingDims = coordDims; 967 } else { 968 #if GR_GL_ATTRIBUTE_MATRICES 969 segments->fVSAttrs.appendf("attribute mat3 %s;\n", texMName.c_str()); 970 locations->fTextureMatrixUni = kSetAsAttribute; 971 #else 972 segments->fVSUnis.appendf("uniform mat3 %s;\n", texMName.c_str()); 973 locations->fTextureMatrixUni = kUseUniform; 974 #endif 975 if (desc.fOptFlags & ProgramDesc::StageDesc::kNoPerspective_OptFlagBit) { 976 varyingDims = coordDims; 977 } else { 978 varyingDims = coordDims + 1; 979 } 980 } 981 982 GrStringBuilder samplerName; 983 sampler_name(stageNum, &samplerName); 984 segments->fFSUnis.appendf("uniform sampler2D %s;\n", samplerName.c_str()); 985 locations->fSamplerUni = kUseUniform; 986 987 GrStringBuilder texelSizeName; 988 if (ProgramDesc::StageDesc::k2x2_FetchMode == desc.fFetchMode) { 989 normalized_texel_size_name(stageNum, &texelSizeName); 990 segments->fFSUnis.appendf("uniform vec2 %s;\n", texelSizeName.c_str()); 991 } 992 993 segments->fVaryings.appendf("varying %s %s;\n", 994 float_vector_type(varyingDims), varyingName.c_str()); 995 996 if (desc.fOptFlags & ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit) { 997 GrAssert(varyingDims == coordDims); 998 segments->fVSCode.appendf("\t%s = %s;\n", varyingName.c_str(), vsInCoord); 999 } else { 1000 // varying = texMatrix * texCoord 1001 segments->fVSCode.appendf("\t%s = (%s * vec3(%s, 1))%s;\n", 1002 varyingName.c_str(), texMName.c_str(), 1003 vsInCoord, vector_all_coords(varyingDims)); 1004 } 1005 1006 GrStringBuilder radial2ParamsName; 1007 radial2_param_name(stageNum, &radial2ParamsName); 1008 // for radial grads without perspective we can pass the linear 1009 // part of the quadratic as a varying. 1010 GrStringBuilder radial2VaryingName; 1011 radial2_varying_name(stageNum, &radial2VaryingName); 1012 1013 if (ProgramDesc::StageDesc::kRadial2Gradient_CoordMapping == desc.fCoordMapping) { 1014 1015 segments->fVSUnis.appendf("uniform %s float %s[6];\n", 1016 GrPrecision(), radial2ParamsName.c_str()); 1017 segments->fFSUnis.appendf("uniform float %s[6];\n", 1018 radial2ParamsName.c_str()); 1019 locations->fRadial2Uni = kUseUniform; 1020 1021 // if there is perspective we don't interpolate this 1022 if (varyingDims == coordDims) { 1023 GrAssert(2 == coordDims); 1024 segments->fVaryings.appendf("varying float %s;\n", radial2VaryingName.c_str()); 1025 1026 // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3]) 1027 segments->fVSCode.appendf("\t%s = 2.0 *(%s[2] * %s.x - %s[3]);\n", 1028 radial2VaryingName.c_str(), radial2ParamsName.c_str(), 1029 varyingName.c_str(), radial2ParamsName.c_str()); 1030 } 1031 } 1032 1033 /// Fragment Shader Stuff 1034 GrStringBuilder fsCoordName; 1035 // function used to access the shader, may be made projective 1036 GrStringBuilder texFunc("texture2D"); 1037 if (desc.fOptFlags & (ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit | 1038 ProgramDesc::StageDesc::kNoPerspective_OptFlagBit)) { 1039 GrAssert(varyingDims == coordDims); 1040 fsCoordName = varyingName; 1041 } else { 1042 // if we have to do some special op on the varyings to get 1043 // our final tex coords then when in perspective we have to 1044 // do an explicit divide. Otherwise, we can use a Proj func. 1045 if (ProgramDesc::StageDesc::kIdentity_CoordMapping == desc.fCoordMapping && 1046 ProgramDesc::StageDesc::kSingle_FetchMode == desc.fFetchMode) { 1047 texFunc.append("Proj"); 1048 fsCoordName = varyingName; 1049 } else { 1050 fsCoordName = "inCoord"; 1051 fsCoordName.appendS32(stageNum); 1052 segments->fFSCode.appendf("\t%s %s = %s%s / %s%s;\n", 1053 float_vector_type(coordDims), 1054 fsCoordName.c_str(), 1055 varyingName.c_str(), 1056 vector_nonhomog_coords(varyingDims), 1057 varyingName.c_str(), 1058 vector_homog_coord(varyingDims)); 1059 } 1060 } 1061 1062 GrStringBuilder sampleCoords; 1063 bool complexCoord = false; 1064 switch (desc.fCoordMapping) { 1065 case ProgramDesc::StageDesc::kIdentity_CoordMapping: 1066 sampleCoords = fsCoordName; 1067 break; 1068 case ProgramDesc::StageDesc::kSweepGradient_CoordMapping: 1069 sampleCoords.printf("vec2(atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5, 0.5)", fsCoordName.c_str(), fsCoordName.c_str()); 1070 complexCoord = true; 1071 break; 1072 case ProgramDesc::StageDesc::kRadialGradient_CoordMapping: 1073 sampleCoords.printf("vec2(length(%s.xy), 0.5)", fsCoordName.c_str()); 1074 complexCoord = true; 1075 break; 1076 case ProgramDesc::StageDesc::kRadial2Gradient_CoordMapping: { 1077 GrStringBuilder cName("c"); 1078 GrStringBuilder ac4Name("ac4"); 1079 GrStringBuilder rootName("root"); 1080 1081 cName.appendS32(stageNum); 1082 ac4Name.appendS32(stageNum); 1083 rootName.appendS32(stageNum); 1084 1085 // if we were able to interpolate the linear component bVar is the varying 1086 // otherwise compute it 1087 GrStringBuilder bVar; 1088 if (coordDims == varyingDims) { 1089 bVar = radial2VaryingName; 1090 GrAssert(2 == varyingDims); 1091 } else { 1092 GrAssert(3 == varyingDims); 1093 bVar = "b"; 1094 bVar.appendS32(stageNum); 1095 segments->fFSCode.appendf("\tfloat %s = 2.0 * (%s[2] * %s.x - %s[3]);\n", 1096 bVar.c_str(), radial2ParamsName.c_str(), 1097 fsCoordName.c_str(), radial2ParamsName.c_str()); 1098 } 1099 1100 // c = (x^2)+(y^2) - params[4] 1101 segments->fFSCode.appendf("\tfloat %s = dot(%s, %s) - %s[4];\n", 1102 cName.c_str(), fsCoordName.c_str(), 1103 fsCoordName.c_str(), 1104 radial2ParamsName.c_str()); 1105 // ac4 = 4.0 * params[0] * c 1106 segments->fFSCode.appendf("\tfloat %s = %s[0] * 4.0 * %s;\n", 1107 ac4Name.c_str(), radial2ParamsName.c_str(), 1108 cName.c_str()); 1109 1110 // root = sqrt(b^2-4ac) 1111 // (abs to avoid exception due to fp precision) 1112 segments->fFSCode.appendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n", 1113 rootName.c_str(), bVar.c_str(), bVar.c_str(), 1114 ac4Name.c_str()); 1115 1116 // x coord is: (-b + params[5] * sqrt(b^2-4ac)) * params[1] 1117 // y coord is 0.5 (texture is effectively 1D) 1118 sampleCoords.printf("vec2((-%s + %s[5] * %s) * %s[1], 0.5)", 1119 bVar.c_str(), radial2ParamsName.c_str(), 1120 rootName.c_str(), radial2ParamsName.c_str()); 1121 complexCoord = true; 1122 break;} 1123 }; 1124 1125 const char* smear; 1126 if (desc.fModulation == ProgramDesc::StageDesc::kAlpha_Modulation) { 1127 smear = ".aaaa"; 1128 } else { 1129 smear = ""; 1130 } 1131 GrStringBuilder modulate; 1132 if (NULL != fsInColor) { 1133 modulate.printf(" * %s", fsInColor); 1134 } 1135 1136 if (desc.fOptFlags & 1137 ProgramDesc::StageDesc::kCustomTextureDomain_OptFlagBit) { 1138 GrStringBuilder texDomainName; 1139 tex_domain_name(stageNum, &texDomainName); 1140 segments->fFSUnis.appendf("uniform %s %s;\n", 1141 float_vector_type(4), 1142 texDomainName.c_str()); 1143 GrStringBuilder coordVar("clampCoord"); 1144 segments->fFSCode.appendf("\t%s %s = clamp(%s, %s.xy, %s.zw);\n", 1145 float_vector_type(coordDims), 1146 coordVar.c_str(), 1147 sampleCoords.c_str(), 1148 texDomainName.c_str(), 1149 texDomainName.c_str()); 1150 sampleCoords = coordVar; 1151 locations->fTexDomUni = kUseUniform; 1152 } 1153 1154 if (ProgramDesc::StageDesc::k2x2_FetchMode == desc.fFetchMode) { 1155 locations->fNormalizedTexelSizeUni = kUseUniform; 1156 if (complexCoord) { 1157 // assign the coord to a var rather than compute 4x. 1158 GrStringBuilder coordVar("tCoord"); 1159 coordVar.appendS32(stageNum); 1160 segments->fFSCode.appendf("\t%s %s = %s;\n", 1161 float_vector_type(coordDims), 1162 coordVar.c_str(), sampleCoords.c_str()); 1163 sampleCoords = coordVar; 1164 } 1165 GrAssert(2 == coordDims); 1166 GrStringBuilder accumVar("accum"); 1167 accumVar.appendS32(stageNum); 1168 segments->fFSCode.appendf("\tvec4 %s = %s(%s, %s + vec2(-%s.x,-%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), texelSizeName.c_str(), texelSizeName.c_str(), smear); 1169 segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(+%s.x,-%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), texelSizeName.c_str(), texelSizeName.c_str(), smear); 1170 segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(-%s.x,+%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), texelSizeName.c_str(), texelSizeName.c_str(), smear); 1171 segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(+%s.x,+%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), texelSizeName.c_str(), texelSizeName.c_str(), smear); 1172 segments->fFSCode.appendf("\t%s = .25 * %s%s;\n", fsOutColor, accumVar.c_str(), modulate.c_str()); 1173 } else { 1174 segments->fFSCode.appendf("\t%s = %s(%s, %s)%s%s;\n", fsOutColor, texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), smear, modulate.c_str()); 1175 } 1176 } 1177