diff options
Diffstat (limited to 'gfx/angle/src/compiler/translator/OutputHLSL.cpp')
-rwxr-xr-x | gfx/angle/src/compiler/translator/OutputHLSL.cpp | 854 |
1 files changed, 452 insertions, 402 deletions
diff --git a/gfx/angle/src/compiler/translator/OutputHLSL.cpp b/gfx/angle/src/compiler/translator/OutputHLSL.cpp index 5ef2e89f9..639178281 100755 --- a/gfx/angle/src/compiler/translator/OutputHLSL.cpp +++ b/gfx/angle/src/compiler/translator/OutputHLSL.cpp @@ -28,11 +28,13 @@ #include "compiler/translator/blocklayout.h" #include "compiler/translator/util.h" -namespace sh +namespace { -namespace +bool IsSequence(TIntermNode *node) { + return node->getAsAggregate() != nullptr && node->getAsAggregate()->getOp() == EOpSequence; +} void WriteSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUnion) { @@ -75,14 +77,14 @@ const TConstantUnion *WriteConstantUnionArray(TInfoSinkBase &out, } // namespace -OutputHLSL::OutputHLSL(sh::GLenum shaderType, - int shaderVersion, - const TExtensionBehavior &extensionBehavior, - const char *sourcePath, - ShShaderOutput outputType, - int numRenderTargets, - const std::vector<Uniform> &uniforms, - ShCompileOptions compileOptions) +namespace sh +{ + +OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion, + const TExtensionBehavior &extensionBehavior, + const char *sourcePath, ShShaderOutput outputType, + int numRenderTargets, const std::vector<Uniform> &uniforms, + int compileOptions) : TIntermTraverser(true, true, true), mShaderType(shaderType), mShaderVersion(shaderVersion), @@ -156,17 +158,12 @@ void OutputHLSL::output(TIntermNode *treeRoot, TInfoSinkBase &objSink) BuiltInFunctionEmulator builtInFunctionEmulator; InitBuiltInFunctionEmulatorForHLSL(&builtInFunctionEmulator); - if ((mCompileOptions & SH_EMULATE_ISNAN_FLOAT_FUNCTION) != 0) - { - InitBuiltInIsnanFunctionEmulatorForHLSLWorkarounds(&builtInFunctionEmulator, - mShaderVersion); - } - builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(treeRoot); // Now that we are done changing the AST, do the analyses need for HLSL generation CallDAG::InitResult success = mCallDag.init(treeRoot, &objSink); ASSERT(success == CallDAG::INITDAG_SUCCESS); + UNUSED_ASSERTION_VARIABLE(success); mASTMetadataList = CreateASTMetadataHLSL(treeRoot, mCallDag); // Output the body and footer first to determine what has to go in the header @@ -842,17 +839,6 @@ bool OutputHLSL::ancestorEvaluatesToSamplerInStruct(Visit visit) return false; } -bool OutputHLSL::visitSwizzle(Visit visit, TIntermSwizzle *node) -{ - TInfoSinkBase &out = getInfoSink(); - if (visit == PostVisit) - { - out << "."; - node->writeOffsetsAsXYZW(&out); - } - return true; -} - bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) { TInfoSinkBase &out = getInfoSink(); @@ -866,139 +852,133 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) switch (node->getOp()) { - case EOpComma: - outputTriplet(out, visit, "(", ", ", ")"); - break; - case EOpAssign: - if (node->getLeft()->isArray()) - { - TIntermAggregate *rightAgg = node->getRight()->getAsAggregate(); - if (rightAgg != nullptr && rightAgg->isConstructor()) - { - const TString &functionName = addArrayConstructIntoFunction(node->getType()); - out << functionName << "("; - node->getLeft()->traverse(this); - TIntermSequence *seq = rightAgg->getSequence(); - for (auto &arrayElement : *seq) - { - out << ", "; - arrayElement->traverse(this); - } - out << ")"; - return false; - } - // ArrayReturnValueToOutParameter should have eliminated expressions where a - // function call is assigned. - ASSERT(rightAgg == nullptr || rightAgg->getOp() != EOpFunctionCall); - - const TString &functionName = addArrayAssignmentFunction(node->getType()); - outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")"); - } - else + case EOpAssign: + if (node->getLeft()->isArray()) + { + TIntermAggregate *rightAgg = node->getRight()->getAsAggregate(); + if (rightAgg != nullptr && rightAgg->isConstructor()) { - outputTriplet(out, visit, "(", " = ", ")"); - } - break; - case EOpInitialize: - if (visit == PreVisit) - { - TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode(); - ASSERT(symbolNode); - TIntermTyped *expression = node->getRight(); - - // Global initializers must be constant at this point. - ASSERT(symbolNode->getQualifier() != EvqGlobal || - canWriteAsHLSLLiteral(expression)); - - // GLSL allows to write things like "float x = x;" where a new variable x is defined - // and the value of an existing variable x is assigned. HLSL uses C semantics (the - // new variable is created before the assignment is evaluated), so we need to - // convert - // this to "float t = x, x = t;". - if (writeSameSymbolInitializer(out, symbolNode, expression)) - { - // Skip initializing the rest of the expression - return false; - } - else if (writeConstantInitialization(out, symbolNode, expression)) + const TString &functionName = addArrayConstructIntoFunction(node->getType()); + out << functionName << "("; + node->getLeft()->traverse(this); + TIntermSequence *seq = rightAgg->getSequence(); + for (auto &arrayElement : *seq) { - return false; + out << ", "; + arrayElement->traverse(this); } + out << ")"; + return false; } - else if (visit == InVisit) - { - out << " = "; - } - break; - case EOpAddAssign: - outputTriplet(out, visit, "(", " += ", ")"); - break; - case EOpSubAssign: - outputTriplet(out, visit, "(", " -= ", ")"); - break; - case EOpMulAssign: - outputTriplet(out, visit, "(", " *= ", ")"); - break; - case EOpVectorTimesScalarAssign: - outputTriplet(out, visit, "(", " *= ", ")"); - break; - case EOpMatrixTimesScalarAssign: - outputTriplet(out, visit, "(", " *= ", ")"); - break; - case EOpVectorTimesMatrixAssign: - if (visit == PreVisit) - { - out << "("; - } - else if (visit == InVisit) - { - out << " = mul("; - node->getLeft()->traverse(this); - out << ", transpose("; - } - else - { - out << ")))"; - } - break; - case EOpMatrixTimesMatrixAssign: - if (visit == PreVisit) - { - out << "("; - } - else if (visit == InVisit) + // ArrayReturnValueToOutParameter should have eliminated expressions where a function call is assigned. + ASSERT(rightAgg == nullptr || rightAgg->getOp() != EOpFunctionCall); + + const TString &functionName = addArrayAssignmentFunction(node->getType()); + outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")"); + } + else + { + outputTriplet(out, visit, "(", " = ", ")"); + } + break; + case EOpInitialize: + if (visit == PreVisit) + { + TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode(); + ASSERT(symbolNode); + TIntermTyped *expression = node->getRight(); + + // Global initializers must be constant at this point. + ASSERT(symbolNode->getQualifier() != EvqGlobal || canWriteAsHLSLLiteral(expression)); + + // GLSL allows to write things like "float x = x;" where a new variable x is defined + // and the value of an existing variable x is assigned. HLSL uses C semantics (the + // new variable is created before the assignment is evaluated), so we need to convert + // this to "float t = x, x = t;". + if (writeSameSymbolInitializer(out, symbolNode, expression)) { - out << " = transpose(mul(transpose("; - node->getLeft()->traverse(this); - out << "), transpose("; + // Skip initializing the rest of the expression + return false; } - else + else if (writeConstantInitialization(out, symbolNode, expression)) { - out << "))))"; + return false; } - break; - case EOpDivAssign: - outputTriplet(out, visit, "(", " /= ", ")"); - break; - case EOpIModAssign: - outputTriplet(out, visit, "(", " %= ", ")"); - break; - case EOpBitShiftLeftAssign: - outputTriplet(out, visit, "(", " <<= ", ")"); - break; - case EOpBitShiftRightAssign: - outputTriplet(out, visit, "(", " >>= ", ")"); - break; - case EOpBitwiseAndAssign: - outputTriplet(out, visit, "(", " &= ", ")"); - break; - case EOpBitwiseXorAssign: - outputTriplet(out, visit, "(", " ^= ", ")"); - break; - case EOpBitwiseOrAssign: - outputTriplet(out, visit, "(", " |= ", ")"); - break; - case EOpIndexDirect: + } + else if (visit == InVisit) + { + out << " = "; + } + break; + case EOpAddAssign: + outputTriplet(out, visit, "(", " += ", ")"); + break; + case EOpSubAssign: + outputTriplet(out, visit, "(", " -= ", ")"); + break; + case EOpMulAssign: + outputTriplet(out, visit, "(", " *= ", ")"); + break; + case EOpVectorTimesScalarAssign: + outputTriplet(out, visit, "(", " *= ", ")"); + break; + case EOpMatrixTimesScalarAssign: + outputTriplet(out, visit, "(", " *= ", ")"); + break; + case EOpVectorTimesMatrixAssign: + if (visit == PreVisit) + { + out << "("; + } + else if (visit == InVisit) + { + out << " = mul("; + node->getLeft()->traverse(this); + out << ", transpose("; + } + else + { + out << ")))"; + } + break; + case EOpMatrixTimesMatrixAssign: + if (visit == PreVisit) + { + out << "("; + } + else if (visit == InVisit) + { + out << " = transpose(mul(transpose("; + node->getLeft()->traverse(this); + out << "), transpose("; + } + else + { + out << "))))"; + } + break; + case EOpDivAssign: + outputTriplet(out, visit, "(", " /= ", ")"); + break; + case EOpIModAssign: + outputTriplet(out, visit, "(", " %= ", ")"); + break; + case EOpBitShiftLeftAssign: + outputTriplet(out, visit, "(", " <<= ", ")"); + break; + case EOpBitShiftRightAssign: + outputTriplet(out, visit, "(", " >>= ", ")"); + break; + case EOpBitwiseAndAssign: + outputTriplet(out, visit, "(", " &= ", ")"); + break; + case EOpBitwiseXorAssign: + outputTriplet(out, visit, "(", " ^= ", ")"); + break; + case EOpBitwiseOrAssign: + outputTriplet(out, visit, "(", " |= ", ")"); + break; + case EOpIndexDirect: { const TType& leftType = node->getLeft()->getType(); if (leftType.isInterfaceBlock()) @@ -1077,6 +1057,42 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) return false; } break; + case EOpVectorSwizzle: + if (visit == InVisit) + { + out << "."; + + TIntermAggregate *swizzle = node->getRight()->getAsAggregate(); + + if (swizzle) + { + TIntermSequence *sequence = swizzle->getSequence(); + + for (TIntermSequence::iterator sit = sequence->begin(); sit != sequence->end(); sit++) + { + TIntermConstantUnion *element = (*sit)->getAsConstantUnion(); + + if (element) + { + int i = element->getIConst(0); + + switch (i) + { + case 0: out << "x"; break; + case 1: out << "y"; break; + case 2: out << "z"; break; + case 3: out << "w"; break; + default: UNREACHABLE(); + } + } + else UNREACHABLE(); + } + } + else UNREACHABLE(); + + return false; // Fully processed + } + break; case EOpAdd: outputTriplet(out, visit, "(", " + ", ")"); break; @@ -1280,12 +1296,9 @@ bool OutputHLSL::visitUnary(Visit visit, TIntermUnary *node) outputTriplet(out, visit, "frac(", "", ")"); break; case EOpIsNan: - if (node->getUseEmulatedFunction()) - writeEmulatedFunctionTriplet(out, visit, "isnan("); - else - outputTriplet(out, visit, "isnan(", "", ")"); - mRequiresIEEEStrictCompiling = true; - break; + outputTriplet(out, visit, "isnan(", "", ")"); + mRequiresIEEEStrictCompiling = true; + break; case EOpIsInf: outputTriplet(out, visit, "isinf(", "", ")"); break; @@ -1418,232 +1431,236 @@ TString OutputHLSL::samplerNamePrefixFromStruct(TIntermTyped *node) } } -bool OutputHLSL::visitBlock(Visit visit, TIntermBlock *node) -{ - TInfoSinkBase &out = getInfoSink(); - - if (mInsideFunction) - { - outputLineDirective(out, node->getLine().first_line); - out << "{\n"; - } - - for (TIntermSequence::iterator sit = node->getSequence()->begin(); - sit != node->getSequence()->end(); sit++) - { - outputLineDirective(out, (*sit)->getLine().first_line); - - (*sit)->traverse(this); - - // Don't output ; after case labels, they're terminated by : - // This is needed especially since outputting a ; after a case statement would turn empty - // case statements into non-empty case statements, disallowing fall-through from them. - // Also no need to output ; after if statements or sequences. This is done just for - // code clarity. - if ((*sit)->getAsCaseNode() == nullptr && (*sit)->getAsIfElseNode() == nullptr && - (*sit)->getAsBlock() == nullptr) - out << ";\n"; - } - - if (mInsideFunction) - { - outputLineDirective(out, node->getLine().last_line); - out << "}\n"; - } - - return false; -} - -bool OutputHLSL::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) +bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) { TInfoSinkBase &out = getInfoSink(); - ASSERT(mCurrentFunctionMetadata == nullptr); - - size_t index = mCallDag.findIndex(node->getFunctionSymbolInfo()); - ASSERT(index != CallDAG::InvalidIndex); - mCurrentFunctionMetadata = &mASTMetadataList[index]; - - out << TypeString(node->getType()) << " "; - - TIntermSequence *parameters = node->getFunctionParameters()->getSequence(); - - if (node->getFunctionSymbolInfo()->isMain()) - { - out << "gl_main("; - } - else - { - out << DecorateFunctionIfNeeded(node->getFunctionSymbolInfo()->getNameObj()) - << DisambiguateFunctionName(parameters) << (mOutputLod0Function ? "Lod0(" : "("); - } - - for (unsigned int i = 0; i < parameters->size(); i++) + switch (node->getOp()) { - TIntermSymbol *symbol = (*parameters)[i]->getAsSymbolNode(); - - if (symbol) + case EOpSequence: { - ensureStructDefined(symbol->getType()); - - out << argumentString(symbol); - - if (i < parameters->size() - 1) + if (mInsideFunction) { - out << ", "; + outputLineDirective(out, node->getLine().first_line); + out << "{\n"; } - } - else - UNREACHABLE(); - } - - out << ")\n"; - - mInsideFunction = true; - // The function body node will output braces. - node->getBody()->traverse(this); - mInsideFunction = false; - - mCurrentFunctionMetadata = nullptr; - - bool needsLod0 = mASTMetadataList[index].mNeedsLod0; - if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER) - { - ASSERT(!node->getFunctionSymbolInfo()->isMain()); - mOutputLod0Function = true; - node->traverse(this); - mOutputLod0Function = false; - } - return false; -} + for (TIntermSequence::iterator sit = node->getSequence()->begin(); sit != node->getSequence()->end(); sit++) + { + outputLineDirective(out, (*sit)->getLine().first_line); + + (*sit)->traverse(this); + + // Don't output ; after case labels, they're terminated by : + // This is needed especially since outputting a ; after a case statement would turn empty + // case statements into non-empty case statements, disallowing fall-through from them. + // Also no need to output ; after selection (if) statements or sequences. This is done just + // for code clarity. + TIntermSelection *asSelection = (*sit)->getAsSelectionNode(); + ASSERT(asSelection == nullptr || !asSelection->usesTernaryOperator()); + if ((*sit)->getAsCaseNode() == nullptr && asSelection == nullptr && !IsSequence(*sit)) + out << ";\n"; + } -bool OutputHLSL::visitDeclaration(Visit visit, TIntermDeclaration *node) -{ - TInfoSinkBase &out = getInfoSink(); - if (visit == PreVisit) - { - TIntermSequence *sequence = node->getSequence(); - TIntermTyped *variable = (*sequence)[0]->getAsTyped(); - ASSERT(sequence->size() == 1); + if (mInsideFunction) + { + outputLineDirective(out, node->getLine().last_line); + out << "}\n"; + } - if (variable && - (variable->getQualifier() == EvqTemporary || variable->getQualifier() == EvqGlobal || - variable->getQualifier() == EvqConst)) + return false; + } + case EOpDeclaration: + if (visit == PreVisit) { - ensureStructDefined(variable->getType()); + TIntermSequence *sequence = node->getSequence(); + TIntermTyped *variable = (*sequence)[0]->getAsTyped(); + ASSERT(sequence->size() == 1); - if (!variable->getAsSymbolNode() || - variable->getAsSymbolNode()->getSymbol() != "") // Variable declaration + if (variable && + (variable->getQualifier() == EvqTemporary || + variable->getQualifier() == EvqGlobal || variable->getQualifier() == EvqConst)) { - if (!mInsideFunction) + ensureStructDefined(variable->getType()); + + if (!variable->getAsSymbolNode() || variable->getAsSymbolNode()->getSymbol() != "") // Variable declaration { - out << "static "; - } + if (!mInsideFunction) + { + out << "static "; + } - out << TypeString(variable->getType()) + " "; + out << TypeString(variable->getType()) + " "; - TIntermSymbol *symbol = variable->getAsSymbolNode(); + TIntermSymbol *symbol = variable->getAsSymbolNode(); - if (symbol) - { - symbol->traverse(this); - out << ArrayString(symbol->getType()); - out << " = " + initializer(symbol->getType()); + if (symbol) + { + symbol->traverse(this); + out << ArrayString(symbol->getType()); + out << " = " + initializer(symbol->getType()); + } + else + { + variable->traverse(this); + } } - else + else if (variable->getAsSymbolNode() && variable->getAsSymbolNode()->getSymbol() == "") // Type (struct) declaration { - variable->traverse(this); + // Already added to constructor map } + else UNREACHABLE(); } - else if (variable->getAsSymbolNode() && - variable->getAsSymbolNode()->getSymbol() == "") // Type (struct) declaration + else if (variable && IsVaryingOut(variable->getQualifier())) { - // Already added to constructor map + for (TIntermSequence::iterator sit = sequence->begin(); sit != sequence->end(); sit++) + { + TIntermSymbol *symbol = (*sit)->getAsSymbolNode(); + + if (symbol) + { + // Vertex (output) varyings which are declared but not written to should still be declared to allow successful linking + mReferencedVaryings[symbol->getSymbol()] = symbol; + } + else + { + (*sit)->traverse(this); + } + } } - else - UNREACHABLE(); + + return false; } - else if (variable && IsVaryingOut(variable->getQualifier())) + else if (visit == InVisit) + { + out << ", "; + } + break; + case EOpInvariantDeclaration: + // Do not do any translation + return false; + case EOpPrototype: + if (visit == PreVisit) { - for (TIntermSequence::iterator sit = sequence->begin(); sit != sequence->end(); sit++) + size_t index = mCallDag.findIndex(node); + // Skip the prototype if it is not implemented (and thus not used) + if (index == CallDAG::InvalidIndex) { - TIntermSymbol *symbol = (*sit)->getAsSymbolNode(); + return false; + } + + TIntermSequence *arguments = node->getSequence(); + + TString name = DecorateFunctionIfNeeded(node->getNameObj()); + out << TypeString(node->getType()) << " " << name << DisambiguateFunctionName(arguments) + << (mOutputLod0Function ? "Lod0(" : "("); + + for (unsigned int i = 0; i < arguments->size(); i++) + { + TIntermSymbol *symbol = (*arguments)[i]->getAsSymbolNode(); if (symbol) { - // Vertex (output) varyings which are declared but not written to should - // still be declared to allow successful linking - mReferencedVaryings[symbol->getSymbol()] = symbol; - } - else - { - (*sit)->traverse(this); + out << argumentString(symbol); + + if (i < arguments->size() - 1) + { + out << ", "; + } } + else UNREACHABLE(); } - } - } - return false; -} -bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) -{ - TInfoSinkBase &out = getInfoSink(); + out << ");\n"; - switch (node->getOp()) - { - case EOpInvariantDeclaration: - // Do not do any translation - return false; - case EOpPrototype: - if (visit == PreVisit) + // Also prototype the Lod0 variant if needed + bool needsLod0 = mASTMetadataList[index].mNeedsLod0; + if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER) { - size_t index = mCallDag.findIndex(node->getFunctionSymbolInfo()); - // Skip the prototype if it is not implemented (and thus not used) - if (index == CallDAG::InvalidIndex) - { - return false; - } + mOutputLod0Function = true; + node->traverse(this); + mOutputLod0Function = false; + } + + return false; + } + break; + case EOpComma: + outputTriplet(out, visit, "(", ", ", ")"); + break; + case EOpFunction: + { + ASSERT(mCurrentFunctionMetadata == nullptr); + TString name = TFunction::unmangleName(node->getNameObj().getString()); + + size_t index = mCallDag.findIndex(node); + ASSERT(index != CallDAG::InvalidIndex); + mCurrentFunctionMetadata = &mASTMetadataList[index]; - TIntermSequence *arguments = node->getSequence(); + out << TypeString(node->getType()) << " "; - TString name = - DecorateFunctionIfNeeded(node->getFunctionSymbolInfo()->getNameObj()); - out << TypeString(node->getType()) << " " << name + TIntermSequence *sequence = node->getSequence(); + TIntermSequence *arguments = (*sequence)[0]->getAsAggregate()->getSequence(); + + if (name == "main") + { + out << "gl_main("; + } + else + { + out << DecorateFunctionIfNeeded(node->getNameObj()) << DisambiguateFunctionName(arguments) << (mOutputLod0Function ? "Lod0(" : "("); + } - for (unsigned int i = 0; i < arguments->size(); i++) + for (unsigned int i = 0; i < arguments->size(); i++) + { + TIntermSymbol *symbol = (*arguments)[i]->getAsSymbolNode(); + + if (symbol) { - TIntermSymbol *symbol = (*arguments)[i]->getAsSymbolNode(); + ensureStructDefined(symbol->getType()); - if (symbol) - { - out << argumentString(symbol); + out << argumentString(symbol); - if (i < arguments->size() - 1) - { - out << ", "; - } + if (i < arguments->size() - 1) + { + out << ", "; } - else - UNREACHABLE(); } + else UNREACHABLE(); + } - out << ");\n"; + out << ")\n"; - // Also prototype the Lod0 variant if needed - bool needsLod0 = mASTMetadataList[index].mNeedsLod0; - if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER) - { - mOutputLod0Function = true; - node->traverse(this); - mOutputLod0Function = false; - } + if (sequence->size() > 1) + { + mInsideFunction = true; + TIntermNode *body = (*sequence)[1]; + // The function body node will output braces. + ASSERT(IsSequence(body)); + body->traverse(this); + mInsideFunction = false; + } + else + { + out << "{}\n"; + } - return false; + mCurrentFunctionMetadata = nullptr; + + bool needsLod0 = mASTMetadataList[index].mNeedsLod0; + if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER) + { + ASSERT(name != "main"); + mOutputLod0Function = true; + node->traverse(this); + mOutputLod0Function = false; } - break; - case EOpFunctionCall: + + return false; + } + break; + case EOpFunctionCall: { TIntermSequence *arguments = node->getSequence(); @@ -1654,23 +1671,23 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) { UNIMPLEMENTED(); } - size_t index = mCallDag.findIndex(node->getFunctionSymbolInfo()); + size_t index = mCallDag.findIndex(node); ASSERT(index != CallDAG::InvalidIndex); lod0 &= mASTMetadataList[index].mNeedsLod0; - out << DecorateFunctionIfNeeded(node->getFunctionSymbolInfo()->getNameObj()); + out << DecorateFunctionIfNeeded(node->getNameObj()); out << DisambiguateFunctionName(node->getSequence()); out << (lod0 ? "Lod0(" : "("); } - else if (node->getFunctionSymbolInfo()->getNameObj().isInternal()) + else if (node->getNameObj().isInternal()) { // This path is used for internal functions that don't have their definitions in the // AST, such as precision emulation functions. - out << DecorateFunctionIfNeeded(node->getFunctionSymbolInfo()->getNameObj()) << "("; + out << DecorateFunctionIfNeeded(node->getNameObj()) << "("; } else { - TString name = TFunction::unmangleName(node->getFunctionSymbolInfo()->getName()); + TString name = TFunction::unmangleName(node->getNameObj().getString()); TBasicType samplerType = (*arguments)[0]->getAsTyped()->getType().getBasicType(); int coords = (*arguments)[1]->getAsTyped()->getNominalSize(); TString textureFunctionName = mTextureFunctionHLSL->useTextureFunction( @@ -1724,6 +1741,7 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) return false; } + break; case EOpParameters: outputTriplet(out, visit, "(", ", ", ")\n{\n"); break; @@ -1802,7 +1820,7 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) case EOpConstructMat4: outputConstructor(out, visit, node->getType(), "mat4", node->getSequence()); break; - case EOpConstructStruct: + case EOpConstructStruct: { if (node->getType().isArray()) { @@ -1831,31 +1849,31 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) case EOpVectorNotEqual: outputTriplet(out, visit, "(", " != ", ")"); break; - case EOpMod: - ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(out, visit, "mod("); - break; - case EOpModf: - outputTriplet(out, visit, "modf(", ", ", ")"); - break; - case EOpPow: - outputTriplet(out, visit, "pow(", ", ", ")"); - break; - case EOpAtan: - ASSERT(node->getSequence()->size() == 2); // atan(x) is a unary operator - ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(out, visit, "atan("); - break; - case EOpMin: - outputTriplet(out, visit, "min(", ", ", ")"); - break; - case EOpMax: - outputTriplet(out, visit, "max(", ", ", ")"); - break; - case EOpClamp: - outputTriplet(out, visit, "clamp(", ", ", ")"); - break; - case EOpMix: + case EOpMod: + ASSERT(node->getUseEmulatedFunction()); + writeEmulatedFunctionTriplet(out, visit, "mod("); + break; + case EOpModf: + outputTriplet(out, visit, "modf(", ", ", ")"); + break; + case EOpPow: + outputTriplet(out, visit, "pow(", ", ", ")"); + break; + case EOpAtan: + ASSERT(node->getSequence()->size() == 2); // atan(x) is a unary operator + ASSERT(node->getUseEmulatedFunction()); + writeEmulatedFunctionTriplet(out, visit, "atan("); + break; + case EOpMin: + outputTriplet(out, visit, "min(", ", ", ")"); + break; + case EOpMax: + outputTriplet(out, visit, "max(", ", ", ")"); + break; + case EOpClamp: + outputTriplet(out, visit, "clamp(", ", ", ")"); + break; + case EOpMix: { TIntermTyped *lastParamNode = (*(node->getSequence()))[2]->getAsTyped(); if (lastParamNode->getType().getBasicType() == EbtBool) @@ -1869,8 +1887,8 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) { outputTriplet(out, visit, "lerp(", ", ", ")"); } - break; } + break; case EOpStep: outputTriplet(out, visit, "step(", ", ", ")"); break; @@ -1886,31 +1904,30 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) case EOpCross: outputTriplet(out, visit, "cross(", ", ", ")"); break; - case EOpFaceForward: - ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(out, visit, "faceforward("); - break; - case EOpReflect: - outputTriplet(out, visit, "reflect(", ", ", ")"); - break; - case EOpRefract: - outputTriplet(out, visit, "refract(", ", ", ")"); - break; - case EOpOuterProduct: - ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(out, visit, "outerProduct("); - break; - case EOpMul: - outputTriplet(out, visit, "(", " * ", ")"); - break; - default: - UNREACHABLE(); + case EOpFaceForward: + ASSERT(node->getUseEmulatedFunction()); + writeEmulatedFunctionTriplet(out, visit, "faceforward("); + break; + case EOpReflect: + outputTriplet(out, visit, "reflect(", ", ", ")"); + break; + case EOpRefract: + outputTriplet(out, visit, "refract(", ", ", ")"); + break; + case EOpOuterProduct: + ASSERT(node->getUseEmulatedFunction()); + writeEmulatedFunctionTriplet(out, visit, "outerProduct("); + break; + case EOpMul: + outputTriplet(out, visit, "(", " * ", ")"); + break; + default: UNREACHABLE(); } return true; } -void OutputHLSL::writeIfElse(TInfoSinkBase &out, TIntermIfElse *node) +void OutputHLSL::writeSelection(TInfoSinkBase &out, TIntermSelection *node) { out << "if ("; @@ -1925,6 +1942,8 @@ void OutputHLSL::writeIfElse(TInfoSinkBase &out, TIntermIfElse *node) if (node->getTrueBlock()) { // The trueBlock child node will output braces. + ASSERT(IsSequence(node->getTrueBlock())); + node->getTrueBlock()->traverse(this); // Detect true discard @@ -1945,7 +1964,9 @@ void OutputHLSL::writeIfElse(TInfoSinkBase &out, TIntermIfElse *node) outputLineDirective(out, node->getFalseBlock()->getLine().first_line); - // The falseBlock child node will output braces. + // Either this is "else if" or the falseBlock child node will output braces. + ASSERT(IsSequence(node->getFalseBlock()) || node->getFalseBlock()->getAsSelectionNode() != nullptr); + node->getFalseBlock()->traverse(this); outputLineDirective(out, node->getFalseBlock()->getLine().first_line); @@ -1961,18 +1982,11 @@ void OutputHLSL::writeIfElse(TInfoSinkBase &out, TIntermIfElse *node) } } -bool OutputHLSL::visitTernary(Visit, TIntermTernary *) -{ - // Ternary ops should have been already converted to something else in the AST. HLSL ternary - // operator doesn't short-circuit, so it's not the same as the GLSL ternary operator. - UNREACHABLE(); - return false; -} - -bool OutputHLSL::visitIfElse(Visit visit, TIntermIfElse *node) +bool OutputHLSL::visitSelection(Visit visit, TIntermSelection *node) { TInfoSinkBase &out = getInfoSink(); + ASSERT(!node->usesTernaryOperator()); ASSERT(mInsideFunction); // D3D errors when there is a gradient operation in a loop in an unflattened if. @@ -1981,7 +1995,7 @@ bool OutputHLSL::visitIfElse(Visit visit, TIntermIfElse *node) out << "FLATTEN "; } - writeIfElse(out, node); + writeSelection(out, node); return false; } @@ -2085,6 +2099,7 @@ bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node) if (node->getBody()) { // The loop body node will output braces. + ASSERT(IsSequence(node->getBody())); node->getBody()->traverse(this); } else @@ -2172,6 +2187,39 @@ bool OutputHLSL::visitBranch(Visit visit, TIntermBranch *node) return true; } +bool OutputHLSL::isSingleStatement(TIntermNode *node) +{ + TIntermAggregate *aggregate = node->getAsAggregate(); + + if (aggregate) + { + if (aggregate->getOp() == EOpSequence) + { + return false; + } + else if (aggregate->getOp() == EOpDeclaration) + { + // Declaring multiple comma-separated variables must be considered multiple statements + // because each individual declaration has side effects which are visible in the next. + return false; + } + else + { + for (TIntermSequence::iterator sit = aggregate->getSequence()->begin(); sit != aggregate->getSequence()->end(); sit++) + { + if (!isSingleStatement(*sit)) + { + return false; + } + } + + return true; + } + } + + return true; +} + // Handle loops with more than 254 iterations (unsupported by D3D9) by splitting them // (The D3D documentation says 255 iterations, but the compiler complains at anything more than 254). bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node) @@ -2189,7 +2237,7 @@ bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node) // Parse index name and intial value if (node->getInit()) { - TIntermDeclaration *init = node->getInit()->getAsDeclarationNode(); + TIntermAggregate *init = node->getInit()->getAsAggregate(); if (init) { @@ -2854,4 +2902,6 @@ void OutputHLSL::ensureStructDefined(const TType &type) } } -} // namespace sh + + +} |