From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- .../src/compiler/translator/OutputGLSLBase.cpp | 1513 ++++++++++++++++++++ 1 file changed, 1513 insertions(+) create mode 100644 gfx/angle/checkout/src/compiler/translator/OutputGLSLBase.cpp (limited to 'gfx/angle/checkout/src/compiler/translator/OutputGLSLBase.cpp') 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..9536fe1d03 --- /dev/null +++ b/gfx/angle/checkout/src/compiler/translator/OutputGLSLBase.cpp @@ -0,0 +1,1513 @@ +// +// 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 + +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 + friend Stream &operator<<(Stream &out, CommaSeparatedListItemPrefixGenerator &gen); +}; + +template +Stream &operator<<(Stream &out, CommaSeparatedListItemPrefixGenerator &gen) +{ + if (gen.mFirst) + { + gen.mFirst = false; + } + else + { + out << ", "; + } + return out; +} + +} // namespace + +TOutputGLSLBase::TOutputGLSLBase(TCompiler *compiler, + TInfoSinkBase &objSink, + const ShCompileOptions &compileOptions) + : TIntermTraverser(true, true, true, &compiler->getSymbolTable()), + mObjSink(objSink), + mDeclaringVariable(false), + mHashFunction(compiler->getHashFunction()), + mNameMap(compiler->getNameMap()), + mShaderType(compiler->getShaderType()), + mShaderVersion(compiler->getShaderVersion()), + mOutput(compiler->getOutputType()), + mHighPrecisionSupported(compiler->isHighPrecisionSupported()), + // If pixel local storage introduces new fragment outputs, we are now required to specify a + // location for _all_ fragment outputs, including previously valid outputs that had an + // implicit location of zero. + mAlwaysSpecifyFragOutLocation(compiler->hasPixelLocalStorageUniforms() && + compileOptions.pls.type == + ShPixelLocalStorageType::FramebufferFetch), + 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(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::writeFunctionTriplet(Visit visit, + const ImmutableString &functionName, + bool useEmulatedFunction) +{ + TInfoSinkBase &out = objSink(); + if (visit == PreVisit) + { + if (useEmulatedFunction) + { + BuiltInFunctionEmulator::WriteEmulatedFunctionName(out, functionName.data()); + } + else + { + out << functionName; + } + 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(TIntermSymbol *variable) +{ + std::ostringstream out; + CommaSeparatedListItemPrefixGenerator listItemPrefix; + + const TType &type = variable->getType(); + const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier(); + + if (type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqFragmentInOut) + { + if (layoutQualifier.index >= 0) + { + out << listItemPrefix << "index = " << layoutQualifier.index; + } + if (layoutQualifier.yuv) + { + out << listItemPrefix << "yuv"; + } + } + + if (type.getQualifier() == EvqFragmentInOut && layoutQualifier.noncoherent) + { + out << listItemPrefix << "noncoherent"; + } + + 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(TIntermSymbol *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 (IsFragmentOutput(type.getQualifier()) || type.getQualifier() == EvqVertexIn || + IsVarying(type.getQualifier())) + { + if (layoutQualifier.location >= 0 || + (mAlwaysSpecifyFragOutLocation && IsFragmentOutput(type.getQualifier()))) + { + out << listItemPrefix << "location = " << std::max(layoutQualifier.location, 0); + } + } + + 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.removeInvariantAndCentroidForESSL3) + { + 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; + } + } + + switch (qualifier) + { + // gl_ClipDistance / gl_CullDistance require different qualifiers based on shader type. + case EvqClipDistance: + case EvqCullDistance: + return mShaderType == GL_FRAGMENT_SHADER ? "in" : "out"; + + // gl_LastFragColor / gl_LastFragData have no qualifiers. + case EvqLastFragData: + case EvqLastFragColor: + return nullptr; + + default: + return sh::getQualifierString(qualifier); + } +} + +namespace +{ + +constexpr char kIndent[] = " "; // 10x2 spaces +constexpr int kIndentWidth = 2; +constexpr int kMaxIndentLevel = sizeof(kIndent) / kIndentWidth; + +} // namespace + +const char *TOutputGLSLBase::getIndentPrefix(int extraIndentation) +{ + int indentDepth = std::min(kMaxIndentLevel, getCurrentBlockDepth() + extraIndentation); + ASSERT(indentDepth >= 0); + return kIndent + (kMaxIndentLevel - indentDepth) * kIndentWidth; +} + +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: + case EOpIndexIndirect: + 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; + + default: + writeFunctionTriplet(visit, node->getFunction()->name(), + node->getUseEmulatedFunction()); + return true; + } + + 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 << getIndentPrefix() << "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); + + out << getIndentPrefix(curNode->getAsCaseNode() ? -1 : 0); + + curNode->traverse(this); + + if (isSingleStatement(curNode)) + out << ";\n"; + } + + // Scope the blocks except when at the global scope. + if (getCurrentTraversalDepth() > 0) + { + out << getIndentPrefix(-1) << "}\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; + if (node->getOp() == EOpConstruct) + { + writeConstructorTriplet(visit, node->getType()); + } + else + { + // Function call. + ImmutableString functionName = node->getFunction()->name(); + if (visit == PreVisit) + { + // No raw function is expected. + ASSERT(node->getOp() != EOpCallInternalRawFunction); + + if (node->getOp() == EOpCallFunctionInAST) + { + functionName = hashFunctionNameIfNeeded(node->getFunction()); + } + else + { + functionName = + translateTextureFunction(node->getFunction()->name(), mCompileOptions); + } + } + writeFunctionTriplet(visit, functionName, node->getUseEmulatedFunction()); + } + return visitChildren; +} + +bool TOutputGLSLBase::visitDeclaration(Visit visit, TIntermDeclaration *node) +{ + TInfoSinkBase &out = objSink(); + + // Variable declaration. + if (visit == PreVisit) + { + const TIntermSequence &sequence = *(node->getSequence()); + TIntermTyped *decl = sequence.front()->getAsTyped(); + TIntermSymbol *symbolNode = decl->getAsSymbolNode(); + if (symbolNode == nullptr) + { + ASSERT(decl->getAsBinaryNode() && decl->getAsBinaryNode()->getOp() == EOpInitialize); + symbolNode = decl->getAsBinaryNode()->getLeft()->getAsSymbolNode(); + } + ASSERT(symbolNode); + + if (symbolNode->getName() != "gl_ClipDistance" && + symbolNode->getName() != "gl_CullDistance") + { + // gl_Clip/CullDistance re-declaration doesn't need layout. + writeLayoutQualifier(symbolNode); + } + + writeVariableType(symbolNode->getType(), &symbolNode->variable(), false); + if (symbolNode->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) + { + out << getIndentPrefix(); + 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) + { + out << getIndentPrefix(1); + const TField *field = fields[i]; + const TType &fieldType = *field->type(); + if (writeVariablePrecision(fieldType.getPrecision())) + { + out << " "; + } + if (fieldType.isPrecise()) + { + writePreciseQualifier(fieldType); + } + out << getTypeName(fieldType) << " " << hashFieldName(field); + if (fieldType.isArray()) + { + out << ArrayString(fieldType); + } + out << ";\n"; + } + out << getIndentPrefix() << "}"; +} + +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) + { + out << getIndentPrefix(1); + if (!IsShaderIoBlock(type.getQualifier()) && type.getQualifier() != EvqPatchIn && + type.getQualifier() != EvqPatchOut) + { + writeFieldLayoutQualifier(field); + } + + const TType &fieldType = *field->type(); + + out << getMemoryQualifiers(fieldType); + if (writeVariablePrecision(fieldType.getPrecision())) + out << " "; + if (fieldType.isInvariant()) + { + writeInvariantQualifier(fieldType); + } + if (fieldType.isPrecise()) + { + writePreciseQualifier(fieldType); + } + + const char *qualifier = getVariableInterpolation(fieldType.getQualifier()); + if (qualifier != nullptr) + out << qualifier; + + out << getTypeName(fieldType) << " " << hashFieldName(field); + + if (fieldType.isArray()) + out << ArrayString(fieldType); + out << ";\n"; + } + out << "}"; +} + +void WritePragma(TInfoSinkBase &out, const ShCompileOptions &compileOptions, const TPragma &pragma) +{ + if (!compileOptions.flattenPragmaSTDGLInvariantAll) + { + if (pragma.stdgl.invariantAll) + out << "#pragma STDGL invariant(all)\n"; + } +} + +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 TOutputGLSLBase::needsToWriteLayoutQualifier(const TType &type) +{ + if (type.getBasicType() == EbtInterfaceBlock) + { + return true; + } + + const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier(); + + if (IsFragmentOutput(type.getQualifier()) || type.getQualifier() == EvqVertexIn || + IsVarying(type.getQualifier())) + { + if (layoutQualifier.location >= 0 || + (mAlwaysSpecifyFragOutLocation && IsFragmentOutput(type.getQualifier()))) + { + return true; + } + } + + if (type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqFragmentInOut) + { + if (layoutQualifier.index >= 0) + { + return true; + } + if (layoutQualifier.yuv) + { + return true; + } + } + + if (type.getQualifier() == EvqFragmentInOut && layoutQualifier.noncoherent) + { + return true; + } + + if (IsOpaqueType(type.getBasicType()) && layoutQualifier.binding != -1) + { + return true; + } + + if (IsImage(type.getBasicType()) && layoutQualifier.imageInternalFormat != EiifUnspecified) + { + return true; + } + return false; +} + +void EmitEarlyFragmentTestsGLSL(const TCompiler &compiler, TInfoSinkBase &sink) +{ + if (compiler.isEarlyFragmentTestsSpecified()) + { + sink << "layout (early_fragment_tests) in;\n"; + } +} + +void EmitWorkGroupSizeGLSL(const TCompiler &compiler, TInfoSinkBase &sink) +{ + if (compiler.isComputeShaderLocalSizeDeclared()) + { + const sh::WorkGroupSize &localSize = compiler.getComputeShaderLocalSize(); + sink << "layout (local_size_x=" << localSize[0] << ", local_size_y=" << localSize[1] + << ", local_size_z=" << localSize[2] << ") in;\n"; + } +} + +void EmitMultiviewGLSL(const TCompiler &compiler, + const ShCompileOptions &compileOptions, + const TExtension extension, + const TBehavior behavior, + TInfoSinkBase &sink) +{ + ASSERT(behavior != EBhUndefined); + if (behavior == EBhDisable) + return; + + const bool isVertexShader = (compiler.getShaderType() == GL_VERTEX_SHADER); + if (compileOptions.initializeBuiltinsForInstancedMultiview) + { + // Emit ARB_shader_viewport_layer_array/NV_viewport_array2 in a vertex shader if the + // SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is set and the + // OVR_multiview(2) extension is requested. + if (isVertexShader && compileOptions.selectViewInNvGLSLVertexShader) + { + sink << "#if defined(GL_ARB_shader_viewport_layer_array)\n" + << "#extension GL_ARB_shader_viewport_layer_array : require\n" + << "#elif defined(GL_NV_viewport_array2)\n" + << "#extension GL_NV_viewport_array2 : require\n" + << "#endif\n"; + } + } + else + { + sink << "#extension GL_OVR_multiview"; + if (extension == TExtension::OVR_multiview2) + { + sink << "2"; + } + sink << " : " << GetBehaviorString(behavior) << "\n"; + + const auto &numViews = compiler.getNumViews(); + if (isVertexShader && numViews != -1) + { + sink << "layout(num_views=" << numViews << ") in;\n"; + } + } +} + +} // namespace sh -- cgit v1.2.3