blob: 55238c8addb9085598c9efffda9fc0330da65cb1 [file] [log] [blame]
zmo@google.com5601ea02011-06-10 18:23:25 +00001//
Jamie Madill02f20dd2013-09-12 12:07:42 -04002// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
zmo@google.com5601ea02011-06-10 18:23:25 +00003// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
Geoff Lang17732822013-08-29 13:46:49 -04007#include "compiler/translator/OutputGLSLBase.h"
8#include "compiler/translator/compilerdebug.h"
zmo@google.com5601ea02011-06-10 18:23:25 +00009
daniel@transgaming.com773ff742013-01-11 04:12:51 +000010#include <cfloat>
daniel@transgaming.com6c1203f2013-01-11 04:12:43 +000011
zmo@google.com5601ea02011-06-10 18:23:25 +000012namespace
13{
zmo@google.com5601ea02011-06-10 18:23:25 +000014TString arrayBrackets(const TType& type)
15{
16 ASSERT(type.isArray());
17 TInfoSinkBase out;
18 out << "[" << type.getArraySize() << "]";
19 return TString(out.c_str());
20}
21
22bool isSingleStatement(TIntermNode* node) {
23 if (const TIntermAggregate* aggregate = node->getAsAggregate())
24 {
25 return (aggregate->getOp() != EOpFunction) &&
26 (aggregate->getOp() != EOpSequence);
27 }
28 else if (const TIntermSelection* selection = node->getAsSelectionNode())
29 {
30 // Ternary operators are usually part of an assignment operator.
31 // This handles those rare cases in which they are all by themselves.
32 return selection->usesTernaryOperator();
33 }
34 else if (node->getAsLoopNode())
35 {
36 return false;
37 }
38 return true;
39}
40} // namespace
41
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +000042TOutputGLSLBase::TOutputGLSLBase(TInfoSinkBase& objSink,
shannon.woods@transgaming.com1d432bb2013-01-25 21:57:28 +000043 ShArrayIndexClampingStrategy clampingStrategy,
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +000044 ShHashFunction64 hashFunction,
45 NameMap& nameMap,
Jamie Madill02f20dd2013-09-12 12:07:42 -040046 TSymbolTable& symbolTable,
47 int shaderVersion)
zmo@google.com5601ea02011-06-10 18:23:25 +000048 : TIntermTraverser(true, true, true),
49 mObjSink(objSink),
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +000050 mDeclaringVariables(false),
shannon.woods@transgaming.com1d432bb2013-01-25 21:57:28 +000051 mClampingStrategy(clampingStrategy),
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +000052 mHashFunction(hashFunction),
53 mNameMap(nameMap),
Jamie Madill02f20dd2013-09-12 12:07:42 -040054 mSymbolTable(symbolTable),
55 mShaderVersion(shaderVersion)
zmo@google.com5601ea02011-06-10 18:23:25 +000056{
57}
58
59void TOutputGLSLBase::writeTriplet(Visit visit, const char* preStr, const char* inStr, const char* postStr)
60{
61 TInfoSinkBase& out = objSink();
62 if (visit == PreVisit && preStr)
63 {
64 out << preStr;
65 }
66 else if (visit == InVisit && inStr)
67 {
68 out << inStr;
69 }
70 else if (visit == PostVisit && postStr)
71 {
72 out << postStr;
73 }
74}
75
76void TOutputGLSLBase::writeVariableType(const TType& type)
77{
78 TInfoSinkBase& out = objSink();
79 TQualifier qualifier = type.getQualifier();
80 // TODO(alokp): Validate qualifier for variable declarations.
81 if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal))
82 out << type.getQualifierString() << " ";
83 // Declare the struct if we have not done so already.
Jamie Madill98493dd2013-07-08 14:39:03 -040084 if ((type.getBasicType() == EbtStruct) && !structDeclared(type.getStruct()))
zmo@google.com5601ea02011-06-10 18:23:25 +000085 {
Jamie Madill98493dd2013-07-08 14:39:03 -040086 declareStruct(type.getStruct());
zmo@google.com5601ea02011-06-10 18:23:25 +000087 }
88 else
89 {
90 if (writeVariablePrecision(type.getPrecision()))
91 out << " ";
92 out << getTypeName(type);
93 }
94}
95
96void TOutputGLSLBase::writeFunctionParameters(const TIntermSequence& args)
97{
98 TInfoSinkBase& out = objSink();
99 for (TIntermSequence::const_iterator iter = args.begin();
100 iter != args.end(); ++iter)
101 {
102 const TIntermSymbol* arg = (*iter)->getAsSymbolNode();
103 ASSERT(arg != NULL);
104
105 const TType& type = arg->getType();
zmo@google.com189be2f2011-06-16 18:28:53 +0000106 writeVariableType(type);
zmo@google.com5601ea02011-06-10 18:23:25 +0000107
108 const TString& name = arg->getSymbol();
109 if (!name.empty())
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +0000110 out << " " << hashName(name);
zmo@google.com5601ea02011-06-10 18:23:25 +0000111 if (type.isArray())
112 out << arrayBrackets(type);
113
114 // Put a comma if this is not the last argument.
115 if (iter != args.end() - 1)
116 out << ", ";
117 }
118}
119
120const ConstantUnion* TOutputGLSLBase::writeConstantUnion(const TType& type,
121 const ConstantUnion* pConstUnion)
122{
123 TInfoSinkBase& out = objSink();
124
125 if (type.getBasicType() == EbtStruct)
126 {
Jamie Madill98493dd2013-07-08 14:39:03 -0400127 const TStructure* structure = type.getStruct();
128 out << hashName(structure->name()) << "(";
129
130 const TFieldList& fields = structure->fields();
131 for (size_t i = 0; i < fields.size(); ++i)
zmo@google.com5601ea02011-06-10 18:23:25 +0000132 {
Jamie Madill98493dd2013-07-08 14:39:03 -0400133 const TType* fieldType = fields[i]->type();
zmo@google.com5601ea02011-06-10 18:23:25 +0000134 ASSERT(fieldType != NULL);
135 pConstUnion = writeConstantUnion(*fieldType, pConstUnion);
Jamie Madill98493dd2013-07-08 14:39:03 -0400136 if (i != fields.size() - 1) out << ", ";
zmo@google.com5601ea02011-06-10 18:23:25 +0000137 }
138 out << ")";
139 }
140 else
141 {
Jamie Madill94bf7f22013-07-08 13:31:15 -0400142 size_t size = type.getObjectSize();
zmo@google.com5601ea02011-06-10 18:23:25 +0000143 bool writeType = size > 1;
144 if (writeType) out << getTypeName(type) << "(";
Jamie Madill94bf7f22013-07-08 13:31:15 -0400145 for (size_t i = 0; i < size; ++i, ++pConstUnion)
zmo@google.com5601ea02011-06-10 18:23:25 +0000146 {
147 switch (pConstUnion->getType())
148 {
daniel@transgaming.com6c1203f2013-01-11 04:12:43 +0000149 case EbtFloat: out << std::min(FLT_MAX, std::max(-FLT_MAX, pConstUnion->getFConst())); break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000150 case EbtInt: out << pConstUnion->getIConst(); break;
151 case EbtBool: out << pConstUnion->getBConst(); break;
152 default: UNREACHABLE();
153 }
154 if (i != size - 1) out << ", ";
155 }
156 if (writeType) out << ")";
157 }
158 return pConstUnion;
159}
160
161void TOutputGLSLBase::visitSymbol(TIntermSymbol* node)
162{
163 TInfoSinkBase& out = objSink();
Zhenyao Mo550c6002014-02-26 15:40:48 -0800164 if (mLoopUnrollStack.needsToReplaceSymbolWithValue(node))
165 out << mLoopUnrollStack.getLoopIndexValue(node);
zmo@google.com5601ea02011-06-10 18:23:25 +0000166 else
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +0000167 out << hashVariableName(node->getSymbol());
zmo@google.com5601ea02011-06-10 18:23:25 +0000168
169 if (mDeclaringVariables && node->getType().isArray())
170 out << arrayBrackets(node->getType());
171}
172
173void TOutputGLSLBase::visitConstantUnion(TIntermConstantUnion* node)
174{
175 writeConstantUnion(node->getType(), node->getUnionArrayPointer());
176}
177
178bool TOutputGLSLBase::visitBinary(Visit visit, TIntermBinary* node)
179{
180 bool visitChildren = true;
181 TInfoSinkBase& out = objSink();
182 switch (node->getOp())
183 {
184 case EOpInitialize:
185 if (visit == InVisit)
186 {
187 out << " = ";
188 // RHS of initialize is not being declared.
189 mDeclaringVariables = false;
190 }
191 break;
192 case EOpAssign: writeTriplet(visit, "(", " = ", ")"); break;
193 case EOpAddAssign: writeTriplet(visit, "(", " += ", ")"); break;
194 case EOpSubAssign: writeTriplet(visit, "(", " -= ", ")"); break;
195 case EOpDivAssign: writeTriplet(visit, "(", " /= ", ")"); break;
196 // Notice the fall-through.
197 case EOpMulAssign:
198 case EOpVectorTimesMatrixAssign:
199 case EOpVectorTimesScalarAssign:
200 case EOpMatrixTimesScalarAssign:
201 case EOpMatrixTimesMatrixAssign:
202 writeTriplet(visit, "(", " *= ", ")");
203 break;
204
205 case EOpIndexDirect:
zmo@google.com5601ea02011-06-10 18:23:25 +0000206 writeTriplet(visit, NULL, "[", "]");
207 break;
daniel@transgaming.com4167cc92013-01-11 04:11:53 +0000208 case EOpIndexIndirect:
209 if (node->getAddIndexClamp())
210 {
211 if (visit == InVisit)
212 {
shannon.woods@transgaming.com1d432bb2013-01-25 21:57:28 +0000213 if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC) {
214 out << "[int(clamp(float(";
215 } else {
216 out << "[webgl_int_clamp(";
217 }
daniel@transgaming.com4167cc92013-01-11 04:11:53 +0000218 }
219 else if (visit == PostVisit)
220 {
221 int maxSize;
222 TIntermTyped *left = node->getLeft();
223 TType leftType = left->getType();
224
225 if (left->isArray())
226 {
227 // The shader will fail validation if the array length is not > 0.
228 maxSize = leftType.getArraySize() - 1;
229 }
230 else
231 {
232 maxSize = leftType.getNominalSize() - 1;
233 }
shannon.woods@transgaming.com1d432bb2013-01-25 21:57:28 +0000234
235 if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC) {
236 out << "), 0.0, float(" << maxSize << ")))]";
237 } else {
238 out << ", 0, " << maxSize << ")]";
239 }
daniel@transgaming.com4167cc92013-01-11 04:11:53 +0000240 }
241 }
242 else
243 {
244 writeTriplet(visit, NULL, "[", "]");
245 }
246 break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000247 case EOpIndexDirectStruct:
248 if (visit == InVisit)
249 {
Jamie Madill98493dd2013-07-08 14:39:03 -0400250 // Here we are writing out "foo.bar", where "foo" is struct
251 // and "bar" is field. In AST, it is represented as a binary
252 // node, where left child represents "foo" and right child "bar".
253 // The node itself represents ".". The struct field "bar" is
254 // actually stored as an index into TStructure::fields.
zmo@google.com5601ea02011-06-10 18:23:25 +0000255 out << ".";
Jamie Madill98493dd2013-07-08 14:39:03 -0400256 const TStructure* structure = node->getLeft()->getType().getStruct();
257 const TIntermConstantUnion* index = node->getRight()->getAsConstantUnion();
258 const TField* field = structure->fields()[index->getIConst(0)];
daniel@transgaming.com97b16d12013-02-01 03:20:42 +0000259
Jamie Madill98493dd2013-07-08 14:39:03 -0400260 TString fieldName = field->name();
Jamie Madill02f20dd2013-09-12 12:07:42 -0400261 if (!mSymbolTable.findBuiltIn(structure->name(), mShaderVersion))
daniel@transgaming.com97b16d12013-02-01 03:20:42 +0000262 fieldName = hashName(fieldName);
263
264 out << fieldName;
zmo@google.com5601ea02011-06-10 18:23:25 +0000265 visitChildren = false;
266 }
267 break;
268 case EOpVectorSwizzle:
269 if (visit == InVisit)
270 {
271 out << ".";
272 TIntermAggregate* rightChild = node->getRight()->getAsAggregate();
273 TIntermSequence& sequence = rightChild->getSequence();
274 for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); ++sit)
275 {
276 TIntermConstantUnion* element = (*sit)->getAsConstantUnion();
277 ASSERT(element->getBasicType() == EbtInt);
278 ASSERT(element->getNominalSize() == 1);
279 const ConstantUnion& data = element->getUnionArrayPointer()[0];
280 ASSERT(data.getType() == EbtInt);
281 switch (data.getIConst())
282 {
283 case 0: out << "x"; break;
284 case 1: out << "y"; break;
285 case 2: out << "z"; break;
286 case 3: out << "w"; break;
287 default: UNREACHABLE(); break;
288 }
289 }
290 visitChildren = false;
291 }
292 break;
293
294 case EOpAdd: writeTriplet(visit, "(", " + ", ")"); break;
295 case EOpSub: writeTriplet(visit, "(", " - ", ")"); break;
296 case EOpMul: writeTriplet(visit, "(", " * ", ")"); break;
297 case EOpDiv: writeTriplet(visit, "(", " / ", ")"); break;
298 case EOpMod: UNIMPLEMENTED(); break;
299 case EOpEqual: writeTriplet(visit, "(", " == ", ")"); break;
300 case EOpNotEqual: writeTriplet(visit, "(", " != ", ")"); break;
301 case EOpLessThan: writeTriplet(visit, "(", " < ", ")"); break;
302 case EOpGreaterThan: writeTriplet(visit, "(", " > ", ")"); break;
303 case EOpLessThanEqual: writeTriplet(visit, "(", " <= ", ")"); break;
304 case EOpGreaterThanEqual: writeTriplet(visit, "(", " >= ", ")"); break;
305
306 // Notice the fall-through.
307 case EOpVectorTimesScalar:
308 case EOpVectorTimesMatrix:
309 case EOpMatrixTimesVector:
310 case EOpMatrixTimesScalar:
311 case EOpMatrixTimesMatrix:
312 writeTriplet(visit, "(", " * ", ")");
313 break;
314
315 case EOpLogicalOr: writeTriplet(visit, "(", " || ", ")"); break;
316 case EOpLogicalXor: writeTriplet(visit, "(", " ^^ ", ")"); break;
317 case EOpLogicalAnd: writeTriplet(visit, "(", " && ", ")"); break;
318 default: UNREACHABLE(); break;
319 }
320
321 return visitChildren;
322}
323
324bool TOutputGLSLBase::visitUnary(Visit visit, TIntermUnary* node)
325{
zmo@google.com32e97312011-08-24 01:03:11 +0000326 TString preString;
327 TString postString = ")";
328
zmo@google.com5601ea02011-06-10 18:23:25 +0000329 switch (node->getOp())
330 {
zmo@google.com32e97312011-08-24 01:03:11 +0000331 case EOpNegative: preString = "(-"; break;
332 case EOpVectorLogicalNot: preString = "not("; break;
333 case EOpLogicalNot: preString = "(!"; break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000334
zmo@google.com32e97312011-08-24 01:03:11 +0000335 case EOpPostIncrement: preString = "("; postString = "++)"; break;
336 case EOpPostDecrement: preString = "("; postString = "--)"; break;
337 case EOpPreIncrement: preString = "(++"; break;
338 case EOpPreDecrement: preString = "(--"; break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000339
340 case EOpConvIntToBool:
341 case EOpConvFloatToBool:
342 switch (node->getOperand()->getType().getNominalSize())
343 {
zmo@google.com32e97312011-08-24 01:03:11 +0000344 case 1: preString = "bool("; break;
345 case 2: preString = "bvec2("; break;
346 case 3: preString = "bvec3("; break;
347 case 4: preString = "bvec4("; break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000348 default: UNREACHABLE();
349 }
350 break;
351 case EOpConvBoolToFloat:
352 case EOpConvIntToFloat:
353 switch (node->getOperand()->getType().getNominalSize())
354 {
zmo@google.com32e97312011-08-24 01:03:11 +0000355 case 1: preString = "float("; break;
356 case 2: preString = "vec2("; break;
357 case 3: preString = "vec3("; break;
358 case 4: preString = "vec4("; break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000359 default: UNREACHABLE();
360 }
361 break;
362 case EOpConvFloatToInt:
363 case EOpConvBoolToInt:
364 switch (node->getOperand()->getType().getNominalSize())
365 {
zmo@google.com32e97312011-08-24 01:03:11 +0000366 case 1: preString = "int("; break;
367 case 2: preString = "ivec2("; break;
368 case 3: preString = "ivec3("; break;
369 case 4: preString = "ivec4("; break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000370 default: UNREACHABLE();
371 }
372 break;
373
zmo@google.com32e97312011-08-24 01:03:11 +0000374 case EOpRadians: preString = "radians("; break;
375 case EOpDegrees: preString = "degrees("; break;
376 case EOpSin: preString = "sin("; break;
377 case EOpCos: preString = "cos("; break;
378 case EOpTan: preString = "tan("; break;
379 case EOpAsin: preString = "asin("; break;
380 case EOpAcos: preString = "acos("; break;
381 case EOpAtan: preString = "atan("; break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000382
zmo@google.com32e97312011-08-24 01:03:11 +0000383 case EOpExp: preString = "exp("; break;
384 case EOpLog: preString = "log("; break;
385 case EOpExp2: preString = "exp2("; break;
386 case EOpLog2: preString = "log2("; break;
387 case EOpSqrt: preString = "sqrt("; break;
388 case EOpInverseSqrt: preString = "inversesqrt("; break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000389
zmo@google.com32e97312011-08-24 01:03:11 +0000390 case EOpAbs: preString = "abs("; break;
391 case EOpSign: preString = "sign("; break;
392 case EOpFloor: preString = "floor("; break;
393 case EOpCeil: preString = "ceil("; break;
394 case EOpFract: preString = "fract("; break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000395
zmo@google.com32e97312011-08-24 01:03:11 +0000396 case EOpLength: preString = "length("; break;
397 case EOpNormalize: preString = "normalize("; break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000398
zmo@google.com32e97312011-08-24 01:03:11 +0000399 case EOpDFdx: preString = "dFdx("; break;
400 case EOpDFdy: preString = "dFdy("; break;
401 case EOpFwidth: preString = "fwidth("; break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000402
zmo@google.com32e97312011-08-24 01:03:11 +0000403 case EOpAny: preString = "any("; break;
404 case EOpAll: preString = "all("; break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000405
406 default: UNREACHABLE(); break;
407 }
408
zmo@google.com32e97312011-08-24 01:03:11 +0000409 if (visit == PreVisit && node->getUseEmulatedFunction())
410 preString = BuiltInFunctionEmulator::GetEmulatedFunctionName(preString);
411 writeTriplet(visit, preString.c_str(), NULL, postString.c_str());
412
zmo@google.com5601ea02011-06-10 18:23:25 +0000413 return true;
414}
415
416bool TOutputGLSLBase::visitSelection(Visit visit, TIntermSelection* node)
417{
418 TInfoSinkBase& out = objSink();
419
420 if (node->usesTernaryOperator())
421 {
422 // Notice two brackets at the beginning and end. The outer ones
423 // encapsulate the whole ternary expression. This preserves the
424 // order of precedence when ternary expressions are used in a
425 // compound expression, i.e., c = 2 * (a < b ? 1 : 2).
426 out << "((";
427 node->getCondition()->traverse(this);
428 out << ") ? (";
429 node->getTrueBlock()->traverse(this);
430 out << ") : (";
431 node->getFalseBlock()->traverse(this);
432 out << "))";
433 }
434 else
435 {
436 out << "if (";
437 node->getCondition()->traverse(this);
438 out << ")\n";
439
Zhenyao Mo7cab38b2013-10-15 12:59:30 -0700440 incrementDepth(node);
zmo@google.com5601ea02011-06-10 18:23:25 +0000441 visitCodeBlock(node->getTrueBlock());
442
443 if (node->getFalseBlock())
444 {
445 out << "else\n";
446 visitCodeBlock(node->getFalseBlock());
447 }
448 decrementDepth();
449 }
450 return false;
451}
452
453bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate* node)
454{
455 bool visitChildren = true;
456 TInfoSinkBase& out = objSink();
zmo@google.comf420c422011-09-12 18:27:59 +0000457 TString preString;
458 bool delayedWrite = false;
zmo@google.com5601ea02011-06-10 18:23:25 +0000459 switch (node->getOp())
460 {
461 case EOpSequence: {
462 // Scope the sequences except when at the global scope.
463 if (depth > 0) out << "{\n";
464
Zhenyao Mo7cab38b2013-10-15 12:59:30 -0700465 incrementDepth(node);
zmo@google.com5601ea02011-06-10 18:23:25 +0000466 const TIntermSequence& sequence = node->getSequence();
467 for (TIntermSequence::const_iterator iter = sequence.begin();
468 iter != sequence.end(); ++iter)
469 {
470 TIntermNode* node = *iter;
471 ASSERT(node != NULL);
472 node->traverse(this);
473
474 if (isSingleStatement(node))
475 out << ";\n";
476 }
477 decrementDepth();
478
479 // Scope the sequences except when at the global scope.
480 if (depth > 0) out << "}\n";
481 visitChildren = false;
482 break;
483 }
484 case EOpPrototype: {
485 // Function declaration.
486 ASSERT(visit == PreVisit);
kbr@chromium.org57f7ce02011-08-15 23:13:05 +0000487 writeVariableType(node->getType());
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +0000488 out << " " << hashName(node->getName());
zmo@google.com5601ea02011-06-10 18:23:25 +0000489
490 out << "(";
491 writeFunctionParameters(node->getSequence());
492 out << ")";
493
494 visitChildren = false;
495 break;
496 }
497 case EOpFunction: {
498 // Function definition.
499 ASSERT(visit == PreVisit);
zmo@google.com189be2f2011-06-16 18:28:53 +0000500 writeVariableType(node->getType());
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +0000501 out << " " << hashFunctionName(node->getName());
zmo@google.com5601ea02011-06-10 18:23:25 +0000502
Zhenyao Mo7cab38b2013-10-15 12:59:30 -0700503 incrementDepth(node);
zmo@google.com5601ea02011-06-10 18:23:25 +0000504 // Function definition node contains one or two children nodes
505 // representing function parameters and function body. The latter
506 // is not present in case of empty function bodies.
507 const TIntermSequence& sequence = node->getSequence();
508 ASSERT((sequence.size() == 1) || (sequence.size() == 2));
509 TIntermSequence::const_iterator seqIter = sequence.begin();
510
511 // Traverse function parameters.
512 TIntermAggregate* params = (*seqIter)->getAsAggregate();
513 ASSERT(params != NULL);
514 ASSERT(params->getOp() == EOpParameters);
515 params->traverse(this);
516
517 // Traverse function body.
518 TIntermAggregate* body = ++seqIter != sequence.end() ?
519 (*seqIter)->getAsAggregate() : NULL;
520 visitCodeBlock(body);
521 decrementDepth();
522
523 // Fully processed; no need to visit children.
524 visitChildren = false;
525 break;
526 }
527 case EOpFunctionCall:
528 // Function call.
529 if (visit == PreVisit)
530 {
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +0000531 out << hashFunctionName(node->getName()) << "(";
zmo@google.com5601ea02011-06-10 18:23:25 +0000532 }
533 else if (visit == InVisit)
534 {
535 out << ", ";
536 }
537 else
538 {
539 out << ")";
540 }
541 break;
542 case EOpParameters: {
543 // Function parameters.
544 ASSERT(visit == PreVisit);
545 out << "(";
546 writeFunctionParameters(node->getSequence());
547 out << ")";
548 visitChildren = false;
549 break;
550 }
551 case EOpDeclaration: {
552 // Variable declaration.
553 if (visit == PreVisit)
554 {
555 const TIntermSequence& sequence = node->getSequence();
556 const TIntermTyped* variable = sequence.front()->getAsTyped();
557 writeVariableType(variable->getType());
558 out << " ";
559 mDeclaringVariables = true;
560 }
561 else if (visit == InVisit)
562 {
563 out << ", ";
564 mDeclaringVariables = true;
565 }
566 else
567 {
568 mDeclaringVariables = false;
569 }
570 break;
571 }
572 case EOpConstructFloat: writeTriplet(visit, "float(", NULL, ")"); break;
573 case EOpConstructVec2: writeTriplet(visit, "vec2(", ", ", ")"); break;
574 case EOpConstructVec3: writeTriplet(visit, "vec3(", ", ", ")"); break;
575 case EOpConstructVec4: writeTriplet(visit, "vec4(", ", ", ")"); break;
576 case EOpConstructBool: writeTriplet(visit, "bool(", NULL, ")"); break;
577 case EOpConstructBVec2: writeTriplet(visit, "bvec2(", ", ", ")"); break;
578 case EOpConstructBVec3: writeTriplet(visit, "bvec3(", ", ", ")"); break;
579 case EOpConstructBVec4: writeTriplet(visit, "bvec4(", ", ", ")"); break;
580 case EOpConstructInt: writeTriplet(visit, "int(", NULL, ")"); break;
581 case EOpConstructIVec2: writeTriplet(visit, "ivec2(", ", ", ")"); break;
582 case EOpConstructIVec3: writeTriplet(visit, "ivec3(", ", ", ")"); break;
583 case EOpConstructIVec4: writeTriplet(visit, "ivec4(", ", ", ")"); break;
584 case EOpConstructMat2: writeTriplet(visit, "mat2(", ", ", ")"); break;
585 case EOpConstructMat3: writeTriplet(visit, "mat3(", ", ", ")"); break;
586 case EOpConstructMat4: writeTriplet(visit, "mat4(", ", ", ")"); break;
587 case EOpConstructStruct:
588 if (visit == PreVisit)
589 {
590 const TType& type = node->getType();
591 ASSERT(type.getBasicType() == EbtStruct);
Jamie Madill98493dd2013-07-08 14:39:03 -0400592 out << hashName(type.getStruct()->name()) << "(";
zmo@google.com5601ea02011-06-10 18:23:25 +0000593 }
594 else if (visit == InVisit)
595 {
596 out << ", ";
597 }
598 else
599 {
600 out << ")";
601 }
602 break;
603
zmo@google.comf420c422011-09-12 18:27:59 +0000604 case EOpLessThan: preString = "lessThan("; delayedWrite = true; break;
605 case EOpGreaterThan: preString = "greaterThan("; delayedWrite = true; break;
606 case EOpLessThanEqual: preString = "lessThanEqual("; delayedWrite = true; break;
607 case EOpGreaterThanEqual: preString = "greaterThanEqual("; delayedWrite = true; break;
608 case EOpVectorEqual: preString = "equal("; delayedWrite = true; break;
609 case EOpVectorNotEqual: preString = "notEqual("; delayedWrite = true; break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000610 case EOpComma: writeTriplet(visit, NULL, ", ", NULL); break;
611
zmo@google.comf420c422011-09-12 18:27:59 +0000612 case EOpMod: preString = "mod("; delayedWrite = true; break;
613 case EOpPow: preString = "pow("; delayedWrite = true; break;
614 case EOpAtan: preString = "atan("; delayedWrite = true; break;
615 case EOpMin: preString = "min("; delayedWrite = true; break;
616 case EOpMax: preString = "max("; delayedWrite = true; break;
617 case EOpClamp: preString = "clamp("; delayedWrite = true; break;
618 case EOpMix: preString = "mix("; delayedWrite = true; break;
619 case EOpStep: preString = "step("; delayedWrite = true; break;
620 case EOpSmoothStep: preString = "smoothstep("; delayedWrite = true; break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000621
zmo@google.comf420c422011-09-12 18:27:59 +0000622 case EOpDistance: preString = "distance("; delayedWrite = true; break;
623 case EOpDot: preString = "dot("; delayedWrite = true; break;
624 case EOpCross: preString = "cross("; delayedWrite = true; break;
625 case EOpFaceForward: preString = "faceforward("; delayedWrite = true; break;
626 case EOpReflect: preString = "reflect("; delayedWrite = true; break;
627 case EOpRefract: preString = "refract("; delayedWrite = true; break;
628 case EOpMul: preString = "matrixCompMult("; delayedWrite = true; break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000629
630 default: UNREACHABLE(); break;
631 }
zmo@google.comf420c422011-09-12 18:27:59 +0000632 if (delayedWrite && visit == PreVisit && node->getUseEmulatedFunction())
633 preString = BuiltInFunctionEmulator::GetEmulatedFunctionName(preString);
634 if (delayedWrite)
635 writeTriplet(visit, preString.c_str(), ", ", ")");
zmo@google.com5601ea02011-06-10 18:23:25 +0000636 return visitChildren;
637}
638
639bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop* node)
640{
641 TInfoSinkBase& out = objSink();
642
Zhenyao Mo7cab38b2013-10-15 12:59:30 -0700643 incrementDepth(node);
zmo@google.com5601ea02011-06-10 18:23:25 +0000644 // Loop header.
645 TLoopType loopType = node->getType();
646 if (loopType == ELoopFor) // for loop
647 {
Zhenyao Mo550c6002014-02-26 15:40:48 -0800648 if (!node->getUnrollFlag())
649 {
zmo@google.com5601ea02011-06-10 18:23:25 +0000650 out << "for (";
651 if (node->getInit())
652 node->getInit()->traverse(this);
653 out << "; ";
654
655 if (node->getCondition())
656 node->getCondition()->traverse(this);
657 out << "; ";
658
659 if (node->getExpression())
660 node->getExpression()->traverse(this);
661 out << ")\n";
662 }
Zhenyao Mo550c6002014-02-26 15:40:48 -0800663 else
664 {
665 // Need to put a one-iteration loop here to handle break.
666 TIntermSequence &declSeq =
667 node->getInit()->getAsAggregate()->getSequence();
668 TIntermSymbol *indexSymbol =
669 declSeq[0]->getAsBinaryNode()->getLeft()->getAsSymbolNode();
670 TString name = hashVariableName(indexSymbol->getSymbol());
671 out << "for (int " << name << " = 0; "
672 << name << " < 1; "
673 << "++" << name << ")\n";
674 }
zmo@google.com5601ea02011-06-10 18:23:25 +0000675 }
676 else if (loopType == ELoopWhile) // while loop
677 {
678 out << "while (";
679 ASSERT(node->getCondition() != NULL);
680 node->getCondition()->traverse(this);
681 out << ")\n";
682 }
683 else // do-while loop
684 {
685 ASSERT(loopType == ELoopDoWhile);
686 out << "do\n";
687 }
688
689 // Loop body.
690 if (node->getUnrollFlag())
691 {
Zhenyao Mo550c6002014-02-26 15:40:48 -0800692 out << "{\n";
693 mLoopUnrollStack.push(node);
694 while (mLoopUnrollStack.satisfiesLoopCondition())
zmo@google.com5601ea02011-06-10 18:23:25 +0000695 {
696 visitCodeBlock(node->getBody());
Zhenyao Mo550c6002014-02-26 15:40:48 -0800697 mLoopUnrollStack.step();
zmo@google.com5601ea02011-06-10 18:23:25 +0000698 }
Zhenyao Mo550c6002014-02-26 15:40:48 -0800699 mLoopUnrollStack.pop();
700 out << "}\n";
zmo@google.com5601ea02011-06-10 18:23:25 +0000701 }
702 else
703 {
704 visitCodeBlock(node->getBody());
705 }
706
707 // Loop footer.
708 if (loopType == ELoopDoWhile) // do-while loop
709 {
710 out << "while (";
711 ASSERT(node->getCondition() != NULL);
712 node->getCondition()->traverse(this);
713 out << ");\n";
714 }
715 decrementDepth();
716
717 // No need to visit children. They have been already processed in
718 // this function.
719 return false;
720}
721
722bool TOutputGLSLBase::visitBranch(Visit visit, TIntermBranch* node)
723{
724 switch (node->getFlowOp())
725 {
726 case EOpKill: writeTriplet(visit, "discard", NULL, NULL); break;
727 case EOpBreak: writeTriplet(visit, "break", NULL, NULL); break;
728 case EOpContinue: writeTriplet(visit, "continue", NULL, NULL); break;
729 case EOpReturn: writeTriplet(visit, "return ", NULL, NULL); break;
730 default: UNREACHABLE(); break;
731 }
732
733 return true;
734}
735
736void TOutputGLSLBase::visitCodeBlock(TIntermNode* node) {
737 TInfoSinkBase &out = objSink();
738 if (node != NULL)
739 {
740 node->traverse(this);
741 // Single statements not part of a sequence need to be terminated
742 // with semi-colon.
743 if (isSingleStatement(node))
744 out << ";\n";
745 }
746 else
747 {
748 out << "{\n}\n"; // Empty code block.
749 }
750}
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +0000751
752TString TOutputGLSLBase::getTypeName(const TType& type)
753{
754 TInfoSinkBase out;
755 if (type.isMatrix())
756 {
757 out << "mat";
758 out << type.getNominalSize();
759 }
760 else if (type.isVector())
761 {
762 switch (type.getBasicType())
763 {
764 case EbtFloat: out << "vec"; break;
765 case EbtInt: out << "ivec"; break;
766 case EbtBool: out << "bvec"; break;
767 default: UNREACHABLE(); break;
768 }
769 out << type.getNominalSize();
770 }
771 else
772 {
773 if (type.getBasicType() == EbtStruct)
Jamie Madill98493dd2013-07-08 14:39:03 -0400774 out << hashName(type.getStruct()->name());
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +0000775 else
776 out << type.getBasicString();
777 }
778 return TString(out.c_str());
779}
780
781TString TOutputGLSLBase::hashName(const TString& name)
782{
783 if (mHashFunction == NULL || name.empty())
784 return name;
785 NameMap::const_iterator it = mNameMap.find(name.c_str());
786 if (it != mNameMap.end())
787 return it->second.c_str();
788 TString hashedName = TIntermTraverser::hash(name, mHashFunction);
789 mNameMap[name.c_str()] = hashedName.c_str();
790 return hashedName;
791}
792
793TString TOutputGLSLBase::hashVariableName(const TString& name)
794{
Jamie Madill02f20dd2013-09-12 12:07:42 -0400795 if (mSymbolTable.findBuiltIn(name, mShaderVersion) != NULL)
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +0000796 return name;
797 return hashName(name);
798}
799
800TString TOutputGLSLBase::hashFunctionName(const TString& mangled_name)
801{
802 TString name = TFunction::unmangleName(mangled_name);
Jamie Madill02f20dd2013-09-12 12:07:42 -0400803 if (mSymbolTable.findBuiltIn(mangled_name, mShaderVersion) != NULL || name == "main")
Nicolas Capens46485082014-04-15 13:12:50 -0400804 {
805 return translateTextureFunction(name);
806 }
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +0000807 return hashName(name);
808}
Jamie Madill98493dd2013-07-08 14:39:03 -0400809
810bool TOutputGLSLBase::structDeclared(const TStructure* structure) const
811{
812 return mDeclaredStructs.find(structure->name()) != mDeclaredStructs.end();
813}
814
815void TOutputGLSLBase::declareStruct(const TStructure* structure)
816{
817 TInfoSinkBase& out = objSink();
818
819 out << "struct " << hashName(structure->name()) << "{\n";
820 const TFieldList& fields = structure->fields();
821 for (size_t i = 0; i < fields.size(); ++i)
822 {
823 const TField* field = fields[i];
824 if (writeVariablePrecision(field->type()->getPrecision()))
825 out << " ";
826 out << getTypeName(*field->type()) << " " << hashName(field->name());
827 if (field->type()->isArray())
828 out << arrayBrackets(*field->type());
829 out << ";\n";
830 }
831 out << "}";
832
833 mDeclaredStructs.insert(structure->name());
834}