blob: 5e3857cdeab28fc1b79ea522527891943fc56c04 [file] [log] [blame]
zmo@google.com5601ea02011-06-10 18:23:25 +00001//
2// Copyright (c) 2002-2011 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7#include "compiler/OutputGLSLBase.h"
8#include "compiler/debug.h"
9
10namespace
11{
12TString getTypeName(const TType& type)
13{
14 TInfoSinkBase out;
15 if (type.isMatrix())
16 {
17 out << "mat";
18 out << type.getNominalSize();
19 }
20 else if (type.isVector())
21 {
22 switch (type.getBasicType())
23 {
24 case EbtFloat: out << "vec"; break;
25 case EbtInt: out << "ivec"; break;
26 case EbtBool: out << "bvec"; break;
27 default: UNREACHABLE(); break;
28 }
29 out << type.getNominalSize();
30 }
31 else
32 {
33 if (type.getBasicType() == EbtStruct)
34 out << type.getTypeName();
35 else
36 out << type.getBasicString();
37 }
38 return TString(out.c_str());
39}
40
41TString arrayBrackets(const TType& type)
42{
43 ASSERT(type.isArray());
44 TInfoSinkBase out;
45 out << "[" << type.getArraySize() << "]";
46 return TString(out.c_str());
47}
48
49bool isSingleStatement(TIntermNode* node) {
50 if (const TIntermAggregate* aggregate = node->getAsAggregate())
51 {
52 return (aggregate->getOp() != EOpFunction) &&
53 (aggregate->getOp() != EOpSequence);
54 }
55 else if (const TIntermSelection* selection = node->getAsSelectionNode())
56 {
57 // Ternary operators are usually part of an assignment operator.
58 // This handles those rare cases in which they are all by themselves.
59 return selection->usesTernaryOperator();
60 }
61 else if (node->getAsLoopNode())
62 {
63 return false;
64 }
65 return true;
66}
67} // namespace
68
69TOutputGLSLBase::TOutputGLSLBase(TInfoSinkBase& objSink)
70 : TIntermTraverser(true, true, true),
71 mObjSink(objSink),
72 mDeclaringVariables(false)
73{
74}
75
76void TOutputGLSLBase::writeTriplet(Visit visit, const char* preStr, const char* inStr, const char* postStr)
77{
78 TInfoSinkBase& out = objSink();
79 if (visit == PreVisit && preStr)
80 {
81 out << preStr;
82 }
83 else if (visit == InVisit && inStr)
84 {
85 out << inStr;
86 }
87 else if (visit == PostVisit && postStr)
88 {
89 out << postStr;
90 }
91}
92
93void TOutputGLSLBase::writeVariableType(const TType& type)
94{
95 TInfoSinkBase& out = objSink();
96 TQualifier qualifier = type.getQualifier();
97 // TODO(alokp): Validate qualifier for variable declarations.
98 if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal))
99 out << type.getQualifierString() << " ";
100 // Declare the struct if we have not done so already.
101 if ((type.getBasicType() == EbtStruct) &&
102 (mDeclaredStructs.find(type.getTypeName()) == mDeclaredStructs.end()))
103 {
104 out << "struct " << type.getTypeName() << "{\n";
105 const TTypeList* structure = type.getStruct();
106 ASSERT(structure != NULL);
107 for (size_t i = 0; i < structure->size(); ++i)
108 {
109 const TType* fieldType = (*structure)[i].type;
110 ASSERT(fieldType != NULL);
111 if (writeVariablePrecision(fieldType->getPrecision()))
112 out << " ";
113 out << getTypeName(*fieldType) << " " << fieldType->getFieldName();
114 if (fieldType->isArray())
115 out << arrayBrackets(*fieldType);
116 out << ";\n";
117 }
118 out << "}";
119 mDeclaredStructs.insert(type.getTypeName());
120 }
121 else
122 {
123 if (writeVariablePrecision(type.getPrecision()))
124 out << " ";
125 out << getTypeName(type);
126 }
127}
128
129void TOutputGLSLBase::writeFunctionParameters(const TIntermSequence& args)
130{
131 TInfoSinkBase& out = objSink();
132 for (TIntermSequence::const_iterator iter = args.begin();
133 iter != args.end(); ++iter)
134 {
135 const TIntermSymbol* arg = (*iter)->getAsSymbolNode();
136 ASSERT(arg != NULL);
137
138 const TType& type = arg->getType();
139 TQualifier qualifier = type.getQualifier();
140 // TODO(alokp): Validate qualifier for function arguments.
141 if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal))
142 out << type.getQualifierString() << " ";
143
144 out << getTypeName(type);
145
146 const TString& name = arg->getSymbol();
147 if (!name.empty())
148 out << " " << name;
149 if (type.isArray())
150 out << arrayBrackets(type);
151
152 // Put a comma if this is not the last argument.
153 if (iter != args.end() - 1)
154 out << ", ";
155 }
156}
157
158const ConstantUnion* TOutputGLSLBase::writeConstantUnion(const TType& type,
159 const ConstantUnion* pConstUnion)
160{
161 TInfoSinkBase& out = objSink();
162
163 if (type.getBasicType() == EbtStruct)
164 {
165 out << type.getTypeName() << "(";
166 const TTypeList* structure = type.getStruct();
167 ASSERT(structure != NULL);
168 for (size_t i = 0; i < structure->size(); ++i)
169 {
170 const TType* fieldType = (*structure)[i].type;
171 ASSERT(fieldType != NULL);
172 pConstUnion = writeConstantUnion(*fieldType, pConstUnion);
173 if (i != structure->size() - 1) out << ", ";
174 }
175 out << ")";
176 }
177 else
178 {
179 int size = type.getObjectSize();
180 bool writeType = size > 1;
181 if (writeType) out << getTypeName(type) << "(";
182 for (int i = 0; i < size; ++i, ++pConstUnion)
183 {
184 switch (pConstUnion->getType())
185 {
186 case EbtFloat: out << pConstUnion->getFConst(); break;
187 case EbtInt: out << pConstUnion->getIConst(); break;
188 case EbtBool: out << pConstUnion->getBConst(); break;
189 default: UNREACHABLE();
190 }
191 if (i != size - 1) out << ", ";
192 }
193 if (writeType) out << ")";
194 }
195 return pConstUnion;
196}
197
198void TOutputGLSLBase::visitSymbol(TIntermSymbol* node)
199{
200 TInfoSinkBase& out = objSink();
201 if (mLoopUnroll.NeedsToReplaceSymbolWithValue(node))
202 out << mLoopUnroll.GetLoopIndexValue(node);
203 else
204 out << node->getSymbol();
205
206 if (mDeclaringVariables && node->getType().isArray())
207 out << arrayBrackets(node->getType());
208}
209
210void TOutputGLSLBase::visitConstantUnion(TIntermConstantUnion* node)
211{
212 writeConstantUnion(node->getType(), node->getUnionArrayPointer());
213}
214
215bool TOutputGLSLBase::visitBinary(Visit visit, TIntermBinary* node)
216{
217 bool visitChildren = true;
218 TInfoSinkBase& out = objSink();
219 switch (node->getOp())
220 {
221 case EOpInitialize:
222 if (visit == InVisit)
223 {
224 out << " = ";
225 // RHS of initialize is not being declared.
226 mDeclaringVariables = false;
227 }
228 break;
229 case EOpAssign: writeTriplet(visit, "(", " = ", ")"); break;
230 case EOpAddAssign: writeTriplet(visit, "(", " += ", ")"); break;
231 case EOpSubAssign: writeTriplet(visit, "(", " -= ", ")"); break;
232 case EOpDivAssign: writeTriplet(visit, "(", " /= ", ")"); break;
233 // Notice the fall-through.
234 case EOpMulAssign:
235 case EOpVectorTimesMatrixAssign:
236 case EOpVectorTimesScalarAssign:
237 case EOpMatrixTimesScalarAssign:
238 case EOpMatrixTimesMatrixAssign:
239 writeTriplet(visit, "(", " *= ", ")");
240 break;
241
242 case EOpIndexDirect:
243 case EOpIndexIndirect:
244 writeTriplet(visit, NULL, "[", "]");
245 break;
246 case EOpIndexDirectStruct:
247 if (visit == InVisit)
248 {
249 out << ".";
250 // TODO(alokp): ASSERT
251 out << node->getType().getFieldName();
252 visitChildren = false;
253 }
254 break;
255 case EOpVectorSwizzle:
256 if (visit == InVisit)
257 {
258 out << ".";
259 TIntermAggregate* rightChild = node->getRight()->getAsAggregate();
260 TIntermSequence& sequence = rightChild->getSequence();
261 for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); ++sit)
262 {
263 TIntermConstantUnion* element = (*sit)->getAsConstantUnion();
264 ASSERT(element->getBasicType() == EbtInt);
265 ASSERT(element->getNominalSize() == 1);
266 const ConstantUnion& data = element->getUnionArrayPointer()[0];
267 ASSERT(data.getType() == EbtInt);
268 switch (data.getIConst())
269 {
270 case 0: out << "x"; break;
271 case 1: out << "y"; break;
272 case 2: out << "z"; break;
273 case 3: out << "w"; break;
274 default: UNREACHABLE(); break;
275 }
276 }
277 visitChildren = false;
278 }
279 break;
280
281 case EOpAdd: writeTriplet(visit, "(", " + ", ")"); break;
282 case EOpSub: writeTriplet(visit, "(", " - ", ")"); break;
283 case EOpMul: writeTriplet(visit, "(", " * ", ")"); break;
284 case EOpDiv: writeTriplet(visit, "(", " / ", ")"); break;
285 case EOpMod: UNIMPLEMENTED(); break;
286 case EOpEqual: writeTriplet(visit, "(", " == ", ")"); break;
287 case EOpNotEqual: writeTriplet(visit, "(", " != ", ")"); break;
288 case EOpLessThan: writeTriplet(visit, "(", " < ", ")"); break;
289 case EOpGreaterThan: writeTriplet(visit, "(", " > ", ")"); break;
290 case EOpLessThanEqual: writeTriplet(visit, "(", " <= ", ")"); break;
291 case EOpGreaterThanEqual: writeTriplet(visit, "(", " >= ", ")"); break;
292
293 // Notice the fall-through.
294 case EOpVectorTimesScalar:
295 case EOpVectorTimesMatrix:
296 case EOpMatrixTimesVector:
297 case EOpMatrixTimesScalar:
298 case EOpMatrixTimesMatrix:
299 writeTriplet(visit, "(", " * ", ")");
300 break;
301
302 case EOpLogicalOr: writeTriplet(visit, "(", " || ", ")"); break;
303 case EOpLogicalXor: writeTriplet(visit, "(", " ^^ ", ")"); break;
304 case EOpLogicalAnd: writeTriplet(visit, "(", " && ", ")"); break;
305 default: UNREACHABLE(); break;
306 }
307
308 return visitChildren;
309}
310
311bool TOutputGLSLBase::visitUnary(Visit visit, TIntermUnary* node)
312{
313 switch (node->getOp())
314 {
315 case EOpNegative: writeTriplet(visit, "(-", NULL, ")"); break;
316 case EOpVectorLogicalNot: writeTriplet(visit, "not(", NULL, ")"); break;
317 case EOpLogicalNot: writeTriplet(visit, "(!", NULL, ")"); break;
318
319 case EOpPostIncrement: writeTriplet(visit, "(", NULL, "++)"); break;
320 case EOpPostDecrement: writeTriplet(visit, "(", NULL, "--)"); break;
321 case EOpPreIncrement: writeTriplet(visit, "(++", NULL, ")"); break;
322 case EOpPreDecrement: writeTriplet(visit, "(--", NULL, ")"); break;
323
324 case EOpConvIntToBool:
325 case EOpConvFloatToBool:
326 switch (node->getOperand()->getType().getNominalSize())
327 {
328 case 1: writeTriplet(visit, "bool(", NULL, ")"); break;
329 case 2: writeTriplet(visit, "bvec2(", NULL, ")"); break;
330 case 3: writeTriplet(visit, "bvec3(", NULL, ")"); break;
331 case 4: writeTriplet(visit, "bvec4(", NULL, ")"); break;
332 default: UNREACHABLE();
333 }
334 break;
335 case EOpConvBoolToFloat:
336 case EOpConvIntToFloat:
337 switch (node->getOperand()->getType().getNominalSize())
338 {
339 case 1: writeTriplet(visit, "float(", NULL, ")"); break;
340 case 2: writeTriplet(visit, "vec2(", NULL, ")"); break;
341 case 3: writeTriplet(visit, "vec3(", NULL, ")"); break;
342 case 4: writeTriplet(visit, "vec4(", NULL, ")"); break;
343 default: UNREACHABLE();
344 }
345 break;
346 case EOpConvFloatToInt:
347 case EOpConvBoolToInt:
348 switch (node->getOperand()->getType().getNominalSize())
349 {
350 case 1: writeTriplet(visit, "int(", NULL, ")"); break;
351 case 2: writeTriplet(visit, "ivec2(", NULL, ")"); break;
352 case 3: writeTriplet(visit, "ivec3(", NULL, ")"); break;
353 case 4: writeTriplet(visit, "ivec4(", NULL, ")"); break;
354 default: UNREACHABLE();
355 }
356 break;
357
358 case EOpRadians: writeTriplet(visit, "radians(", NULL, ")"); break;
359 case EOpDegrees: writeTriplet(visit, "degrees(", NULL, ")"); break;
360 case EOpSin: writeTriplet(visit, "sin(", NULL, ")"); break;
361 case EOpCos: writeTriplet(visit, "cos(", NULL, ")"); break;
362 case EOpTan: writeTriplet(visit, "tan(", NULL, ")"); break;
363 case EOpAsin: writeTriplet(visit, "asin(", NULL, ")"); break;
364 case EOpAcos: writeTriplet(visit, "acos(", NULL, ")"); break;
365 case EOpAtan: writeTriplet(visit, "atan(", NULL, ")"); break;
366
367 case EOpExp: writeTriplet(visit, "exp(", NULL, ")"); break;
368 case EOpLog: writeTriplet(visit, "log(", NULL, ")"); break;
369 case EOpExp2: writeTriplet(visit, "exp2(", NULL, ")"); break;
370 case EOpLog2: writeTriplet(visit, "log2(", NULL, ")"); break;
371 case EOpSqrt: writeTriplet(visit, "sqrt(", NULL, ")"); break;
372 case EOpInverseSqrt: writeTriplet(visit, "inversesqrt(", NULL, ")"); break;
373
374 case EOpAbs: writeTriplet(visit, "abs(", NULL, ")"); break;
375 case EOpSign: writeTriplet(visit, "sign(", NULL, ")"); break;
376 case EOpFloor: writeTriplet(visit, "floor(", NULL, ")"); break;
377 case EOpCeil: writeTriplet(visit, "ceil(", NULL, ")"); break;
378 case EOpFract: writeTriplet(visit, "fract(", NULL, ")"); break;
379
380 case EOpLength: writeTriplet(visit, "length(", NULL, ")"); break;
381 case EOpNormalize: writeTriplet(visit, "normalize(", NULL, ")"); break;
382
383 case EOpDFdx: writeTriplet(visit, "dFdx(", NULL, ")"); break;
384 case EOpDFdy: writeTriplet(visit, "dFdy(", NULL, ")"); break;
385 case EOpFwidth: writeTriplet(visit, "fwidth(", NULL, ")"); break;
386
387 case EOpAny: writeTriplet(visit, "any(", NULL, ")"); break;
388 case EOpAll: writeTriplet(visit, "all(", NULL, ")"); break;
389
390 default: UNREACHABLE(); break;
391 }
392
393 return true;
394}
395
396bool TOutputGLSLBase::visitSelection(Visit visit, TIntermSelection* node)
397{
398 TInfoSinkBase& out = objSink();
399
400 if (node->usesTernaryOperator())
401 {
402 // Notice two brackets at the beginning and end. The outer ones
403 // encapsulate the whole ternary expression. This preserves the
404 // order of precedence when ternary expressions are used in a
405 // compound expression, i.e., c = 2 * (a < b ? 1 : 2).
406 out << "((";
407 node->getCondition()->traverse(this);
408 out << ") ? (";
409 node->getTrueBlock()->traverse(this);
410 out << ") : (";
411 node->getFalseBlock()->traverse(this);
412 out << "))";
413 }
414 else
415 {
416 out << "if (";
417 node->getCondition()->traverse(this);
418 out << ")\n";
419
420 incrementDepth();
421 visitCodeBlock(node->getTrueBlock());
422
423 if (node->getFalseBlock())
424 {
425 out << "else\n";
426 visitCodeBlock(node->getFalseBlock());
427 }
428 decrementDepth();
429 }
430 return false;
431}
432
433bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate* node)
434{
435 bool visitChildren = true;
436 TInfoSinkBase& out = objSink();
437 switch (node->getOp())
438 {
439 case EOpSequence: {
440 // Scope the sequences except when at the global scope.
441 if (depth > 0) out << "{\n";
442
443 incrementDepth();
444 const TIntermSequence& sequence = node->getSequence();
445 for (TIntermSequence::const_iterator iter = sequence.begin();
446 iter != sequence.end(); ++iter)
447 {
448 TIntermNode* node = *iter;
449 ASSERT(node != NULL);
450 node->traverse(this);
451
452 if (isSingleStatement(node))
453 out << ";\n";
454 }
455 decrementDepth();
456
457 // Scope the sequences except when at the global scope.
458 if (depth > 0) out << "}\n";
459 visitChildren = false;
460 break;
461 }
462 case EOpPrototype: {
463 // Function declaration.
464 ASSERT(visit == PreVisit);
465 TString returnType = getTypeName(node->getType());
466 out << returnType << " " << node->getName();
467
468 out << "(";
469 writeFunctionParameters(node->getSequence());
470 out << ")";
471
472 visitChildren = false;
473 break;
474 }
475 case EOpFunction: {
476 // Function definition.
477 ASSERT(visit == PreVisit);
478 TString returnType = getTypeName(node->getType());
479 TString functionName = TFunction::unmangleName(node->getName());
480 out << returnType << " " << functionName;
481
482 incrementDepth();
483 // Function definition node contains one or two children nodes
484 // representing function parameters and function body. The latter
485 // is not present in case of empty function bodies.
486 const TIntermSequence& sequence = node->getSequence();
487 ASSERT((sequence.size() == 1) || (sequence.size() == 2));
488 TIntermSequence::const_iterator seqIter = sequence.begin();
489
490 // Traverse function parameters.
491 TIntermAggregate* params = (*seqIter)->getAsAggregate();
492 ASSERT(params != NULL);
493 ASSERT(params->getOp() == EOpParameters);
494 params->traverse(this);
495
496 // Traverse function body.
497 TIntermAggregate* body = ++seqIter != sequence.end() ?
498 (*seqIter)->getAsAggregate() : NULL;
499 visitCodeBlock(body);
500 decrementDepth();
501
502 // Fully processed; no need to visit children.
503 visitChildren = false;
504 break;
505 }
506 case EOpFunctionCall:
507 // Function call.
508 if (visit == PreVisit)
509 {
510 TString functionName = TFunction::unmangleName(node->getName());
511 out << functionName << "(";
512 }
513 else if (visit == InVisit)
514 {
515 out << ", ";
516 }
517 else
518 {
519 out << ")";
520 }
521 break;
522 case EOpParameters: {
523 // Function parameters.
524 ASSERT(visit == PreVisit);
525 out << "(";
526 writeFunctionParameters(node->getSequence());
527 out << ")";
528 visitChildren = false;
529 break;
530 }
531 case EOpDeclaration: {
532 // Variable declaration.
533 if (visit == PreVisit)
534 {
535 const TIntermSequence& sequence = node->getSequence();
536 const TIntermTyped* variable = sequence.front()->getAsTyped();
537 writeVariableType(variable->getType());
538 out << " ";
539 mDeclaringVariables = true;
540 }
541 else if (visit == InVisit)
542 {
543 out << ", ";
544 mDeclaringVariables = true;
545 }
546 else
547 {
548 mDeclaringVariables = false;
549 }
550 break;
551 }
552 case EOpConstructFloat: writeTriplet(visit, "float(", NULL, ")"); break;
553 case EOpConstructVec2: writeTriplet(visit, "vec2(", ", ", ")"); break;
554 case EOpConstructVec3: writeTriplet(visit, "vec3(", ", ", ")"); break;
555 case EOpConstructVec4: writeTriplet(visit, "vec4(", ", ", ")"); break;
556 case EOpConstructBool: writeTriplet(visit, "bool(", NULL, ")"); break;
557 case EOpConstructBVec2: writeTriplet(visit, "bvec2(", ", ", ")"); break;
558 case EOpConstructBVec3: writeTriplet(visit, "bvec3(", ", ", ")"); break;
559 case EOpConstructBVec4: writeTriplet(visit, "bvec4(", ", ", ")"); break;
560 case EOpConstructInt: writeTriplet(visit, "int(", NULL, ")"); break;
561 case EOpConstructIVec2: writeTriplet(visit, "ivec2(", ", ", ")"); break;
562 case EOpConstructIVec3: writeTriplet(visit, "ivec3(", ", ", ")"); break;
563 case EOpConstructIVec4: writeTriplet(visit, "ivec4(", ", ", ")"); break;
564 case EOpConstructMat2: writeTriplet(visit, "mat2(", ", ", ")"); break;
565 case EOpConstructMat3: writeTriplet(visit, "mat3(", ", ", ")"); break;
566 case EOpConstructMat4: writeTriplet(visit, "mat4(", ", ", ")"); break;
567 case EOpConstructStruct:
568 if (visit == PreVisit)
569 {
570 const TType& type = node->getType();
571 ASSERT(type.getBasicType() == EbtStruct);
572 out << type.getTypeName() << "(";
573 }
574 else if (visit == InVisit)
575 {
576 out << ", ";
577 }
578 else
579 {
580 out << ")";
581 }
582 break;
583
584 case EOpLessThan: writeTriplet(visit, "lessThan(", ", ", ")"); break;
585 case EOpGreaterThan: writeTriplet(visit, "greaterThan(", ", ", ")"); break;
586 case EOpLessThanEqual: writeTriplet(visit, "lessThanEqual(", ", ", ")"); break;
587 case EOpGreaterThanEqual: writeTriplet(visit, "greaterThanEqual(", ", ", ")"); break;
588 case EOpVectorEqual: writeTriplet(visit, "equal(", ", ", ")"); break;
589 case EOpVectorNotEqual: writeTriplet(visit, "notEqual(", ", ", ")"); break;
590 case EOpComma: writeTriplet(visit, NULL, ", ", NULL); break;
591
592 case EOpMod: writeTriplet(visit, "mod(", ", ", ")"); break;
593 case EOpPow: writeTriplet(visit, "pow(", ", ", ")"); break;
594 case EOpAtan: writeTriplet(visit, "atan(", ", ", ")"); break;
595 case EOpMin: writeTriplet(visit, "min(", ", ", ")"); break;
596 case EOpMax: writeTriplet(visit, "max(", ", ", ")"); break;
597 case EOpClamp: writeTriplet(visit, "clamp(", ", ", ")"); break;
598 case EOpMix: writeTriplet(visit, "mix(", ", ", ")"); break;
599 case EOpStep: writeTriplet(visit, "step(", ", ", ")"); break;
600 case EOpSmoothStep: writeTriplet(visit, "smoothstep(", ", ", ")"); break;
601
602 case EOpDistance: writeTriplet(visit, "distance(", ", ", ")"); break;
603 case EOpDot: writeTriplet(visit, "dot(", ", ", ")"); break;
604 case EOpCross: writeTriplet(visit, "cross(", ", ", ")"); break;
605 case EOpFaceForward: writeTriplet(visit, "faceforward(", ", ", ")"); break;
606 case EOpReflect: writeTriplet(visit, "reflect(", ", ", ")"); break;
607 case EOpRefract: writeTriplet(visit, "refract(", ", ", ")"); break;
608 case EOpMul: writeTriplet(visit, "matrixCompMult(", ", ", ")"); break;
609
610 default: UNREACHABLE(); break;
611 }
612 return visitChildren;
613}
614
615bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop* node)
616{
617 TInfoSinkBase& out = objSink();
618
619 incrementDepth();
620 // Loop header.
621 TLoopType loopType = node->getType();
622 if (loopType == ELoopFor) // for loop
623 {
624 if (!node->getUnrollFlag()) {
625 out << "for (";
626 if (node->getInit())
627 node->getInit()->traverse(this);
628 out << "; ";
629
630 if (node->getCondition())
631 node->getCondition()->traverse(this);
632 out << "; ";
633
634 if (node->getExpression())
635 node->getExpression()->traverse(this);
636 out << ")\n";
637 }
638 }
639 else if (loopType == ELoopWhile) // while loop
640 {
641 out << "while (";
642 ASSERT(node->getCondition() != NULL);
643 node->getCondition()->traverse(this);
644 out << ")\n";
645 }
646 else // do-while loop
647 {
648 ASSERT(loopType == ELoopDoWhile);
649 out << "do\n";
650 }
651
652 // Loop body.
653 if (node->getUnrollFlag())
654 {
655 TLoopIndexInfo indexInfo;
656 mLoopUnroll.FillLoopIndexInfo(node, indexInfo);
657 mLoopUnroll.Push(indexInfo);
658 while (mLoopUnroll.SatisfiesLoopCondition())
659 {
660 visitCodeBlock(node->getBody());
661 mLoopUnroll.Step();
662 }
663 mLoopUnroll.Pop();
664 }
665 else
666 {
667 visitCodeBlock(node->getBody());
668 }
669
670 // Loop footer.
671 if (loopType == ELoopDoWhile) // do-while loop
672 {
673 out << "while (";
674 ASSERT(node->getCondition() != NULL);
675 node->getCondition()->traverse(this);
676 out << ");\n";
677 }
678 decrementDepth();
679
680 // No need to visit children. They have been already processed in
681 // this function.
682 return false;
683}
684
685bool TOutputGLSLBase::visitBranch(Visit visit, TIntermBranch* node)
686{
687 switch (node->getFlowOp())
688 {
689 case EOpKill: writeTriplet(visit, "discard", NULL, NULL); break;
690 case EOpBreak: writeTriplet(visit, "break", NULL, NULL); break;
691 case EOpContinue: writeTriplet(visit, "continue", NULL, NULL); break;
692 case EOpReturn: writeTriplet(visit, "return ", NULL, NULL); break;
693 default: UNREACHABLE(); break;
694 }
695
696 return true;
697}
698
699void TOutputGLSLBase::visitCodeBlock(TIntermNode* node) {
700 TInfoSinkBase &out = objSink();
701 if (node != NULL)
702 {
703 node->traverse(this);
704 // Single statements not part of a sequence need to be terminated
705 // with semi-colon.
706 if (isSingleStatement(node))
707 out << ";\n";
708 }
709 else
710 {
711 out << "{\n}\n"; // Empty code block.
712 }
713}