blob: 319995c0bfefe47bb0af4219d6a5e17101631e6e [file] [log] [blame]
junov@google.comf93e7172011-03-31 21:26:24 +00001/*
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"
junov@google.comf93e7172011-03-31 21:26:24 +000021#include "GrMemory.h"
junov@google.comf93e7172011-03-31 21:26:24 +000022
Scroggo97c88c22011-05-11 14:05:25 +000023#include "SkXfermode.h"
tomhudson@google.com278cbb42011-06-30 19:37:01 +000024#include SK_USER_TRACE_INCLUDE_FILE
Scroggo97c88c22011-05-11 14:05:25 +000025
junov@google.comf93e7172011-03-31 21:26:24 +000026namespace {
27
28const char* GrPrecision() {
29 if (GR_GL_SUPPORT_ES2) {
30 return "mediump";
31 } else {
bsalomon@google.com91961302011-05-09 18:39:58 +000032 return " ";
junov@google.comf93e7172011-03-31 21:26:24 +000033 }
34}
35
36const char* GrShaderPrecision() {
37 if (GR_GL_SUPPORT_ES2) {
38 return "precision mediump float;\n";
39 } else {
40 return "";
41 }
42}
43
44} // namespace
45
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +000046#define PRINT_SHADERS 0
47
bsalomon@google.com4be283f2011-04-19 21:15:09 +000048#if GR_GL_ATTRIBUTE_MATRICES
junov@google.comf93e7172011-03-31 21:26:24 +000049 #define VIEW_MATRIX_NAME "aViewM"
50#else
51 #define VIEW_MATRIX_NAME "uViewM"
52#endif
53
54#define POS_ATTR_NAME "aPosition"
55#define COL_ATTR_NAME "aColor"
bsalomon@google.com4be283f2011-04-19 21:15:09 +000056#define COL_UNI_NAME "uColor"
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +000057#define EDGES_UNI_NAME "uEdges"
Scroggo97c88c22011-05-11 14:05:25 +000058#define COL_FILTER_UNI_NAME "uColorFilter"
junov@google.comf93e7172011-03-31 21:26:24 +000059
junov@google.comf93e7172011-03-31 21:26:24 +000060static inline void tex_attr_name(int coordIdx, GrStringBuilder* s) {
61 *s = "aTexCoord";
bsalomon@google.comfc296292011-05-06 13:53:47 +000062 s->appendS32(coordIdx);
junov@google.comf93e7172011-03-31 21:26:24 +000063}
64
65static inline const char* float_vector_type(int count) {
66 static const char* FLOAT_VECS[] = {"ERROR", "float", "vec2", "vec3", "vec4"};
67 GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(FLOAT_VECS));
68 return FLOAT_VECS[count];
69}
70
71static inline const char* vector_homog_coord(int count) {
72 static const char* HOMOGS[] = {"ERROR", "", ".y", ".z", ".w"};
73 GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(HOMOGS));
74 return HOMOGS[count];
75}
76
77static inline const char* vector_nonhomog_coords(int count) {
78 static const char* NONHOMOGS[] = {"ERROR", "", ".x", ".xy", ".xyz"};
79 GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(NONHOMOGS));
80 return NONHOMOGS[count];
81}
82
83static inline const char* vector_all_coords(int count) {
84 static const char* ALL[] = {"ERROR", "", ".xy", ".xyz", ".xyzw"};
85 GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ALL));
86 return ALL[count];
87}
88
bsalomon@google.comf2d91552011-05-16 20:56:06 +000089static inline const char* all_ones_vec(int count) {
tomhudson@google.com0d831722011-06-02 15:37:14 +000090 static const char* ONESVEC[] = {"ERROR", "1.0", "vec2(1,1)",
bsalomon@google.comf2d91552011-05-16 20:56:06 +000091 "vec3(1,1,1)", "vec4(1,1,1,1)"};
92 GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ONESVEC));
93 return ONESVEC[count];
94}
95
96static inline const char* all_zeros_vec(int count) {
tomhudson@google.com0d831722011-06-02 15:37:14 +000097 static const char* ZEROSVEC[] = {"ERROR", "0.0", "vec2(0,0)",
bsalomon@google.comf2d91552011-05-16 20:56:06 +000098 "vec3(0,0,0)", "vec4(0,0,0,0)"};
99 GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ZEROSVEC));
100 return ZEROSVEC[count];
101}
102
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000103static inline const char* declared_color_output_name() { return "fsColorOut"; }
104static inline const char* dual_source_output_name() { return "dualSourceOut"; }
105
junov@google.comf93e7172011-03-31 21:26:24 +0000106static void tex_matrix_name(int stage, GrStringBuilder* s) {
bsalomon@google.com4be283f2011-04-19 21:15:09 +0000107#if GR_GL_ATTRIBUTE_MATRICES
junov@google.comf93e7172011-03-31 21:26:24 +0000108 *s = "aTexM";
109#else
110 *s = "uTexM";
111#endif
bsalomon@google.comfc296292011-05-06 13:53:47 +0000112 s->appendS32(stage);
junov@google.comf93e7172011-03-31 21:26:24 +0000113}
114
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000115static void normalized_texel_size_name(int stage, GrStringBuilder* s) {
116 *s = "uTexelSize";
bsalomon@google.comfc296292011-05-06 13:53:47 +0000117 s->appendS32(stage);
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000118}
119
junov@google.comf93e7172011-03-31 21:26:24 +0000120static void sampler_name(int stage, GrStringBuilder* s) {
121 *s = "uSampler";
bsalomon@google.comfc296292011-05-06 13:53:47 +0000122 s->appendS32(stage);
junov@google.comf93e7172011-03-31 21:26:24 +0000123}
124
125static void stage_varying_name(int stage, GrStringBuilder* s) {
126 *s = "vStage";
bsalomon@google.comfc296292011-05-06 13:53:47 +0000127 s->appendS32(stage);
junov@google.comf93e7172011-03-31 21:26:24 +0000128}
129
130static void radial2_param_name(int stage, GrStringBuilder* s) {
131 *s = "uRadial2Params";
bsalomon@google.comfc296292011-05-06 13:53:47 +0000132 s->appendS32(stage);
junov@google.comf93e7172011-03-31 21:26:24 +0000133}
134
135static void radial2_varying_name(int stage, GrStringBuilder* s) {
136 *s = "vB";
bsalomon@google.comfc296292011-05-06 13:53:47 +0000137 s->appendS32(stage);
junov@google.comf93e7172011-03-31 21:26:24 +0000138}
139
junov@google.com6acc9b32011-05-16 18:32:07 +0000140static void tex_domain_name(int stage, GrStringBuilder* s) {
141 *s = "uTexDom";
142 s->appendS32(stage);
143}
144
junov@google.comf93e7172011-03-31 21:26:24 +0000145GrGLProgram::GrGLProgram() {
junov@google.comf93e7172011-03-31 21:26:24 +0000146}
147
148GrGLProgram::~GrGLProgram() {
junov@google.comf93e7172011-03-31 21:26:24 +0000149}
150
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +0000151void GrGLProgram::overrideBlend(GrBlendCoeff* srcCoeff,
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000152 GrBlendCoeff* dstCoeff) const {
153 switch (fProgramDesc.fDualSrcOutput) {
154 case ProgramDesc::kNone_DualSrcOutput:
155 break;
156 // the prog will write a coverage value to the secondary
157 // output and the dst is blended by one minus that value.
158 case ProgramDesc::kCoverage_DualSrcOutput:
159 case ProgramDesc::kCoverageISA_DualSrcOutput:
160 case ProgramDesc::kCoverageISC_DualSrcOutput:
161 *dstCoeff = (GrBlendCoeff)GrGpu::kIS2C_BlendCoeff;
162 break;
163 default:
164 GrCrash("Unexpected dual source blend output");
165 break;
166 }
167}
168
junov@google.comf93e7172011-03-31 21:26:24 +0000169void GrGLProgram::buildKey(GrBinHashKeyBuilder& key) const {
170 // Add stage configuration to the key
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +0000171 key.keyData(reinterpret_cast<const uint32_t*>(&fProgramDesc), sizeof(ProgramDesc));
junov@google.comf93e7172011-03-31 21:26:24 +0000172}
173
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000174// assigns modulation of two vars to an output var
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000175// vars can be vec4s or floats (or one of each)
176// result is always vec4
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000177// if either var is "" then assign to the other var
178// if both are "" then assign all ones
179static inline void modulate_helper(const char* outputVar,
180 const char* var0,
181 const char* var1,
182 GrStringBuilder* code) {
183 GrAssert(NULL != outputVar);
184 GrAssert(NULL != var0);
185 GrAssert(NULL != var1);
186 GrAssert(NULL != code);
187
188 bool has0 = '\0' != *var0;
189 bool has1 = '\0' != *var1;
190
191 if (!has0 && !has1) {
192 code->appendf("\t%s = %s;\n", outputVar, all_ones_vec(4));
193 } else if (!has0) {
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000194 code->appendf("\t%s = vec4(%s);\n", outputVar, var1);
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000195 } else if (!has1) {
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000196 code->appendf("\t%s = vec4(%s);\n", outputVar, var0);
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000197 } else {
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000198 code->appendf("\t%s = vec4(%s * %s);\n", outputVar, var0, var1);
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000199 }
200}
201
202// assigns addition of two vars to an output var
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000203// vars can be vec4s or floats (or one of each)
204// result is always vec4
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000205// if either var is "" then assign to the other var
206// if both are "" then assign all zeros
207static inline void add_helper(const char* outputVar,
208 const char* var0,
209 const char* var1,
210 GrStringBuilder* code) {
211 GrAssert(NULL != outputVar);
212 GrAssert(NULL != var0);
213 GrAssert(NULL != var1);
214 GrAssert(NULL != code);
215
216 bool has0 = '\0' != *var0;
217 bool has1 = '\0' != *var1;
218
219 if (!has0 && !has1) {
220 code->appendf("\t%s = %s;\n", outputVar, all_zeros_vec(4));
221 } else if (!has0) {
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000222 code->appendf("\t%s = vec4(%s);\n", outputVar, var1);
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000223 } else if (!has1) {
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000224 code->appendf("\t%s = vec4(%s);\n", outputVar, var0);
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000225 } else {
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000226 code->appendf("\t%s = vec4(%s + %s);\n", outputVar, var0, var1);
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000227 }
228}
229
230// given two blend coeffecients determine whether the src
231// and/or dst computation can be omitted.
232static inline void needBlendInputs(SkXfermode::Coeff srcCoeff,
233 SkXfermode::Coeff dstCoeff,
234 bool* needSrcValue,
235 bool* needDstValue) {
236 if (SkXfermode::kZero_Coeff == srcCoeff) {
237 switch (dstCoeff) {
238 // these all read the src
239 case SkXfermode::kSC_Coeff:
240 case SkXfermode::kISC_Coeff:
241 case SkXfermode::kSA_Coeff:
242 case SkXfermode::kISA_Coeff:
243 *needSrcValue = true;
244 break;
245 default:
246 *needSrcValue = false;
247 break;
248 }
249 } else {
250 *needSrcValue = true;
251 }
252 if (SkXfermode::kZero_Coeff == dstCoeff) {
253 switch (srcCoeff) {
254 // these all read the dst
255 case SkXfermode::kDC_Coeff:
256 case SkXfermode::kIDC_Coeff:
257 case SkXfermode::kDA_Coeff:
258 case SkXfermode::kIDA_Coeff:
259 *needDstValue = true;
260 break;
261 default:
262 *needDstValue = false;
263 break;
264 }
265 } else {
266 *needDstValue = true;
Scroggo97c88c22011-05-11 14:05:25 +0000267 }
268}
269
270/**
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000271 * Create a blend_coeff * value string to be used in shader code. Sets empty
272 * string if result is trivially zero.
273 */
274static void blendTermString(GrStringBuilder* str, SkXfermode::Coeff coeff,
275 const char* src, const char* dst,
276 const char* value) {
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000277 switch (coeff) {
278 case SkXfermode::kZero_Coeff: /** 0 */
279 *str = "";
280 break;
281 case SkXfermode::kOne_Coeff: /** 1 */
282 *str = value;
283 break;
284 case SkXfermode::kSC_Coeff:
285 str->printf("(%s * %s)", src, value);
286 break;
287 case SkXfermode::kISC_Coeff:
288 str->printf("((%s - %s) * %s)", all_ones_vec(4), src, value);
289 break;
290 case SkXfermode::kDC_Coeff:
291 str->printf("(%s * %s)", dst, value);
292 break;
293 case SkXfermode::kIDC_Coeff:
294 str->printf("((%s - %s) * %s)", all_ones_vec(4), dst, value);
295 break;
296 case SkXfermode::kSA_Coeff: /** src alpha */
297 str->printf("(%s.a * %s)", src, value);
298 break;
299 case SkXfermode::kISA_Coeff: /** inverse src alpha (i.e. 1 - sa) */
300 str->printf("((1.0 - %s.a) * %s)", src, value);
301 break;
302 case SkXfermode::kDA_Coeff: /** dst alpha */
303 str->printf("(%s.a * %s)", dst, value);
304 break;
305 case SkXfermode::kIDA_Coeff: /** inverse dst alpha (i.e. 1 - da) */
306 str->printf("((1.0 - %s.a) * %s)", dst, value);
307 break;
308 default:
309 GrCrash("Unexpected xfer coeff.");
310 break;
311 }
312}
313/**
Scroggo97c88c22011-05-11 14:05:25 +0000314 * Adds a line to the fragment shader code which modifies the color by
315 * the specified color filter.
316 */
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000317static void addColorFilter(GrStringBuilder* fsCode, const char * outputVar,
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +0000318 SkXfermode::Coeff uniformCoeff,
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000319 SkXfermode::Coeff colorCoeff,
320 const char* inColor) {
321 GrStringBuilder colorStr, constStr;
322 blendTermString(&colorStr, colorCoeff, COL_FILTER_UNI_NAME,
323 inColor, inColor);
324 blendTermString(&constStr, uniformCoeff, COL_FILTER_UNI_NAME,
325 inColor, COL_FILTER_UNI_NAME);
326
327 add_helper(outputVar, colorStr.c_str(), constStr.c_str(), fsCode);
Scroggo97c88c22011-05-11 14:05:25 +0000328}
329
bsalomon@google.com91961302011-05-09 18:39:58 +0000330bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
junov@google.comf93e7172011-03-31 21:26:24 +0000331
332 ShaderCodeSegments segments;
333 const uint32_t& layout = fProgramDesc.fVertexLayout;
334
bsalomon@google.com91961302011-05-09 18:39:58 +0000335 programData->fUniLocations.reset();
junov@google.comf93e7172011-03-31 21:26:24 +0000336
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000337 SkXfermode::Coeff colorCoeff, uniformCoeff;
338 // The rest of transfer mode color filters have not been implemented
339 if (fProgramDesc.fColorFilterXfermode < SkXfermode::kCoeffModesCnt) {
340 GR_DEBUGCODE(bool success =)
tomhudson@google.com0d831722011-06-02 15:37:14 +0000341 SkXfermode::ModeAsCoeff(static_cast<SkXfermode::Mode>
342 (fProgramDesc.fColorFilterXfermode),
343 &uniformCoeff, &colorCoeff);
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000344 GR_DEBUGASSERT(success);
345 } else {
346 colorCoeff = SkXfermode::kOne_Coeff;
347 uniformCoeff = SkXfermode::kZero_Coeff;
348 }
349
350 bool needColorFilterUniform;
351 bool needComputedColor;
352 needBlendInputs(uniformCoeff, colorCoeff,
353 &needColorFilterUniform, &needComputedColor);
354
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000355 // the dual source output has no canonical var name, have to
356 // declare an output, which is incompatible with gl_FragColor/gl_FragData.
357 const char* fsColorOutput;
358 bool dualSourceOutputWritten = false;
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +0000359 bool usingDeclaredOutputs = ProgramDesc::kNone_DualSrcOutput !=
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000360 fProgramDesc.fDualSrcOutput;
361 if (usingDeclaredOutputs) {
362 GrAssert(0 == segments.fHeader.size());
363 segments.fHeader.printf("#version 150\n");
364 fsColorOutput = declared_color_output_name();
365 segments.fFSOutputs.appendf("out vec4 %s;\n", fsColorOutput);
366 } else {
367 fsColorOutput = "gl_FragColor";
368 }
369
bsalomon@google.com4be283f2011-04-19 21:15:09 +0000370#if GR_GL_ATTRIBUTE_MATRICES
371 segments.fVSAttrs += "attribute mat3 " VIEW_MATRIX_NAME ";\n";
bsalomon@google.com91961302011-05-09 18:39:58 +0000372 programData->fUniLocations.fViewMatrixUni = kSetAsAttribute;
junov@google.comf93e7172011-03-31 21:26:24 +0000373#else
bsalomon@google.com4be283f2011-04-19 21:15:09 +0000374 segments.fVSUnis += "uniform mat3 " VIEW_MATRIX_NAME ";\n";
bsalomon@google.com91961302011-05-09 18:39:58 +0000375 programData->fUniLocations.fViewMatrixUni = kUseUniform;
junov@google.comf93e7172011-03-31 21:26:24 +0000376#endif
377 segments.fVSAttrs += "attribute vec2 " POS_ATTR_NAME ";\n";
junov@google.comf93e7172011-03-31 21:26:24 +0000378
bsalomon@google.com91961302011-05-09 18:39:58 +0000379 segments.fVSCode.append(
380 "void main() {\n"
381 "\tvec3 pos3 = " VIEW_MATRIX_NAME " * vec3("POS_ATTR_NAME", 1);\n"
382 "\tgl_Position = vec4(pos3.xy, 0, pos3.z);\n");
bsalomon@google.com4be283f2011-04-19 21:15:09 +0000383
bsalomon@google.com91961302011-05-09 18:39:58 +0000384 // incoming color to current stage being processed.
385 GrStringBuilder inColor;
bsalomon@google.com4be283f2011-04-19 21:15:09 +0000386
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000387 if (needComputedColor) {
388 switch (fProgramDesc.fColorType) {
389 case ProgramDesc::kAttribute_ColorType:
390 segments.fVSAttrs.append( "attribute vec4 " COL_ATTR_NAME ";\n");
391 segments.fVaryings.append("varying vec4 vColor;\n");
392 segments.fVSCode.append( "\tvColor = " COL_ATTR_NAME ";\n");
393 inColor = "vColor";
394 break;
395 case ProgramDesc::kUniform_ColorType:
396 segments.fFSUnis.append( "uniform vec4 " COL_UNI_NAME ";\n");
397 programData->fUniLocations.fColorUni = kUseUniform;
398 inColor = COL_UNI_NAME;
399 break;
400 default:
401 GrAssert(ProgramDesc::kNone_ColorType == fProgramDesc.fColorType);
402 break;
403 }
junov@google.comf93e7172011-03-31 21:26:24 +0000404 }
405
bsalomon@google.com4be283f2011-04-19 21:15:09 +0000406 if (fProgramDesc.fEmitsPointSize){
bsalomon@google.com91961302011-05-09 18:39:58 +0000407 segments.fVSCode.append("\tgl_PointSize = 1.0;\n");
junov@google.comf93e7172011-03-31 21:26:24 +0000408 }
bsalomon@google.com4be283f2011-04-19 21:15:09 +0000409
bsalomon@google.com91961302011-05-09 18:39:58 +0000410 segments.fFSCode.append("void main() {\n");
junov@google.comf93e7172011-03-31 21:26:24 +0000411
412 // add texture coordinates that are used to the list of vertex attr decls
bsalomon@google.com91961302011-05-09 18:39:58 +0000413 GrStringBuilder texCoordAttrs[GrDrawTarget::kMaxTexCoords];
junov@google.comf93e7172011-03-31 21:26:24 +0000414 for (int t = 0; t < GrDrawTarget::kMaxTexCoords; ++t) {
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000415 if (GrDrawTarget::VertexUsesTexCoordIdx(t, layout)) {
junov@google.comf93e7172011-03-31 21:26:24 +0000416 tex_attr_name(t, texCoordAttrs + t);
bsalomon@google.com91961302011-05-09 18:39:58 +0000417 segments.fVSAttrs.appendf("attribute vec2 %s;\n", texCoordAttrs[t].c_str());
junov@google.comf93e7172011-03-31 21:26:24 +0000418 }
419 }
420
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000421 ///////////////////////////////////////////////////////////////////////////
422 // compute the final color
Scroggo97c88c22011-05-11 14:05:25 +0000423
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000424 // if we have color stages string them together, feeding the output color
425 // of each to the next and generating code for each stage.
426 if (needComputedColor) {
427 GrStringBuilder outColor;
428 for (int s = 0; s < fProgramDesc.fFirstCoverageStage; ++s) {
tomhudson@google.com0d831722011-06-02 15:37:14 +0000429 if (fProgramDesc.fStages[s].isEnabled()) {
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000430 // create var to hold stage result
431 outColor = "color";
432 outColor.appendS32(s);
433 segments.fFSCode.appendf("\tvec4 %s;\n", outColor.c_str());
434
435 const char* inCoords;
436 // figure out what our input coords are
437 if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) &
438 layout) {
439 inCoords = POS_ATTR_NAME;
Scroggo97c88c22011-05-11 14:05:25 +0000440 } else {
441 int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout);
442 // we better have input tex coordinates if stage is enabled.
443 GrAssert(tcIdx >= 0);
444 GrAssert(texCoordAttrs[tcIdx].size());
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000445 inCoords = texCoordAttrs[tcIdx].c_str();
junov@google.comf93e7172011-03-31 21:26:24 +0000446 }
447
448 genStageCode(s,
449 fProgramDesc.fStages[s],
bsalomon@google.comfc296292011-05-06 13:53:47 +0000450 inColor.size() ? inColor.c_str() : NULL,
451 outColor.c_str(),
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000452 inCoords,
junov@google.comf93e7172011-03-31 21:26:24 +0000453 &segments,
454 &programData->fUniLocations.fStages[s]);
junov@google.comf93e7172011-03-31 21:26:24 +0000455 inColor = outColor;
junov@google.comf93e7172011-03-31 21:26:24 +0000456 }
457 }
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000458 }
Scroggo97c88c22011-05-11 14:05:25 +0000459
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000460 // if have all ones for the "dst" input to the color filter then we can make
461 // additional optimizations.
tomhudson@google.com0d831722011-06-02 15:37:14 +0000462 if (needColorFilterUniform && !inColor.size() &&
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000463 (SkXfermode::kIDC_Coeff == uniformCoeff ||
464 SkXfermode::kIDA_Coeff == uniformCoeff)) {
465 uniformCoeff = SkXfermode::kZero_Coeff;
466 bool bogus;
tomhudson@google.com0d831722011-06-02 15:37:14 +0000467 needBlendInputs(SkXfermode::kZero_Coeff, colorCoeff,
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000468 &needColorFilterUniform, &bogus);
469 }
470 if (needColorFilterUniform) {
471 segments.fFSUnis.append( "uniform vec4 " COL_FILTER_UNI_NAME ";\n");
472 programData->fUniLocations.fColorFilterUni = kUseUniform;
473 }
474
475 bool wroteFragColorZero = false;
tomhudson@google.com0d831722011-06-02 15:37:14 +0000476 if (SkXfermode::kZero_Coeff == uniformCoeff &&
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000477 SkXfermode::kZero_Coeff == colorCoeff) {
tomhudson@google.com0d831722011-06-02 15:37:14 +0000478 segments.fFSCode.appendf("\t%s = %s;\n",
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000479 fsColorOutput,
480 all_zeros_vec(4));
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000481 wroteFragColorZero = true;
482 } else if (SkXfermode::kDst_Mode != fProgramDesc.fColorFilterXfermode) {
483 segments.fFSCode.appendf("\tvec4 filteredColor;\n");
484 const char* color = inColor.size() ? inColor.c_str() : all_ones_vec(4);
tomhudson@google.com0d831722011-06-02 15:37:14 +0000485 addColorFilter(&segments.fFSCode, "filteredColor", uniformCoeff,
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000486 colorCoeff, color);
487 inColor = "filteredColor";
488 }
489
490 ///////////////////////////////////////////////////////////////////////////
491 // compute the partial coverage (coverage stages and edge aa)
492
493 GrStringBuilder inCoverage;
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000494
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000495 // we don't need to compute coverage at all if we know the final shader
496 // output will be zero and we don't have a dual src blend output.
497 if (!wroteFragColorZero ||
498 ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000499 if (fProgramDesc.fEdgeAANumEdges > 0) {
500 segments.fFSUnis.append("uniform vec3 " EDGES_UNI_NAME "[");
501 segments.fFSUnis.appendS32(fProgramDesc.fEdgeAANumEdges);
502 segments.fFSUnis.append("];\n");
503 programData->fUniLocations.fEdgesUni = kUseUniform;
504 int count = fProgramDesc.fEdgeAANumEdges;
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000505 segments.fFSCode.append(
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000506 "\tvec3 pos = vec3(gl_FragCoord.xy, 1);\n");
507 for (int i = 0; i < count; i++) {
508 segments.fFSCode.append("\tfloat a");
509 segments.fFSCode.appendS32(i);
510 segments.fFSCode.append(" = clamp(dot(" EDGES_UNI_NAME "[");
511 segments.fFSCode.appendS32(i);
512 segments.fFSCode.append("], pos), 0.0, 1.0);\n");
513 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000514 if (fProgramDesc.fEdgeAAConcave && (count & 0x01) == 0) {
515 // For concave polys, we consider the edges in pairs.
516 segments.fFSFunctions.append("float cross2(vec2 a, vec2 b) {\n");
517 segments.fFSFunctions.append("\treturn dot(a, vec2(b.y, -b.x));\n");
518 segments.fFSFunctions.append("}\n");
519 for (int i = 0; i < count; i += 2) {
520 segments.fFSCode.appendf("\tfloat eb%d;\n", i / 2);
521 segments.fFSCode.appendf("\tif (cross2(" EDGES_UNI_NAME "[%d].xy, " EDGES_UNI_NAME "[%d].xy) < 0.0) {\n", i, i + 1);
522 segments.fFSCode.appendf("\t\teb%d = a%d * a%d;\n", i / 2, i, i + 1);
523 segments.fFSCode.append("\t} else {\n");
524 segments.fFSCode.appendf("\t\teb%d = a%d + a%d - a%d * a%d;\n", i / 2, i, i + 1, i, i + 1);
525 segments.fFSCode.append("\t}\n");
526 }
527 segments.fFSCode.append("\tfloat edgeAlpha = ");
528 for (int i = 0; i < count / 2 - 1; i++) {
529 segments.fFSCode.appendf("min(eb%d, ", i);
530 }
531 segments.fFSCode.appendf("eb%d", count / 2 - 1);
532 for (int i = 0; i < count / 2 - 1; i++) {
533 segments.fFSCode.append(")");
534 }
535 segments.fFSCode.append(";\n");
536 } else {
537 segments.fFSCode.append("\tfloat edgeAlpha = ");
538 for (int i = 0; i < count - 1; i++) {
539 segments.fFSCode.appendf("min(a%d * a%d, ", i, i + 1);
540 }
541 segments.fFSCode.appendf("a%d * a0", count - 1);
542 for (int i = 0; i < count - 1; i++) {
543 segments.fFSCode.append(")");
544 }
545 segments.fFSCode.append(";\n");
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000546 }
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000547 inCoverage = "edgeAlpha";
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000548 }
549
550 GrStringBuilder outCoverage;
551 const int& startStage = fProgramDesc.fFirstCoverageStage;
552 for (int s = startStage; s < GrDrawTarget::kNumStages; ++s) {
tomhudson@google.com0d831722011-06-02 15:37:14 +0000553 if (fProgramDesc.fStages[s].isEnabled()) {
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000554 // create var to hold stage output
555 outCoverage = "coverage";
556 outCoverage.appendS32(s);
557 segments.fFSCode.appendf("\tvec4 %s;\n", outCoverage.c_str());
558
559 const char* inCoords;
560 // figure out what our input coords are
561 if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) & layout) {
562 inCoords = POS_ATTR_NAME;
563 } else {
564 int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout);
565 // we better have input tex coordinates if stage is enabled.
566 GrAssert(tcIdx >= 0);
567 GrAssert(texCoordAttrs[tcIdx].size());
568 inCoords = texCoordAttrs[tcIdx].c_str();
569 }
570
571 genStageCode(s,
572 fProgramDesc.fStages[s],
573 inCoverage.size() ? inCoverage.c_str() : NULL,
574 outCoverage.c_str(),
575 inCoords,
576 &segments,
577 &programData->fUniLocations.fStages[s]);
578 inCoverage = outCoverage;
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000579 }
580 }
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000581 if (ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
tomhudson@google.com0d831722011-06-02 15:37:14 +0000582 segments.fFSOutputs.appendf("out vec4 %s;\n",
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000583 dual_source_output_name());
584 bool outputIsZero = false;
585 GrStringBuilder coeff;
tomhudson@google.com0d831722011-06-02 15:37:14 +0000586 if (ProgramDesc::kCoverage_DualSrcOutput !=
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000587 fProgramDesc.fDualSrcOutput && !wroteFragColorZero) {
588 if (!inColor.size()) {
589 outputIsZero = true;
590 } else {
tomhudson@google.com0d831722011-06-02 15:37:14 +0000591 if (fProgramDesc.fDualSrcOutput ==
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000592 ProgramDesc::kCoverageISA_DualSrcOutput) {
593 coeff.printf("(1 - %s.a)", inColor.c_str());
594 } else {
595 coeff.printf("(vec4(1,1,1,1) - %s)", inColor.c_str());
596 }
597 }
598 }
599 if (outputIsZero) {
tomhudson@google.com0d831722011-06-02 15:37:14 +0000600 segments.fFSCode.appendf("\t%s = %s;\n",
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000601 dual_source_output_name(),
602 all_zeros_vec(4));
603 } else {
604 modulate_helper(dual_source_output_name(),
605 coeff.c_str(),
606 inCoverage.c_str(),
607 &segments.fFSCode);
608 }
609 dualSourceOutputWritten = true;
610 }
junov@google.comf93e7172011-03-31 21:26:24 +0000611 }
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000612
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000613 ///////////////////////////////////////////////////////////////////////////
614 // combine color and coverage as frag color
615
616 if (!wroteFragColorZero) {
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000617 modulate_helper(fsColorOutput,
618 inColor.c_str(),
619 inCoverage.c_str(),
620 &segments.fFSCode);
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000621 }
622
bsalomon@google.com91961302011-05-09 18:39:58 +0000623 segments.fVSCode.append("}\n");
624 segments.fFSCode.append("}\n");
625
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000626 ///////////////////////////////////////////////////////////////////////////
627 // compile and setup attribs and unis
628
bsalomon@google.com91961302011-05-09 18:39:58 +0000629 if (!CompileFSAndVS(segments, programData)) {
630 return false;
631 }
632
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000633 if (!this->bindOutputsAttribsAndLinkProgram(texCoordAttrs,
634 usingDeclaredOutputs,
635 dualSourceOutputWritten,
636 programData)) {
bsalomon@google.com91961302011-05-09 18:39:58 +0000637 return false;
638 }
639
640 this->getUniformLocationsAndInitCache(programData);
641
642 return true;
643}
644
645bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
646 CachedData* programData) {
junov@google.comf93e7172011-03-31 21:26:24 +0000647
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000648 static const int MAX_STRINGS = 6;
649 const char* strings[MAX_STRINGS];
650 int lengths[MAX_STRINGS];
junov@google.comf93e7172011-03-31 21:26:24 +0000651 int stringCnt = 0;
652
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000653 if (segments.fHeader.size()) {
654 strings[stringCnt] = segments.fHeader.c_str();
655 lengths[stringCnt] = segments.fHeader.size();
656 ++stringCnt;
657 }
bsalomon@google.comfc296292011-05-06 13:53:47 +0000658 if (segments.fVSUnis.size()) {
659 strings[stringCnt] = segments.fVSUnis.c_str();
660 lengths[stringCnt] = segments.fVSUnis.size();
junov@google.comf93e7172011-03-31 21:26:24 +0000661 ++stringCnt;
662 }
bsalomon@google.comfc296292011-05-06 13:53:47 +0000663 if (segments.fVSAttrs.size()) {
664 strings[stringCnt] = segments.fVSAttrs.c_str();
665 lengths[stringCnt] = segments.fVSAttrs.size();
junov@google.comf93e7172011-03-31 21:26:24 +0000666 ++stringCnt;
667 }
bsalomon@google.comfc296292011-05-06 13:53:47 +0000668 if (segments.fVaryings.size()) {
669 strings[stringCnt] = segments.fVaryings.c_str();
670 lengths[stringCnt] = segments.fVaryings.size();
junov@google.comf93e7172011-03-31 21:26:24 +0000671 ++stringCnt;
672 }
673
bsalomon@google.comfc296292011-05-06 13:53:47 +0000674 GrAssert(segments.fVSCode.size());
675 strings[stringCnt] = segments.fVSCode.c_str();
676 lengths[stringCnt] = segments.fVSCode.size();
junov@google.comf93e7172011-03-31 21:26:24 +0000677 ++stringCnt;
678
679#if PRINT_SHADERS
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000680 GrPrintf(segments.fHeader.c_str());
bsalomon@google.comfc296292011-05-06 13:53:47 +0000681 GrPrintf(segments.fVSUnis.c_str());
682 GrPrintf(segments.fVSAttrs.c_str());
683 GrPrintf(segments.fVaryings.c_str());
684 GrPrintf(segments.fVSCode.c_str());
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000685 GrPrintf("\n");
junov@google.comf93e7172011-03-31 21:26:24 +0000686#endif
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000687 GrAssert(stringCnt <= MAX_STRINGS);
junov@google.comf93e7172011-03-31 21:26:24 +0000688 programData->fVShaderID = CompileShader(GR_GL_VERTEX_SHADER,
689 stringCnt,
690 strings,
691 lengths);
692
bsalomon@google.com91961302011-05-09 18:39:58 +0000693 if (!programData->fVShaderID) {
694 return false;
695 }
696
junov@google.comf93e7172011-03-31 21:26:24 +0000697 stringCnt = 0;
698
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000699 if (segments.fHeader.size()) {
700 strings[stringCnt] = segments.fHeader.c_str();
701 lengths[stringCnt] = segments.fHeader.size();
702 ++stringCnt;
703 }
junov@google.comf93e7172011-03-31 21:26:24 +0000704 if (strlen(GrShaderPrecision()) > 1) {
705 strings[stringCnt] = GrShaderPrecision();
706 lengths[stringCnt] = strlen(GrShaderPrecision());
707 ++stringCnt;
708 }
bsalomon@google.comfc296292011-05-06 13:53:47 +0000709 if (segments.fFSUnis.size()) {
710 strings[stringCnt] = segments.fFSUnis.c_str();
711 lengths[stringCnt] = segments.fFSUnis.size();
junov@google.comf93e7172011-03-31 21:26:24 +0000712 ++stringCnt;
713 }
bsalomon@google.comfc296292011-05-06 13:53:47 +0000714 if (segments.fVaryings.size()) {
715 strings[stringCnt] = segments.fVaryings.c_str();
716 lengths[stringCnt] = segments.fVaryings.size();
junov@google.comf93e7172011-03-31 21:26:24 +0000717 ++stringCnt;
718 }
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000719 if (segments.fFSOutputs.size()) {
720 strings[stringCnt] = segments.fFSOutputs.c_str();
721 lengths[stringCnt] = segments.fFSOutputs.size();
722 ++stringCnt;
723 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000724 if (segments.fFSFunctions.size()) {
725 strings[stringCnt] = segments.fFSFunctions.c_str();
726 lengths[stringCnt] = segments.fFSFunctions.size();
727 ++stringCnt;
728 }
junov@google.comf93e7172011-03-31 21:26:24 +0000729
bsalomon@google.comfc296292011-05-06 13:53:47 +0000730 GrAssert(segments.fFSCode.size());
731 strings[stringCnt] = segments.fFSCode.c_str();
732 lengths[stringCnt] = segments.fFSCode.size();
junov@google.comf93e7172011-03-31 21:26:24 +0000733 ++stringCnt;
734
735#if PRINT_SHADERS
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000736 GrPrintf(segments.fHeader.c_str());
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000737 GrPrintf(GrShaderPrecision());
bsalomon@google.comfc296292011-05-06 13:53:47 +0000738 GrPrintf(segments.fFSUnis.c_str());
739 GrPrintf(segments.fVaryings.c_str());
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000740 GrPrintf(segments.fFSOutputs.c_str());
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000741 GrPrintf(segments.fFSFunctions.c_str());
bsalomon@google.comfc296292011-05-06 13:53:47 +0000742 GrPrintf(segments.fFSCode.c_str());
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000743 GrPrintf("\n");
junov@google.comf93e7172011-03-31 21:26:24 +0000744#endif
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000745 GrAssert(stringCnt <= MAX_STRINGS);
junov@google.comf93e7172011-03-31 21:26:24 +0000746 programData->fFShaderID = CompileShader(GR_GL_FRAGMENT_SHADER,
747 stringCnt,
748 strings,
749 lengths);
750
bsalomon@google.com91961302011-05-09 18:39:58 +0000751 if (!programData->fFShaderID) {
752 return false;
junov@google.comf93e7172011-03-31 21:26:24 +0000753 }
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000754
bsalomon@google.com91961302011-05-09 18:39:58 +0000755 return true;
junov@google.comf93e7172011-03-31 21:26:24 +0000756}
757
758GrGLuint GrGLProgram::CompileShader(GrGLenum type,
759 int stringCnt,
760 const char** strings,
761 int* stringLengths) {
tomhudson@google.com278cbb42011-06-30 19:37:01 +0000762 SK_TRACE_EVENT1("GrGLProgram::CompileShader",
763 "stringCount", SkStringPrintf("%i", stringCnt).c_str());
764
junov@google.comf93e7172011-03-31 21:26:24 +0000765 GrGLuint shader = GR_GL(CreateShader(type));
766 if (0 == shader) {
767 return 0;
768 }
769
770 GrGLint compiled = GR_GL_INIT_ZERO;
771 GR_GL(ShaderSource(shader, stringCnt, strings, stringLengths));
772 GR_GL(CompileShader(shader));
773 GR_GL(GetShaderiv(shader, GR_GL_COMPILE_STATUS, &compiled));
774
775 if (!compiled) {
776 GrGLint infoLen = GR_GL_INIT_ZERO;
777 GR_GL(GetShaderiv(shader, GR_GL_INFO_LOG_LENGTH, &infoLen));
778 GrAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger
779 if (infoLen > 0) {
780 GR_GL(GetShaderInfoLog(shader, infoLen+1, NULL, (char*)log.get()));
781 for (int i = 0; i < stringCnt; ++i) {
782 if (NULL == stringLengths || stringLengths[i] < 0) {
783 GrPrintf(strings[i]);
784 } else {
785 GrPrintf("%.*s", stringLengths[i], strings[i]);
786 }
787 }
788 GrPrintf("\n%s", log.get());
789 }
790 GrAssert(!"Shader compilation failed!");
791 GR_GL(DeleteShader(shader));
792 return 0;
793 }
794 return shader;
795}
796
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000797bool GrGLProgram::bindOutputsAttribsAndLinkProgram(
798 GrStringBuilder texCoordAttrNames[],
799 bool bindColorOut,
tomhudson@google.com0d831722011-06-02 15:37:14 +0000800 bool bindDualSrcOut,
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000801 CachedData* programData) const {
bsalomon@google.com91961302011-05-09 18:39:58 +0000802 programData->fProgramID = GR_GL(CreateProgram());
803 if (!programData->fProgramID) {
804 return false;
805 }
806 const GrGLint& progID = programData->fProgramID;
807
808 GR_GL(AttachShader(progID, programData->fVShaderID));
809 GR_GL(AttachShader(progID, programData->fFShaderID));
810
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000811 if (bindColorOut) {
tomhudson@google.com0d831722011-06-02 15:37:14 +0000812 GR_GL(BindFragDataLocationIndexed(programData->fProgramID,
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000813 0, 0, declared_color_output_name()));
814 }
815 if (bindDualSrcOut) {
816 GR_GL(BindFragDataLocationIndexed(programData->fProgramID,
817 0, 1, dual_source_output_name()));
818 }
819
bsalomon@google.com91961302011-05-09 18:39:58 +0000820 // Bind the attrib locations to same values for all shaders
821 GR_GL(BindAttribLocation(progID, PositionAttributeIdx(), POS_ATTR_NAME));
822 for (int t = 0; t < GrDrawTarget::kMaxTexCoords; ++t) {
823 if (texCoordAttrNames[t].size()) {
824 GR_GL(BindAttribLocation(progID,
825 TexCoordAttributeIdx(t),
826 texCoordAttrNames[t].c_str()));
827 }
828 }
829
bsalomon@google.com91961302011-05-09 18:39:58 +0000830 if (kSetAsAttribute == programData->fUniLocations.fViewMatrixUni) {
831 GR_GL(BindAttribLocation(progID,
832 ViewMatrixAttributeIdx(),
833 VIEW_MATRIX_NAME));
834 }
835
836 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
bsalomon@google.com72b4fcb2011-05-09 20:47:34 +0000837 const StageUniLocations& unis = programData->fUniLocations.fStages[s];
838 if (kSetAsAttribute == unis.fTextureMatrixUni) {
bsalomon@google.com91961302011-05-09 18:39:58 +0000839 GrStringBuilder matName;
840 tex_matrix_name(s, &matName);
841 GR_GL(BindAttribLocation(progID,
842 TextureMatrixAttributeIdx(s),
843 matName.c_str()));
844 }
845 }
846
bsalomon@google.com91961302011-05-09 18:39:58 +0000847 GR_GL(BindAttribLocation(progID, ColorAttributeIdx(), COL_ATTR_NAME));
848
849 GR_GL(LinkProgram(progID));
850
851 GrGLint linked = GR_GL_INIT_ZERO;
852 GR_GL(GetProgramiv(progID, GR_GL_LINK_STATUS, &linked));
853 if (!linked) {
854 GrGLint infoLen = GR_GL_INIT_ZERO;
855 GR_GL(GetProgramiv(progID, GR_GL_INFO_LOG_LENGTH, &infoLen));
856 GrAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger
857 if (infoLen > 0) {
858 GR_GL(GetProgramInfoLog(progID,
859 infoLen+1,
860 NULL,
861 (char*)log.get()));
862 GrPrintf((char*)log.get());
863 }
864 GrAssert(!"Error linking program");
865 GR_GL(DeleteProgram(progID));
866 programData->fProgramID = 0;
867 return false;
868 }
869 return true;
870}
871
872void GrGLProgram::getUniformLocationsAndInitCache(CachedData* programData) const {
873 const GrGLint& progID = programData->fProgramID;
874
875 if (kUseUniform == programData->fUniLocations.fViewMatrixUni) {
876 programData->fUniLocations.fViewMatrixUni =
877 GR_GL(GetUniformLocation(progID, VIEW_MATRIX_NAME));
878 GrAssert(kUnusedUniform != programData->fUniLocations.fViewMatrixUni);
879 }
880 if (kUseUniform == programData->fUniLocations.fColorUni) {
tomhudson@google.com0d831722011-06-02 15:37:14 +0000881 programData->fUniLocations.fColorUni =
bsalomon@google.com91961302011-05-09 18:39:58 +0000882 GR_GL(GetUniformLocation(progID, COL_UNI_NAME));
883 GrAssert(kUnusedUniform != programData->fUniLocations.fColorUni);
884 }
Scroggo97c88c22011-05-11 14:05:25 +0000885 if (kUseUniform == programData->fUniLocations.fColorFilterUni) {
tomhudson@google.com0d831722011-06-02 15:37:14 +0000886 programData->fUniLocations.fColorFilterUni =
Scroggo97c88c22011-05-11 14:05:25 +0000887 GR_GL(GetUniformLocation(progID, COL_FILTER_UNI_NAME));
888 GrAssert(kUnusedUniform != programData->fUniLocations.fColorFilterUni);
889 }
bsalomon@google.com91961302011-05-09 18:39:58 +0000890
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000891 if (kUseUniform == programData->fUniLocations.fEdgesUni) {
tomhudson@google.com0d831722011-06-02 15:37:14 +0000892 programData->fUniLocations.fEdgesUni =
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000893 GR_GL(GetUniformLocation(progID, EDGES_UNI_NAME));
894 GrAssert(kUnusedUniform != programData->fUniLocations.fEdgesUni);
895 } else {
896 programData->fUniLocations.fEdgesUni = kUnusedUniform;
897 }
898
bsalomon@google.com91961302011-05-09 18:39:58 +0000899 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
900 StageUniLocations& locations = programData->fUniLocations.fStages[s];
tomhudson@google.com0d831722011-06-02 15:37:14 +0000901 if (fProgramDesc.fStages[s].isEnabled()) {
bsalomon@google.com91961302011-05-09 18:39:58 +0000902 if (kUseUniform == locations.fTextureMatrixUni) {
903 GrStringBuilder texMName;
904 tex_matrix_name(s, &texMName);
905 locations.fTextureMatrixUni = GR_GL(GetUniformLocation(
906 progID,
907 texMName.c_str()));
908 GrAssert(kUnusedUniform != locations.fTextureMatrixUni);
909 }
910
911 if (kUseUniform == locations.fSamplerUni) {
912 GrStringBuilder samplerName;
913 sampler_name(s, &samplerName);
914 locations.fSamplerUni = GR_GL(GetUniformLocation(
915 progID,
916 samplerName.c_str()));
917 GrAssert(kUnusedUniform != locations.fSamplerUni);
918 }
919
920 if (kUseUniform == locations.fNormalizedTexelSizeUni) {
921 GrStringBuilder texelSizeName;
922 normalized_texel_size_name(s, &texelSizeName);
tomhudson@google.com0d831722011-06-02 15:37:14 +0000923 locations.fNormalizedTexelSizeUni =
bsalomon@google.com91961302011-05-09 18:39:58 +0000924 GR_GL(GetUniformLocation(progID, texelSizeName.c_str()));
925 GrAssert(kUnusedUniform != locations.fNormalizedTexelSizeUni);
926 }
927
928 if (kUseUniform == locations.fRadial2Uni) {
929 GrStringBuilder radial2ParamName;
930 radial2_param_name(s, &radial2ParamName);
931 locations.fRadial2Uni = GR_GL(GetUniformLocation(
932 progID,
933 radial2ParamName.c_str()));
934 GrAssert(kUnusedUniform != locations.fRadial2Uni);
935 }
junov@google.com6acc9b32011-05-16 18:32:07 +0000936
937 if (kUseUniform == locations.fTexDomUni) {
938 GrStringBuilder texDomName;
939 tex_domain_name(s, &texDomName);
940 locations.fTexDomUni = GR_GL(GetUniformLocation(
941 progID,
942 texDomName.c_str()));
943 GrAssert(kUnusedUniform != locations.fTexDomUni);
944 }
bsalomon@google.com91961302011-05-09 18:39:58 +0000945 }
946 }
947 GR_GL(UseProgram(progID));
948
949 // init sampler unis and set bogus values for state tracking
950 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
951 if (kUnusedUniform != programData->fUniLocations.fStages[s].fSamplerUni) {
952 GR_GL(Uniform1i(programData->fUniLocations.fStages[s].fSamplerUni, s));
953 }
954 programData->fTextureMatrices[s] = GrMatrix::InvalidMatrix();
955 programData->fRadial2CenterX1[s] = GR_ScalarMax;
956 programData->fRadial2Radius0[s] = -GR_ScalarMax;
957 programData->fTextureWidth[s] = -1;
958 programData->fTextureHeight[s] = -1;
959 }
960 programData->fViewMatrix = GrMatrix::InvalidMatrix();
961 programData->fColor = GrColor_ILLEGAL;
Scroggo97c88c22011-05-11 14:05:25 +0000962 programData->fColorFilterColor = GrColor_ILLEGAL;
bsalomon@google.com91961302011-05-09 18:39:58 +0000963}
964
junov@google.comf93e7172011-03-31 21:26:24 +0000965//============================================================================
966// Stage code generation
967//============================================================================
968
969void GrGLProgram::genStageCode(int stageNum,
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000970 const GrGLProgram::ProgramDesc::StageDesc& desc,
971 const char* fsInColor, // NULL means no incoming color
972 const char* fsOutColor,
973 const char* vsInCoord,
974 ShaderCodeSegments* segments,
975 StageUniLocations* locations) const {
junov@google.comf93e7172011-03-31 21:26:24 +0000976
977 GrAssert(stageNum >= 0 && stageNum <= 9);
978
bsalomon@google.com91961302011-05-09 18:39:58 +0000979 GrStringBuilder varyingName;
junov@google.comf93e7172011-03-31 21:26:24 +0000980 stage_varying_name(stageNum, &varyingName);
981
982 // First decide how many coords are needed to access the texture
983 // Right now it's always 2 but we could start using 1D textures for
984 // gradients.
985 static const int coordDims = 2;
986 int varyingDims;
987 /// Vertex Shader Stuff
988
989 // decide whether we need a matrix to transform texture coords
990 // and whether the varying needs a perspective coord.
bsalomon@google.com91961302011-05-09 18:39:58 +0000991 GrStringBuilder texMName;
junov@google.comf93e7172011-03-31 21:26:24 +0000992 tex_matrix_name(stageNum, &texMName);
993 if (desc.fOptFlags & ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit) {
994 varyingDims = coordDims;
995 } else {
bsalomon@google.com4be283f2011-04-19 21:15:09 +0000996 #if GR_GL_ATTRIBUTE_MATRICES
bsalomon@google.com91961302011-05-09 18:39:58 +0000997 segments->fVSAttrs.appendf("attribute mat3 %s;\n", texMName.c_str());
998 locations->fTextureMatrixUni = kSetAsAttribute;
junov@google.comf93e7172011-03-31 21:26:24 +0000999 #else
bsalomon@google.com91961302011-05-09 18:39:58 +00001000 segments->fVSUnis.appendf("uniform mat3 %s;\n", texMName.c_str());
1001 locations->fTextureMatrixUni = kUseUniform;
junov@google.comf93e7172011-03-31 21:26:24 +00001002 #endif
1003 if (desc.fOptFlags & ProgramDesc::StageDesc::kNoPerspective_OptFlagBit) {
1004 varyingDims = coordDims;
1005 } else {
1006 varyingDims = coordDims + 1;
1007 }
1008 }
1009
bsalomon@google.com91961302011-05-09 18:39:58 +00001010 GrStringBuilder samplerName;
junov@google.comf93e7172011-03-31 21:26:24 +00001011 sampler_name(stageNum, &samplerName);
bsalomon@google.com91961302011-05-09 18:39:58 +00001012 segments->fFSUnis.appendf("uniform sampler2D %s;\n", samplerName.c_str());
1013 locations->fSamplerUni = kUseUniform;
junov@google.comf93e7172011-03-31 21:26:24 +00001014
bsalomon@google.com91961302011-05-09 18:39:58 +00001015 GrStringBuilder texelSizeName;
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001016 if (ProgramDesc::StageDesc::k2x2_FetchMode == desc.fFetchMode) {
1017 normalized_texel_size_name(stageNum, &texelSizeName);
bsalomon@google.com91961302011-05-09 18:39:58 +00001018 segments->fFSUnis.appendf("uniform vec2 %s;\n", texelSizeName.c_str());
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001019 }
1020
tomhudson@google.com0d831722011-06-02 15:37:14 +00001021 segments->fVaryings.appendf("varying %s %s;\n",
bsalomon@google.com91961302011-05-09 18:39:58 +00001022 float_vector_type(varyingDims), varyingName.c_str());
junov@google.comf93e7172011-03-31 21:26:24 +00001023
1024 if (desc.fOptFlags & ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit) {
1025 GrAssert(varyingDims == coordDims);
bsalomon@google.com91961302011-05-09 18:39:58 +00001026 segments->fVSCode.appendf("\t%s = %s;\n", varyingName.c_str(), vsInCoord);
junov@google.comf93e7172011-03-31 21:26:24 +00001027 } else {
bsalomon@google.com91961302011-05-09 18:39:58 +00001028 // varying = texMatrix * texCoord
1029 segments->fVSCode.appendf("\t%s = (%s * vec3(%s, 1))%s;\n",
1030 varyingName.c_str(), texMName.c_str(),
1031 vsInCoord, vector_all_coords(varyingDims));
junov@google.comf93e7172011-03-31 21:26:24 +00001032 }
1033
bsalomon@google.com91961302011-05-09 18:39:58 +00001034 GrStringBuilder radial2ParamsName;
junov@google.comf93e7172011-03-31 21:26:24 +00001035 radial2_param_name(stageNum, &radial2ParamsName);
1036 // for radial grads without perspective we can pass the linear
1037 // part of the quadratic as a varying.
bsalomon@google.com91961302011-05-09 18:39:58 +00001038 GrStringBuilder radial2VaryingName;
junov@google.comf93e7172011-03-31 21:26:24 +00001039 radial2_varying_name(stageNum, &radial2VaryingName);
1040
1041 if (ProgramDesc::StageDesc::kRadial2Gradient_CoordMapping == desc.fCoordMapping) {
1042
tomhudson@google.com0d831722011-06-02 15:37:14 +00001043 segments->fVSUnis.appendf("uniform %s float %s[6];\n",
bsalomon@google.com91961302011-05-09 18:39:58 +00001044 GrPrecision(), radial2ParamsName.c_str());
tomhudson@google.com0d831722011-06-02 15:37:14 +00001045 segments->fFSUnis.appendf("uniform float %s[6];\n",
bsalomon@google.com91961302011-05-09 18:39:58 +00001046 radial2ParamsName.c_str());
1047 locations->fRadial2Uni = kUseUniform;
junov@google.comf93e7172011-03-31 21:26:24 +00001048
1049 // if there is perspective we don't interpolate this
1050 if (varyingDims == coordDims) {
1051 GrAssert(2 == coordDims);
bsalomon@google.com91961302011-05-09 18:39:58 +00001052 segments->fVaryings.appendf("varying float %s;\n", radial2VaryingName.c_str());
junov@google.comf93e7172011-03-31 21:26:24 +00001053
bsalomon@google.com91961302011-05-09 18:39:58 +00001054 // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3])
1055 segments->fVSCode.appendf("\t%s = 2.0 *(%s[2] * %s.x - %s[3]);\n",
1056 radial2VaryingName.c_str(), radial2ParamsName.c_str(),
1057 varyingName.c_str(), radial2ParamsName.c_str());
junov@google.comf93e7172011-03-31 21:26:24 +00001058 }
1059 }
1060
1061 /// Fragment Shader Stuff
bsalomon@google.com91961302011-05-09 18:39:58 +00001062 GrStringBuilder fsCoordName;
junov@google.comf93e7172011-03-31 21:26:24 +00001063 // function used to access the shader, may be made projective
bsalomon@google.com91961302011-05-09 18:39:58 +00001064 GrStringBuilder texFunc("texture2D");
junov@google.comf93e7172011-03-31 21:26:24 +00001065 if (desc.fOptFlags & (ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit |
1066 ProgramDesc::StageDesc::kNoPerspective_OptFlagBit)) {
1067 GrAssert(varyingDims == coordDims);
1068 fsCoordName = varyingName;
1069 } else {
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001070 // if we have to do some special op on the varyings to get
junov@google.comf93e7172011-03-31 21:26:24 +00001071 // our final tex coords then when in perspective we have to
bsalomon@google.com91961302011-05-09 18:39:58 +00001072 // do an explicit divide. Otherwise, we can use a Proj func.
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001073 if (ProgramDesc::StageDesc::kIdentity_CoordMapping == desc.fCoordMapping &&
1074 ProgramDesc::StageDesc::kSingle_FetchMode == desc.fFetchMode) {
bsalomon@google.com91961302011-05-09 18:39:58 +00001075 texFunc.append("Proj");
junov@google.comf93e7172011-03-31 21:26:24 +00001076 fsCoordName = varyingName;
1077 } else {
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001078 fsCoordName = "inCoord";
bsalomon@google.comfc296292011-05-06 13:53:47 +00001079 fsCoordName.appendS32(stageNum);
bsalomon@google.com91961302011-05-09 18:39:58 +00001080 segments->fFSCode.appendf("\t%s %s = %s%s / %s%s;\n",
1081 float_vector_type(coordDims),
1082 fsCoordName.c_str(),
1083 varyingName.c_str(),
1084 vector_nonhomog_coords(varyingDims),
1085 varyingName.c_str(),
1086 vector_homog_coord(varyingDims));
junov@google.comf93e7172011-03-31 21:26:24 +00001087 }
1088 }
1089
bsalomon@google.comfc296292011-05-06 13:53:47 +00001090 GrStringBuilder sampleCoords;
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001091 bool complexCoord = false;
junov@google.comf93e7172011-03-31 21:26:24 +00001092 switch (desc.fCoordMapping) {
1093 case ProgramDesc::StageDesc::kIdentity_CoordMapping:
1094 sampleCoords = fsCoordName;
1095 break;
1096 case ProgramDesc::StageDesc::kSweepGradient_CoordMapping:
bsalomon@google.com91961302011-05-09 18:39:58 +00001097 sampleCoords.printf("vec2(atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5, 0.5)", fsCoordName.c_str(), fsCoordName.c_str());
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001098 complexCoord = true;
junov@google.comf93e7172011-03-31 21:26:24 +00001099 break;
1100 case ProgramDesc::StageDesc::kRadialGradient_CoordMapping:
bsalomon@google.com91961302011-05-09 18:39:58 +00001101 sampleCoords.printf("vec2(length(%s.xy), 0.5)", fsCoordName.c_str());
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001102 complexCoord = true;
junov@google.comf93e7172011-03-31 21:26:24 +00001103 break;
1104 case ProgramDesc::StageDesc::kRadial2Gradient_CoordMapping: {
bsalomon@google.com91961302011-05-09 18:39:58 +00001105 GrStringBuilder cName("c");
1106 GrStringBuilder ac4Name("ac4");
1107 GrStringBuilder rootName("root");
junov@google.comf93e7172011-03-31 21:26:24 +00001108
bsalomon@google.comfc296292011-05-06 13:53:47 +00001109 cName.appendS32(stageNum);
1110 ac4Name.appendS32(stageNum);
1111 rootName.appendS32(stageNum);
junov@google.comf93e7172011-03-31 21:26:24 +00001112
bsalomon@google.com91961302011-05-09 18:39:58 +00001113 // if we were able to interpolate the linear component bVar is the varying
1114 // otherwise compute it
1115 GrStringBuilder bVar;
junov@google.comf93e7172011-03-31 21:26:24 +00001116 if (coordDims == varyingDims) {
1117 bVar = radial2VaryingName;
1118 GrAssert(2 == varyingDims);
1119 } else {
1120 GrAssert(3 == varyingDims);
1121 bVar = "b";
bsalomon@google.comfc296292011-05-06 13:53:47 +00001122 bVar.appendS32(stageNum);
bsalomon@google.com91961302011-05-09 18:39:58 +00001123 segments->fFSCode.appendf("\tfloat %s = 2.0 * (%s[2] * %s.x - %s[3]);\n",
1124 bVar.c_str(), radial2ParamsName.c_str(),
1125 fsCoordName.c_str(), radial2ParamsName.c_str());
junov@google.comf93e7172011-03-31 21:26:24 +00001126 }
1127
bsalomon@google.com91961302011-05-09 18:39:58 +00001128 // c = (x^2)+(y^2) - params[4]
1129 segments->fFSCode.appendf("\tfloat %s = dot(%s, %s) - %s[4];\n",
1130 cName.c_str(), fsCoordName.c_str(),
1131 fsCoordName.c_str(),
1132 radial2ParamsName.c_str());
1133 // ac4 = 4.0 * params[0] * c
1134 segments->fFSCode.appendf("\tfloat %s = %s[0] * 4.0 * %s;\n",
1135 ac4Name.c_str(), radial2ParamsName.c_str(),
1136 cName.c_str());
junov@google.comf93e7172011-03-31 21:26:24 +00001137
bsalomon@google.com91961302011-05-09 18:39:58 +00001138 // root = sqrt(b^2-4ac)
1139 // (abs to avoid exception due to fp precision)
1140 segments->fFSCode.appendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
1141 rootName.c_str(), bVar.c_str(), bVar.c_str(),
1142 ac4Name.c_str());
junov@google.comf93e7172011-03-31 21:26:24 +00001143
bsalomon@google.com91961302011-05-09 18:39:58 +00001144 // x coord is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
1145 // y coord is 0.5 (texture is effectively 1D)
1146 sampleCoords.printf("vec2((-%s + %s[5] * %s) * %s[1], 0.5)",
1147 bVar.c_str(), radial2ParamsName.c_str(),
1148 rootName.c_str(), radial2ParamsName.c_str());
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001149 complexCoord = true;
junov@google.comf93e7172011-03-31 21:26:24 +00001150 break;}
1151 };
1152
bsalomon@google.com91961302011-05-09 18:39:58 +00001153 const char* smear;
1154 if (desc.fModulation == ProgramDesc::StageDesc::kAlpha_Modulation) {
1155 smear = ".aaaa";
1156 } else {
1157 smear = "";
1158 }
1159 GrStringBuilder modulate;
1160 if (NULL != fsInColor) {
1161 modulate.printf(" * %s", fsInColor);
1162 }
1163
tomhudson@google.com0d831722011-06-02 15:37:14 +00001164 if (desc.fOptFlags &
junov@google.com6acc9b32011-05-16 18:32:07 +00001165 ProgramDesc::StageDesc::kCustomTextureDomain_OptFlagBit) {
1166 GrStringBuilder texDomainName;
1167 tex_domain_name(stageNum, &texDomainName);
tomhudson@google.com0d831722011-06-02 15:37:14 +00001168 segments->fFSUnis.appendf("uniform %s %s;\n",
junov@google.com6acc9b32011-05-16 18:32:07 +00001169 float_vector_type(4),
1170 texDomainName.c_str());
1171 GrStringBuilder coordVar("clampCoord");
1172 segments->fFSCode.appendf("\t%s %s = clamp(%s, %s.xy, %s.zw);\n",
1173 float_vector_type(coordDims),
1174 coordVar.c_str(),
1175 sampleCoords.c_str(),
1176 texDomainName.c_str(),
1177 texDomainName.c_str());
1178 sampleCoords = coordVar;
1179 locations->fTexDomUni = kUseUniform;
1180 }
1181
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001182 if (ProgramDesc::StageDesc::k2x2_FetchMode == desc.fFetchMode) {
bsalomon@google.com91961302011-05-09 18:39:58 +00001183 locations->fNormalizedTexelSizeUni = kUseUniform;
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001184 if (complexCoord) {
bsalomon@google.com91961302011-05-09 18:39:58 +00001185 // assign the coord to a var rather than compute 4x.
1186 GrStringBuilder coordVar("tCoord");
bsalomon@google.comfc296292011-05-06 13:53:47 +00001187 coordVar.appendS32(stageNum);
bsalomon@google.com91961302011-05-09 18:39:58 +00001188 segments->fFSCode.appendf("\t%s %s = %s;\n",
1189 float_vector_type(coordDims),
1190 coordVar.c_str(), sampleCoords.c_str());
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001191 sampleCoords = coordVar;
1192 }
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001193 GrAssert(2 == coordDims);
bsalomon@google.com91961302011-05-09 18:39:58 +00001194 GrStringBuilder accumVar("accum");
1195 accumVar.appendS32(stageNum);
1196 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);
1197 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);
1198 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);
1199 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);
1200 segments->fFSCode.appendf("\t%s = .25 * %s%s;\n", fsOutColor, accumVar.c_str(), modulate.c_str());
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001201 } else {
bsalomon@google.com271cffc2011-05-20 14:13:56 +00001202 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());
junov@google.comf93e7172011-03-31 21:26:24 +00001203 }
junov@google.comf93e7172011-03-31 21:26:24 +00001204}