// // Copyright (c) 2002-2014 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/translator/OutputGLSLBase.h" #include "angle_gl.h" #include "common/debug.h" #include "common/mathutil.h" #include "compiler/translator/Compiler.h" #include "compiler/translator/util.h" #include namespace sh { namespace { bool isSingleStatement(TIntermNode *node) { if (node->getAsFunctionDefinition()) { return false; } else if (node->getAsBlock()) { return false; } else if (node->getAsIfElseNode()) { return false; } else if (node->getAsLoopNode()) { return false; } else if (node->getAsSwitchNode()) { return false; } else if (node->getAsCaseNode()) { return false; } return true; } class CommaSeparatedListItemPrefixGenerator { public: CommaSeparatedListItemPrefixGenerator() : mFirst(true) {} private: bool mFirst; friend TInfoSinkBase &operator<<(TInfoSinkBase &out, CommaSeparatedListItemPrefixGenerator &gen); }; TInfoSinkBase &operator<<(TInfoSinkBase &out, CommaSeparatedListItemPrefixGenerator &gen) { if (gen.mFirst) { gen.mFirst = false; } else { out << ", "; } return out; } } // namespace TOutputGLSLBase::TOutputGLSLBase(TInfoSinkBase &objSink, ShArrayIndexClampingStrategy clampingStrategy, ShHashFunction64 hashFunction, NameMap &nameMap, TSymbolTable *symbolTable, sh::GLenum shaderType, int shaderVersion, ShShaderOutput output, ShCompileOptions compileOptions) : TIntermTraverser(true, true, true, symbolTable), mObjSink(objSink), mDeclaringVariable(false), mClampingStrategy(clampingStrategy), mHashFunction(hashFunction), mNameMap(nameMap), mShaderType(shaderType), mShaderVersion(shaderVersion), mOutput(output), mCompileOptions(compileOptions) { } void TOutputGLSLBase::writeInvariantQualifier(const TType &type) { if (!sh::RemoveInvariant(mShaderType, mShaderVersion, mOutput, mCompileOptions)) { TInfoSinkBase &out = objSink(); out << "invariant "; } } void TOutputGLSLBase::writeFloat(TInfoSinkBase &out, float f) { if ((gl::isInf(f) || gl::isNaN(f)) && mShaderVersion >= 300) { out << "uintBitsToFloat(" << gl::bitCast(f) << "u)"; } else { out << std::min(FLT_MAX, std::max(-FLT_MAX, f)); } } void TOutputGLSLBase::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 TOutputGLSLBase::writeBuiltInFunctionTriplet(Visit visit, TOperator op, bool useEmulatedFunction) { TInfoSinkBase &out = objSink(); if (visit == PreVisit) { const char *opStr(GetOperatorString(op)); if (useEmulatedFunction) { BuiltInFunctionEmulator::WriteEmulatedFunctionName(out, opStr); } else { out << opStr; } out << "("; } else { writeTriplet(visit, nullptr, ", ", ")"); } } void TOutputGLSLBase::writeLayoutQualifier(TIntermTyped *variable) { const TType &type = variable->getType(); if (!NeedsToWriteLayoutQualifier(type)) { return; } TInfoSinkBase &out = objSink(); const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier(); out << "layout("; CommaSeparatedListItemPrefixGenerator listItemPrefix; if (type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn || IsVarying(type.getQualifier())) { if (layoutQualifier.location >= 0) { out << listItemPrefix << "location = " << layoutQualifier.location; } } if (type.getQualifier() == EvqFragmentOut) { if (layoutQualifier.yuv == true) { out << listItemPrefix << "yuv"; } } if (IsOpaqueType(type.getBasicType())) { if (layoutQualifier.binding >= 0) { out << listItemPrefix << "binding = " << layoutQualifier.binding; } } if (IsImage(type.getBasicType())) { if (layoutQualifier.imageInternalFormat != EiifUnspecified) { ASSERT(type.getQualifier() == EvqTemporary || type.getQualifier() == EvqUniform); out << listItemPrefix << getImageInternalFormatString(layoutQualifier.imageInternalFormat); } } if (IsAtomicCounter(type.getBasicType())) { out << listItemPrefix << "offset = " << layoutQualifier.offset; } out << ") "; } const char *TOutputGLSLBase::mapQualifierToString(TQualifier qualifier) { if (sh::IsGLSL410OrOlder(mOutput) && mShaderVersion >= 300 && (mCompileOptions & SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3) != 0) { switch (qualifier) { // The return string is consistent with sh::getQualifierString() from // BaseTypes.h minus the "centroid" keyword. case EvqCentroid: return ""; case EvqCentroidIn: return "smooth in"; case EvqCentroidOut: return "smooth out"; default: break; } } if (sh::IsGLSL130OrNewer(mOutput)) { switch (qualifier) { case EvqAttribute: return "in"; case EvqVaryingIn: return "in"; case EvqVaryingOut: return "out"; default: break; } } return sh::getQualifierString(qualifier); } void TOutputGLSLBase::writeVariableType(const TType &type) { TQualifier qualifier = type.getQualifier(); TInfoSinkBase &out = objSink(); if (type.isInvariant()) { writeInvariantQualifier(type); } if (type.getBasicType() == EbtInterfaceBlock) { TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); declareInterfaceBlockLayout(interfaceBlock); } if (qualifier != EvqTemporary && qualifier != EvqGlobal) { const char *qualifierString = mapQualifierToString(qualifier); if (qualifierString && qualifierString[0] != '\0') { out << qualifierString << " "; } } const TMemoryQualifier &memoryQualifier = type.getMemoryQualifier(); if (memoryQualifier.readonly) { ASSERT(IsImage(type.getBasicType())); out << "readonly "; } if (memoryQualifier.writeonly) { ASSERT(IsImage(type.getBasicType())); out << "writeonly "; } if (memoryQualifier.coherent) { ASSERT(IsImage(type.getBasicType())); out << "coherent "; } if (memoryQualifier.restrictQualifier) { ASSERT(IsImage(type.getBasicType())); out << "restrict "; } if (memoryQualifier.volatileQualifier) { ASSERT(IsImage(type.getBasicType())); out << "volatile "; } // Declare the struct if we have not done so already. if (type.getBasicType() == EbtStruct && !structDeclared(type.getStruct())) { const TStructure *structure = type.getStruct(); declareStruct(structure); if (!structure->name().empty()) { mDeclaredStructs.insert(structure->uniqueId()); } } else if (type.getBasicType() == EbtInterfaceBlock) { TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); declareInterfaceBlock(interfaceBlock); } else { if (writeVariablePrecision(type.getPrecision())) out << " "; out << getTypeName(type); } } void TOutputGLSLBase::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 != nullptr); const TType &type = arg->getType(); writeVariableType(type); if (!arg->getName().getString().empty()) out << " " << hashName(arg->getName()); if (type.isArray()) out << ArrayString(type); // Put a comma if this is not the last argument. if (iter != args.end() - 1) out << ", "; } } const TConstantUnion *TOutputGLSLBase::writeConstantUnion(const TType &type, const TConstantUnion *pConstUnion) { TInfoSinkBase &out = objSink(); if (type.getBasicType() == EbtStruct) { const TStructure *structure = type.getStruct(); out << hashName(TName(structure->name())) << "("; const TFieldList &fields = structure->fields(); for (size_t i = 0; i < fields.size(); ++i) { const TType *fieldType = fields[i]->type(); ASSERT(fieldType != nullptr); pConstUnion = writeConstantUnion(*fieldType, pConstUnion); if (i != fields.size() - 1) out << ", "; } out << ")"; } else { size_t size = type.getObjectSize(); bool writeType = size > 1; if (writeType) out << getTypeName(type) << "("; for (size_t i = 0; i < size; ++i, ++pConstUnion) { switch (pConstUnion->getType()) { case EbtFloat: writeFloat(out, pConstUnion->getFConst()); break; case EbtInt: out << pConstUnion->getIConst(); break; case EbtUInt: out << pConstUnion->getUConst() << "u"; break; case EbtBool: out << pConstUnion->getBConst(); break; case EbtYuvCscStandardEXT: out << getYuvCscStandardEXTString(pConstUnion->getYuvCscStandardEXTConst()); break; default: UNREACHABLE(); } if (i != size - 1) out << ", "; } if (writeType) out << ")"; } return pConstUnion; } void TOutputGLSLBase::writeConstructorTriplet(Visit visit, const TType &type) { TInfoSinkBase &out = objSink(); if (visit == PreVisit) { if (type.isArray()) { out << getTypeName(type); out << ArrayString(type); out << "("; } else { out << getTypeName(type) << "("; } } else { writeTriplet(visit, nullptr, ", ", ")"); } } void TOutputGLSLBase::visitSymbol(TIntermSymbol *node) { TInfoSinkBase &out = objSink(); out << hashVariableName(node->getName()); if (mDeclaringVariable && node->getType().isArray()) out << ArrayString(node->getType()); } void TOutputGLSLBase::visitConstantUnion(TIntermConstantUnion *node) { writeConstantUnion(node->getType(), node->getUnionArrayPointer()); } bool TOutputGLSLBase::visitSwizzle(Visit visit, TIntermSwizzle *node) { TInfoSinkBase &out = objSink(); if (visit == PostVisit) { out << "."; node->writeOffsetsAsXYZW(&out); } return true; } bool TOutputGLSLBase::visitBinary(Visit visit, TIntermBinary *node) { bool visitChildren = true; TInfoSinkBase &out = objSink(); switch (node->getOp()) { case EOpComma: writeTriplet(visit, "(", ", ", ")"); break; case EOpInitialize: if (visit == InVisit) { out << " = "; // RHS of initialize is not being declared. mDeclaringVariable = false; } break; case EOpAssign: writeTriplet(visit, "(", " = ", ")"); break; case EOpAddAssign: writeTriplet(visit, "(", " += ", ")"); break; case EOpSubAssign: writeTriplet(visit, "(", " -= ", ")"); break; case EOpDivAssign: writeTriplet(visit, "(", " /= ", ")"); break; case EOpIModAssign: writeTriplet(visit, "(", " %= ", ")"); break; // Notice the fall-through. case EOpMulAssign: case EOpVectorTimesMatrixAssign: case EOpVectorTimesScalarAssign: case EOpMatrixTimesScalarAssign: case EOpMatrixTimesMatrixAssign: writeTriplet(visit, "(", " *= ", ")"); break; case EOpBitShiftLeftAssign: writeTriplet(visit, "(", " <<= ", ")"); break; case EOpBitShiftRightAssign: writeTriplet(visit, "(", " >>= ", ")"); break; case EOpBitwiseAndAssign: writeTriplet(visit, "(", " &= ", ")"); break; case EOpBitwiseXorAssign: writeTriplet(visit, "(", " ^= ", ")"); break; case EOpBitwiseOrAssign: writeTriplet(visit, "(", " |= ", ")"); break; case EOpIndexDirect: writeTriplet(visit, nullptr, "[", "]"); break; case EOpIndexIndirect: if (node->getAddIndexClamp()) { if (visit == InVisit) { if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC) out << "[int(clamp(float("; else out << "[webgl_int_clamp("; } else if (visit == PostVisit) { TIntermTyped *left = node->getLeft(); TType leftType = left->getType(); if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC) out << "), 0.0, float("; else out << ", 0, "; if (leftType.isUnsizedArray()) { // For runtime-sized arrays in ESSL 3.10 we need to call the length method // to get the length to clamp against. See ESSL 3.10 section 4.1.9. Note // that a runtime-sized array expression is guaranteed not to have side // effects, so it's fine to add the expression to the output twice. ASSERT(mShaderVersion >= 310); ASSERT(!left->hasSideEffects()); left->traverse(this); out << ".length() - 1"; } else { int maxSize; if (leftType.isArray()) { maxSize = static_cast(leftType.getOutermostArraySize()) - 1; } else { maxSize = leftType.getNominalSize() - 1; } out << maxSize; } if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC) out << ")))]"; else out << ")]"; } } else { writeTriplet(visit, nullptr, "[", "]"); } break; case EOpIndexDirectStruct: if (visit == InVisit) { // Here we are writing out "foo.bar", where "foo" is struct // and "bar" is field. In AST, it is represented as a binary // node, where left child represents "foo" and right child "bar". // The node itself represents ".". The struct field "bar" is // actually stored as an index into TStructure::fields. out << "."; const TStructure *structure = node->getLeft()->getType().getStruct(); const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion(); const TField *field = structure->fields()[index->getIConst(0)]; TString fieldName = field->name(); if (!mSymbolTable->findBuiltIn(structure->name(), mShaderVersion)) fieldName = hashName(TName(fieldName)); out << fieldName; visitChildren = false; } break; case EOpIndexDirectInterfaceBlock: if (visit == InVisit) { out << "."; const TInterfaceBlock *interfaceBlock = node->getLeft()->getType().getInterfaceBlock(); const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion(); const TField *field = interfaceBlock->fields()[index->getIConst(0)]; TString fieldName = field->name(); if (!mSymbolTable->findBuiltIn(interfaceBlock->name(), mShaderVersion)) { fieldName = hashName(TName(fieldName)); } else { ASSERT(interfaceBlock->name() == "gl_PerVertex"); } out << fieldName; visitChildren = false; } break; case EOpAdd: writeTriplet(visit, "(", " + ", ")"); break; case EOpSub: writeTriplet(visit, "(", " - ", ")"); break; case EOpMul: writeTriplet(visit, "(", " * ", ")"); break; case EOpDiv: writeTriplet(visit, "(", " / ", ")"); break; case EOpIMod: writeTriplet(visit, "(", " % ", ")"); break; case EOpBitShiftLeft: writeTriplet(visit, "(", " << ", ")"); break; case EOpBitShiftRight: writeTriplet(visit, "(", " >> ", ")"); break; case EOpBitwiseAnd: writeTriplet(visit, "(", " & ", ")"); break; case EOpBitwiseXor: writeTriplet(visit, "(", " ^ ", ")"); break; case EOpBitwiseOr: writeTriplet(visit, "(", " | ", ")"); 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(); } return visitChildren; } bool TOutputGLSLBase::visitUnary(Visit visit, TIntermUnary *node) { TString preString; TString postString = ")"; switch (node->getOp()) { case EOpNegative: preString = "(-"; break; case EOpPositive: preString = "(+"; break; case EOpLogicalNot: preString = "(!"; break; case EOpBitwiseNot: preString = "(~"; break; case EOpPostIncrement: preString = "("; postString = "++)"; break; case EOpPostDecrement: preString = "("; postString = "--)"; break; case EOpPreIncrement: preString = "(++"; break; case EOpPreDecrement: preString = "(--"; break; case EOpArrayLength: preString = "(("; postString = ").length())"; break; case EOpRadians: case EOpDegrees: case EOpSin: case EOpCos: case EOpTan: case EOpAsin: case EOpAcos: case EOpAtan: case EOpSinh: case EOpCosh: case EOpTanh: case EOpAsinh: case EOpAcosh: case EOpAtanh: case EOpExp: case EOpLog: case EOpExp2: case EOpLog2: case EOpSqrt: case EOpInverseSqrt: case EOpAbs: case EOpSign: case EOpFloor: case EOpTrunc: case EOpRound: case EOpRoundEven: case EOpCeil: case EOpFract: case EOpIsNan: case EOpIsInf: case EOpFloatBitsToInt: case EOpFloatBitsToUint: case EOpIntBitsToFloat: case EOpUintBitsToFloat: case EOpPackSnorm2x16: case EOpPackUnorm2x16: case EOpPackHalf2x16: case EOpUnpackSnorm2x16: case EOpUnpackUnorm2x16: case EOpUnpackHalf2x16: case EOpPackUnorm4x8: case EOpPackSnorm4x8: case EOpUnpackUnorm4x8: case EOpUnpackSnorm4x8: case EOpLength: case EOpNormalize: case EOpDFdx: case EOpDFdy: case EOpFwidth: case EOpTranspose: case EOpDeterminant: case EOpInverse: case EOpAny: case EOpAll: case EOpLogicalNotComponentWise: case EOpBitfieldReverse: case EOpBitCount: case EOpFindLSB: case EOpFindMSB: writeBuiltInFunctionTriplet(visit, node->getOp(), node->getUseEmulatedFunction()); return true; default: UNREACHABLE(); } writeTriplet(visit, preString.c_str(), nullptr, postString.c_str()); return true; } bool TOutputGLSLBase::visitTernary(Visit visit, TIntermTernary *node) { TInfoSinkBase &out = objSink(); // 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->getTrueExpression()->traverse(this); out << ") : ("; node->getFalseExpression()->traverse(this); out << "))"; return false; } bool TOutputGLSLBase::visitIfElse(Visit visit, TIntermIfElse *node) { TInfoSinkBase &out = objSink(); out << "if ("; node->getCondition()->traverse(this); out << ")\n"; visitCodeBlock(node->getTrueBlock()); if (node->getFalseBlock()) { out << "else\n"; visitCodeBlock(node->getFalseBlock()); } return false; } bool TOutputGLSLBase::visitSwitch(Visit visit, TIntermSwitch *node) { ASSERT(node->getStatementList()); writeTriplet(visit, "switch (", ") ", nullptr); // The curly braces get written when visiting the statementList aggregate return true; } bool TOutputGLSLBase::visitCase(Visit visit, TIntermCase *node) { if (node->hasCondition()) { writeTriplet(visit, "case (", nullptr, "):\n"); return true; } else { TInfoSinkBase &out = objSink(); out << "default:\n"; return false; } } bool TOutputGLSLBase::visitBlock(Visit visit, TIntermBlock *node) { TInfoSinkBase &out = objSink(); // Scope the blocks except when at the global scope. if (mDepth > 0) { out << "{\n"; } for (TIntermSequence::const_iterator iter = node->getSequence()->begin(); iter != node->getSequence()->end(); ++iter) { TIntermNode *curNode = *iter; ASSERT(curNode != nullptr); curNode->traverse(this); if (isSingleStatement(curNode)) out << ";\n"; } // Scope the blocks except when at the global scope. if (mDepth > 0) { out << "}\n"; } return false; } bool TOutputGLSLBase::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) { TIntermFunctionPrototype *prototype = node->getFunctionPrototype(); prototype->traverse(this); visitCodeBlock(node->getBody()); // Fully processed; no need to visit children. return false; } bool TOutputGLSLBase::visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node) { TInfoSinkBase &out = objSink(); ASSERT(visit == PreVisit); const TIntermSymbol *symbol = node->getSymbol(); out << "invariant " << hashVariableName(symbol->getName()); return false; } bool TOutputGLSLBase::visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node) { TInfoSinkBase &out = objSink(); ASSERT(visit == PreVisit); const TType &type = node->getType(); writeVariableType(type); if (type.isArray()) out << ArrayString(type); out << " " << hashFunctionNameIfNeeded(*node->getFunctionSymbolInfo()); out << "("; writeFunctionParameters(*(node->getSequence())); out << ")"; return false; } bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node) { bool visitChildren = true; TInfoSinkBase &out = objSink(); switch (node->getOp()) { case EOpCallFunctionInAST: case EOpCallInternalRawFunction: case EOpCallBuiltInFunction: // Function call. if (visit == PreVisit) { if (node->getOp() == EOpCallBuiltInFunction) { out << translateTextureFunction(node->getFunctionSymbolInfo()->getName()); } else { out << hashFunctionNameIfNeeded(*node->getFunctionSymbolInfo()); } out << "("; } else if (visit == InVisit) out << ", "; else out << ")"; break; case EOpConstruct: writeConstructorTriplet(visit, node->getType()); break; case EOpEqualComponentWise: case EOpNotEqualComponentWise: case EOpLessThanComponentWise: case EOpGreaterThanComponentWise: case EOpLessThanEqualComponentWise: case EOpGreaterThanEqualComponentWise: case EOpMod: case EOpModf: case EOpPow: case EOpAtan: case EOpMin: case EOpMax: case EOpClamp: case EOpMix: case EOpStep: case EOpSmoothStep: case EOpFrexp: case EOpLdexp: case EOpDistance: case EOpDot: case EOpCross: case EOpFaceforward: case EOpReflect: case EOpRefract: case EOpMulMatrixComponentWise: case EOpOuterProduct: case EOpBitfieldExtract: case EOpBitfieldInsert: case EOpUaddCarry: case EOpUsubBorrow: case EOpUmulExtended: case EOpImulExtended: case EOpBarrier: case EOpMemoryBarrier: case EOpMemoryBarrierAtomicCounter: case EOpMemoryBarrierBuffer: case EOpMemoryBarrierImage: case EOpMemoryBarrierShared: case EOpGroupMemoryBarrier: case EOpEmitVertex: case EOpEndPrimitive: writeBuiltInFunctionTriplet(visit, node->getOp(), node->getUseEmulatedFunction()); break; default: UNREACHABLE(); } return visitChildren; } bool TOutputGLSLBase::visitDeclaration(Visit visit, TIntermDeclaration *node) { TInfoSinkBase &out = objSink(); // Variable declaration. if (visit == PreVisit) { const TIntermSequence &sequence = *(node->getSequence()); TIntermTyped *variable = sequence.front()->getAsTyped(); writeLayoutQualifier(variable); writeVariableType(variable->getType()); if (variable->getAsSymbolNode() == nullptr || !variable->getAsSymbolNode()->getSymbol().empty()) { out << " "; } mDeclaringVariable = true; } else if (visit == InVisit) { UNREACHABLE(); } else { mDeclaringVariable = false; } return true; } bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop *node) { TInfoSinkBase &out = objSink(); TLoopType loopType = node->getType(); if (loopType == ELoopFor) // for loop { out << "for ("; if (node->getInit()) node->getInit()->traverse(this); out << "; "; if (node->getCondition()) node->getCondition()->traverse(this); out << "; "; if (node->getExpression()) node->getExpression()->traverse(this); out << ")\n"; visitCodeBlock(node->getBody()); } else if (loopType == ELoopWhile) // while loop { out << "while ("; ASSERT(node->getCondition() != nullptr); node->getCondition()->traverse(this); out << ")\n"; visitCodeBlock(node->getBody()); } else // do-while loop { ASSERT(loopType == ELoopDoWhile); out << "do\n"; visitCodeBlock(node->getBody()); out << "while ("; ASSERT(node->getCondition() != nullptr); node->getCondition()->traverse(this); out << ");\n"; } // No need to visit children. They have been already processed in // this function. return false; } bool TOutputGLSLBase::visitBranch(Visit visit, TIntermBranch *node) { switch (node->getFlowOp()) { case EOpKill: writeTriplet(visit, "discard", nullptr, nullptr); break; case EOpBreak: writeTriplet(visit, "break", nullptr, nullptr); break; case EOpContinue: writeTriplet(visit, "continue", nullptr, nullptr); break; case EOpReturn: writeTriplet(visit, "return ", nullptr, nullptr); break; default: UNREACHABLE(); } return true; } void TOutputGLSLBase::visitCodeBlock(TIntermBlock *node) { TInfoSinkBase &out = objSink(); if (node != nullptr) { 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. } } TString TOutputGLSLBase::getTypeName(const TType &type) { return GetTypeName(type, mHashFunction, &mNameMap); } TString TOutputGLSLBase::hashName(const TName &name) { return HashName(name, mHashFunction, &mNameMap); } TString TOutputGLSLBase::hashVariableName(const TName &name) { if (mSymbolTable->findBuiltIn(name.getString(), mShaderVersion) != nullptr || name.getString().substr(0, 3) == "gl_") { if (mCompileOptions & SH_TRANSLATE_VIEWID_OVR_TO_UNIFORM && name.getString() == "gl_ViewID_OVR") { TName uniformName(TString("ViewID_OVR")); uniformName.setInternal(true); return hashName(uniformName); } return name.getString(); } return hashName(name); } TString TOutputGLSLBase::hashFunctionNameIfNeeded(const TFunctionSymbolInfo &info) { if (info.isMain()) { return info.getName(); } else { return hashName(info.getNameObj()); } } bool TOutputGLSLBase::structDeclared(const TStructure *structure) const { ASSERT(structure); if (structure->name().empty()) { return false; } return (mDeclaredStructs.count(structure->uniqueId()) > 0); } void TOutputGLSLBase::declareStruct(const TStructure *structure) { TInfoSinkBase &out = objSink(); out << "struct " << hashName(TName(structure->name())) << "{\n"; const TFieldList &fields = structure->fields(); for (size_t i = 0; i < fields.size(); ++i) { const TField *field = fields[i]; if (writeVariablePrecision(field->type()->getPrecision())) out << " "; out << getTypeName(*field->type()) << " " << hashName(TName(field->name())); if (field->type()->isArray()) out << ArrayString(*field->type()); out << ";\n"; } out << "}"; } void TOutputGLSLBase::declareInterfaceBlockLayout(const TInterfaceBlock *interfaceBlock) { TInfoSinkBase &out = objSink(); out << "layout("; switch (interfaceBlock->blockStorage()) { case EbsUnspecified: case EbsShared: // Default block storage is shared. out << "shared"; break; case EbsPacked: out << "packed"; break; case EbsStd140: out << "std140"; break; case EbsStd430: out << "std430"; break; default: UNREACHABLE(); break; } out << ", "; if (interfaceBlock->blockBinding() > 0) { out << "binding = " << interfaceBlock->blockBinding(); out << ", "; } switch (interfaceBlock->matrixPacking()) { case EmpUnspecified: case EmpColumnMajor: // Default matrix packing is column major. out << "column_major"; break; case EmpRowMajor: out << "row_major"; break; default: UNREACHABLE(); break; } out << ") "; } void TOutputGLSLBase::declareInterfaceBlock(const TInterfaceBlock *interfaceBlock) { TInfoSinkBase &out = objSink(); out << hashName(TName(interfaceBlock->name())) << "{\n"; const TFieldList &fields = interfaceBlock->fields(); for (size_t i = 0; i < fields.size(); ++i) { const TField *field = fields[i]; if (writeVariablePrecision(field->type()->getPrecision())) out << " "; out << getTypeName(*field->type()) << " " << hashName(TName(field->name())); if (field->type()->isArray()) out << ArrayString(*field->type()); out << ";\n"; } out << "}"; } void WriteGeometryShaderLayoutQualifiers(TInfoSinkBase &out, sh::TLayoutPrimitiveType inputPrimitive, int invocations, sh::TLayoutPrimitiveType outputPrimitive, int maxVertices) { // Omit 'invocations = 1' if (inputPrimitive != EptUndefined || invocations > 1) { out << "layout ("; if (inputPrimitive != EptUndefined) { out << getGeometryShaderPrimitiveTypeString(inputPrimitive); } if (invocations > 1) { if (inputPrimitive != EptUndefined) { out << ", "; } out << "invocations = " << invocations; } out << ") in;\n"; } if (outputPrimitive != EptUndefined || maxVertices != -1) { out << "layout ("; if (outputPrimitive != EptUndefined) { out << getGeometryShaderPrimitiveTypeString(outputPrimitive); } if (maxVertices != -1) { if (outputPrimitive != EptUndefined) { out << ", "; } out << "max_vertices = " << maxVertices; } out << ") out;\n"; } } // If SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS is enabled, layout qualifiers are spilled whenever // variables with specified layout qualifiers are copied. Additional checks are needed against the // type and storage qualifier of the variable to verify that layout qualifiers have to be outputted. // TODO (mradev): Fix layout qualifier spilling in ScalarizeVecAndMatConstructorArgs and remove // NeedsToWriteLayoutQualifier. bool NeedsToWriteLayoutQualifier(const TType &type) { if (type.getBasicType() == EbtInterfaceBlock) { return false; } const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier(); if ((type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn || IsVarying(type.getQualifier())) && layoutQualifier.location >= 0) { return true; } if (type.getQualifier() == EvqFragmentOut && layoutQualifier.yuv == true) { return true; } if (IsOpaqueType(type.getBasicType()) && layoutQualifier.binding != -1) { return true; } if (IsImage(type.getBasicType()) && layoutQualifier.imageInternalFormat != EiifUnspecified) { return true; } return false; } } // namespace sh