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
25namespace {
26
27const char* GrPrecision() {
28    if (GR_GL_SUPPORT_ES2) {
29        return "mediump";
30    } else {
31        return " ";
32    }
33}
34
35const 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
59static inline void tex_attr_name(int coordIdx, GrStringBuilder* s) {
60    *s = "aTexCoord";
61    s->appendS32(coordIdx);
62}
63
64static 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
70static 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
76static 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
82static 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
88static 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
95static 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
102static inline const char* declared_color_output_name() { return "fsColorOut"; }
103static inline const char* dual_source_output_name() { return "dualSourceOut"; }
104
105static 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
114static void normalized_texel_size_name(int stage, GrStringBuilder* s) {
115    *s = "uTexelSize";
116    s->appendS32(stage);
117}
118
119static void sampler_name(int stage, GrStringBuilder* s) {
120    *s = "uSampler";
121    s->appendS32(stage);
122}
123
124static void stage_varying_name(int stage, GrStringBuilder* s) {
125    *s = "vStage";
126    s->appendS32(stage);
127}
128
129static void radial2_param_name(int stage, GrStringBuilder* s) {
130    *s = "uRadial2Params";
131    s->appendS32(stage);
132}
133
134static void radial2_varying_name(int stage, GrStringBuilder* s) {
135    *s = "vB";
136    s->appendS32(stage);
137}
138
139static void tex_domain_name(int stage, GrStringBuilder* s) {
140    *s = "uTexDom";
141    s->appendS32(stage);
142}
143
144GrGLProgram::GrGLProgram() {
145}
146
147GrGLProgram::~GrGLProgram() {
148}
149
150void 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
168void 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
178static 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
206static 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.
231static 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 */
273static 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 */
316static 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
329bool 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
626bool 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
733GrGLuint 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
769bool 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
844void 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
941void 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