summaryrefslogtreecommitdiffstats
path: root/Source/ThirdParty/ANGLE/src/compiler/OutputGLSL.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/ThirdParty/ANGLE/src/compiler/OutputGLSL.cpp')
-rw-r--r--Source/ThirdParty/ANGLE/src/compiler/OutputGLSL.cpp677
1 files changed, 677 insertions, 0 deletions
diff --git a/Source/ThirdParty/ANGLE/src/compiler/OutputGLSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/OutputGLSL.cpp
new file mode 100644
index 0000000..fd263b6
--- /dev/null
+++ b/Source/ThirdParty/ANGLE/src/compiler/OutputGLSL.cpp
@@ -0,0 +1,677 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/OutputGLSL.h"
+#include "compiler/debug.h"
+
+namespace
+{
+TString getTypeName(const TType& type)
+{
+ TInfoSinkBase out;
+ if (type.isMatrix())
+ {
+ out << "mat";
+ out << type.getNominalSize();
+ }
+ else if (type.isVector())
+ {
+ switch (type.getBasicType())
+ {
+ case EbtFloat: out << "vec"; break;
+ case EbtInt: out << "ivec"; break;
+ case EbtBool: out << "bvec"; break;
+ default: UNREACHABLE(); break;
+ }
+ out << type.getNominalSize();
+ }
+ else
+ {
+ if (type.getBasicType() == EbtStruct)
+ out << type.getTypeName();
+ else
+ out << type.getBasicString();
+ }
+ return TString(out.c_str());
+}
+
+TString arrayBrackets(const TType& type)
+{
+ ASSERT(type.isArray());
+ TInfoSinkBase out;
+ out << "[" << type.getArraySize() << "]";
+ return TString(out.c_str());
+}
+
+bool isSingleStatement(TIntermNode* node) {
+ if (const TIntermAggregate* aggregate = node->getAsAggregate())
+ {
+ return (aggregate->getOp() != EOpFunction) &&
+ (aggregate->getOp() != EOpSequence);
+ }
+ else if (const TIntermSelection* selection = node->getAsSelectionNode())
+ {
+ // Ternary operators are usually part of an assignment operator.
+ // This handles those rare cases in which they are all by themselves.
+ return selection->usesTernaryOperator();
+ }
+ else if (node->getAsLoopNode())
+ {
+ return false;
+ }
+ return true;
+}
+} // namespace
+
+TOutputGLSL::TOutputGLSL(TInfoSinkBase& objSink)
+ : TIntermTraverser(true, true, true),
+ mObjSink(objSink),
+ mDeclaringVariables(false)
+{
+}
+
+void TOutputGLSL::writeTriplet(Visit visit, const char* preStr, const char* inStr, const char* postStr)
+{
+ TInfoSinkBase& out = objSink();
+ if (visit == PreVisit && preStr)
+ {
+ out << preStr;
+ }
+ else if (visit == InVisit && inStr)
+ {
+ out << inStr;
+ }
+ else if (visit == PostVisit && postStr)
+ {
+ out << postStr;
+ }
+}
+
+void TOutputGLSL::writeVariableType(const TType& type)
+{
+ TInfoSinkBase& out = objSink();
+ TQualifier qualifier = type.getQualifier();
+ // TODO(alokp): Validate qualifier for variable declarations.
+ if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal))
+ out << type.getQualifierString() << " ";
+
+ // Declare the struct if we have not done so already.
+ if ((type.getBasicType() == EbtStruct) &&
+ (mDeclaredStructs.find(type.getTypeName()) == mDeclaredStructs.end()))
+ {
+ out << "struct " << type.getTypeName() << "{\n";
+ const TTypeList* structure = type.getStruct();
+ ASSERT(structure != NULL);
+ for (size_t i = 0; i < structure->size(); ++i)
+ {
+ const TType* fieldType = (*structure)[i].type;
+ ASSERT(fieldType != NULL);
+ out << getTypeName(*fieldType) << " " << fieldType->getFieldName();
+ if (fieldType->isArray())
+ out << arrayBrackets(*fieldType);
+ out << ";\n";
+ }
+ out << "}";
+ mDeclaredStructs.insert(type.getTypeName());
+ }
+ else
+ {
+ out << getTypeName(type);
+ }
+}
+
+void TOutputGLSL::writeFunctionParameters(const TIntermSequence& args)
+{
+ TInfoSinkBase& out = objSink();
+ for (TIntermSequence::const_iterator iter = args.begin();
+ iter != args.end(); ++iter)
+ {
+ const TIntermSymbol* arg = (*iter)->getAsSymbolNode();
+ ASSERT(arg != NULL);
+
+ const TType& type = arg->getType();
+ TQualifier qualifier = type.getQualifier();
+ // TODO(alokp): Validate qualifier for function arguments.
+ if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal))
+ out << type.getQualifierString() << " ";
+
+ out << getTypeName(type);
+
+ const TString& name = arg->getSymbol();
+ if (!name.empty())
+ out << " " << name;
+ if (type.isArray())
+ out << arrayBrackets(type);
+
+ // Put a comma if this is not the last argument.
+ if (iter != args.end() - 1)
+ out << ", ";
+ }
+}
+
+const ConstantUnion* TOutputGLSL::writeConstantUnion(const TType& type,
+ const ConstantUnion* pConstUnion)
+{
+ TInfoSinkBase& out = objSink();
+
+ if (type.getBasicType() == EbtStruct)
+ {
+ out << type.getTypeName() << "(";
+ const TTypeList* structure = type.getStruct();
+ ASSERT(structure != NULL);
+ for (size_t i = 0; i < structure->size(); ++i)
+ {
+ const TType* fieldType = (*structure)[i].type;
+ ASSERT(fieldType != NULL);
+ pConstUnion = writeConstantUnion(*fieldType, pConstUnion);
+ if (i != structure->size() - 1) out << ", ";
+ }
+ out << ")";
+ }
+ else
+ {
+ int size = type.getObjectSize();
+ bool writeType = size > 1;
+ if (writeType) out << getTypeName(type) << "(";
+ for (int i = 0; i < size; ++i, ++pConstUnion)
+ {
+ switch (pConstUnion->getType())
+ {
+ case EbtFloat: out << pConstUnion->getFConst(); break;
+ case EbtInt: out << pConstUnion->getIConst(); break;
+ case EbtBool: out << pConstUnion->getBConst(); break;
+ default: UNREACHABLE();
+ }
+ if (i != size - 1) out << ", ";
+ }
+ if (writeType) out << ")";
+ }
+ return pConstUnion;
+}
+
+void TOutputGLSL::visitSymbol(TIntermSymbol* node)
+{
+ TInfoSinkBase& out = objSink();
+ out << node->getSymbol();
+
+ if (mDeclaringVariables && node->getType().isArray())
+ out << arrayBrackets(node->getType());
+}
+
+void TOutputGLSL::visitConstantUnion(TIntermConstantUnion* node)
+{
+ writeConstantUnion(node->getType(), node->getUnionArrayPointer());
+}
+
+bool TOutputGLSL::visitBinary(Visit visit, TIntermBinary* node)
+{
+ bool visitChildren = true;
+ TInfoSinkBase& out = objSink();
+ switch (node->getOp())
+ {
+ case EOpInitialize:
+ if (visit == InVisit)
+ {
+ out << " = ";
+ // RHS of initialize is not being declared.
+ mDeclaringVariables = false;
+ }
+ break;
+ case EOpAssign: writeTriplet(visit, "(", " = ", ")"); break;
+ case EOpAddAssign: writeTriplet(visit, "(", " += ", ")"); break;
+ case EOpSubAssign: writeTriplet(visit, "(", " -= ", ")"); break;
+ case EOpDivAssign: writeTriplet(visit, "(", " /= ", ")"); break;
+ // Notice the fall-through.
+ case EOpMulAssign:
+ case EOpVectorTimesMatrixAssign:
+ case EOpVectorTimesScalarAssign:
+ case EOpMatrixTimesScalarAssign:
+ case EOpMatrixTimesMatrixAssign:
+ writeTriplet(visit, "(", " *= ", ")");
+ break;
+
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ writeTriplet(visit, NULL, "[", "]");
+ break;
+ case EOpIndexDirectStruct:
+ if (visit == InVisit)
+ {
+ out << ".";
+ // TODO(alokp): ASSERT
+ out << node->getType().getFieldName();
+ visitChildren = false;
+ }
+ break;
+ case EOpVectorSwizzle:
+ if (visit == InVisit)
+ {
+ out << ".";
+ TIntermAggregate* rightChild = node->getRight()->getAsAggregate();
+ TIntermSequence& sequence = rightChild->getSequence();
+ for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); ++sit)
+ {
+ TIntermConstantUnion* element = (*sit)->getAsConstantUnion();
+ ASSERT(element->getBasicType() == EbtInt);
+ ASSERT(element->getNominalSize() == 1);
+ const ConstantUnion& data = element->getUnionArrayPointer()[0];
+ ASSERT(data.getType() == EbtInt);
+ switch (data.getIConst())
+ {
+ case 0: out << "x"; break;
+ case 1: out << "y"; break;
+ case 2: out << "z"; break;
+ case 3: out << "w"; break;
+ default: UNREACHABLE(); break;
+ }
+ }
+ visitChildren = false;
+ }
+ break;
+
+ case EOpAdd: writeTriplet(visit, "(", " + ", ")"); break;
+ case EOpSub: writeTriplet(visit, "(", " - ", ")"); break;
+ case EOpMul: writeTriplet(visit, "(", " * ", ")"); break;
+ case EOpDiv: writeTriplet(visit, "(", " / ", ")"); break;
+ case EOpMod: UNIMPLEMENTED(); break;
+ case EOpEqual: writeTriplet(visit, "(", " == ", ")"); break;
+ case EOpNotEqual: writeTriplet(visit, "(", " != ", ")"); break;
+ case EOpLessThan: writeTriplet(visit, "(", " < ", ")"); break;
+ case EOpGreaterThan: writeTriplet(visit, "(", " > ", ")"); break;
+ case EOpLessThanEqual: writeTriplet(visit, "(", " <= ", ")"); break;
+ case EOpGreaterThanEqual: writeTriplet(visit, "(", " >= ", ")"); break;
+
+ // Notice the fall-through.
+ case EOpVectorTimesScalar:
+ case EOpVectorTimesMatrix:
+ case EOpMatrixTimesVector:
+ case EOpMatrixTimesScalar:
+ case EOpMatrixTimesMatrix:
+ writeTriplet(visit, "(", " * ", ")");
+ break;
+
+ case EOpLogicalOr: writeTriplet(visit, "(", " || ", ")"); break;
+ case EOpLogicalXor: writeTriplet(visit, "(", " ^^ ", ")"); break;
+ case EOpLogicalAnd: writeTriplet(visit, "(", " && ", ")"); break;
+ default: UNREACHABLE(); break;
+ }
+
+ return visitChildren;
+}
+
+bool TOutputGLSL::visitUnary(Visit visit, TIntermUnary* node)
+{
+ switch (node->getOp())
+ {
+ case EOpNegative: writeTriplet(visit, "(-", NULL, ")"); break;
+ case EOpVectorLogicalNot: writeTriplet(visit, "not(", NULL, ")"); break;
+ case EOpLogicalNot: writeTriplet(visit, "(!", NULL, ")"); break;
+
+ case EOpPostIncrement: writeTriplet(visit, "(", NULL, "++)"); break;
+ case EOpPostDecrement: writeTriplet(visit, "(", NULL, "--)"); break;
+ case EOpPreIncrement: writeTriplet(visit, "(++", NULL, ")"); break;
+ case EOpPreDecrement: writeTriplet(visit, "(--", NULL, ")"); break;
+
+ case EOpConvIntToBool:
+ case EOpConvFloatToBool:
+ switch (node->getOperand()->getType().getNominalSize())
+ {
+ case 1: writeTriplet(visit, "bool(", NULL, ")"); break;
+ case 2: writeTriplet(visit, "bvec2(", NULL, ")"); break;
+ case 3: writeTriplet(visit, "bvec3(", NULL, ")"); break;
+ case 4: writeTriplet(visit, "bvec4(", NULL, ")"); break;
+ default: UNREACHABLE();
+ }
+ break;
+ case EOpConvBoolToFloat:
+ case EOpConvIntToFloat:
+ switch (node->getOperand()->getType().getNominalSize())
+ {
+ case 1: writeTriplet(visit, "float(", NULL, ")"); break;
+ case 2: writeTriplet(visit, "vec2(", NULL, ")"); break;
+ case 3: writeTriplet(visit, "vec3(", NULL, ")"); break;
+ case 4: writeTriplet(visit, "vec4(", NULL, ")"); break;
+ default: UNREACHABLE();
+ }
+ break;
+ case EOpConvFloatToInt:
+ case EOpConvBoolToInt:
+ switch (node->getOperand()->getType().getNominalSize())
+ {
+ case 1: writeTriplet(visit, "int(", NULL, ")"); break;
+ case 2: writeTriplet(visit, "ivec2(", NULL, ")"); break;
+ case 3: writeTriplet(visit, "ivec3(", NULL, ")"); break;
+ case 4: writeTriplet(visit, "ivec4(", NULL, ")"); break;
+ default: UNREACHABLE();
+ }
+ break;
+
+ case EOpRadians: writeTriplet(visit, "radians(", NULL, ")"); break;
+ case EOpDegrees: writeTriplet(visit, "degrees(", NULL, ")"); break;
+ case EOpSin: writeTriplet(visit, "sin(", NULL, ")"); break;
+ case EOpCos: writeTriplet(visit, "cos(", NULL, ")"); break;
+ case EOpTan: writeTriplet(visit, "tan(", NULL, ")"); break;
+ case EOpAsin: writeTriplet(visit, "asin(", NULL, ")"); break;
+ case EOpAcos: writeTriplet(visit, "acos(", NULL, ")"); break;
+ case EOpAtan: writeTriplet(visit, "atan(", NULL, ")"); break;
+
+ case EOpExp: writeTriplet(visit, "exp(", NULL, ")"); break;
+ case EOpLog: writeTriplet(visit, "log(", NULL, ")"); break;
+ case EOpExp2: writeTriplet(visit, "exp2(", NULL, ")"); break;
+ case EOpLog2: writeTriplet(visit, "log2(", NULL, ")"); break;
+ case EOpSqrt: writeTriplet(visit, "sqrt(", NULL, ")"); break;
+ case EOpInverseSqrt: writeTriplet(visit, "inversesqrt(", NULL, ")"); break;
+
+ case EOpAbs: writeTriplet(visit, "abs(", NULL, ")"); break;
+ case EOpSign: writeTriplet(visit, "sign(", NULL, ")"); break;
+ case EOpFloor: writeTriplet(visit, "floor(", NULL, ")"); break;
+ case EOpCeil: writeTriplet(visit, "ceil(", NULL, ")"); break;
+ case EOpFract: writeTriplet(visit, "fract(", NULL, ")"); break;
+
+ case EOpLength: writeTriplet(visit, "length(", NULL, ")"); break;
+ case EOpNormalize: writeTriplet(visit, "normalize(", NULL, ")"); break;
+
+ case EOpAny: writeTriplet(visit, "any(", NULL, ")"); break;
+ case EOpAll: writeTriplet(visit, "all(", NULL, ")"); break;
+
+ default: UNREACHABLE(); break;
+ }
+
+ return true;
+}
+
+bool TOutputGLSL::visitSelection(Visit visit, TIntermSelection* node)
+{
+ TInfoSinkBase& out = objSink();
+
+ if (node->usesTernaryOperator())
+ {
+ // Notice two brackets at the beginning and end. The outer ones
+ // encapsulate the whole ternary expression. This preserves the
+ // order of precedence when ternary expressions are used in a
+ // compound expression, i.e., c = 2 * (a < b ? 1 : 2).
+ out << "((";
+ node->getCondition()->traverse(this);
+ out << ") ? (";
+ node->getTrueBlock()->traverse(this);
+ out << ") : (";
+ node->getFalseBlock()->traverse(this);
+ out << "))";
+ }
+ else
+ {
+ out << "if (";
+ node->getCondition()->traverse(this);
+ out << ")\n";
+
+ incrementDepth();
+ visitCodeBlock(node->getTrueBlock());
+
+ if (node->getFalseBlock())
+ {
+ out << "else\n";
+ visitCodeBlock(node->getFalseBlock());
+ }
+ decrementDepth();
+ }
+ return false;
+}
+
+bool TOutputGLSL::visitAggregate(Visit visit, TIntermAggregate* node)
+{
+ bool visitChildren = true;
+ TInfoSinkBase& out = objSink();
+ switch (node->getOp())
+ {
+ case EOpSequence: {
+ // Scope the sequences except when at the global scope.
+ if (depth > 0) out << "{\n";
+
+ incrementDepth();
+ const TIntermSequence& sequence = node->getSequence();
+ for (TIntermSequence::const_iterator iter = sequence.begin();
+ iter != sequence.end(); ++iter)
+ {
+ TIntermNode* node = *iter;
+ ASSERT(node != NULL);
+ node->traverse(this);
+
+ if (isSingleStatement(node))
+ out << ";\n";
+ }
+ decrementDepth();
+
+ // Scope the sequences except when at the global scope.
+ if (depth > 0) out << "}\n";
+ visitChildren = false;
+ break;
+ }
+ case EOpPrototype: {
+ // Function declaration.
+ ASSERT(visit == PreVisit);
+ TString returnType = getTypeName(node->getType());
+ out << returnType << " " << node->getName();
+
+ out << "(";
+ writeFunctionParameters(node->getSequence());
+ out << ")";
+
+ visitChildren = false;
+ break;
+ }
+ case EOpFunction: {
+ // Function definition.
+ ASSERT(visit == PreVisit);
+ TString returnType = getTypeName(node->getType());
+ TString functionName = TFunction::unmangleName(node->getName());
+ out << returnType << " " << functionName;
+
+ incrementDepth();
+ // Function definition node contains one or two children nodes
+ // representing function parameters and function body. The latter
+ // is not present in case of empty function bodies.
+ const TIntermSequence& sequence = node->getSequence();
+ ASSERT((sequence.size() == 1) || (sequence.size() == 2));
+ TIntermSequence::const_iterator seqIter = sequence.begin();
+
+ // Traverse function parameters.
+ TIntermAggregate* params = (*seqIter)->getAsAggregate();
+ ASSERT(params != NULL);
+ ASSERT(params->getOp() == EOpParameters);
+ params->traverse(this);
+
+ // Traverse function body.
+ TIntermAggregate* body = ++seqIter != sequence.end() ?
+ (*seqIter)->getAsAggregate() : NULL;
+ visitCodeBlock(body);
+ decrementDepth();
+
+ // Fully processed; no need to visit children.
+ visitChildren = false;
+ break;
+ }
+ case EOpFunctionCall:
+ // Function call.
+ if (visit == PreVisit)
+ {
+ TString functionName = TFunction::unmangleName(node->getName());
+ out << functionName << "(";
+ }
+ else if (visit == InVisit)
+ {
+ out << ", ";
+ }
+ else
+ {
+ out << ")";
+ }
+ break;
+ case EOpParameters: {
+ // Function parameters.
+ ASSERT(visit == PreVisit);
+ out << "(";
+ writeFunctionParameters(node->getSequence());
+ out << ")";
+ visitChildren = false;
+ break;
+ }
+ case EOpDeclaration: {
+ // Variable declaration.
+ if (visit == PreVisit)
+ {
+ const TIntermSequence& sequence = node->getSequence();
+ const TIntermTyped* variable = sequence.front()->getAsTyped();
+ writeVariableType(variable->getType());
+ out << " ";
+ mDeclaringVariables = true;
+ }
+ else if (visit == InVisit)
+ {
+ out << ", ";
+ mDeclaringVariables = true;
+ }
+ else
+ {
+ mDeclaringVariables = false;
+ }
+ break;
+ }
+ case EOpConstructFloat: writeTriplet(visit, "float(", NULL, ")"); break;
+ case EOpConstructVec2: writeTriplet(visit, "vec2(", ", ", ")"); break;
+ case EOpConstructVec3: writeTriplet(visit, "vec3(", ", ", ")"); break;
+ case EOpConstructVec4: writeTriplet(visit, "vec4(", ", ", ")"); break;
+ case EOpConstructBool: writeTriplet(visit, "bool(", NULL, ")"); break;
+ case EOpConstructBVec2: writeTriplet(visit, "bvec2(", ", ", ")"); break;
+ case EOpConstructBVec3: writeTriplet(visit, "bvec3(", ", ", ")"); break;
+ case EOpConstructBVec4: writeTriplet(visit, "bvec4(", ", ", ")"); break;
+ case EOpConstructInt: writeTriplet(visit, "int(", NULL, ")"); break;
+ case EOpConstructIVec2: writeTriplet(visit, "ivec2(", ", ", ")"); break;
+ case EOpConstructIVec3: writeTriplet(visit, "ivec3(", ", ", ")"); break;
+ case EOpConstructIVec4: writeTriplet(visit, "ivec4(", ", ", ")"); break;
+ case EOpConstructMat2: writeTriplet(visit, "mat2(", ", ", ")"); break;
+ case EOpConstructMat3: writeTriplet(visit, "mat3(", ", ", ")"); break;
+ case EOpConstructMat4: writeTriplet(visit, "mat4(", ", ", ")"); break;
+ case EOpConstructStruct:
+ if (visit == PreVisit)
+ {
+ const TType& type = node->getType();
+ ASSERT(type.getBasicType() == EbtStruct);
+ out << type.getTypeName() << "(";
+ }
+ else if (visit == InVisit)
+ {
+ out << ", ";
+ }
+ else
+ {
+ out << ")";
+ }
+ break;
+
+ case EOpLessThan: writeTriplet(visit, "lessThan(", ", ", ")"); break;
+ case EOpGreaterThan: writeTriplet(visit, "greaterThan(", ", ", ")"); break;
+ case EOpLessThanEqual: writeTriplet(visit, "lessThanEqual(", ", ", ")"); break;
+ case EOpGreaterThanEqual: writeTriplet(visit, "greaterThanEqual(", ", ", ")"); break;
+ case EOpVectorEqual: writeTriplet(visit, "equal(", ", ", ")"); break;
+ case EOpVectorNotEqual: writeTriplet(visit, "notEqual(", ", ", ")"); break;
+ case EOpComma: writeTriplet(visit, NULL, ", ", NULL); break;
+
+ case EOpMod: writeTriplet(visit, "mod(", ", ", ")"); break;
+ case EOpPow: writeTriplet(visit, "pow(", ", ", ")"); break;
+ case EOpAtan: writeTriplet(visit, "atan(", ", ", ")"); break;
+ case EOpMin: writeTriplet(visit, "min(", ", ", ")"); break;
+ case EOpMax: writeTriplet(visit, "max(", ", ", ")"); break;
+ case EOpClamp: writeTriplet(visit, "clamp(", ", ", ")"); break;
+ case EOpMix: writeTriplet(visit, "mix(", ", ", ")"); break;
+ case EOpStep: writeTriplet(visit, "step(", ", ", ")"); break;
+ case EOpSmoothStep: writeTriplet(visit, "smoothstep(", ", ", ")"); break;
+
+ case EOpDistance: writeTriplet(visit, "distance(", ", ", ")"); break;
+ case EOpDot: writeTriplet(visit, "dot(", ", ", ")"); break;
+ case EOpCross: writeTriplet(visit, "cross(", ", ", ")"); break;
+ case EOpFaceForward: writeTriplet(visit, "faceforward(", ", ", ")"); break;
+ case EOpReflect: writeTriplet(visit, "reflect(", ", ", ")"); break;
+ case EOpRefract: writeTriplet(visit, "refract(", ", ", ")"); break;
+ case EOpMul: writeTriplet(visit, "matrixCompMult(", ", ", ")"); break;
+
+ default: UNREACHABLE(); break;
+ }
+ return visitChildren;
+}
+
+bool TOutputGLSL::visitLoop(Visit visit, TIntermLoop* node)
+{
+ TInfoSinkBase& out = objSink();
+
+ incrementDepth();
+ // Loop header.
+ if (node->testFirst()) // for loop
+ {
+ out << "for (";
+ if (node->getInit())
+ node->getInit()->traverse(this);
+ out << "; ";
+
+ ASSERT(node->getTest() != NULL);
+ node->getTest()->traverse(this);
+ out << "; ";
+
+ if (node->getTerminal())
+ node->getTerminal()->traverse(this);
+ out << ")\n";
+ }
+ else // do-while loop
+ {
+ out << "do\n";
+ }
+
+ // Loop body.
+ visitCodeBlock(node->getBody());
+
+ // Loop footer.
+ if (!node->testFirst()) // while loop
+ {
+ out << "while (";
+ ASSERT(node->getTest() != NULL);
+ node->getTest()->traverse(this);
+ out << ");\n";
+ }
+ decrementDepth();
+
+ // No need to visit children. They have been already processed in
+ // this function.
+ return false;
+}
+
+bool TOutputGLSL::visitBranch(Visit visit, TIntermBranch* node)
+{
+ switch (node->getFlowOp())
+ {
+ case EOpKill: writeTriplet(visit, "discard", NULL, NULL); break;
+ case EOpBreak: writeTriplet(visit, "break", NULL, NULL); break;
+ case EOpContinue: writeTriplet(visit, "continue", NULL, NULL); break;
+ case EOpReturn: writeTriplet(visit, "return ", NULL, NULL); break;
+ default: UNREACHABLE(); break;
+ }
+
+ return true;
+}
+
+void TOutputGLSL::visitCodeBlock(TIntermNode* node) {
+ TInfoSinkBase &out = objSink();
+ if (node != NULL)
+ {
+ node->traverse(this);
+ // Single statements not part of a sequence need to be terminated
+ // with semi-colon.
+ if (isSingleStatement(node))
+ out << ";\n";
+ }
+ else
+ {
+ out << "{\n}\n"; // Empty code block.
+ }
+}