diff options
Diffstat (limited to 'gfx/angle/checkout/src/compiler/translator/OutputGLSLBase.cpp')
-rw-r--r-- | gfx/angle/checkout/src/compiler/translator/OutputGLSLBase.cpp | 1533 |
1 files changed, 1533 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/compiler/translator/OutputGLSLBase.cpp b/gfx/angle/checkout/src/compiler/translator/OutputGLSLBase.cpp new file mode 100644 index 0000000000..539f462e7d --- /dev/null +++ b/gfx/angle/checkout/src/compiler/translator/OutputGLSLBase.cpp @@ -0,0 +1,1533 @@ +// +// Copyright 2002 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 <cfloat> + +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; + } + else if (node->getAsPreprocessorDirective()) + { + return false; + } + return true; +} + +class CommaSeparatedListItemPrefixGenerator +{ + public: + CommaSeparatedListItemPrefixGenerator() : mFirst(true) {} + + private: + bool mFirst; + + template <typename Stream> + friend Stream &operator<<(Stream &out, CommaSeparatedListItemPrefixGenerator &gen); +}; + +template <typename Stream> +Stream &operator<<(Stream &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::writePreciseQualifier(const TType &type) +{ + TInfoSinkBase &out = objSink(); + out << "precise "; +} + +void TOutputGLSLBase::writeFloat(TInfoSinkBase &out, float f) +{ + if ((gl::isInf(f) || gl::isNaN(f)) && mShaderVersion >= 300) + { + out << "uintBitsToFloat(" << gl::bitCast<uint32_t>(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, ", ", ")"); + } +} + +// Outputs what goes inside layout(), except for location and binding qualifiers, as they are +// handled differently between GL GLSL and Vulkan GLSL. +std::string TOutputGLSLBase::getCommonLayoutQualifiers(TIntermTyped *variable) +{ + std::ostringstream out; + CommaSeparatedListItemPrefixGenerator listItemPrefix; + + const TType &type = variable->getType(); + const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier(); + + if (type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn || + IsVarying(type.getQualifier())) + { + if (type.getQualifier() == EvqFragmentOut && layoutQualifier.index >= 0) + { + out << listItemPrefix << "index = " << layoutQualifier.index; + } + } + + if (type.getQualifier() == EvqFragmentOut) + { + if (layoutQualifier.yuv == true) + { + out << listItemPrefix << "yuv"; + } + } + + 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; + } + + return out.str(); +} + +// Outputs memory qualifiers applied to images, buffers and its fields, as well as image function +// arguments. +std::string TOutputGLSLBase::getMemoryQualifiers(const TType &type) +{ + std::ostringstream out; + + const TMemoryQualifier &memoryQualifier = type.getMemoryQualifier(); + if (memoryQualifier.readonly) + { + out << "readonly "; + } + + if (memoryQualifier.writeonly) + { + out << "writeonly "; + } + + if (memoryQualifier.coherent) + { + out << "coherent "; + } + + if (memoryQualifier.restrictQualifier) + { + out << "restrict "; + } + + if (memoryQualifier.volatileQualifier) + { + out << "volatile "; + } + + return out.str(); +} + +void TOutputGLSLBase::writeLayoutQualifier(TIntermTyped *variable) +{ + const TType &type = variable->getType(); + + if (!NeedsToWriteLayoutQualifier(type)) + { + return; + } + + if (type.getBasicType() == EbtInterfaceBlock) + { + declareInterfaceBlockLayout(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 (IsOpaqueType(type.getBasicType())) + { + if (layoutQualifier.binding >= 0) + { + out << listItemPrefix << "binding = " << layoutQualifier.binding; + } + } + + std::string otherQualifiers = getCommonLayoutQualifiers(variable); + if (!otherQualifiers.empty()) + { + out << listItemPrefix << otherQualifiers; + } + + out << ") "; +} + +void TOutputGLSLBase::writeFieldLayoutQualifier(const TField *field) +{ + if (!field->type()->isMatrix() && !field->type()->isStructureContainingMatrices()) + { + return; + } + + TInfoSinkBase &out = objSink(); + + out << "layout("; + switch (field->type()->getLayoutQualifier().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::writeQualifier(TQualifier qualifier, const TType &type, const TSymbol *symbol) +{ + const char *result = mapQualifierToString(qualifier); + if (result && result[0] != '\0') + { + objSink() << result << " "; + } + + objSink() << getMemoryQualifiers(type); +} + +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, + const TSymbol *symbol, + bool isFunctionArgument) +{ + TQualifier qualifier = type.getQualifier(); + TInfoSinkBase &out = objSink(); + if (type.isInvariant()) + { + writeInvariantQualifier(type); + } + if (type.isPrecise()) + { + writePreciseQualifier(type); + } + if (qualifier != EvqTemporary && qualifier != EvqGlobal) + { + writeQualifier(qualifier, type, symbol); + } + if (isFunctionArgument) + { + // Function arguments are the only place (other than image/SSBO/field declaration) where + // memory qualifiers can appear. + out << getMemoryQualifiers(type); + } + + // Declare the struct. + if (type.isStructSpecifier()) + { + const TStructure *structure = type.getStruct(); + + declareStruct(structure); + } + else if (type.getBasicType() == EbtInterfaceBlock) + { + declareInterfaceBlock(type); + } + else + { + if (writeVariablePrecision(type.getPrecision())) + out << " "; + out << getTypeName(type); + } +} + +void TOutputGLSLBase::writeFunctionParameters(const TFunction *func) +{ + TInfoSinkBase &out = objSink(); + size_t paramCount = func->getParamCount(); + for (size_t i = 0; i < paramCount; ++i) + { + const TVariable *param = func->getParam(i); + const TType &type = param->getType(); + writeVariableType(type, param, true); + + if (param->symbolType() != SymbolType::Empty) + out << " " << hashName(param); + if (type.isArray()) + out << ArrayString(type); + + // Put a comma if this is not the last argument. + if (i != paramCount - 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(structure) << "("; + + 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 << hashName(&node->variable()); + + if (mDeclaringVariable && node->getType().isArray()) + out << ArrayString(node->getType()); +} + +void TOutputGLSLBase::visitConstantUnion(TIntermConstantUnion *node) +{ + writeConstantUnion(node->getType(), node->getConstantValue()); +} + +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<int>(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)]; + + out << hashFieldName(field); + 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)]; + out << hashFieldName(field); + 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) +{ + const char *preString = ""; + const char *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, nullptr, postString); + + 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 (getCurrentTraversalDepth() > 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 (getCurrentTraversalDepth() > 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::visitGlobalQualifierDeclaration(Visit visit, + TIntermGlobalQualifierDeclaration *node) +{ + TInfoSinkBase &out = objSink(); + ASSERT(visit == PreVisit); + const TIntermSymbol *symbol = node->getSymbol(); + out << (node->isPrecise() ? "precise " : "invariant ") << hashName(&symbol->variable()); + return false; +} + +void TOutputGLSLBase::visitFunctionPrototype(TIntermFunctionPrototype *node) +{ + TInfoSinkBase &out = objSink(); + + const TType &type = node->getType(); + writeVariableType(type, node->getFunction(), false); + if (type.isArray()) + out << ArrayString(type); + + out << " " << hashFunctionNameIfNeeded(node->getFunction()); + + out << "("; + writeFunctionParameters(node->getFunction()); + out << ")"; +} + +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->getFunction()->name(), mCompileOptions); + } + else + { + out << hashFunctionNameIfNeeded(node->getFunction()); + } + 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 EOpFma: + 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 EOpAtomicAdd: + case EOpAtomicMin: + case EOpAtomicMax: + case EOpAtomicAnd: + case EOpAtomicOr: + case EOpAtomicXor: + case EOpAtomicExchange: + case EOpAtomicCompSwap: + 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(); + TIntermSymbol *symbolNode = variable->getAsSymbolNode(); + if (!symbolNode || (symbolNode->getName() != "gl_ClipDistance" && + symbolNode->getName() != "gl_CullDistance")) + { + // gl_Clip/CullDistance re-declaration doesn't need layout. + writeLayoutQualifier(variable); + } + writeVariableType(variable->getType(), symbolNode ? &symbolNode->variable() : nullptr, + false); + if (variable->getAsSymbolNode() == nullptr || + variable->getAsSymbolNode()->variable().symbolType() != SymbolType::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. + } +} + +void TOutputGLSLBase::visitPreprocessorDirective(TIntermPreprocessorDirective *node) +{ + TInfoSinkBase &out = objSink(); + + out << "\n"; + + switch (node->getDirective()) + { + case PreprocessorDirective::Define: + out << "#define"; + break; + case PreprocessorDirective::Endif: + out << "#endif"; + break; + case PreprocessorDirective::If: + out << "#if"; + break; + case PreprocessorDirective::Ifdef: + out << "#ifdef"; + break; + + default: + UNREACHABLE(); + break; + } + + if (!node->getCommand().empty()) + { + out << " " << node->getCommand(); + } + + out << "\n"; +} + +ImmutableString TOutputGLSLBase::getTypeName(const TType &type) +{ + if (type.getBasicType() == EbtSamplerVideoWEBGL) + { + // TODO(http://anglebug.com/3889): translate SamplerVideoWEBGL into different token + // when necessary (e.g. on Android devices) + return ImmutableString("sampler2D"); + } + + return GetTypeName(type, mHashFunction, &mNameMap); +} + +ImmutableString TOutputGLSLBase::hashName(const TSymbol *symbol) +{ + return HashName(symbol, mHashFunction, &mNameMap); +} + +ImmutableString TOutputGLSLBase::hashFieldName(const TField *field) +{ + ASSERT(field->symbolType() != SymbolType::Empty); + if (field->symbolType() == SymbolType::UserDefined) + { + return HashName(field->name(), mHashFunction, &mNameMap); + } + + return field->name(); +} + +ImmutableString TOutputGLSLBase::hashFunctionNameIfNeeded(const TFunction *func) +{ + if (func->isMain()) + { + return func->name(); + } + else + { + return hashName(func); + } +} + +void TOutputGLSLBase::declareStruct(const TStructure *structure) +{ + TInfoSinkBase &out = objSink(); + + out << "struct "; + + if (structure->symbolType() != SymbolType::Empty) + { + out << hashName(structure) << " "; + } + out << "{\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()) << " " << hashFieldName(field); + if (field->type()->isArray()) + out << ArrayString(*field->type()); + out << ";\n"; + } + out << "}"; +} + +void TOutputGLSLBase::declareInterfaceBlockLayout(const TType &type) +{ + // 4.4.5 Uniform and Shader Storage Block Layout Qualifiers in GLSL 4.5 spec. + // Layout qualifiers can be used for uniform and shader storage blocks, + // but not for non-block uniform declarations. + if (IsShaderIoBlock(type.getQualifier())) + { + return; + } + + const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); + 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; + } + + if (interfaceBlock->blockBinding() >= 0) + { + out << ", "; + out << "binding = " << interfaceBlock->blockBinding(); + } + + out << ") "; +} + +const char *getVariableInterpolation(TQualifier qualifier) +{ + switch (qualifier) + { + case EvqSmoothOut: + return "smooth out "; + case EvqFlatOut: + return "flat out "; + case EvqNoPerspectiveOut: + return "noperspective out "; + case EvqCentroidOut: + return "centroid out "; + case EvqSmoothIn: + return "smooth in "; + case EvqFlatIn: + return "flat in "; + case EvqNoPerspectiveIn: + return "noperspective in "; + case EvqCentroidIn: + return "centroid in "; + default: + break; + } + return nullptr; +} + +void TOutputGLSLBase::declareInterfaceBlock(const TType &type) +{ + const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); + TInfoSinkBase &out = objSink(); + + out << hashName(interfaceBlock) << "{\n"; + const TFieldList &fields = interfaceBlock->fields(); + for (const TField *field : fields) + { + if (!IsShaderIoBlock(type.getQualifier()) && type.getQualifier() != EvqPatchIn && + type.getQualifier() != EvqPatchOut) + { + writeFieldLayoutQualifier(field); + } + out << getMemoryQualifiers(*field->type()); + if (writeVariablePrecision(field->type()->getPrecision())) + out << " "; + + const char *qualifier = getVariableInterpolation(field->type()->getQualifier()); + if (qualifier != nullptr) + out << qualifier; + + out << getTypeName(*field->type()) << " " << hashFieldName(field); + + 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"; + } +} + +void WriteTessControlShaderLayoutQualifiers(TInfoSinkBase &out, int inputVertices) +{ + if (inputVertices != 0) + { + out << "layout (vertices = " << inputVertices << ") out;\n"; + } +} + +void WriteTessEvaluationShaderLayoutQualifiers(TInfoSinkBase &out, + sh::TLayoutTessEvaluationType inputPrimitive, + sh::TLayoutTessEvaluationType inputVertexSpacing, + sh::TLayoutTessEvaluationType inputOrdering, + sh::TLayoutTessEvaluationType inputPoint) +{ + if (inputPrimitive != EtetUndefined) + { + out << "layout ("; + out << getTessEvaluationShaderTypeString(inputPrimitive); + if (inputVertexSpacing != EtetUndefined) + { + out << ", " << getTessEvaluationShaderTypeString(inputVertexSpacing); + } + if (inputOrdering != EtetUndefined) + { + out << ", " << getTessEvaluationShaderTypeString(inputOrdering); + } + if (inputPoint != EtetUndefined) + { + out << ", " << getTessEvaluationShaderTypeString(inputPoint); + } + out << ") in;\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 true; + } + + 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 |