blob: 31571ab59787d7ac9af6e3a83ed28719624d759d [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
tomhudson@google.com0c8d93a2011-07-01 17:08:26 +000022#include "SkTrace.h"
Scroggo97c88c22011-05-11 14:05:25 +000023#include "SkXfermode.h"
24
junov@google.comf93e7172011-03-31 21:26:24 +000025namespace {
26
27const char* GrPrecision() {
28 if (GR_GL_SUPPORT_ES2) {
29 return "mediump";
30 } else {
bsalomon@google.com91961302011-05-09 18:39:58 +000031 return " ";
junov@google.comf93e7172011-03-31 21:26:24 +000032 }
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
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +000045#define PRINT_SHADERS 0
46
bsalomon@google.com4be283f2011-04-19 21:15:09 +000047#if GR_GL_ATTRIBUTE_MATRICES
junov@google.comf93e7172011-03-31 21:26:24 +000048 #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"
bsalomon@google.com4be283f2011-04-19 21:15:09 +000055#define COL_UNI_NAME "uColor"
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +000056#define EDGES_UNI_NAME "uEdges"
Scroggo97c88c22011-05-11 14:05:25 +000057#define COL_FILTER_UNI_NAME "uColorFilter"
junov@google.comf93e7172011-03-31 21:26:24 +000058
junov@google.comf93e7172011-03-31 21:26:24 +000059static inline void tex_attr_name(int coordIdx, GrStringBuilder* s) {
60 *s = "aTexCoord";
bsalomon@google.comfc296292011-05-06 13:53:47 +000061 s->appendS32(coordIdx);
junov@google.comf93e7172011-03-31 21:26:24 +000062}
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
bsalomon@google.comf2d91552011-05-16 20:56:06 +000088static inline const char* all_ones_vec(int count) {
tomhudson@google.com0d831722011-06-02 15:37:14 +000089 static const char* ONESVEC[] = {"ERROR", "1.0", "vec2(1,1)",
bsalomon@google.comf2d91552011-05-16 20:56:06 +000090 "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) {
tomhudson@google.com0d831722011-06-02 15:37:14 +000096 static const char* ZEROSVEC[] = {"ERROR", "0.0", "vec2(0,0)",
bsalomon@google.comf2d91552011-05-16 20:56:06 +000097 "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
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000102static inline const char* declared_color_output_name() { return "fsColorOut"; }
103static inline const char* dual_source_output_name() { return "dualSourceOut"; }
104
junov@google.comf93e7172011-03-31 21:26:24 +0000105static void tex_matrix_name(int stage, GrStringBuilder* s) {
bsalomon@google.com4be283f2011-04-19 21:15:09 +0000106#if GR_GL_ATTRIBUTE_MATRICES
junov@google.comf93e7172011-03-31 21:26:24 +0000107 *s = "aTexM";
108#else
109 *s = "uTexM";
110#endif
bsalomon@google.comfc296292011-05-06 13:53:47 +0000111 s->appendS32(stage);
junov@google.comf93e7172011-03-31 21:26:24 +0000112}
113
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000114static void normalized_texel_size_name(int stage, GrStringBuilder* s) {
115 *s = "uTexelSize";
bsalomon@google.comfc296292011-05-06 13:53:47 +0000116 s->appendS32(stage);
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000117}
118
junov@google.comf93e7172011-03-31 21:26:24 +0000119static void sampler_name(int stage, GrStringBuilder* s) {
120 *s = "uSampler";
bsalomon@google.comfc296292011-05-06 13:53:47 +0000121 s->appendS32(stage);
junov@google.comf93e7172011-03-31 21:26:24 +0000122}
123
124static void stage_varying_name(int stage, GrStringBuilder* s) {
125 *s = "vStage";
bsalomon@google.comfc296292011-05-06 13:53:47 +0000126 s->appendS32(stage);
junov@google.comf93e7172011-03-31 21:26:24 +0000127}
128
129static void radial2_param_name(int stage, GrStringBuilder* s) {
130 *s = "uRadial2Params";
bsalomon@google.comfc296292011-05-06 13:53:47 +0000131 s->appendS32(stage);
junov@google.comf93e7172011-03-31 21:26:24 +0000132}
133
134static void radial2_varying_name(int stage, GrStringBuilder* s) {
135 *s = "vB";
bsalomon@google.comfc296292011-05-06 13:53:47 +0000136 s->appendS32(stage);
junov@google.comf93e7172011-03-31 21:26:24 +0000137}
138
junov@google.com6acc9b32011-05-16 18:32:07 +0000139static void tex_domain_name(int stage, GrStringBuilder* s) {
140 *s = "uTexDom";
141 s->appendS32(stage);
142}
143
junov@google.comf93e7172011-03-31 21:26:24 +0000144GrGLProgram::GrGLProgram() {
junov@google.comf93e7172011-03-31 21:26:24 +0000145}
146
147GrGLProgram::~GrGLProgram() {
junov@google.comf93e7172011-03-31 21:26:24 +0000148}
149
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +0000150void GrGLProgram::overrideBlend(GrBlendCoeff* srcCoeff,
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000151 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
junov@google.comf93e7172011-03-31 21:26:24 +0000168void GrGLProgram::buildKey(GrBinHashKeyBuilder& key) const {
169 // Add stage configuration to the key
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +0000170 key.keyData(reinterpret_cast<const uint32_t*>(&fProgramDesc), sizeof(ProgramDesc));
junov@google.comf93e7172011-03-31 21:26:24 +0000171}
172
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000173// assigns modulation of two vars to an output var
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000174// vars can be vec4s or floats (or one of each)
175// result is always vec4
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000176// 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) {
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000193 code->appendf("\t%s = vec4(%s);\n", outputVar, var1);
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000194 } else if (!has1) {
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000195 code->appendf("\t%s = vec4(%s);\n", outputVar, var0);
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000196 } else {
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000197 code->appendf("\t%s = vec4(%s * %s);\n", outputVar, var0, var1);
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000198 }
199}
200
201// assigns addition of two vars to an output var
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000202// vars can be vec4s or floats (or one of each)
203// result is always vec4
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000204// 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) {
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000221 code->appendf("\t%s = vec4(%s);\n", outputVar, var1);
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000222 } else if (!has1) {
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000223 code->appendf("\t%s = vec4(%s);\n", outputVar, var0);
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000224 } else {
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000225 code->appendf("\t%s = vec4(%s + %s);\n", outputVar, var0, var1);
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000226 }
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;
Scroggo97c88c22011-05-11 14:05:25 +0000266 }
267}
268
269/**
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000270 * 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) {
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000276 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/**
Scroggo97c88c22011-05-11 14:05:25 +0000313 * Adds a line to the fragment shader code which modifies the color by
314 * the specified color filter.
315 */
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000316static void addColorFilter(GrStringBuilder* fsCode, const char * outputVar,
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +0000317 SkXfermode::Coeff uniformCoeff,
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000318 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);
Scroggo97c88c22011-05-11 14:05:25 +0000327}
328
bsalomon@google.com91961302011-05-09 18:39:58 +0000329bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
junov@google.comf93e7172011-03-31 21:26:24 +0000330
331 ShaderCodeSegments segments;
332 const uint32_t& layout = fProgramDesc.fVertexLayout;
333
bsalomon@google.com91961302011-05-09 18:39:58 +0000334 programData->fUniLocations.reset();
junov@google.comf93e7172011-03-31 21:26:24 +0000335
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000336 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 =)
tomhudson@google.com0d831722011-06-02 15:37:14 +0000340 SkXfermode::ModeAsCoeff(static_cast<SkXfermode::Mode>
341 (fProgramDesc.fColorFilterXfermode),
342 &uniformCoeff, &colorCoeff);
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000343 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
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000354 // 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;
tomhudson@google.com0d3f1fb2011-06-01 19:27:31 +0000358 bool usingDeclaredOutputs = ProgramDesc::kNone_DualSrcOutput !=
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000359 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
bsalomon@google.com4be283f2011-04-19 21:15:09 +0000369#if GR_GL_ATTRIBUTE_MATRICES
370 segments.fVSAttrs += "attribute mat3 " VIEW_MATRIX_NAME ";\n";
bsalomon@google.com91961302011-05-09 18:39:58 +0000371 programData->fUniLocations.fViewMatrixUni = kSetAsAttribute;
junov@google.comf93e7172011-03-31 21:26:24 +0000372#else
bsalomon@google.com4be283f2011-04-19 21:15:09 +0000373 segments.fVSUnis += "uniform mat3 " VIEW_MATRIX_NAME ";\n";
bsalomon@google.com91961302011-05-09 18:39:58 +0000374 programData->fUniLocations.fViewMatrixUni = kUseUniform;
junov@google.comf93e7172011-03-31 21:26:24 +0000375#endif
376 segments.fVSAttrs += "attribute vec2 " POS_ATTR_NAME ";\n";
junov@google.comf93e7172011-03-31 21:26:24 +0000377
bsalomon@google.com91961302011-05-09 18:39:58 +0000378 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");
bsalomon@google.com4be283f2011-04-19 21:15:09 +0000382
bsalomon@google.com91961302011-05-09 18:39:58 +0000383 // incoming color to current stage being processed.
384 GrStringBuilder inColor;
bsalomon@google.com4be283f2011-04-19 21:15:09 +0000385
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000386 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 }
junov@google.comf93e7172011-03-31 21:26:24 +0000403 }
404
bsalomon@google.com4be283f2011-04-19 21:15:09 +0000405 if (fProgramDesc.fEmitsPointSize){
bsalomon@google.com91961302011-05-09 18:39:58 +0000406 segments.fVSCode.append("\tgl_PointSize = 1.0;\n");
junov@google.comf93e7172011-03-31 21:26:24 +0000407 }
bsalomon@google.com4be283f2011-04-19 21:15:09 +0000408
bsalomon@google.com91961302011-05-09 18:39:58 +0000409 segments.fFSCode.append("void main() {\n");
junov@google.comf93e7172011-03-31 21:26:24 +0000410
411 // add texture coordinates that are used to the list of vertex attr decls
bsalomon@google.com91961302011-05-09 18:39:58 +0000412 GrStringBuilder texCoordAttrs[GrDrawTarget::kMaxTexCoords];
junov@google.comf93e7172011-03-31 21:26:24 +0000413 for (int t = 0; t < GrDrawTarget::kMaxTexCoords; ++t) {
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000414 if (GrDrawTarget::VertexUsesTexCoordIdx(t, layout)) {
junov@google.comf93e7172011-03-31 21:26:24 +0000415 tex_attr_name(t, texCoordAttrs + t);
bsalomon@google.com91961302011-05-09 18:39:58 +0000416 segments.fVSAttrs.appendf("attribute vec2 %s;\n", texCoordAttrs[t].c_str());
junov@google.comf93e7172011-03-31 21:26:24 +0000417 }
418 }
419
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000420 ///////////////////////////////////////////////////////////////////////////
421 // compute the final color
Scroggo97c88c22011-05-11 14:05:25 +0000422
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000423 // 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) {
tomhudson@google.com0d831722011-06-02 15:37:14 +0000428 if (fProgramDesc.fStages[s].isEnabled()) {
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000429 // 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;
Scroggo97c88c22011-05-11 14:05:25 +0000439 } 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());
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000444 inCoords = texCoordAttrs[tcIdx].c_str();
junov@google.comf93e7172011-03-31 21:26:24 +0000445 }
446
447 genStageCode(s,
448 fProgramDesc.fStages[s],
bsalomon@google.comfc296292011-05-06 13:53:47 +0000449 inColor.size() ? inColor.c_str() : NULL,
450 outColor.c_str(),
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000451 inCoords,
junov@google.comf93e7172011-03-31 21:26:24 +0000452 &segments,
453 &programData->fUniLocations.fStages[s]);
junov@google.comf93e7172011-03-31 21:26:24 +0000454 inColor = outColor;
junov@google.comf93e7172011-03-31 21:26:24 +0000455 }
456 }
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000457 }
Scroggo97c88c22011-05-11 14:05:25 +0000458
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000459 // if have all ones for the "dst" input to the color filter then we can make
460 // additional optimizations.
tomhudson@google.com0d831722011-06-02 15:37:14 +0000461 if (needColorFilterUniform && !inColor.size() &&
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000462 (SkXfermode::kIDC_Coeff == uniformCoeff ||
463 SkXfermode::kIDA_Coeff == uniformCoeff)) {
464 uniformCoeff = SkXfermode::kZero_Coeff;
465 bool bogus;
tomhudson@google.com0d831722011-06-02 15:37:14 +0000466 needBlendInputs(SkXfermode::kZero_Coeff, colorCoeff,
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000467 &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;
tomhudson@google.com0d831722011-06-02 15:37:14 +0000475 if (SkXfermode::kZero_Coeff == uniformCoeff &&
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000476 SkXfermode::kZero_Coeff == colorCoeff) {
tomhudson@google.com0d831722011-06-02 15:37:14 +0000477 segments.fFSCode.appendf("\t%s = %s;\n",
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000478 fsColorOutput,
479 all_zeros_vec(4));
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000480 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);
tomhudson@google.com0d831722011-06-02 15:37:14 +0000484 addColorFilter(&segments.fFSCode, "filteredColor", uniformCoeff,
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000485 colorCoeff, color);
486 inColor = "filteredColor";
487 }
488
489 ///////////////////////////////////////////////////////////////////////////
490 // compute the partial coverage (coverage stages and edge aa)
491
492 GrStringBuilder inCoverage;
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000493
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000494 // 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) {
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000498 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;
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000504 segments.fFSCode.append(
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000505 "\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 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000513 if (fProgramDesc.fEdgeAAConcave && (count & 0x01) == 0) {
514 // For concave polys, we consider the edges in pairs.
515 segments.fFSFunctions.append("float cross2(vec2 a, vec2 b) {\n");
516 segments.fFSFunctions.append("\treturn dot(a, vec2(b.y, -b.x));\n");
517 segments.fFSFunctions.append("}\n");
518 for (int i = 0; i < count; i += 2) {
519 segments.fFSCode.appendf("\tfloat eb%d;\n", i / 2);
520 segments.fFSCode.appendf("\tif (cross2(" EDGES_UNI_NAME "[%d].xy, " EDGES_UNI_NAME "[%d].xy) < 0.0) {\n", i, i + 1);
521 segments.fFSCode.appendf("\t\teb%d = a%d * a%d;\n", i / 2, i, i + 1);
522 segments.fFSCode.append("\t} else {\n");
523 segments.fFSCode.appendf("\t\teb%d = a%d + a%d - a%d * a%d;\n", i / 2, i, i + 1, i, i + 1);
524 segments.fFSCode.append("\t}\n");
525 }
526 segments.fFSCode.append("\tfloat edgeAlpha = ");
527 for (int i = 0; i < count / 2 - 1; i++) {
528 segments.fFSCode.appendf("min(eb%d, ", i);
529 }
530 segments.fFSCode.appendf("eb%d", count / 2 - 1);
531 for (int i = 0; i < count / 2 - 1; i++) {
532 segments.fFSCode.append(")");
533 }
534 segments.fFSCode.append(";\n");
535 } else {
536 segments.fFSCode.append("\tfloat edgeAlpha = ");
537 for (int i = 0; i < count - 1; i++) {
538 segments.fFSCode.appendf("min(a%d * a%d, ", i, i + 1);
539 }
540 segments.fFSCode.appendf("a%d * a0", count - 1);
541 for (int i = 0; i < count - 1; i++) {
542 segments.fFSCode.append(")");
543 }
544 segments.fFSCode.append(";\n");
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000545 }
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000546 inCoverage = "edgeAlpha";
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000547 }
548
549 GrStringBuilder outCoverage;
550 const int& startStage = fProgramDesc.fFirstCoverageStage;
551 for (int s = startStage; s < GrDrawTarget::kNumStages; ++s) {
tomhudson@google.com0d831722011-06-02 15:37:14 +0000552 if (fProgramDesc.fStages[s].isEnabled()) {
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000553 // create var to hold stage output
554 outCoverage = "coverage";
555 outCoverage.appendS32(s);
556 segments.fFSCode.appendf("\tvec4 %s;\n", outCoverage.c_str());
557
558 const char* inCoords;
559 // figure out what our input coords are
560 if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) & layout) {
561 inCoords = POS_ATTR_NAME;
562 } else {
563 int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout);
564 // we better have input tex coordinates if stage is enabled.
565 GrAssert(tcIdx >= 0);
566 GrAssert(texCoordAttrs[tcIdx].size());
567 inCoords = texCoordAttrs[tcIdx].c_str();
568 }
569
570 genStageCode(s,
571 fProgramDesc.fStages[s],
572 inCoverage.size() ? inCoverage.c_str() : NULL,
573 outCoverage.c_str(),
574 inCoords,
575 &segments,
576 &programData->fUniLocations.fStages[s]);
577 inCoverage = outCoverage;
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000578 }
579 }
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000580 if (ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
tomhudson@google.com0d831722011-06-02 15:37:14 +0000581 segments.fFSOutputs.appendf("out vec4 %s;\n",
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000582 dual_source_output_name());
583 bool outputIsZero = false;
584 GrStringBuilder coeff;
tomhudson@google.com0d831722011-06-02 15:37:14 +0000585 if (ProgramDesc::kCoverage_DualSrcOutput !=
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000586 fProgramDesc.fDualSrcOutput && !wroteFragColorZero) {
587 if (!inColor.size()) {
588 outputIsZero = true;
589 } else {
tomhudson@google.com0d831722011-06-02 15:37:14 +0000590 if (fProgramDesc.fDualSrcOutput ==
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000591 ProgramDesc::kCoverageISA_DualSrcOutput) {
592 coeff.printf("(1 - %s.a)", inColor.c_str());
593 } else {
594 coeff.printf("(vec4(1,1,1,1) - %s)", inColor.c_str());
595 }
596 }
597 }
598 if (outputIsZero) {
tomhudson@google.com0d831722011-06-02 15:37:14 +0000599 segments.fFSCode.appendf("\t%s = %s;\n",
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000600 dual_source_output_name(),
601 all_zeros_vec(4));
602 } else {
603 modulate_helper(dual_source_output_name(),
604 coeff.c_str(),
605 inCoverage.c_str(),
606 &segments.fFSCode);
607 }
608 dualSourceOutputWritten = true;
609 }
junov@google.comf93e7172011-03-31 21:26:24 +0000610 }
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000611
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000612 ///////////////////////////////////////////////////////////////////////////
613 // combine color and coverage as frag color
614
615 if (!wroteFragColorZero) {
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000616 modulate_helper(fsColorOutput,
617 inColor.c_str(),
618 inCoverage.c_str(),
619 &segments.fFSCode);
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000620 }
621
bsalomon@google.com91961302011-05-09 18:39:58 +0000622 segments.fVSCode.append("}\n");
623 segments.fFSCode.append("}\n");
624
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000625 ///////////////////////////////////////////////////////////////////////////
626 // compile and setup attribs and unis
627
bsalomon@google.com91961302011-05-09 18:39:58 +0000628 if (!CompileFSAndVS(segments, programData)) {
629 return false;
630 }
631
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000632 if (!this->bindOutputsAttribsAndLinkProgram(texCoordAttrs,
633 usingDeclaredOutputs,
634 dualSourceOutputWritten,
635 programData)) {
bsalomon@google.com91961302011-05-09 18:39:58 +0000636 return false;
637 }
638
639 this->getUniformLocationsAndInitCache(programData);
640
641 return true;
642}
643
644bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
645 CachedData* programData) {
junov@google.comf93e7172011-03-31 21:26:24 +0000646
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000647 static const int MAX_STRINGS = 6;
648 const char* strings[MAX_STRINGS];
649 int lengths[MAX_STRINGS];
junov@google.comf93e7172011-03-31 21:26:24 +0000650 int stringCnt = 0;
651
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000652 if (segments.fHeader.size()) {
653 strings[stringCnt] = segments.fHeader.c_str();
654 lengths[stringCnt] = segments.fHeader.size();
655 ++stringCnt;
656 }
bsalomon@google.comfc296292011-05-06 13:53:47 +0000657 if (segments.fVSUnis.size()) {
658 strings[stringCnt] = segments.fVSUnis.c_str();
659 lengths[stringCnt] = segments.fVSUnis.size();
junov@google.comf93e7172011-03-31 21:26:24 +0000660 ++stringCnt;
661 }
bsalomon@google.comfc296292011-05-06 13:53:47 +0000662 if (segments.fVSAttrs.size()) {
663 strings[stringCnt] = segments.fVSAttrs.c_str();
664 lengths[stringCnt] = segments.fVSAttrs.size();
junov@google.comf93e7172011-03-31 21:26:24 +0000665 ++stringCnt;
666 }
bsalomon@google.comfc296292011-05-06 13:53:47 +0000667 if (segments.fVaryings.size()) {
668 strings[stringCnt] = segments.fVaryings.c_str();
669 lengths[stringCnt] = segments.fVaryings.size();
junov@google.comf93e7172011-03-31 21:26:24 +0000670 ++stringCnt;
671 }
672
bsalomon@google.comfc296292011-05-06 13:53:47 +0000673 GrAssert(segments.fVSCode.size());
674 strings[stringCnt] = segments.fVSCode.c_str();
675 lengths[stringCnt] = segments.fVSCode.size();
junov@google.comf93e7172011-03-31 21:26:24 +0000676 ++stringCnt;
677
678#if PRINT_SHADERS
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000679 GrPrintf(segments.fHeader.c_str());
bsalomon@google.comfc296292011-05-06 13:53:47 +0000680 GrPrintf(segments.fVSUnis.c_str());
681 GrPrintf(segments.fVSAttrs.c_str());
682 GrPrintf(segments.fVaryings.c_str());
683 GrPrintf(segments.fVSCode.c_str());
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000684 GrPrintf("\n");
junov@google.comf93e7172011-03-31 21:26:24 +0000685#endif
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000686 GrAssert(stringCnt <= MAX_STRINGS);
junov@google.comf93e7172011-03-31 21:26:24 +0000687 programData->fVShaderID = CompileShader(GR_GL_VERTEX_SHADER,
688 stringCnt,
689 strings,
690 lengths);
691
bsalomon@google.com91961302011-05-09 18:39:58 +0000692 if (!programData->fVShaderID) {
693 return false;
694 }
695
junov@google.comf93e7172011-03-31 21:26:24 +0000696 stringCnt = 0;
697
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000698 if (segments.fHeader.size()) {
699 strings[stringCnt] = segments.fHeader.c_str();
700 lengths[stringCnt] = segments.fHeader.size();
701 ++stringCnt;
702 }
junov@google.comf93e7172011-03-31 21:26:24 +0000703 if (strlen(GrShaderPrecision()) > 1) {
704 strings[stringCnt] = GrShaderPrecision();
705 lengths[stringCnt] = strlen(GrShaderPrecision());
706 ++stringCnt;
707 }
bsalomon@google.comfc296292011-05-06 13:53:47 +0000708 if (segments.fFSUnis.size()) {
709 strings[stringCnt] = segments.fFSUnis.c_str();
710 lengths[stringCnt] = segments.fFSUnis.size();
junov@google.comf93e7172011-03-31 21:26:24 +0000711 ++stringCnt;
712 }
bsalomon@google.comfc296292011-05-06 13:53:47 +0000713 if (segments.fVaryings.size()) {
714 strings[stringCnt] = segments.fVaryings.c_str();
715 lengths[stringCnt] = segments.fVaryings.size();
junov@google.comf93e7172011-03-31 21:26:24 +0000716 ++stringCnt;
717 }
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000718 if (segments.fFSOutputs.size()) {
719 strings[stringCnt] = segments.fFSOutputs.c_str();
720 lengths[stringCnt] = segments.fFSOutputs.size();
721 ++stringCnt;
722 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000723 if (segments.fFSFunctions.size()) {
724 strings[stringCnt] = segments.fFSFunctions.c_str();
725 lengths[stringCnt] = segments.fFSFunctions.size();
726 ++stringCnt;
727 }
junov@google.comf93e7172011-03-31 21:26:24 +0000728
bsalomon@google.comfc296292011-05-06 13:53:47 +0000729 GrAssert(segments.fFSCode.size());
730 strings[stringCnt] = segments.fFSCode.c_str();
731 lengths[stringCnt] = segments.fFSCode.size();
junov@google.comf93e7172011-03-31 21:26:24 +0000732 ++stringCnt;
733
734#if PRINT_SHADERS
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000735 GrPrintf(segments.fHeader.c_str());
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000736 GrPrintf(GrShaderPrecision());
bsalomon@google.comfc296292011-05-06 13:53:47 +0000737 GrPrintf(segments.fFSUnis.c_str());
738 GrPrintf(segments.fVaryings.c_str());
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000739 GrPrintf(segments.fFSOutputs.c_str());
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000740 GrPrintf(segments.fFSFunctions.c_str());
bsalomon@google.comfc296292011-05-06 13:53:47 +0000741 GrPrintf(segments.fFSCode.c_str());
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000742 GrPrintf("\n");
junov@google.comf93e7172011-03-31 21:26:24 +0000743#endif
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000744 GrAssert(stringCnt <= MAX_STRINGS);
junov@google.comf93e7172011-03-31 21:26:24 +0000745 programData->fFShaderID = CompileShader(GR_GL_FRAGMENT_SHADER,
746 stringCnt,
747 strings,
748 lengths);
749
bsalomon@google.com91961302011-05-09 18:39:58 +0000750 if (!programData->fFShaderID) {
751 return false;
junov@google.comf93e7172011-03-31 21:26:24 +0000752 }
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000753
bsalomon@google.com91961302011-05-09 18:39:58 +0000754 return true;
junov@google.comf93e7172011-03-31 21:26:24 +0000755}
756
757GrGLuint GrGLProgram::CompileShader(GrGLenum type,
758 int stringCnt,
759 const char** strings,
760 int* stringLengths) {
tomhudson@google.com278cbb42011-06-30 19:37:01 +0000761 SK_TRACE_EVENT1("GrGLProgram::CompileShader",
762 "stringCount", SkStringPrintf("%i", stringCnt).c_str());
763
junov@google.comf93e7172011-03-31 21:26:24 +0000764 GrGLuint shader = GR_GL(CreateShader(type));
765 if (0 == shader) {
766 return 0;
767 }
768
769 GrGLint compiled = GR_GL_INIT_ZERO;
770 GR_GL(ShaderSource(shader, stringCnt, strings, stringLengths));
771 GR_GL(CompileShader(shader));
772 GR_GL(GetShaderiv(shader, GR_GL_COMPILE_STATUS, &compiled));
773
774 if (!compiled) {
775 GrGLint infoLen = GR_GL_INIT_ZERO;
776 GR_GL(GetShaderiv(shader, GR_GL_INFO_LOG_LENGTH, &infoLen));
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000777 SkAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger
junov@google.comf93e7172011-03-31 21:26:24 +0000778 if (infoLen > 0) {
779 GR_GL(GetShaderInfoLog(shader, infoLen+1, NULL, (char*)log.get()));
780 for (int i = 0; i < stringCnt; ++i) {
781 if (NULL == stringLengths || stringLengths[i] < 0) {
782 GrPrintf(strings[i]);
783 } else {
784 GrPrintf("%.*s", stringLengths[i], strings[i]);
785 }
786 }
787 GrPrintf("\n%s", log.get());
788 }
789 GrAssert(!"Shader compilation failed!");
790 GR_GL(DeleteShader(shader));
791 return 0;
792 }
793 return shader;
794}
795
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000796bool GrGLProgram::bindOutputsAttribsAndLinkProgram(
797 GrStringBuilder texCoordAttrNames[],
798 bool bindColorOut,
tomhudson@google.com0d831722011-06-02 15:37:14 +0000799 bool bindDualSrcOut,
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000800 CachedData* programData) const {
bsalomon@google.com91961302011-05-09 18:39:58 +0000801 programData->fProgramID = GR_GL(CreateProgram());
802 if (!programData->fProgramID) {
803 return false;
804 }
805 const GrGLint& progID = programData->fProgramID;
806
807 GR_GL(AttachShader(progID, programData->fVShaderID));
808 GR_GL(AttachShader(progID, programData->fFShaderID));
809
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000810 if (bindColorOut) {
tomhudson@google.com0d831722011-06-02 15:37:14 +0000811 GR_GL(BindFragDataLocationIndexed(programData->fProgramID,
bsalomon@google.com271cffc2011-05-20 14:13:56 +0000812 0, 0, declared_color_output_name()));
813 }
814 if (bindDualSrcOut) {
815 GR_GL(BindFragDataLocationIndexed(programData->fProgramID,
816 0, 1, dual_source_output_name()));
817 }
818
bsalomon@google.com91961302011-05-09 18:39:58 +0000819 // Bind the attrib locations to same values for all shaders
820 GR_GL(BindAttribLocation(progID, PositionAttributeIdx(), POS_ATTR_NAME));
821 for (int t = 0; t < GrDrawTarget::kMaxTexCoords; ++t) {
822 if (texCoordAttrNames[t].size()) {
823 GR_GL(BindAttribLocation(progID,
824 TexCoordAttributeIdx(t),
825 texCoordAttrNames[t].c_str()));
826 }
827 }
828
bsalomon@google.com91961302011-05-09 18:39:58 +0000829 if (kSetAsAttribute == programData->fUniLocations.fViewMatrixUni) {
830 GR_GL(BindAttribLocation(progID,
831 ViewMatrixAttributeIdx(),
832 VIEW_MATRIX_NAME));
833 }
834
835 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
bsalomon@google.com72b4fcb2011-05-09 20:47:34 +0000836 const StageUniLocations& unis = programData->fUniLocations.fStages[s];
837 if (kSetAsAttribute == unis.fTextureMatrixUni) {
bsalomon@google.com91961302011-05-09 18:39:58 +0000838 GrStringBuilder matName;
839 tex_matrix_name(s, &matName);
840 GR_GL(BindAttribLocation(progID,
841 TextureMatrixAttributeIdx(s),
842 matName.c_str()));
843 }
844 }
845
bsalomon@google.com91961302011-05-09 18:39:58 +0000846 GR_GL(BindAttribLocation(progID, ColorAttributeIdx(), COL_ATTR_NAME));
847
848 GR_GL(LinkProgram(progID));
849
850 GrGLint linked = GR_GL_INIT_ZERO;
851 GR_GL(GetProgramiv(progID, GR_GL_LINK_STATUS, &linked));
852 if (!linked) {
853 GrGLint infoLen = GR_GL_INIT_ZERO;
854 GR_GL(GetProgramiv(progID, GR_GL_INFO_LOG_LENGTH, &infoLen));
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000855 SkAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger
bsalomon@google.com91961302011-05-09 18:39:58 +0000856 if (infoLen > 0) {
857 GR_GL(GetProgramInfoLog(progID,
858 infoLen+1,
859 NULL,
860 (char*)log.get()));
861 GrPrintf((char*)log.get());
862 }
863 GrAssert(!"Error linking program");
864 GR_GL(DeleteProgram(progID));
865 programData->fProgramID = 0;
866 return false;
867 }
868 return true;
869}
870
871void GrGLProgram::getUniformLocationsAndInitCache(CachedData* programData) const {
872 const GrGLint& progID = programData->fProgramID;
873
874 if (kUseUniform == programData->fUniLocations.fViewMatrixUni) {
875 programData->fUniLocations.fViewMatrixUni =
876 GR_GL(GetUniformLocation(progID, VIEW_MATRIX_NAME));
877 GrAssert(kUnusedUniform != programData->fUniLocations.fViewMatrixUni);
878 }
879 if (kUseUniform == programData->fUniLocations.fColorUni) {
tomhudson@google.com0d831722011-06-02 15:37:14 +0000880 programData->fUniLocations.fColorUni =
bsalomon@google.com91961302011-05-09 18:39:58 +0000881 GR_GL(GetUniformLocation(progID, COL_UNI_NAME));
882 GrAssert(kUnusedUniform != programData->fUniLocations.fColorUni);
883 }
Scroggo97c88c22011-05-11 14:05:25 +0000884 if (kUseUniform == programData->fUniLocations.fColorFilterUni) {
tomhudson@google.com0d831722011-06-02 15:37:14 +0000885 programData->fUniLocations.fColorFilterUni =
Scroggo97c88c22011-05-11 14:05:25 +0000886 GR_GL(GetUniformLocation(progID, COL_FILTER_UNI_NAME));
887 GrAssert(kUnusedUniform != programData->fUniLocations.fColorFilterUni);
888 }
bsalomon@google.com91961302011-05-09 18:39:58 +0000889
bsalomon@google.comf2d91552011-05-16 20:56:06 +0000890 if (kUseUniform == programData->fUniLocations.fEdgesUni) {
tomhudson@google.com0d831722011-06-02 15:37:14 +0000891 programData->fUniLocations.fEdgesUni =
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000892 GR_GL(GetUniformLocation(progID, EDGES_UNI_NAME));
893 GrAssert(kUnusedUniform != programData->fUniLocations.fEdgesUni);
894 } else {
895 programData->fUniLocations.fEdgesUni = kUnusedUniform;
896 }
897
bsalomon@google.com91961302011-05-09 18:39:58 +0000898 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
899 StageUniLocations& locations = programData->fUniLocations.fStages[s];
tomhudson@google.com0d831722011-06-02 15:37:14 +0000900 if (fProgramDesc.fStages[s].isEnabled()) {
bsalomon@google.com91961302011-05-09 18:39:58 +0000901 if (kUseUniform == locations.fTextureMatrixUni) {
902 GrStringBuilder texMName;
903 tex_matrix_name(s, &texMName);
904 locations.fTextureMatrixUni = GR_GL(GetUniformLocation(
905 progID,
906 texMName.c_str()));
907 GrAssert(kUnusedUniform != locations.fTextureMatrixUni);
908 }
909
910 if (kUseUniform == locations.fSamplerUni) {
911 GrStringBuilder samplerName;
912 sampler_name(s, &samplerName);
913 locations.fSamplerUni = GR_GL(GetUniformLocation(
914 progID,
915 samplerName.c_str()));
916 GrAssert(kUnusedUniform != locations.fSamplerUni);
917 }
918
919 if (kUseUniform == locations.fNormalizedTexelSizeUni) {
920 GrStringBuilder texelSizeName;
921 normalized_texel_size_name(s, &texelSizeName);
tomhudson@google.com0d831722011-06-02 15:37:14 +0000922 locations.fNormalizedTexelSizeUni =
bsalomon@google.com91961302011-05-09 18:39:58 +0000923 GR_GL(GetUniformLocation(progID, texelSizeName.c_str()));
924 GrAssert(kUnusedUniform != locations.fNormalizedTexelSizeUni);
925 }
926
927 if (kUseUniform == locations.fRadial2Uni) {
928 GrStringBuilder radial2ParamName;
929 radial2_param_name(s, &radial2ParamName);
930 locations.fRadial2Uni = GR_GL(GetUniformLocation(
931 progID,
932 radial2ParamName.c_str()));
933 GrAssert(kUnusedUniform != locations.fRadial2Uni);
934 }
junov@google.com6acc9b32011-05-16 18:32:07 +0000935
936 if (kUseUniform == locations.fTexDomUni) {
937 GrStringBuilder texDomName;
938 tex_domain_name(s, &texDomName);
939 locations.fTexDomUni = GR_GL(GetUniformLocation(
940 progID,
941 texDomName.c_str()));
942 GrAssert(kUnusedUniform != locations.fTexDomUni);
943 }
bsalomon@google.com91961302011-05-09 18:39:58 +0000944 }
945 }
946 GR_GL(UseProgram(progID));
947
948 // init sampler unis and set bogus values for state tracking
949 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
950 if (kUnusedUniform != programData->fUniLocations.fStages[s].fSamplerUni) {
951 GR_GL(Uniform1i(programData->fUniLocations.fStages[s].fSamplerUni, s));
952 }
953 programData->fTextureMatrices[s] = GrMatrix::InvalidMatrix();
954 programData->fRadial2CenterX1[s] = GR_ScalarMax;
955 programData->fRadial2Radius0[s] = -GR_ScalarMax;
956 programData->fTextureWidth[s] = -1;
957 programData->fTextureHeight[s] = -1;
958 }
959 programData->fViewMatrix = GrMatrix::InvalidMatrix();
960 programData->fColor = GrColor_ILLEGAL;
Scroggo97c88c22011-05-11 14:05:25 +0000961 programData->fColorFilterColor = GrColor_ILLEGAL;
bsalomon@google.com91961302011-05-09 18:39:58 +0000962}
963
junov@google.comf93e7172011-03-31 21:26:24 +0000964//============================================================================
965// Stage code generation
966//============================================================================
967
968void GrGLProgram::genStageCode(int stageNum,
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000969 const GrGLProgram::ProgramDesc::StageDesc& desc,
970 const char* fsInColor, // NULL means no incoming color
971 const char* fsOutColor,
972 const char* vsInCoord,
973 ShaderCodeSegments* segments,
974 StageUniLocations* locations) const {
junov@google.comf93e7172011-03-31 21:26:24 +0000975
976 GrAssert(stageNum >= 0 && stageNum <= 9);
977
bsalomon@google.com91961302011-05-09 18:39:58 +0000978 GrStringBuilder varyingName;
junov@google.comf93e7172011-03-31 21:26:24 +0000979 stage_varying_name(stageNum, &varyingName);
980
981 // First decide how many coords are needed to access the texture
982 // Right now it's always 2 but we could start using 1D textures for
983 // gradients.
984 static const int coordDims = 2;
985 int varyingDims;
986 /// Vertex Shader Stuff
987
988 // decide whether we need a matrix to transform texture coords
989 // and whether the varying needs a perspective coord.
bsalomon@google.com91961302011-05-09 18:39:58 +0000990 GrStringBuilder texMName;
junov@google.comf93e7172011-03-31 21:26:24 +0000991 tex_matrix_name(stageNum, &texMName);
992 if (desc.fOptFlags & ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit) {
993 varyingDims = coordDims;
994 } else {
bsalomon@google.com4be283f2011-04-19 21:15:09 +0000995 #if GR_GL_ATTRIBUTE_MATRICES
bsalomon@google.com91961302011-05-09 18:39:58 +0000996 segments->fVSAttrs.appendf("attribute mat3 %s;\n", texMName.c_str());
997 locations->fTextureMatrixUni = kSetAsAttribute;
junov@google.comf93e7172011-03-31 21:26:24 +0000998 #else
bsalomon@google.com91961302011-05-09 18:39:58 +0000999 segments->fVSUnis.appendf("uniform mat3 %s;\n", texMName.c_str());
1000 locations->fTextureMatrixUni = kUseUniform;
junov@google.comf93e7172011-03-31 21:26:24 +00001001 #endif
1002 if (desc.fOptFlags & ProgramDesc::StageDesc::kNoPerspective_OptFlagBit) {
1003 varyingDims = coordDims;
1004 } else {
1005 varyingDims = coordDims + 1;
1006 }
1007 }
1008
bsalomon@google.com91961302011-05-09 18:39:58 +00001009 GrStringBuilder samplerName;
junov@google.comf93e7172011-03-31 21:26:24 +00001010 sampler_name(stageNum, &samplerName);
bsalomon@google.com91961302011-05-09 18:39:58 +00001011 segments->fFSUnis.appendf("uniform sampler2D %s;\n", samplerName.c_str());
1012 locations->fSamplerUni = kUseUniform;
junov@google.comf93e7172011-03-31 21:26:24 +00001013
bsalomon@google.com91961302011-05-09 18:39:58 +00001014 GrStringBuilder texelSizeName;
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001015 if (ProgramDesc::StageDesc::k2x2_FetchMode == desc.fFetchMode) {
1016 normalized_texel_size_name(stageNum, &texelSizeName);
bsalomon@google.com91961302011-05-09 18:39:58 +00001017 segments->fFSUnis.appendf("uniform vec2 %s;\n", texelSizeName.c_str());
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001018 }
1019
tomhudson@google.com0d831722011-06-02 15:37:14 +00001020 segments->fVaryings.appendf("varying %s %s;\n",
bsalomon@google.com91961302011-05-09 18:39:58 +00001021 float_vector_type(varyingDims), varyingName.c_str());
junov@google.comf93e7172011-03-31 21:26:24 +00001022
1023 if (desc.fOptFlags & ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit) {
1024 GrAssert(varyingDims == coordDims);
bsalomon@google.com91961302011-05-09 18:39:58 +00001025 segments->fVSCode.appendf("\t%s = %s;\n", varyingName.c_str(), vsInCoord);
junov@google.comf93e7172011-03-31 21:26:24 +00001026 } else {
bsalomon@google.com91961302011-05-09 18:39:58 +00001027 // varying = texMatrix * texCoord
1028 segments->fVSCode.appendf("\t%s = (%s * vec3(%s, 1))%s;\n",
1029 varyingName.c_str(), texMName.c_str(),
1030 vsInCoord, vector_all_coords(varyingDims));
junov@google.comf93e7172011-03-31 21:26:24 +00001031 }
1032
bsalomon@google.com91961302011-05-09 18:39:58 +00001033 GrStringBuilder radial2ParamsName;
junov@google.comf93e7172011-03-31 21:26:24 +00001034 radial2_param_name(stageNum, &radial2ParamsName);
1035 // for radial grads without perspective we can pass the linear
1036 // part of the quadratic as a varying.
bsalomon@google.com91961302011-05-09 18:39:58 +00001037 GrStringBuilder radial2VaryingName;
junov@google.comf93e7172011-03-31 21:26:24 +00001038 radial2_varying_name(stageNum, &radial2VaryingName);
1039
1040 if (ProgramDesc::StageDesc::kRadial2Gradient_CoordMapping == desc.fCoordMapping) {
1041
tomhudson@google.com0d831722011-06-02 15:37:14 +00001042 segments->fVSUnis.appendf("uniform %s float %s[6];\n",
bsalomon@google.com91961302011-05-09 18:39:58 +00001043 GrPrecision(), radial2ParamsName.c_str());
tomhudson@google.com0d831722011-06-02 15:37:14 +00001044 segments->fFSUnis.appendf("uniform float %s[6];\n",
bsalomon@google.com91961302011-05-09 18:39:58 +00001045 radial2ParamsName.c_str());
1046 locations->fRadial2Uni = kUseUniform;
junov@google.comf93e7172011-03-31 21:26:24 +00001047
1048 // if there is perspective we don't interpolate this
1049 if (varyingDims == coordDims) {
1050 GrAssert(2 == coordDims);
bsalomon@google.com91961302011-05-09 18:39:58 +00001051 segments->fVaryings.appendf("varying float %s;\n", radial2VaryingName.c_str());
junov@google.comf93e7172011-03-31 21:26:24 +00001052
bsalomon@google.com91961302011-05-09 18:39:58 +00001053 // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3])
1054 segments->fVSCode.appendf("\t%s = 2.0 *(%s[2] * %s.x - %s[3]);\n",
1055 radial2VaryingName.c_str(), radial2ParamsName.c_str(),
1056 varyingName.c_str(), radial2ParamsName.c_str());
junov@google.comf93e7172011-03-31 21:26:24 +00001057 }
1058 }
1059
1060 /// Fragment Shader Stuff
bsalomon@google.com91961302011-05-09 18:39:58 +00001061 GrStringBuilder fsCoordName;
junov@google.comf93e7172011-03-31 21:26:24 +00001062 // function used to access the shader, may be made projective
bsalomon@google.com91961302011-05-09 18:39:58 +00001063 GrStringBuilder texFunc("texture2D");
junov@google.comf93e7172011-03-31 21:26:24 +00001064 if (desc.fOptFlags & (ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit |
1065 ProgramDesc::StageDesc::kNoPerspective_OptFlagBit)) {
1066 GrAssert(varyingDims == coordDims);
1067 fsCoordName = varyingName;
1068 } else {
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001069 // if we have to do some special op on the varyings to get
junov@google.comf93e7172011-03-31 21:26:24 +00001070 // our final tex coords then when in perspective we have to
bsalomon@google.com91961302011-05-09 18:39:58 +00001071 // do an explicit divide. Otherwise, we can use a Proj func.
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001072 if (ProgramDesc::StageDesc::kIdentity_CoordMapping == desc.fCoordMapping &&
1073 ProgramDesc::StageDesc::kSingle_FetchMode == desc.fFetchMode) {
bsalomon@google.com91961302011-05-09 18:39:58 +00001074 texFunc.append("Proj");
junov@google.comf93e7172011-03-31 21:26:24 +00001075 fsCoordName = varyingName;
1076 } else {
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001077 fsCoordName = "inCoord";
bsalomon@google.comfc296292011-05-06 13:53:47 +00001078 fsCoordName.appendS32(stageNum);
bsalomon@google.com91961302011-05-09 18:39:58 +00001079 segments->fFSCode.appendf("\t%s %s = %s%s / %s%s;\n",
1080 float_vector_type(coordDims),
1081 fsCoordName.c_str(),
1082 varyingName.c_str(),
1083 vector_nonhomog_coords(varyingDims),
1084 varyingName.c_str(),
1085 vector_homog_coord(varyingDims));
junov@google.comf93e7172011-03-31 21:26:24 +00001086 }
1087 }
1088
bsalomon@google.comfc296292011-05-06 13:53:47 +00001089 GrStringBuilder sampleCoords;
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001090 bool complexCoord = false;
junov@google.comf93e7172011-03-31 21:26:24 +00001091 switch (desc.fCoordMapping) {
1092 case ProgramDesc::StageDesc::kIdentity_CoordMapping:
1093 sampleCoords = fsCoordName;
1094 break;
1095 case ProgramDesc::StageDesc::kSweepGradient_CoordMapping:
bsalomon@google.com91961302011-05-09 18:39:58 +00001096 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 +00001097 complexCoord = true;
junov@google.comf93e7172011-03-31 21:26:24 +00001098 break;
1099 case ProgramDesc::StageDesc::kRadialGradient_CoordMapping:
bsalomon@google.com91961302011-05-09 18:39:58 +00001100 sampleCoords.printf("vec2(length(%s.xy), 0.5)", fsCoordName.c_str());
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001101 complexCoord = true;
junov@google.comf93e7172011-03-31 21:26:24 +00001102 break;
1103 case ProgramDesc::StageDesc::kRadial2Gradient_CoordMapping: {
bsalomon@google.com91961302011-05-09 18:39:58 +00001104 GrStringBuilder cName("c");
1105 GrStringBuilder ac4Name("ac4");
1106 GrStringBuilder rootName("root");
junov@google.comf93e7172011-03-31 21:26:24 +00001107
bsalomon@google.comfc296292011-05-06 13:53:47 +00001108 cName.appendS32(stageNum);
1109 ac4Name.appendS32(stageNum);
1110 rootName.appendS32(stageNum);
junov@google.comf93e7172011-03-31 21:26:24 +00001111
bsalomon@google.com91961302011-05-09 18:39:58 +00001112 // if we were able to interpolate the linear component bVar is the varying
1113 // otherwise compute it
1114 GrStringBuilder bVar;
junov@google.comf93e7172011-03-31 21:26:24 +00001115 if (coordDims == varyingDims) {
1116 bVar = radial2VaryingName;
1117 GrAssert(2 == varyingDims);
1118 } else {
1119 GrAssert(3 == varyingDims);
1120 bVar = "b";
bsalomon@google.comfc296292011-05-06 13:53:47 +00001121 bVar.appendS32(stageNum);
bsalomon@google.com91961302011-05-09 18:39:58 +00001122 segments->fFSCode.appendf("\tfloat %s = 2.0 * (%s[2] * %s.x - %s[3]);\n",
1123 bVar.c_str(), radial2ParamsName.c_str(),
1124 fsCoordName.c_str(), radial2ParamsName.c_str());
junov@google.comf93e7172011-03-31 21:26:24 +00001125 }
1126
bsalomon@google.com91961302011-05-09 18:39:58 +00001127 // c = (x^2)+(y^2) - params[4]
1128 segments->fFSCode.appendf("\tfloat %s = dot(%s, %s) - %s[4];\n",
1129 cName.c_str(), fsCoordName.c_str(),
1130 fsCoordName.c_str(),
1131 radial2ParamsName.c_str());
1132 // ac4 = 4.0 * params[0] * c
1133 segments->fFSCode.appendf("\tfloat %s = %s[0] * 4.0 * %s;\n",
1134 ac4Name.c_str(), radial2ParamsName.c_str(),
1135 cName.c_str());
junov@google.comf93e7172011-03-31 21:26:24 +00001136
bsalomon@google.com91961302011-05-09 18:39:58 +00001137 // root = sqrt(b^2-4ac)
1138 // (abs to avoid exception due to fp precision)
1139 segments->fFSCode.appendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
1140 rootName.c_str(), bVar.c_str(), bVar.c_str(),
1141 ac4Name.c_str());
junov@google.comf93e7172011-03-31 21:26:24 +00001142
bsalomon@google.com91961302011-05-09 18:39:58 +00001143 // x coord is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
1144 // y coord is 0.5 (texture is effectively 1D)
1145 sampleCoords.printf("vec2((-%s + %s[5] * %s) * %s[1], 0.5)",
1146 bVar.c_str(), radial2ParamsName.c_str(),
1147 rootName.c_str(), radial2ParamsName.c_str());
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001148 complexCoord = true;
junov@google.comf93e7172011-03-31 21:26:24 +00001149 break;}
1150 };
1151
bsalomon@google.com91961302011-05-09 18:39:58 +00001152 const char* smear;
1153 if (desc.fModulation == ProgramDesc::StageDesc::kAlpha_Modulation) {
1154 smear = ".aaaa";
1155 } else {
1156 smear = "";
1157 }
1158 GrStringBuilder modulate;
1159 if (NULL != fsInColor) {
1160 modulate.printf(" * %s", fsInColor);
1161 }
1162
tomhudson@google.com0d831722011-06-02 15:37:14 +00001163 if (desc.fOptFlags &
junov@google.com6acc9b32011-05-16 18:32:07 +00001164 ProgramDesc::StageDesc::kCustomTextureDomain_OptFlagBit) {
1165 GrStringBuilder texDomainName;
1166 tex_domain_name(stageNum, &texDomainName);
tomhudson@google.com0d831722011-06-02 15:37:14 +00001167 segments->fFSUnis.appendf("uniform %s %s;\n",
junov@google.com6acc9b32011-05-16 18:32:07 +00001168 float_vector_type(4),
1169 texDomainName.c_str());
1170 GrStringBuilder coordVar("clampCoord");
1171 segments->fFSCode.appendf("\t%s %s = clamp(%s, %s.xy, %s.zw);\n",
1172 float_vector_type(coordDims),
1173 coordVar.c_str(),
1174 sampleCoords.c_str(),
1175 texDomainName.c_str(),
1176 texDomainName.c_str());
1177 sampleCoords = coordVar;
1178 locations->fTexDomUni = kUseUniform;
1179 }
1180
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001181 if (ProgramDesc::StageDesc::k2x2_FetchMode == desc.fFetchMode) {
bsalomon@google.com91961302011-05-09 18:39:58 +00001182 locations->fNormalizedTexelSizeUni = kUseUniform;
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001183 if (complexCoord) {
bsalomon@google.com91961302011-05-09 18:39:58 +00001184 // assign the coord to a var rather than compute 4x.
1185 GrStringBuilder coordVar("tCoord");
bsalomon@google.comfc296292011-05-06 13:53:47 +00001186 coordVar.appendS32(stageNum);
bsalomon@google.com91961302011-05-09 18:39:58 +00001187 segments->fFSCode.appendf("\t%s %s = %s;\n",
1188 float_vector_type(coordDims),
1189 coordVar.c_str(), sampleCoords.c_str());
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001190 sampleCoords = coordVar;
1191 }
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001192 GrAssert(2 == coordDims);
bsalomon@google.com91961302011-05-09 18:39:58 +00001193 GrStringBuilder accumVar("accum");
1194 accumVar.appendS32(stageNum);
1195 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);
1196 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);
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 = .25 * %s%s;\n", fsOutColor, accumVar.c_str(), modulate.c_str());
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001200 } else {
bsalomon@google.com271cffc2011-05-20 14:13:56 +00001201 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 +00001202 }
junov@google.comf93e7172011-03-31 21:26:24 +00001203}