diff options
Diffstat (limited to 'src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp')
-rw-r--r-- | src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp | 3540 |
1 files changed, 1480 insertions, 2060 deletions
diff --git a/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp index 253b96696c..d5ff761430 100644 --- a/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp +++ b/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp @@ -15,33 +15,95 @@ #include "common/utilities.h" #include "compiler/translator/BuiltInFunctionEmulator.h" #include "compiler/translator/BuiltInFunctionEmulatorHLSL.h" -#include "compiler/translator/FlagStd140Structs.h" +#include "compiler/translator/ImageFunctionHLSL.h" #include "compiler/translator/InfoSink.h" #include "compiler/translator/NodeSearch.h" #include "compiler/translator/RemoveSwitchFallThrough.h" #include "compiler/translator/SearchSymbol.h" #include "compiler/translator/StructureHLSL.h" +#include "compiler/translator/TextureFunctionHLSL.h" #include "compiler/translator/TranslatorHLSL.h" #include "compiler/translator/UniformHLSL.h" #include "compiler/translator/UtilsHLSL.h" #include "compiler/translator/blocklayout.h" #include "compiler/translator/util.h" +namespace sh +{ + namespace { -bool IsSequence(TIntermNode *node) +TString ArrayHelperFunctionName(const char *prefix, const TType &type) +{ + TStringStream fnName; + fnName << prefix << "_"; + if (type.isArray()) + { + for (unsigned int arraySize : *type.getArraySizes()) + { + fnName << arraySize << "_"; + } + } + fnName << TypeString(type); + return fnName.str(); +} + +bool IsDeclarationWrittenOut(TIntermDeclaration *node) +{ + TIntermSequence *sequence = node->getSequence(); + TIntermTyped *variable = (*sequence)[0]->getAsTyped(); + ASSERT(sequence->size() == 1); + ASSERT(variable); + return (variable->getQualifier() == EvqTemporary || variable->getQualifier() == EvqGlobal || + variable->getQualifier() == EvqConst); +} + +bool IsInStd140InterfaceBlock(TIntermTyped *node) +{ + TIntermBinary *binaryNode = node->getAsBinaryNode(); + + if (binaryNode) + { + return IsInStd140InterfaceBlock(binaryNode->getLeft()); + } + + const TType &type = node->getType(); + + // determine if we are in the standard layout + const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); + if (interfaceBlock) + { + return (interfaceBlock->blockStorage() == EbsStd140); + } + + return false; +} + +} // anonymous namespace + +void OutputHLSL::writeFloat(TInfoSinkBase &out, float f) { - return node->getAsAggregate() != nullptr && node->getAsAggregate()->getOp() == EOpSequence; + // This is known not to work for NaN on all drivers but make the best effort to output NaNs + // regardless. + if ((gl::isInf(f) || gl::isNaN(f)) && mShaderVersion >= 300 && + mOutputType == SH_HLSL_4_1_OUTPUT) + { + out << "asfloat(" << gl::bitCast<uint32_t>(f) << "u)"; + } + else + { + out << std::min(FLT_MAX, std::max(-FLT_MAX, f)); + } } -void WriteSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUnion) +void OutputHLSL::writeSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUnion) { ASSERT(constUnion != nullptr); switch (constUnion->getType()) { case EbtFloat: - out << std::min(FLT_MAX, std::max(-FLT_MAX, constUnion->getFConst())); + writeFloat(out, constUnion->getFConst()); break; case EbtInt: out << constUnion->getIConst(); @@ -57,14 +119,14 @@ void WriteSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUn } } -const TConstantUnion *WriteConstantUnionArray(TInfoSinkBase &out, - const TConstantUnion *const constUnion, - const size_t size) +const TConstantUnion *OutputHLSL::writeConstantUnionArray(TInfoSinkBase &out, + const TConstantUnion *const constUnion, + const size_t size) { const TConstantUnion *constUnionIterated = constUnion; for (size_t i = 0; i < size; i++, constUnionIterated++) { - WriteSingleConstant(out, constUnionIterated); + writeSingleConstant(out, constUnionIterated); if (i != size - 1) { @@ -74,71 +136,17 @@ const TConstantUnion *WriteConstantUnionArray(TInfoSinkBase &out, return constUnionIterated; } -} // namespace - -namespace sh -{ - -TString OutputHLSL::TextureFunction::name() const -{ - TString name = "gl_texture"; - - // We need to include full the sampler type in the function name to make the signature unique - // on D3D11, where samplers are passed to texture functions as indices. - name += TextureTypeSuffix(this->sampler); - - if (proj) - { - name += "Proj"; - } - - if (offset) - { - name += "Offset"; - } - - switch(method) - { - case IMPLICIT: break; - case BIAS: break; // Extra parameter makes the signature unique - case LOD: name += "Lod"; break; - case LOD0: name += "Lod0"; break; - case LOD0BIAS: name += "Lod0"; break; // Extra parameter makes the signature unique - case SIZE: name += "Size"; break; - case FETCH: name += "Fetch"; break; - case GRAD: name += "Grad"; break; - default: UNREACHABLE(); - } - - return name + "("; -} - -bool OutputHLSL::TextureFunction::operator<(const TextureFunction &rhs) const -{ - if (sampler < rhs.sampler) return true; - if (sampler > rhs.sampler) return false; - - if (coords < rhs.coords) return true; - if (coords > rhs.coords) return false; - - if (!proj && rhs.proj) return true; - if (proj && !rhs.proj) return false; - - if (!offset && rhs.offset) return true; - if (offset && !rhs.offset) return false; - - if (method < rhs.method) return true; - if (method > rhs.method) return false; - - return false; -} - -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), +OutputHLSL::OutputHLSL(sh::GLenum shaderType, + int shaderVersion, + const TExtensionBehavior &extensionBehavior, + const char *sourcePath, + ShShaderOutput outputType, + int numRenderTargets, + const std::vector<Uniform> &uniforms, + ShCompileOptions compileOptions, + TSymbolTable *symbolTable, + PerformanceDiagnostics *perfDiagnostics) + : TIntermTraverser(true, true, true, symbolTable), mShaderType(shaderType), mShaderVersion(shaderVersion), mExtensionBehavior(extensionBehavior), @@ -146,51 +154,66 @@ OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion, mOutputType(outputType), mCompileOptions(compileOptions), mNumRenderTargets(numRenderTargets), - mCurrentFunctionMetadata(nullptr) + mCurrentFunctionMetadata(nullptr), + mPerfDiagnostics(perfDiagnostics) { mInsideFunction = false; - mUsesFragColor = false; - mUsesFragData = false; - mUsesDepthRange = false; - mUsesFragCoord = false; - mUsesPointCoord = false; - mUsesFrontFacing = false; - mUsesPointSize = false; - mUsesInstanceID = false; - mUsesFragDepth = false; - mUsesXor = false; - mUsesDiscardRewriting = false; - mUsesNestedBreak = false; + mUsesFragColor = false; + mUsesFragData = false; + mUsesDepthRange = false; + mUsesFragCoord = false; + mUsesPointCoord = false; + mUsesFrontFacing = false; + mUsesPointSize = false; + mUsesInstanceID = false; + mHasMultiviewExtensionEnabled = + IsExtensionEnabled(mExtensionBehavior, TExtension::OVR_multiview); + mUsesViewID = false; + mUsesVertexID = false; + mUsesFragDepth = false; + mUsesNumWorkGroups = false; + mUsesWorkGroupID = false; + mUsesLocalInvocationID = false; + mUsesGlobalInvocationID = false; + mUsesLocalInvocationIndex = false; + mUsesXor = false; + mUsesDiscardRewriting = false; + mUsesNestedBreak = false; mRequiresIEEEStrictCompiling = false; mUniqueIndex = 0; - mOutputLod0Function = false; + mOutputLod0Function = false; mInsideDiscontinuousLoop = false; - mNestedLoopDepth = 0; + mNestedLoopDepth = 0; - mExcessiveLoopIndex = NULL; + mExcessiveLoopIndex = nullptr; - mStructureHLSL = new StructureHLSL; - mUniformHLSL = new UniformHLSL(mStructureHLSL, outputType, uniforms); + mStructureHLSL = new StructureHLSL; + mUniformHLSL = new UniformHLSL(shaderType, mStructureHLSL, outputType, uniforms); + mTextureFunctionHLSL = new TextureFunctionHLSL; + mImageFunctionHLSL = new ImageFunctionHLSL; if (mOutputType == SH_HLSL_3_0_OUTPUT) { // Fragment shaders need dx_DepthRange, dx_ViewCoords and dx_DepthFront. - // Vertex shaders need a slightly different set: dx_DepthRange, dx_ViewCoords and dx_ViewAdjust. + // Vertex shaders need a slightly different set: dx_DepthRange, dx_ViewCoords and + // dx_ViewAdjust. // In both cases total 3 uniform registers need to be reserved. mUniformHLSL->reserveUniformRegisters(3); } // Reserve registers for the default uniform block and driver constants - mUniformHLSL->reserveInterfaceBlockRegisters(2); + mUniformHLSL->reserveUniformBlockRegisters(2); } OutputHLSL::~OutputHLSL() { SafeDelete(mStructureHLSL); SafeDelete(mUniformHLSL); + SafeDelete(mTextureFunctionHLSL); + SafeDelete(mImageFunctionHLSL); for (auto &eqFunction : mStructEqualityFunctions) { SafeDelete(eqFunction); @@ -203,71 +226,48 @@ OutputHLSL::~OutputHLSL() void OutputHLSL::output(TIntermNode *treeRoot, TInfoSinkBase &objSink) { - const std::vector<TIntermTyped*> &flaggedStructs = FlagStd140ValueStructs(treeRoot); - makeFlaggedStructMaps(flaggedStructs); - BuiltInFunctionEmulator builtInFunctionEmulator; InitBuiltInFunctionEmulatorForHLSL(&builtInFunctionEmulator); - builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(treeRoot); + 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); + CallDAG::InitResult success = mCallDag.init(treeRoot, nullptr); ASSERT(success == CallDAG::INITDAG_SUCCESS); - UNUSED_ASSERTION_VARIABLE(success); mASTMetadataList = CreateASTMetadataHLSL(treeRoot, mCallDag); + const std::vector<MappedStruct> std140Structs = FlagStd140Structs(treeRoot); + // TODO(oetuaho): The std140Structs could be filtered based on which ones actually get used in + // the shader code. When we add shader storage blocks we might also consider an alternative + // solution, since the struct mapping won't work very well for shader storage blocks. + // Output the body and footer first to determine what has to go in the header mInfoSinkStack.push(&mBody); treeRoot->traverse(this); mInfoSinkStack.pop(); mInfoSinkStack.push(&mFooter); - if (!mDeferredGlobalInitializers.empty()) - { - writeDeferredGlobalInitializers(mFooter); - } mInfoSinkStack.pop(); mInfoSinkStack.push(&mHeader); - header(mHeader, &builtInFunctionEmulator); + header(mHeader, std140Structs, &builtInFunctionEmulator); mInfoSinkStack.pop(); objSink << mHeader.c_str(); objSink << mBody.c_str(); objSink << mFooter.c_str(); - builtInFunctionEmulator.Cleanup(); -} - -void OutputHLSL::makeFlaggedStructMaps(const std::vector<TIntermTyped *> &flaggedStructs) -{ - for (unsigned int structIndex = 0; structIndex < flaggedStructs.size(); structIndex++) - { - TIntermTyped *flaggedNode = flaggedStructs[structIndex]; - - TInfoSinkBase structInfoSink; - mInfoSinkStack.push(&structInfoSink); - - // This will mark the necessary block elements as referenced - flaggedNode->traverse(this); - - TString structName(structInfoSink.c_str()); - mInfoSinkStack.pop(); - - mFlaggedStructOriginalNames[flaggedNode] = structName; - - for (size_t pos = structName.find('.'); pos != std::string::npos; pos = structName.find('.')) - { - structName.erase(pos, 1); - } - - mFlaggedStructMappedNames[flaggedNode] = "map" + structName; - } + builtInFunctionEmulator.cleanup(); } -const std::map<std::string, unsigned int> &OutputHLSL::getInterfaceBlockRegisterMap() const +const std::map<std::string, unsigned int> &OutputHLSL::getUniformBlockRegisterMap() const { - return mUniformHLSL->getInterfaceBlockRegisterMap(); + return mUniformHLSL->getUniformBlockRegisterMap(); } const std::map<std::string, unsigned int> &OutputHLSL::getUniformRegisterMap() const @@ -275,95 +275,161 @@ const std::map<std::string, unsigned int> &OutputHLSL::getUniformRegisterMap() c return mUniformHLSL->getUniformRegisterMap(); } -int OutputHLSL::vectorSize(const TType &type) const -{ - int elementSize = type.isMatrix() ? type.getCols() : 1; - int arraySize = type.isArray() ? type.getArraySize() : 1; - - return elementSize * arraySize; -} - -TString OutputHLSL::structInitializerString(int indent, const TStructure &structure, const TString &rhsStructName) +TString OutputHLSL::structInitializerString(int indent, + const TType &type, + const TString &name) const { TString init; - TString preIndentString; - TString fullIndentString; + TString indentString; + for (int spaces = 0; spaces < indent; spaces++) + { + indentString += " "; + } - for (int spaces = 0; spaces < (indent * 4); spaces++) + if (type.isArray()) { - preIndentString += ' '; + init += indentString + "{\n"; + for (unsigned int arrayIndex = 0u; arrayIndex < type.getOutermostArraySize(); ++arrayIndex) + { + TStringStream indexedString; + indexedString << name << "[" << arrayIndex << "]"; + TType elementType = type; + elementType.toArrayElementType(); + init += structInitializerString(indent + 1, elementType, indexedString.str()); + if (arrayIndex < type.getOutermostArraySize() - 1) + { + init += ","; + } + init += "\n"; + } + init += indentString + "}"; } + else if (type.getBasicType() == EbtStruct) + { + init += indentString + "{\n"; + const TStructure &structure = *type.getStruct(); + const TFieldList &fields = structure.fields(); + for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) + { + const TField &field = *fields[fieldIndex]; + const TString &fieldName = name + "." + Decorate(field.name()); + const TType &fieldType = *field.type(); - for (int spaces = 0; spaces < ((indent+1) * 4); spaces++) + init += structInitializerString(indent + 1, fieldType, fieldName); + if (fieldIndex < fields.size() - 1) + { + init += ","; + } + init += "\n"; + } + init += indentString + "}"; + } + else { - fullIndentString += ' '; + init += indentString + name; } - init += preIndentString + "{\n"; + return init; +} + +TString OutputHLSL::generateStructMapping(const std::vector<MappedStruct> &std140Structs) const +{ + TString mappedStructs; - const TFieldList &fields = structure.fields(); - for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) + for (auto &mappedStruct : std140Structs) { - const TField &field = *fields[fieldIndex]; - const TString &fieldName = rhsStructName + "." + Decorate(field.name()); - const TType &fieldType = *field.type(); - - if (fieldType.getStruct()) + TInterfaceBlock *interfaceBlock = + mappedStruct.blockDeclarator->getType().getInterfaceBlock(); + const TString &interfaceBlockName = interfaceBlock->name(); + const TName &instanceName = mappedStruct.blockDeclarator->getName(); + if (mReferencedUniformBlocks.count(interfaceBlockName) == 0 && + (instanceName.getString() == "" || + mReferencedUniformBlocks.count(instanceName.getString()) == 0)) { - init += structInitializerString(indent + 1, *fieldType.getStruct(), fieldName); + continue; } - else + + unsigned int instanceCount = 1u; + bool isInstanceArray = mappedStruct.blockDeclarator->isArray(); + if (isInstanceArray) { - init += fullIndentString + fieldName + ",\n"; + instanceCount = mappedStruct.blockDeclarator->getOutermostArraySize(); } - } - init += preIndentString + "}" + (indent == 0 ? ";" : ",") + "\n"; + for (unsigned int instanceArrayIndex = 0; instanceArrayIndex < instanceCount; + ++instanceArrayIndex) + { + TString originalName; + TString mappedName("map"); - return init; + if (instanceName.getString() != "") + { + unsigned int instanceStringArrayIndex = GL_INVALID_INDEX; + if (isInstanceArray) + instanceStringArrayIndex = instanceArrayIndex; + TString instanceString = mUniformHLSL->uniformBlockInstanceString( + *interfaceBlock, instanceStringArrayIndex); + originalName += instanceString; + mappedName += instanceString; + originalName += "."; + mappedName += "_"; + } + + TString fieldName = Decorate(mappedStruct.field->name()); + originalName += fieldName; + mappedName += fieldName; + + TType *structType = mappedStruct.field->type(); + mappedStructs += + "static " + Decorate(structType->getStruct()->name()) + " " + mappedName; + + if (structType->isArray()) + { + mappedStructs += ArrayString(*mappedStruct.field->type()); + } + + mappedStructs += " =\n"; + mappedStructs += structInitializerString(0, *structType, originalName); + mappedStructs += ";\n"; + } + } + return mappedStructs; } -void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *builtInFunctionEmulator) +void OutputHLSL::header(TInfoSinkBase &out, + const std::vector<MappedStruct> &std140Structs, + const BuiltInFunctionEmulator *builtInFunctionEmulator) const { TString varyings; TString attributes; - TString flaggedStructs; + TString mappedStructs = generateStructMapping(std140Structs); - for (std::map<TIntermTyped*, TString>::const_iterator flaggedStructIt = mFlaggedStructMappedNames.begin(); flaggedStructIt != mFlaggedStructMappedNames.end(); flaggedStructIt++) + for (ReferencedSymbols::const_iterator varying = mReferencedVaryings.begin(); + varying != mReferencedVaryings.end(); varying++) { - TIntermTyped *structNode = flaggedStructIt->first; - const TString &mappedName = flaggedStructIt->second; - const TStructure &structure = *structNode->getType().getStruct(); - const TString &originalName = mFlaggedStructOriginalNames[structNode]; - - flaggedStructs += "static " + Decorate(structure.name()) + " " + mappedName + " =\n"; - flaggedStructs += structInitializerString(0, structure, originalName); - flaggedStructs += "\n"; - } - - for (ReferencedSymbols::const_iterator varying = mReferencedVaryings.begin(); varying != mReferencedVaryings.end(); varying++) - { - const TType &type = varying->second->getType(); + const TType &type = varying->second->getType(); const TString &name = varying->second->getSymbol(); // Program linking depends on this exact format - varyings += "static " + InterpolationString(type.getQualifier()) + " " + TypeString(type) + " " + - Decorate(name) + ArrayString(type) + " = " + initializer(type) + ";\n"; + varyings += "static " + InterpolationString(type.getQualifier()) + " " + TypeString(type) + + " " + Decorate(name) + ArrayString(type) + " = " + initializer(type) + ";\n"; } - for (ReferencedSymbols::const_iterator attribute = mReferencedAttributes.begin(); attribute != mReferencedAttributes.end(); attribute++) + for (ReferencedSymbols::const_iterator attribute = mReferencedAttributes.begin(); + attribute != mReferencedAttributes.end(); attribute++) { - const TType &type = attribute->second->getType(); + const TType &type = attribute->second->getType(); const TString &name = attribute->second->getSymbol(); - attributes += "static " + TypeString(type) + " " + Decorate(name) + ArrayString(type) + " = " + initializer(type) + ";\n"; + attributes += "static " + TypeString(type) + " " + Decorate(name) + ArrayString(type) + + " = " + initializer(type) + ";\n"; } out << mStructureHLSL->structsHeader(); - mUniformHLSL->uniformsHeader(out, mOutputType, mReferencedUniforms); - out << mUniformHLSL->interfaceBlocksHeader(mReferencedInterfaceBlocks); + mUniformHLSL->uniformsHeader(out, mOutputType, mReferencedUniforms, mSymbolTable); + out << mUniformHLSL->uniformBlocksHeader(mReferencedUniformBlocks); if (!mEqualityFunctions.empty()) { @@ -415,22 +481,24 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built if (mShaderType == GL_FRAGMENT_SHADER) { - TExtensionBehavior::const_iterator iter = mExtensionBehavior.find("GL_EXT_draw_buffers"); - const bool usingMRTExtension = (iter != mExtensionBehavior.end() && (iter->second == EBhEnable || iter->second == EBhRequire)); + const bool usingMRTExtension = + IsExtensionEnabled(mExtensionBehavior, TExtension::EXT_draw_buffers); out << "// Varyings\n"; - out << varyings; + out << varyings; out << "\n"; if (mShaderVersion >= 300) { - for (ReferencedSymbols::const_iterator outputVariableIt = mReferencedOutputVariables.begin(); outputVariableIt != mReferencedOutputVariables.end(); outputVariableIt++) + for (ReferencedSymbols::const_iterator outputVariableIt = + mReferencedOutputVariables.begin(); + outputVariableIt != mReferencedOutputVariables.end(); outputVariableIt++) { const TString &variableName = outputVariableIt->first; - const TType &variableType = outputVariableIt->second->getType(); + const TType &variableType = outputVariableIt->second->getType(); - out << "static " + TypeString(variableType) + " out_" + variableName + ArrayString(variableType) + - " = " + initializer(variableType) + ";\n"; + out << "static " + TypeString(variableType) + " out_" + variableName + + ArrayString(variableType) + " = " + initializer(variableType) + ";\n"; } } else @@ -438,7 +506,7 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built const unsigned int numColorValues = usingMRTExtension ? mNumRenderTargets : 1; out << "static float4 gl_Color[" << numColorValues << "] =\n" - "{\n"; + "{\n"; for (unsigned int i = 0; i < numColorValues; i++) { out << " float4(0, 0, 0, 0)"; @@ -512,6 +580,18 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built out << " float2 dx_ViewScale : packoffset(c3);\n"; } + if (mHasMultiviewExtensionEnabled) + { + // We have to add a value which we can use to keep track of which multi-view code + // path is to be selected in the GS. + out << " float multiviewSelectViewportIndex : packoffset(c3.z);\n"; + } + + if (mOutputType == SH_HLSL_4_1_OUTPUT) + { + mUniformHLSL->samplerMetadataUniforms(out, "c4"); + } + out << "};\n"; } else @@ -536,15 +616,16 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built if (mUsesDepthRange) { - out << "static gl_DepthRangeParameters gl_DepthRange = {dx_DepthRange.x, dx_DepthRange.y, dx_DepthRange.z};\n" + out << "static gl_DepthRangeParameters gl_DepthRange = {dx_DepthRange.x, " + "dx_DepthRange.y, dx_DepthRange.z};\n" "\n"; } - if (!flaggedStructs.empty()) + if (!mappedStructs.empty()) { - out << "// Std140 Structures accessed by value\n"; + out << "// Structures from std140 blocks with padding removed\n"; out << "\n"; - out << flaggedStructs; + out << mappedStructs; out << "\n"; } @@ -563,10 +644,10 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built out << "#define GL_USES_FRAG_DATA\n"; } } - else // Vertex shader + else if (mShaderType == GL_VERTEX_SHADER) { out << "// Attributes\n"; - out << attributes; + out << attributes; out << "\n" "static float4 gl_Position = float4(0, 0, 0, 0);\n"; @@ -580,9 +661,14 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built out << "static int gl_InstanceID;"; } + if (mUsesVertexID) + { + out << "static int gl_VertexID;"; + } + out << "\n" "// Varyings\n"; - out << varyings; + out << varyings; out << "\n"; if (mUsesDepthRange) @@ -599,7 +685,7 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) { out << "cbuffer DriverConstants : register(b1)\n" - "{\n"; + "{\n"; if (mUsesDepthRange) { @@ -614,6 +700,18 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built out << " float2 dx_ViewCoords : packoffset(c2);\n"; out << " float2 dx_ViewScale : packoffset(c3);\n"; + if (mHasMultiviewExtensionEnabled) + { + // We have to add a value which we can use to keep track of which multi-view code + // path is to be selected in the GS. + out << " float multiviewSelectViewportIndex : packoffset(c3.z);\n"; + } + + if (mOutputType == SH_HLSL_4_1_OUTPUT) + { + mUniformHLSL->samplerMetadataUniforms(out, "c4"); + } + out << "};\n" "\n"; } @@ -631,740 +729,62 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built if (mUsesDepthRange) { - out << "static gl_DepthRangeParameters gl_DepthRange = {dx_DepthRange.x, dx_DepthRange.y, dx_DepthRange.z};\n" + out << "static gl_DepthRangeParameters gl_DepthRange = {dx_DepthRange.x, " + "dx_DepthRange.y, dx_DepthRange.z};\n" "\n"; } - if (!flaggedStructs.empty()) + if (!mappedStructs.empty()) { - out << "// Std140 Structures accessed by value\n"; + out << "// Structures from std140 blocks with padding removed\n"; out << "\n"; - out << flaggedStructs; + out << mappedStructs; out << "\n"; } } - - for (TextureFunctionSet::const_iterator textureFunction = mUsesTexture.begin(); textureFunction != mUsesTexture.end(); textureFunction++) + else // Compute shader { - // Return type - if (textureFunction->method == TextureFunction::SIZE) - { - switch(textureFunction->sampler) - { - case EbtSampler2D: out << "int2 "; break; - case EbtSampler3D: out << "int3 "; break; - case EbtSamplerCube: out << "int2 "; break; - case EbtSampler2DArray: out << "int3 "; break; - case EbtISampler2D: out << "int2 "; break; - case EbtISampler3D: out << "int3 "; break; - case EbtISamplerCube: out << "int2 "; break; - case EbtISampler2DArray: out << "int3 "; break; - case EbtUSampler2D: out << "int2 "; break; - case EbtUSampler3D: out << "int3 "; break; - case EbtUSamplerCube: out << "int2 "; break; - case EbtUSampler2DArray: out << "int3 "; break; - case EbtSampler2DShadow: out << "int2 "; break; - case EbtSamplerCubeShadow: out << "int2 "; break; - case EbtSampler2DArrayShadow: out << "int3 "; break; - default: UNREACHABLE(); - } - } - else // Sampling function - { - switch(textureFunction->sampler) - { - case EbtSampler2D: out << "float4 "; break; - case EbtSampler3D: out << "float4 "; break; - case EbtSamplerCube: out << "float4 "; break; - case EbtSampler2DArray: out << "float4 "; break; - case EbtISampler2D: out << "int4 "; break; - case EbtISampler3D: out << "int4 "; break; - case EbtISamplerCube: out << "int4 "; break; - case EbtISampler2DArray: out << "int4 "; break; - case EbtUSampler2D: out << "uint4 "; break; - case EbtUSampler3D: out << "uint4 "; break; - case EbtUSamplerCube: out << "uint4 "; break; - case EbtUSampler2DArray: out << "uint4 "; break; - case EbtSampler2DShadow: out << "float "; break; - case EbtSamplerCubeShadow: out << "float "; break; - case EbtSampler2DArrayShadow: out << "float "; break; - default: UNREACHABLE(); - } - } - - // Function name - out << textureFunction->name(); - - // Argument list - int hlslCoords = 4; - - if (mOutputType == SH_HLSL_3_0_OUTPUT) - { - switch(textureFunction->sampler) - { - case EbtSampler2D: out << "sampler2D s"; hlslCoords = 2; break; - case EbtSamplerCube: out << "samplerCUBE s"; hlslCoords = 3; break; - default: UNREACHABLE(); - } - - switch(textureFunction->method) - { - case TextureFunction::IMPLICIT: break; - case TextureFunction::BIAS: hlslCoords = 4; break; - case TextureFunction::LOD: hlslCoords = 4; break; - case TextureFunction::LOD0: hlslCoords = 4; break; - case TextureFunction::LOD0BIAS: hlslCoords = 4; break; - default: UNREACHABLE(); - } - } - else - { - hlslCoords = HLSLTextureCoordsCount(textureFunction->sampler); - if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) - { - out << TextureString(textureFunction->sampler) << " x, " - << SamplerString(textureFunction->sampler) << " s"; - } - else - { - ASSERT(mOutputType == SH_HLSL_4_1_OUTPUT); - out << "const uint samplerIndex"; - } - } - - if (textureFunction->method == TextureFunction::FETCH) // Integer coordinates - { - switch(textureFunction->coords) - { - case 2: out << ", int2 t"; break; - case 3: out << ", int3 t"; break; - default: UNREACHABLE(); - } - } - else // Floating-point coordinates (except textureSize) - { - switch(textureFunction->coords) - { - case 1: out << ", int lod"; break; // textureSize() - case 2: out << ", float2 t"; break; - case 3: out << ", float3 t"; break; - case 4: out << ", float4 t"; break; - default: UNREACHABLE(); - } - } + ASSERT(mShaderType == GL_COMPUTE_SHADER); - if (textureFunction->method == TextureFunction::GRAD) - { - switch(textureFunction->sampler) - { - case EbtSampler2D: - case EbtISampler2D: - case EbtUSampler2D: - case EbtSampler2DArray: - case EbtISampler2DArray: - case EbtUSampler2DArray: - case EbtSampler2DShadow: - case EbtSampler2DArrayShadow: - out << ", float2 ddx, float2 ddy"; - break; - case EbtSampler3D: - case EbtISampler3D: - case EbtUSampler3D: - case EbtSamplerCube: - case EbtISamplerCube: - case EbtUSamplerCube: - case EbtSamplerCubeShadow: - out << ", float3 ddx, float3 ddy"; - break; - default: UNREACHABLE(); - } - } - - switch(textureFunction->method) + out << "cbuffer DriverConstants : register(b1)\n" + "{\n"; + if (mUsesNumWorkGroups) { - case TextureFunction::IMPLICIT: break; - case TextureFunction::BIAS: break; // Comes after the offset parameter - case TextureFunction::LOD: out << ", float lod"; break; - case TextureFunction::LOD0: break; - case TextureFunction::LOD0BIAS: break; // Comes after the offset parameter - case TextureFunction::SIZE: break; - case TextureFunction::FETCH: out << ", int mip"; break; - case TextureFunction::GRAD: break; - default: UNREACHABLE(); + out << " uint3 gl_NumWorkGroups : packoffset(c0);\n"; } + ASSERT(mOutputType == SH_HLSL_4_1_OUTPUT); + mUniformHLSL->samplerMetadataUniforms(out, "c1"); + out << "};\n"; - if (textureFunction->offset) + // Follow built-in variables would be initialized in + // DynamicHLSL::generateComputeShaderLinkHLSL, if they + // are used in compute shader. + if (mUsesWorkGroupID) { - switch(textureFunction->sampler) - { - case EbtSampler2D: out << ", int2 offset"; break; - case EbtSampler3D: out << ", int3 offset"; break; - case EbtSampler2DArray: out << ", int2 offset"; break; - case EbtISampler2D: out << ", int2 offset"; break; - case EbtISampler3D: out << ", int3 offset"; break; - case EbtISampler2DArray: out << ", int2 offset"; break; - case EbtUSampler2D: out << ", int2 offset"; break; - case EbtUSampler3D: out << ", int3 offset"; break; - case EbtUSampler2DArray: out << ", int2 offset"; break; - case EbtSampler2DShadow: out << ", int2 offset"; break; - case EbtSampler2DArrayShadow: out << ", int2 offset"; break; - default: UNREACHABLE(); - } + out << "static uint3 gl_WorkGroupID = uint3(0, 0, 0);\n"; } - if (textureFunction->method == TextureFunction::BIAS || - textureFunction->method == TextureFunction::LOD0BIAS) + if (mUsesLocalInvocationID) { - out << ", float bias"; + out << "static uint3 gl_LocalInvocationID = uint3(0, 0, 0);\n"; } - out << ")\n" - "{\n"; - - // In some cases we use a variable to store the texture/sampler objects, but to work around - // a D3D11 compiler bug related to discard inside a loop that is conditional on texture - // sampling we need to call the function directly on a reference to the array. The bug was - // found using dEQP-GLES3.functional.shaders.discard*loop_texture* tests. - TString textureReference("x"); - TString samplerReference("s"); - if (mOutputType == SH_HLSL_4_1_OUTPUT) + if (mUsesGlobalInvocationID) { - TString suffix = TextureGroupSuffix(textureFunction->sampler); - if (TextureGroup(textureFunction->sampler) == HLSL_TEXTURE_2D) - { - textureReference = TString("textures") + suffix + "[samplerIndex]"; - samplerReference = TString("samplers") + suffix + "[samplerIndex]"; - } - else - { - out << " const uint textureIndex = samplerIndex - textureIndexOffset" << suffix - << ";\n"; - textureReference = TString("textures") + suffix + "[textureIndex]"; - out << " const uint samplerArrayIndex = samplerIndex - samplerIndexOffset" - << suffix << ";\n"; - samplerReference = TString("samplers") + suffix + "[samplerArrayIndex]"; - } + out << "static uint3 gl_GlobalInvocationID = uint3(0, 0, 0);\n"; } - if (textureFunction->method == TextureFunction::SIZE) + if (mUsesLocalInvocationIndex) { - if (IsSampler2D(textureFunction->sampler) || IsSamplerCube(textureFunction->sampler)) - { - if (IsSamplerArray(textureFunction->sampler)) - { - out << " uint width; uint height; uint layers; uint numberOfLevels;\n" - << " " << textureReference - << ".GetDimensions(lod, width, height, layers, numberOfLevels);\n"; - } - else - { - out << " uint width; uint height; uint numberOfLevels;\n" - << " " << textureReference - << ".GetDimensions(lod, width, height, numberOfLevels);\n"; - } - } - else if (IsSampler3D(textureFunction->sampler)) - { - out << " uint width; uint height; uint depth; uint numberOfLevels;\n" - << " " << textureReference - << ".GetDimensions(lod, width, height, depth, numberOfLevels);\n"; - } - else UNREACHABLE(); - - switch(textureFunction->sampler) - { - case EbtSampler2D: out << " return int2(width, height);"; break; - case EbtSampler3D: out << " return int3(width, height, depth);"; break; - case EbtSamplerCube: out << " return int2(width, height);"; break; - case EbtSampler2DArray: out << " return int3(width, height, layers);"; break; - case EbtISampler2D: out << " return int2(width, height);"; break; - case EbtISampler3D: out << " return int3(width, height, depth);"; break; - case EbtISamplerCube: out << " return int2(width, height);"; break; - case EbtISampler2DArray: out << " return int3(width, height, layers);"; break; - case EbtUSampler2D: out << " return int2(width, height);"; break; - case EbtUSampler3D: out << " return int3(width, height, depth);"; break; - case EbtUSamplerCube: out << " return int2(width, height);"; break; - case EbtUSampler2DArray: out << " return int3(width, height, layers);"; break; - case EbtSampler2DShadow: out << " return int2(width, height);"; break; - case EbtSamplerCubeShadow: out << " return int2(width, height);"; break; - case EbtSampler2DArrayShadow: out << " return int3(width, height, layers);"; break; - default: UNREACHABLE(); - } - } - else - { - if (IsIntegerSampler(textureFunction->sampler) && IsSamplerCube(textureFunction->sampler)) - { - out << " float width; float height; float layers; float levels;\n"; - - out << " uint mip = 0;\n"; - - out << " " << textureReference - << ".GetDimensions(mip, width, height, layers, levels);\n"; - - out << " bool xMajor = abs(t.x) > abs(t.y) && abs(t.x) > abs(t.z);\n"; - out << " bool yMajor = abs(t.y) > abs(t.z) && abs(t.y) > abs(t.x);\n"; - out << " bool zMajor = abs(t.z) > abs(t.x) && abs(t.z) > abs(t.y);\n"; - out << " bool negative = (xMajor && t.x < 0.0f) || (yMajor && t.y < 0.0f) || (zMajor && t.z < 0.0f);\n"; - - // FACE_POSITIVE_X = 000b - // FACE_NEGATIVE_X = 001b - // FACE_POSITIVE_Y = 010b - // FACE_NEGATIVE_Y = 011b - // FACE_POSITIVE_Z = 100b - // FACE_NEGATIVE_Z = 101b - out << " int face = (int)negative + (int)yMajor * 2 + (int)zMajor * 4;\n"; - - out << " float u = xMajor ? -t.z : (yMajor && t.y < 0.0f ? -t.x : t.x);\n"; - out << " float v = yMajor ? t.z : (negative ? t.y : -t.y);\n"; - out << " float m = xMajor ? t.x : (yMajor ? t.y : t.z);\n"; - - out << " t.x = (u * 0.5f / m) + 0.5f;\n"; - out << " t.y = (v * 0.5f / m) + 0.5f;\n"; - - // Mip level computation. - if (textureFunction->method == TextureFunction::IMPLICIT) - { - out << " float2 tSized = float2(t.x * width, t.y * height);\n" - " float2 dx = ddx(tSized);\n" - " float2 dy = ddy(tSized);\n" - " float lod = 0.5f * log2(max(dot(dx, dx), dot(dy, dy)));\n" - " mip = uint(min(max(round(lod), 0), levels - 1));\n" - << " " << textureReference - << ".GetDimensions(mip, width, height, layers, levels);\n"; - } - } - else if (IsIntegerSampler(textureFunction->sampler) && - textureFunction->method != TextureFunction::FETCH) - { - if (IsSampler2D(textureFunction->sampler)) - { - if (IsSamplerArray(textureFunction->sampler)) - { - out << " float width; float height; float layers; float levels;\n"; - - if (textureFunction->method == TextureFunction::LOD0) - { - out << " uint mip = 0;\n"; - } - else if (textureFunction->method == TextureFunction::LOD0BIAS) - { - out << " uint mip = bias;\n"; - } - else - { - - out << " " << textureReference - << ".GetDimensions(0, width, height, layers, levels);\n"; - if (textureFunction->method == TextureFunction::IMPLICIT || - textureFunction->method == TextureFunction::BIAS) - { - out << " float2 tSized = float2(t.x * width, t.y * height);\n" - " float dx = length(ddx(tSized));\n" - " float dy = length(ddy(tSized));\n" - " float lod = log2(max(dx, dy));\n"; - - if (textureFunction->method == TextureFunction::BIAS) - { - out << " lod += bias;\n"; - } - } - else if (textureFunction->method == TextureFunction::GRAD) - { - out << " float lod = log2(max(length(ddx), length(ddy)));\n"; - } - - out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; - } - - out << " " << textureReference - << ".GetDimensions(mip, width, height, layers, levels);\n"; - } - else - { - out << " float width; float height; float levels;\n"; - - if (textureFunction->method == TextureFunction::LOD0) - { - out << " uint mip = 0;\n"; - } - else if (textureFunction->method == TextureFunction::LOD0BIAS) - { - out << " uint mip = bias;\n"; - } - else - { - out << " " << textureReference - << ".GetDimensions(0, width, height, levels);\n"; - - if (textureFunction->method == TextureFunction::IMPLICIT || - textureFunction->method == TextureFunction::BIAS) - { - out << " float2 tSized = float2(t.x * width, t.y * height);\n" - " float dx = length(ddx(tSized));\n" - " float dy = length(ddy(tSized));\n" - " float lod = log2(max(dx, dy));\n"; - - if (textureFunction->method == TextureFunction::BIAS) - { - out << " lod += bias;\n"; - } - } - else if (textureFunction->method == TextureFunction::GRAD) - { - out << " float lod = log2(max(length(ddx), length(ddy)));\n"; - } - - out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; - } - - out << " " << textureReference - << ".GetDimensions(mip, width, height, levels);\n"; - } - } - else if (IsSampler3D(textureFunction->sampler)) - { - out << " float width; float height; float depth; float levels;\n"; - - if (textureFunction->method == TextureFunction::LOD0) - { - out << " uint mip = 0;\n"; - } - else if (textureFunction->method == TextureFunction::LOD0BIAS) - { - out << " uint mip = bias;\n"; - } - else - { - out << " " << textureReference - << ".GetDimensions(0, width, height, depth, levels);\n"; - - if (textureFunction->method == TextureFunction::IMPLICIT || - textureFunction->method == TextureFunction::BIAS) - { - out << " float3 tSized = float3(t.x * width, t.y * height, t.z * " - "depth);\n" - " float dx = length(ddx(tSized));\n" - " float dy = length(ddy(tSized));\n" - " float lod = log2(max(dx, dy));\n"; - - if (textureFunction->method == TextureFunction::BIAS) - { - out << " lod += bias;\n"; - } - } - else if (textureFunction->method == TextureFunction::GRAD) - { - out << " float lod = log2(max(length(ddx), length(ddy)));\n"; - } - - out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; - } - - out << " " << textureReference - << ".GetDimensions(mip, width, height, depth, levels);\n"; - } - else UNREACHABLE(); - } - - out << " return "; - - // HLSL intrinsic - if (mOutputType == SH_HLSL_3_0_OUTPUT) - { - switch(textureFunction->sampler) - { - case EbtSampler2D: out << "tex2D"; break; - case EbtSamplerCube: out << "texCUBE"; break; - default: UNREACHABLE(); - } - - switch(textureFunction->method) - { - case TextureFunction::IMPLICIT: - out << "(" << samplerReference << ", "; - break; - case TextureFunction::BIAS: - out << "bias(" << samplerReference << ", "; - break; - case TextureFunction::LOD: - out << "lod(" << samplerReference << ", "; - break; - case TextureFunction::LOD0: - out << "lod(" << samplerReference << ", "; - break; - case TextureFunction::LOD0BIAS: - out << "lod(" << samplerReference << ", "; - break; - default: UNREACHABLE(); - } - } - else if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) - { - if (textureFunction->method == TextureFunction::GRAD) - { - if (IsIntegerSampler(textureFunction->sampler)) - { - out << "" << textureReference << ".Load("; - } - else if (IsShadowSampler(textureFunction->sampler)) - { - out << "" << textureReference << ".SampleCmpLevelZero(" << samplerReference - << ", "; - } - else - { - out << "" << textureReference << ".SampleGrad(" << samplerReference << ", "; - } - } - else if (IsIntegerSampler(textureFunction->sampler) || - textureFunction->method == TextureFunction::FETCH) - { - out << "" << textureReference << ".Load("; - } - else if (IsShadowSampler(textureFunction->sampler)) - { - switch(textureFunction->method) - { - case TextureFunction::IMPLICIT: - out << "" << textureReference << ".SampleCmp(" << samplerReference - << ", "; - break; - case TextureFunction::BIAS: - out << "" << textureReference << ".SampleCmp(" << samplerReference - << ", "; - break; - case TextureFunction::LOD: - out << "" << textureReference << ".SampleCmp(" << samplerReference - << ", "; - break; - case TextureFunction::LOD0: - out << "" << textureReference << ".SampleCmpLevelZero(" - << samplerReference << ", "; - break; - case TextureFunction::LOD0BIAS: - out << "" << textureReference << ".SampleCmpLevelZero(" - << samplerReference << ", "; - break; - default: UNREACHABLE(); - } - } - else - { - switch(textureFunction->method) - { - case TextureFunction::IMPLICIT: - out << "" << textureReference << ".Sample(" << samplerReference << ", "; - break; - case TextureFunction::BIAS: - out << "" << textureReference << ".SampleBias(" << samplerReference - << ", "; - break; - case TextureFunction::LOD: - out << "" << textureReference << ".SampleLevel(" << samplerReference - << ", "; - break; - case TextureFunction::LOD0: - out << "" << textureReference << ".SampleLevel(" << samplerReference - << ", "; - break; - case TextureFunction::LOD0BIAS: - out << "" << textureReference << ".SampleLevel(" << samplerReference - << ", "; - break; - default: UNREACHABLE(); - } - } - } - else UNREACHABLE(); - - // Integer sampling requires integer addresses - TString addressx = ""; - TString addressy = ""; - TString addressz = ""; - TString close = ""; - - if (IsIntegerSampler(textureFunction->sampler) || - textureFunction->method == TextureFunction::FETCH) - { - switch(hlslCoords) - { - case 2: out << "int3("; break; - case 3: out << "int4("; break; - default: UNREACHABLE(); - } - - // Convert from normalized floating-point to integer - if (textureFunction->method != TextureFunction::FETCH) - { - addressx = "int(floor(width * frac(("; - addressy = "int(floor(height * frac(("; - - if (IsSamplerArray(textureFunction->sampler)) - { - addressz = "int(max(0, min(layers - 1, floor(0.5 + "; - } - else if (IsSamplerCube(textureFunction->sampler)) - { - addressz = "(((("; - } - else - { - addressz = "int(floor(depth * frac(("; - } - - close = "))))"; - } - } - else - { - switch(hlslCoords) - { - case 2: out << "float2("; break; - case 3: out << "float3("; break; - case 4: out << "float4("; break; - default: UNREACHABLE(); - } - } - - TString proj = ""; // Only used for projected textures - - if (textureFunction->proj) - { - switch(textureFunction->coords) - { - case 3: proj = " / t.z"; break; - case 4: proj = " / t.w"; break; - default: UNREACHABLE(); - } - } - - out << addressx + ("t.x" + proj) + close + ", " + addressy + ("t.y" + proj) + close; - - if (mOutputType == SH_HLSL_3_0_OUTPUT) - { - if (hlslCoords >= 3) - { - if (textureFunction->coords < 3) - { - out << ", 0"; - } - else - { - out << ", t.z" + proj; - } - } - - if (hlslCoords == 4) - { - switch(textureFunction->method) - { - case TextureFunction::BIAS: out << ", bias"; break; - case TextureFunction::LOD: out << ", lod"; break; - case TextureFunction::LOD0: out << ", 0"; break; - case TextureFunction::LOD0BIAS: out << ", bias"; break; - default: UNREACHABLE(); - } - } - - out << "));\n"; - } - else if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) - { - if (hlslCoords >= 3) - { - if (IsIntegerSampler(textureFunction->sampler) && IsSamplerCube(textureFunction->sampler)) - { - out << ", face"; - } - else - { - out << ", " + addressz + ("t.z" + proj) + close; - } - } - - if (textureFunction->method == TextureFunction::GRAD) - { - if (IsIntegerSampler(textureFunction->sampler)) - { - out << ", mip)"; - } - else if (IsShadowSampler(textureFunction->sampler)) - { - // Compare value - if (textureFunction->proj) - { - // According to ESSL 3.00.4 sec 8.8 p95 on textureProj: - // The resulting third component of P' in the shadow forms is used as Dref - out << "), t.z" << proj; - } - else - { - switch(textureFunction->coords) - { - case 3: out << "), t.z"; break; - case 4: out << "), t.w"; break; - default: UNREACHABLE(); - } - } - } - else - { - out << "), ddx, ddy"; - } - } - else if (IsIntegerSampler(textureFunction->sampler) || - textureFunction->method == TextureFunction::FETCH) - { - out << ", mip)"; - } - else if (IsShadowSampler(textureFunction->sampler)) - { - // Compare value - if (textureFunction->proj) - { - // According to ESSL 3.00.4 sec 8.8 p95 on textureProj: - // The resulting third component of P' in the shadow forms is used as Dref - out << "), t.z" << proj; - } - else - { - switch(textureFunction->coords) - { - case 3: out << "), t.z"; break; - case 4: out << "), t.w"; break; - default: UNREACHABLE(); - } - } - } - else - { - switch(textureFunction->method) - { - case TextureFunction::IMPLICIT: out << ")"; break; - case TextureFunction::BIAS: out << "), bias"; break; - case TextureFunction::LOD: out << "), lod"; break; - case TextureFunction::LOD0: out << "), 0"; break; - case TextureFunction::LOD0BIAS: out << "), bias"; break; - default: UNREACHABLE(); - } - } - - if (textureFunction->offset) - { - out << ", offset"; - } - - out << ");"; - } - else UNREACHABLE(); + out << "static uint gl_LocalInvocationIndex = uint(0);\n"; } - - out << "\n" - "}\n" - "\n"; } + bool getDimensionsIgnoresBaseLevel = + (mCompileOptions & SH_HLSL_GET_DIMENSIONS_IGNORES_BASE_LEVEL) != 0; + mTextureFunctionHLSL->textureFunctionHeader(out, mOutputType, getDimensionsIgnoresBaseLevel); + mImageFunctionHLSL->imageFunctionHeader(out); + if (mUsesFragCoord) { out << "#define GL_USES_FRAG_COORD\n"; @@ -1385,6 +805,16 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built out << "#define GL_USES_POINT_SIZE\n"; } + if (mHasMultiviewExtensionEnabled) + { + out << "#define GL_ANGLE_MULTIVIEW_ENABLED\n"; + } + + if (mUsesViewID) + { + out << "#define GL_USES_VIEW_ID\n"; + } + if (mUsesFragDepth) { out << "#define GL_USES_FRAG_DEPTH\n"; @@ -1395,6 +825,31 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built out << "#define GL_USES_DEPTH_RANGE\n"; } + if (mUsesNumWorkGroups) + { + out << "#define GL_USES_NUM_WORK_GROUPS\n"; + } + + if (mUsesWorkGroupID) + { + out << "#define GL_USES_WORK_GROUP_ID\n"; + } + + if (mUsesLocalInvocationID) + { + out << "#define GL_USES_LOCAL_INVOCATION_ID\n"; + } + + if (mUsesGlobalInvocationID) + { + out << "#define GL_USES_GLOBAL_INVOCATION_ID\n"; + } + + if (mUsesLocalInvocationIndex) + { + out << "#define GL_USES_LOCAL_INVOCATION_INDEX\n"; + } + if (mUsesXor) { out << "bool xor(bool p, bool q)\n" @@ -1404,7 +859,7 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built "\n"; } - builtInFunctionEmulator->OutputEmulatedFunctions(out); + builtInFunctionEmulator->outputEmulatedFunctions(out); } void OutputHLSL::visitSymbol(TIntermSymbol *node) @@ -1412,10 +867,9 @@ void OutputHLSL::visitSymbol(TIntermSymbol *node) TInfoSinkBase &out = getInfoSink(); // Handle accessing std140 structs by value - if (mFlaggedStructMappedNames.count(node) > 0) + if (IsInStd140InterfaceBlock(node) && node->getBasicType() == EbtStruct) { - out << mFlaggedStructMappedNames[node]; - return; + out << "map"; } TString name = node->getSymbol(); @@ -1427,25 +881,25 @@ void OutputHLSL::visitSymbol(TIntermSymbol *node) } else { + const TType &nodeType = node->getType(); TQualifier qualifier = node->getQualifier(); + ensureStructDefined(nodeType); + if (qualifier == EvqUniform) { - const TType &nodeType = node->getType(); const TInterfaceBlock *interfaceBlock = nodeType.getInterfaceBlock(); if (interfaceBlock) { - mReferencedInterfaceBlocks[interfaceBlock->name()] = node; + mReferencedUniformBlocks[interfaceBlock->name()] = node; } else { mReferencedUniforms[name] = node; } - ensureStructDefined(nodeType); - - out << DecorateUniform(name, nodeType); + out << DecorateVariableIfNeeded(node->getName()); } else if (qualifier == EvqAttribute || qualifier == EvqVertexIn) { @@ -1456,6 +910,10 @@ void OutputHLSL::visitSymbol(TIntermSymbol *node) { mReferencedVaryings[name] = node; out << Decorate(name); + if (name == "ViewID_OVR") + { + mUsesViewID = true; + } } else if (qualifier == EvqFragmentOut) { @@ -1497,14 +955,44 @@ void OutputHLSL::visitSymbol(TIntermSymbol *node) mUsesInstanceID = true; out << name; } + else if (qualifier == EvqVertexID) + { + mUsesVertexID = true; + out << name; + } else if (name == "gl_FragDepthEXT" || name == "gl_FragDepth") { mUsesFragDepth = true; out << "gl_Depth"; } + else if (qualifier == EvqNumWorkGroups) + { + mUsesNumWorkGroups = true; + out << name; + } + else if (qualifier == EvqWorkGroupID) + { + mUsesWorkGroupID = true; + out << name; + } + else if (qualifier == EvqLocalInvocationID) + { + mUsesLocalInvocationID = true; + out << name; + } + else if (qualifier == EvqGlobalInvocationID) + { + mUsesGlobalInvocationID = true; + out << name; + } + else if (qualifier == EvqLocalInvocationIndex) + { + mUsesLocalInvocationIndex = true; + out << name; + } else { - out << DecorateIfNeeded(node->getName()); + out << DecorateVariableIfNeeded(node->getName()); } } } @@ -1553,315 +1041,372 @@ void OutputHLSL::outputEqual(Visit visit, const TType &type, TOperator op, TInfo } } -bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) +void OutputHLSL::outputAssign(Visit visit, const TType &type, TInfoSinkBase &out) { - TInfoSinkBase &out = getInfoSink(); - - // Handle accessing std140 structs by value - if (mFlaggedStructMappedNames.count(node) > 0) + if (type.isArray()) { - out << mFlaggedStructMappedNames[node]; - return false; + const TString &functionName = addArrayAssignmentFunction(type); + outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")"); + } + else + { + outputTriplet(out, visit, "(", " = ", ")"); } +} - switch (node->getOp()) +bool OutputHLSL::ancestorEvaluatesToSamplerInStruct() +{ + for (unsigned int n = 0u; getAncestorNode(n) != nullptr; ++n) { - case EOpAssign: - if (node->getLeft()->isArray()) + TIntermNode *ancestor = getAncestorNode(n); + const TIntermBinary *ancestorBinary = ancestor->getAsBinaryNode(); + if (ancestorBinary == nullptr) { - TIntermAggregate *rightAgg = node->getRight()->getAsAggregate(); - if (rightAgg != nullptr && rightAgg->isConstructor()) + return false; + } + switch (ancestorBinary->getOp()) + { + case EOpIndexDirectStruct: { - const TString &functionName = addArrayConstructIntoFunction(node->getType()); - out << functionName << "("; - node->getLeft()->traverse(this); - TIntermSequence *seq = rightAgg->getSequence(); - for (auto &arrayElement : *seq) + const TStructure *structure = ancestorBinary->getLeft()->getType().getStruct(); + const TIntermConstantUnion *index = + ancestorBinary->getRight()->getAsConstantUnion(); + const TField *field = structure->fields()[index->getIConst(0)]; + if (IsSampler(field->type()->getBasicType())) { - out << ", "; - arrayElement->traverse(this); + return true; } - out << ")"; - return false; + break; } - // 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) - { - // 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;". - - TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode(); - ASSERT(symbolNode); - TIntermTyped *expression = node->getRight(); - - // TODO (jmadill): do a 'deep' scan to know if an expression is statically const - if (symbolNode->getQualifier() == EvqGlobal && expression->getQualifier() != EvqConst) - { - // For variables which are not constant, defer their real initialization until - // after we initialize uniforms. - TIntermBinary *deferredInit = new TIntermBinary(EOpAssign); - deferredInit->setLeft(node->getLeft()); - deferredInit->setRight(node->getRight()); - deferredInit->setType(node->getType()); - mDeferredGlobalInitializers.push_back(deferredInit); - const TString &initString = initializer(node->getType()); - node->setRight(new TIntermRaw(node->getType(), initString)); - } - else if (writeSameSymbolInitializer(out, symbolNode, expression)) - { - // Skip initializing the rest of the expression + case EOpIndexDirect: + break; + default: + // Returning a sampler from indirect indexing is not supported. return false; + } + } + 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(); + + switch (node->getOp()) + { + case EOpComma: + outputTriplet(out, visit, "(", ", ", ")"); + break; + case EOpAssign: + if (node->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); } - else if (writeConstantInitialization(out, symbolNode, expression)) + outputAssign(visit, node->getType(), out); + 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)) + { + return false; + } + } + else if (visit == InVisit) { - return false; + out << " = "; } - } - 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 + 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: { - 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(); + const TType &leftType = node->getLeft()->getType(); if (leftType.isInterfaceBlock()) { if (visit == PreVisit) { - TInterfaceBlock* interfaceBlock = leftType.getInterfaceBlock(); + TInterfaceBlock *interfaceBlock = leftType.getInterfaceBlock(); const int arrayIndex = node->getRight()->getAsConstantUnion()->getIConst(0); - mReferencedInterfaceBlocks[interfaceBlock->instanceName()] = node->getLeft()->getAsSymbolNode(); - out << mUniformHLSL->interfaceBlockInstanceString(*interfaceBlock, arrayIndex); + mReferencedUniformBlocks[interfaceBlock->instanceName()] = + node->getLeft()->getAsSymbolNode(); + out << mUniformHLSL->uniformBlockInstanceString(*interfaceBlock, arrayIndex); return false; } } + else if (ancestorEvaluatesToSamplerInStruct()) + { + // All parts of an expression that access a sampler in a struct need to use _ as + // separator to access the sampler variable that has been moved out of the struct. + outputTriplet(out, visit, "", "_", ""); + } else { outputTriplet(out, visit, "", "[", "]"); } } break; - case EOpIndexIndirect: - // We do not currently support indirect references to interface blocks - ASSERT(node->getLeft()->getBasicType() != EbtInterfaceBlock); - outputTriplet(out, visit, "", "[", "]"); - break; - case EOpIndexDirectStruct: - if (visit == InVisit) + case EOpIndexIndirect: + // We do not currently support indirect references to interface blocks + ASSERT(node->getLeft()->getBasicType() != EbtInterfaceBlock); + outputTriplet(out, visit, "", "[", "]"); + break; + case EOpIndexDirectStruct: { - const TStructure* structure = node->getLeft()->getType().getStruct(); - const TIntermConstantUnion* index = node->getRight()->getAsConstantUnion(); - const TField* field = structure->fields()[index->getIConst(0)]; - out << "." + DecorateField(field->name(), *structure); + const TStructure *structure = node->getLeft()->getType().getStruct(); + const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion(); + const TField *field = structure->fields()[index->getIConst(0)]; - return false; - } - break; - case EOpIndexDirectInterfaceBlock: - if (visit == InVisit) - { - const TInterfaceBlock* interfaceBlock = node->getLeft()->getType().getInterfaceBlock(); - const TIntermConstantUnion* index = node->getRight()->getAsConstantUnion(); - const TField* field = interfaceBlock->fields()[index->getIConst(0)]; - out << "." + Decorate(field->name()); + // In cases where indexing returns a sampler, we need to access the sampler variable + // that has been moved out of the struct. + bool indexingReturnsSampler = IsSampler(field->type()->getBasicType()); + if (visit == PreVisit && indexingReturnsSampler) + { + // Samplers extracted from structs have "angle" prefix to avoid name conflicts. + // This prefix is only output at the beginning of the indexing expression, which + // may have multiple parts. + out << "angle"; + } + if (!indexingReturnsSampler) + { + // All parts of an expression that access a sampler in a struct need to use _ as + // separator to access the sampler variable that has been moved out of the struct. + indexingReturnsSampler = ancestorEvaluatesToSamplerInStruct(); + } + if (visit == InVisit) + { + if (indexingReturnsSampler) + { + out << "_" + field->name(); + } + else + { + out << "." + DecorateField(field->name(), *structure); + } - return false; + return false; + } } break; - case EOpVectorSwizzle: - if (visit == InVisit) + case EOpIndexDirectInterfaceBlock: { - out << "."; - - TIntermAggregate *swizzle = node->getRight()->getAsAggregate(); - - if (swizzle) + bool structInStd140Block = + node->getBasicType() == EbtStruct && IsInStd140InterfaceBlock(node->getLeft()); + if (visit == PreVisit && structInStd140Block) { - TIntermSequence *sequence = swizzle->getSequence(); - - for (TIntermSequence::iterator sit = sequence->begin(); sit != sequence->end(); sit++) + out << "map"; + } + if (visit == InVisit) + { + const TInterfaceBlock *interfaceBlock = + node->getLeft()->getType().getInterfaceBlock(); + const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion(); + const TField *field = interfaceBlock->fields()[index->getIConst(0)]; + if (structInStd140Block) { - 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(); + out << "_"; } - } - else UNREACHABLE(); + else + { + out << "."; + } + out << Decorate(field->name()); - return false; // Fully processed + return false; + } + break; } - break; - case EOpAdd: - outputTriplet(out, visit, "(", " + ", ")"); - break; - case EOpSub: - outputTriplet(out, visit, "(", " - ", ")"); - break; - case EOpMul: - outputTriplet(out, visit, "(", " * ", ")"); - break; - case EOpDiv: - outputTriplet(out, visit, "(", " / ", ")"); - break; - case EOpIMod: - outputTriplet(out, visit, "(", " % ", ")"); - break; - case EOpBitShiftLeft: - outputTriplet(out, visit, "(", " << ", ")"); - break; - case EOpBitShiftRight: - outputTriplet(out, visit, "(", " >> ", ")"); - break; - case EOpBitwiseAnd: - outputTriplet(out, visit, "(", " & ", ")"); - break; - case EOpBitwiseXor: - outputTriplet(out, visit, "(", " ^ ", ")"); - break; - case EOpBitwiseOr: - outputTriplet(out, visit, "(", " | ", ")"); - break; - case EOpEqual: - case EOpNotEqual: - outputEqual(visit, node->getLeft()->getType(), node->getOp(), out); - break; - case EOpLessThan: - outputTriplet(out, visit, "(", " < ", ")"); - break; - case EOpGreaterThan: - outputTriplet(out, visit, "(", " > ", ")"); - break; - case EOpLessThanEqual: - outputTriplet(out, visit, "(", " <= ", ")"); - break; - case EOpGreaterThanEqual: - outputTriplet(out, visit, "(", " >= ", ")"); - break; - case EOpVectorTimesScalar: - outputTriplet(out, visit, "(", " * ", ")"); - break; - case EOpMatrixTimesScalar: - outputTriplet(out, visit, "(", " * ", ")"); - break; - case EOpVectorTimesMatrix: - outputTriplet(out, visit, "mul(", ", transpose(", "))"); - break; - case EOpMatrixTimesVector: - outputTriplet(out, visit, "mul(transpose(", "), ", ")"); - break; - case EOpMatrixTimesMatrix: - outputTriplet(out, visit, "transpose(mul(transpose(", "), transpose(", ")))"); - break; - case EOpLogicalOr: - // HLSL doesn't short-circuit ||, so we assume that || affected by short-circuiting have been unfolded. - ASSERT(!node->getRight()->hasSideEffects()); - outputTriplet(out, visit, "(", " || ", ")"); - return true; - case EOpLogicalXor: - mUsesXor = true; - outputTriplet(out, visit, "xor(", ", ", ")"); - break; - case EOpLogicalAnd: - // HLSL doesn't short-circuit &&, so we assume that && affected by short-circuiting have been unfolded. - ASSERT(!node->getRight()->hasSideEffects()); - outputTriplet(out, visit, "(", " && ", ")"); - return true; - default: UNREACHABLE(); + case EOpAdd: + outputTriplet(out, visit, "(", " + ", ")"); + break; + case EOpSub: + outputTriplet(out, visit, "(", " - ", ")"); + break; + case EOpMul: + outputTriplet(out, visit, "(", " * ", ")"); + break; + case EOpDiv: + outputTriplet(out, visit, "(", " / ", ")"); + break; + case EOpIMod: + outputTriplet(out, visit, "(", " % ", ")"); + break; + case EOpBitShiftLeft: + outputTriplet(out, visit, "(", " << ", ")"); + break; + case EOpBitShiftRight: + outputTriplet(out, visit, "(", " >> ", ")"); + break; + case EOpBitwiseAnd: + outputTriplet(out, visit, "(", " & ", ")"); + break; + case EOpBitwiseXor: + outputTriplet(out, visit, "(", " ^ ", ")"); + break; + case EOpBitwiseOr: + outputTriplet(out, visit, "(", " | ", ")"); + break; + case EOpEqual: + case EOpNotEqual: + outputEqual(visit, node->getLeft()->getType(), node->getOp(), out); + break; + case EOpLessThan: + outputTriplet(out, visit, "(", " < ", ")"); + break; + case EOpGreaterThan: + outputTriplet(out, visit, "(", " > ", ")"); + break; + case EOpLessThanEqual: + outputTriplet(out, visit, "(", " <= ", ")"); + break; + case EOpGreaterThanEqual: + outputTriplet(out, visit, "(", " >= ", ")"); + break; + case EOpVectorTimesScalar: + outputTriplet(out, visit, "(", " * ", ")"); + break; + case EOpMatrixTimesScalar: + outputTriplet(out, visit, "(", " * ", ")"); + break; + case EOpVectorTimesMatrix: + outputTriplet(out, visit, "mul(", ", transpose(", "))"); + break; + case EOpMatrixTimesVector: + outputTriplet(out, visit, "mul(transpose(", "), ", ")"); + break; + case EOpMatrixTimesMatrix: + outputTriplet(out, visit, "transpose(mul(transpose(", "), transpose(", ")))"); + break; + case EOpLogicalOr: + // HLSL doesn't short-circuit ||, so we assume that || affected by short-circuiting have + // been unfolded. + ASSERT(!node->getRight()->hasSideEffects()); + outputTriplet(out, visit, "(", " || ", ")"); + return true; + case EOpLogicalXor: + mUsesXor = true; + outputTriplet(out, visit, "xor(", ", ", ")"); + break; + case EOpLogicalAnd: + // HLSL doesn't short-circuit &&, so we assume that && affected by short-circuiting have + // been unfolded. + ASSERT(!node->getRight()->hasSideEffects()); + outputTriplet(out, visit, "(", " && ", ")"); + return true; + default: + UNREACHABLE(); } return true; @@ -1879,9 +1424,6 @@ bool OutputHLSL::visitUnary(Visit visit, TIntermUnary *node) case EOpPositive: outputTriplet(out, visit, "(+", "", ")"); break; - case EOpVectorLogicalNot: - outputTriplet(out, visit, "(!", "", ")"); - break; case EOpLogicalNot: outputTriplet(out, visit, "(!", "", ")"); break; @@ -1933,542 +1475,516 @@ bool OutputHLSL::visitUnary(Visit visit, TIntermUnary *node) case EOpTanh: outputTriplet(out, visit, "tanh(", "", ")"); break; - case EOpAsinh: - ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(out, visit, "asinh("); - break; - case EOpAcosh: - ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(out, visit, "acosh("); - break; - case EOpAtanh: - ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(out, visit, "atanh("); - break; - case EOpExp: - outputTriplet(out, visit, "exp(", "", ")"); - break; - case EOpLog: - outputTriplet(out, visit, "log(", "", ")"); - break; - case EOpExp2: - outputTriplet(out, visit, "exp2(", "", ")"); - break; - case EOpLog2: - outputTriplet(out, visit, "log2(", "", ")"); - break; - case EOpSqrt: - outputTriplet(out, visit, "sqrt(", "", ")"); - break; - case EOpInverseSqrt: - outputTriplet(out, visit, "rsqrt(", "", ")"); - break; - case EOpAbs: - outputTriplet(out, visit, "abs(", "", ")"); - break; - case EOpSign: - outputTriplet(out, visit, "sign(", "", ")"); - break; - case EOpFloor: - outputTriplet(out, visit, "floor(", "", ")"); - break; - case EOpTrunc: - outputTriplet(out, visit, "trunc(", "", ")"); - break; - case EOpRound: - outputTriplet(out, visit, "round(", "", ")"); - break; - case EOpRoundEven: - ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(out, visit, "roundEven("); - break; - case EOpCeil: - outputTriplet(out, visit, "ceil(", "", ")"); - break; - case EOpFract: - outputTriplet(out, visit, "frac(", "", ")"); - break; - case EOpIsNan: - outputTriplet(out, visit, "isnan(", "", ")"); - mRequiresIEEEStrictCompiling = true; - break; - case EOpIsInf: - outputTriplet(out, visit, "isinf(", "", ")"); - break; - case EOpFloatBitsToInt: - outputTriplet(out, visit, "asint(", "", ")"); - break; - case EOpFloatBitsToUint: - outputTriplet(out, visit, "asuint(", "", ")"); - break; - case EOpIntBitsToFloat: - outputTriplet(out, visit, "asfloat(", "", ")"); - break; - case EOpUintBitsToFloat: - outputTriplet(out, visit, "asfloat(", "", ")"); - break; - case EOpPackSnorm2x16: - ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(out, visit, "packSnorm2x16("); - break; - case EOpPackUnorm2x16: - ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(out, visit, "packUnorm2x16("); - break; - case EOpPackHalf2x16: - ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(out, visit, "packHalf2x16("); - break; - case EOpUnpackSnorm2x16: - ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(out, visit, "unpackSnorm2x16("); - break; - case EOpUnpackUnorm2x16: - ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(out, visit, "unpackUnorm2x16("); - break; - case EOpUnpackHalf2x16: - ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(out, visit, "unpackHalf2x16("); - break; - case EOpLength: - outputTriplet(out, visit, "length(", "", ")"); - break; - case EOpNormalize: - outputTriplet(out, visit, "normalize(", "", ")"); - break; - case EOpDFdx: - if(mInsideDiscontinuousLoop || mOutputLod0Function) - { - outputTriplet(out, visit, "(", "", ", 0.0)"); - } - else - { - outputTriplet(out, visit, "ddx(", "", ")"); - } - break; - case EOpDFdy: - if(mInsideDiscontinuousLoop || mOutputLod0Function) - { - outputTriplet(out, visit, "(", "", ", 0.0)"); - } - else + case EOpAsinh: + case EOpAcosh: + case EOpAtanh: + ASSERT(node->getUseEmulatedFunction()); + writeEmulatedFunctionTriplet(out, visit, node->getOp()); + break; + case EOpExp: + outputTriplet(out, visit, "exp(", "", ")"); + break; + case EOpLog: + outputTriplet(out, visit, "log(", "", ")"); + break; + case EOpExp2: + outputTriplet(out, visit, "exp2(", "", ")"); + break; + case EOpLog2: + outputTriplet(out, visit, "log2(", "", ")"); + break; + case EOpSqrt: + outputTriplet(out, visit, "sqrt(", "", ")"); + break; + case EOpInverseSqrt: + outputTriplet(out, visit, "rsqrt(", "", ")"); + break; + case EOpAbs: + outputTriplet(out, visit, "abs(", "", ")"); + break; + case EOpSign: + outputTriplet(out, visit, "sign(", "", ")"); + break; + case EOpFloor: + outputTriplet(out, visit, "floor(", "", ")"); + break; + case EOpTrunc: + outputTriplet(out, visit, "trunc(", "", ")"); + break; + case EOpRound: + outputTriplet(out, visit, "round(", "", ")"); + break; + case EOpRoundEven: + ASSERT(node->getUseEmulatedFunction()); + writeEmulatedFunctionTriplet(out, visit, node->getOp()); + break; + case EOpCeil: + outputTriplet(out, visit, "ceil(", "", ")"); + break; + case EOpFract: + outputTriplet(out, visit, "frac(", "", ")"); + break; + case EOpIsNan: + if (node->getUseEmulatedFunction()) + writeEmulatedFunctionTriplet(out, visit, node->getOp()); + else + outputTriplet(out, visit, "isnan(", "", ")"); + mRequiresIEEEStrictCompiling = true; + break; + case EOpIsInf: + outputTriplet(out, visit, "isinf(", "", ")"); + break; + case EOpFloatBitsToInt: + outputTriplet(out, visit, "asint(", "", ")"); + break; + case EOpFloatBitsToUint: + outputTriplet(out, visit, "asuint(", "", ")"); + break; + case EOpIntBitsToFloat: + outputTriplet(out, visit, "asfloat(", "", ")"); + break; + case EOpUintBitsToFloat: + outputTriplet(out, visit, "asfloat(", "", ")"); + break; + case EOpPackSnorm2x16: + case EOpPackUnorm2x16: + case EOpPackHalf2x16: + case EOpUnpackSnorm2x16: + case EOpUnpackUnorm2x16: + case EOpUnpackHalf2x16: + case EOpPackUnorm4x8: + case EOpPackSnorm4x8: + case EOpUnpackUnorm4x8: + case EOpUnpackSnorm4x8: + ASSERT(node->getUseEmulatedFunction()); + writeEmulatedFunctionTriplet(out, visit, node->getOp()); + break; + case EOpLength: + outputTriplet(out, visit, "length(", "", ")"); + break; + case EOpNormalize: + outputTriplet(out, visit, "normalize(", "", ")"); + break; + case EOpDFdx: + if (mInsideDiscontinuousLoop || mOutputLod0Function) + { + outputTriplet(out, visit, "(", "", ", 0.0)"); + } + else + { + outputTriplet(out, visit, "ddx(", "", ")"); + } + break; + case EOpDFdy: + if (mInsideDiscontinuousLoop || mOutputLod0Function) + { + outputTriplet(out, visit, "(", "", ", 0.0)"); + } + else + { + outputTriplet(out, visit, "ddy(", "", ")"); + } + break; + case EOpFwidth: + if (mInsideDiscontinuousLoop || mOutputLod0Function) + { + outputTriplet(out, visit, "(", "", ", 0.0)"); + } + else + { + outputTriplet(out, visit, "fwidth(", "", ")"); + } + break; + case EOpTranspose: + outputTriplet(out, visit, "transpose(", "", ")"); + break; + case EOpDeterminant: + outputTriplet(out, visit, "determinant(transpose(", "", "))"); + break; + case EOpInverse: + ASSERT(node->getUseEmulatedFunction()); + writeEmulatedFunctionTriplet(out, visit, node->getOp()); + break; + + case EOpAny: + outputTriplet(out, visit, "any(", "", ")"); + break; + case EOpAll: + outputTriplet(out, visit, "all(", "", ")"); + break; + case EOpLogicalNotComponentWise: + outputTriplet(out, visit, "(!", "", ")"); + break; + case EOpBitfieldReverse: + outputTriplet(out, visit, "reversebits(", "", ")"); + break; + case EOpBitCount: + outputTriplet(out, visit, "countbits(", "", ")"); + break; + case EOpFindLSB: + // Note that it's unclear from the HLSL docs what this returns for 0, but this is tested + // in GLSLTest and results are consistent with GL. + outputTriplet(out, visit, "firstbitlow(", "", ")"); + break; + case EOpFindMSB: + // Note that it's unclear from the HLSL docs what this returns for 0 or -1, but this is + // tested in GLSLTest and results are consistent with GL. + outputTriplet(out, visit, "firstbithigh(", "", ")"); + break; + default: + UNREACHABLE(); + } + + return true; +} + +TString OutputHLSL::samplerNamePrefixFromStruct(TIntermTyped *node) +{ + if (node->getAsSymbolNode()) + { + return node->getAsSymbolNode()->getSymbol(); + } + TIntermBinary *nodeBinary = node->getAsBinaryNode(); + switch (nodeBinary->getOp()) + { + case EOpIndexDirect: { - outputTriplet(out, visit, "ddy(", "", ")"); + int index = nodeBinary->getRight()->getAsConstantUnion()->getIConst(0); + + TInfoSinkBase prefixSink; + prefixSink << samplerNamePrefixFromStruct(nodeBinary->getLeft()) << "_" << index; + return TString(prefixSink.c_str()); } - break; - case EOpFwidth: - if(mInsideDiscontinuousLoop || mOutputLod0Function) + case EOpIndexDirectStruct: { - outputTriplet(out, visit, "(", "", ", 0.0)"); + const TStructure *s = nodeBinary->getLeft()->getAsTyped()->getType().getStruct(); + int index = nodeBinary->getRight()->getAsConstantUnion()->getIConst(0); + const TField *field = s->fields()[index]; + + TInfoSinkBase prefixSink; + prefixSink << samplerNamePrefixFromStruct(nodeBinary->getLeft()) << "_" + << field->name(); + return TString(prefixSink.c_str()); } - else + default: + UNREACHABLE(); + return TString(""); + } +} + +bool OutputHLSL::visitBlock(Visit visit, TIntermBlock *node) +{ + TInfoSinkBase &out = getInfoSink(); + + if (mInsideFunction) + { + outputLineDirective(out, node->getLine().first_line); + out << "{\n"; + } + + for (TIntermNode *statement : *node->getSequence()) + { + outputLineDirective(out, statement->getLine().first_line); + + statement->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 the output code is clearer if we don't output ; after statements where it is not + // needed: + // * if statements + // * switch statements + // * blocks + // * function definitions + // * loops (do-while loops output the semicolon in VisitLoop) + // * declarations that don't generate output. + if (statement->getAsCaseNode() == nullptr && statement->getAsIfElseNode() == nullptr && + statement->getAsBlock() == nullptr && statement->getAsLoopNode() == nullptr && + statement->getAsSwitchNode() == nullptr && + statement->getAsFunctionDefinition() == nullptr && + (statement->getAsDeclarationNode() == nullptr || + IsDeclarationWrittenOut(statement->getAsDeclarationNode())) && + statement->getAsInvariantDeclarationNode() == nullptr) { - outputTriplet(out, visit, "fwidth(", "", ")"); + out << ";\n"; } - break; - case EOpTranspose: - outputTriplet(out, visit, "transpose(", "", ")"); - break; - case EOpDeterminant: - outputTriplet(out, visit, "determinant(transpose(", "", "))"); - break; - case EOpInverse: - ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(out, visit, "inverse("); - break; + } - case EOpAny: - outputTriplet(out, visit, "any(", "", ")"); - break; - case EOpAll: - outputTriplet(out, visit, "all(", "", ")"); - break; - default: UNREACHABLE(); + if (mInsideFunction) + { + outputLineDirective(out, node->getLine().last_line); + out << "}\n"; } - return true; + return false; } -bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) +bool OutputHLSL::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) { TInfoSinkBase &out = getInfoSink(); - switch (node->getOp()) + ASSERT(mCurrentFunctionMetadata == nullptr); + + size_t index = mCallDag.findIndex(node->getFunctionSymbolInfo()); + ASSERT(index != CallDAG::InvalidIndex); + mCurrentFunctionMetadata = &mASTMetadataList[index]; + + out << TypeString(node->getFunctionPrototype()->getType()) << " "; + + TIntermSequence *parameters = node->getFunctionPrototype()->getSequence(); + + if (node->getFunctionSymbolInfo()->isMain()) { - case EOpSequence: - { - if (mInsideFunction) - { - outputLineDirective(out, node->getLine().first_line); - out << "{\n"; - } + out << "gl_main("; + } + else + { + out << DecorateFunctionIfNeeded(node->getFunctionSymbolInfo()->getNameObj()) + << DisambiguateFunctionName(parameters) << (mOutputLod0Function ? "Lod0(" : "("); + } - for (TIntermSequence::iterator sit = node->getSequence()->begin(); sit != node->getSequence()->end(); sit++) - { - outputLineDirective(out, (*sit)->getLine().first_line); + for (unsigned int i = 0; i < parameters->size(); i++) + { + TIntermSymbol *symbol = (*parameters)[i]->getAsSymbolNode(); - (*sit)->traverse(this); + if (symbol) + { + ensureStructDefined(symbol->getType()); - // 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"; - } + out << argumentString(symbol); - if (mInsideFunction) + if (i < parameters->size() - 1) { - outputLineDirective(out, node->getLine().last_line); - out << "}\n"; + out << ", "; } - - return false; } - case EOpDeclaration: - if (visit == PreVisit) - { - TIntermSequence *sequence = node->getSequence(); - TIntermTyped *variable = (*sequence)[0]->getAsTyped(); - ASSERT(sequence->size() == 1); + else + UNREACHABLE(); + } - if (variable && - (variable->getQualifier() == EvqTemporary || - variable->getQualifier() == EvqGlobal || variable->getQualifier() == EvqConst)) - { - ensureStructDefined(variable->getType()); + out << ")\n"; - if (!variable->getAsSymbolNode() || variable->getAsSymbolNode()->getSymbol() != "") // Variable declaration - { - if (!mInsideFunction) - { - out << "static "; - } + mInsideFunction = true; + // The function body node will output braces. + node->getBody()->traverse(this); + mInsideFunction = false; - out << TypeString(variable->getType()) + " "; + mCurrentFunctionMetadata = nullptr; - TIntermSymbol *symbol = variable->getAsSymbolNode(); + bool needsLod0 = mASTMetadataList[index].mNeedsLod0; + if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER) + { + ASSERT(!node->getFunctionSymbolInfo()->isMain()); + mOutputLod0Function = true; + node->traverse(this); + mOutputLod0Function = false; + } - if (symbol) - { - symbol->traverse(this); - out << ArrayString(symbol->getType()); - out << " = " + initializer(symbol->getType()); - } - else - { - variable->traverse(this); - } - } - else if (variable->getAsSymbolNode() && variable->getAsSymbolNode()->getSymbol() == "") // Type (struct) declaration - { - // Already added to constructor map - } - else UNREACHABLE(); - } - else if (variable && IsVaryingOut(variable->getQualifier())) - { - for (TIntermSequence::iterator sit = sequence->begin(); sit != sequence->end(); sit++) - { - TIntermSymbol *symbol = (*sit)->getAsSymbolNode(); + return false; +} - 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); - } - } - } +bool OutputHLSL::visitDeclaration(Visit visit, TIntermDeclaration *node) +{ + if (visit == PreVisit) + { + TIntermSequence *sequence = node->getSequence(); + TIntermTyped *variable = (*sequence)[0]->getAsTyped(); + ASSERT(sequence->size() == 1); + ASSERT(variable); - return false; - } - else if (visit == InVisit) - { - out << ", "; - } - break; - case EOpInvariantDeclaration: - // Do not do any translation - return false; - case EOpPrototype: - if (visit == PreVisit) + if (IsDeclarationWrittenOut(node)) { - size_t index = mCallDag.findIndex(node); - // Skip the prototype if it is not implemented (and thus not used) - if (index == CallDAG::InvalidIndex) - { - return false; - } + TInfoSinkBase &out = getInfoSink(); + ensureStructDefined(variable->getType()); - TString name = DecorateFunctionIfNeeded(node->getNameObj()); - out << TypeString(node->getType()) << " " << name - << (mOutputLod0Function ? "Lod0(" : "("); + if (!variable->getAsSymbolNode() || + variable->getAsSymbolNode()->getSymbol() != "") // Variable declaration + { + if (!mInsideFunction) + { + out << "static "; + } - TIntermSequence *arguments = node->getSequence(); + out << TypeString(variable->getType()) + " "; - for (unsigned int i = 0; i < arguments->size(); i++) - { - TIntermSymbol *symbol = (*arguments)[i]->getAsSymbolNode(); + TIntermSymbol *symbol = variable->getAsSymbolNode(); if (symbol) { - out << argumentString(symbol); - - if (i < arguments->size() - 1) - { - out << ", "; - } + symbol->traverse(this); + out << ArrayString(symbol->getType()); + out << " = " + initializer(symbol->getType()); + } + else + { + variable->traverse(this); } - else UNREACHABLE(); } - - out << ");\n"; - - // Also prototype the Lod0 variant if needed - bool needsLod0 = mASTMetadataList[index].mNeedsLod0; - if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER) + else if (variable->getAsSymbolNode() && + variable->getAsSymbolNode()->getSymbol() == "") // Type (struct) declaration { - mOutputLod0Function = true; - node->traverse(this); - mOutputLod0Function = false; + ASSERT(variable->getBasicType() == EbtStruct); + // ensureStructDefined has already been called. } - - return false; + else + UNREACHABLE(); } - break; - case EOpComma: - outputTriplet(out, visit, "(", ", ", ")"); - break; - case EOpFunction: + else if (IsVaryingOut(variable->getQualifier())) { - ASSERT(mCurrentFunctionMetadata == nullptr); - TString name = TFunction::unmangleName(node->getNameObj().getString()); + TIntermSymbol *symbol = variable->getAsSymbolNode(); + ASSERT(symbol); // Varying declarations can't have initializers. - size_t index = mCallDag.findIndex(node); - ASSERT(index != CallDAG::InvalidIndex); - mCurrentFunctionMetadata = &mASTMetadataList[index]; + // Vertex outputs which are declared but not written to should still be declared to + // allow successful linking. + mReferencedVaryings[symbol->getSymbol()] = symbol; + } + } + return false; +} - out << TypeString(node->getType()) << " "; +bool OutputHLSL::visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node) +{ + // Do not do any translation + return false; +} - if (name == "main") - { - out << "gl_main("; - } - else - { - out << DecorateFunctionIfNeeded(node->getNameObj()) - << (mOutputLod0Function ? "Lod0(" : "("); - } +bool OutputHLSL::visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node) +{ + TInfoSinkBase &out = getInfoSink(); - TIntermSequence *sequence = node->getSequence(); - TIntermSequence *arguments = (*sequence)[0]->getAsAggregate()->getSequence(); + ASSERT(visit == PreVisit); + 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; + } - for (unsigned int i = 0; i < arguments->size(); i++) - { - TIntermSymbol *symbol = (*arguments)[i]->getAsSymbolNode(); + TIntermSequence *arguments = node->getSequence(); - if (symbol) - { - ensureStructDefined(symbol->getType()); + TString name = DecorateFunctionIfNeeded(node->getFunctionSymbolInfo()->getNameObj()); + out << TypeString(node->getType()) << " " << name << DisambiguateFunctionName(arguments) + << (mOutputLod0Function ? "Lod0(" : "("); - out << argumentString(symbol); + for (unsigned int i = 0; i < arguments->size(); i++) + { + TIntermSymbol *symbol = (*arguments)[i]->getAsSymbolNode(); + ASSERT(symbol != nullptr); - if (i < arguments->size() - 1) - { - out << ", "; - } - } - else UNREACHABLE(); - } + out << argumentString(symbol); - out << ")\n"; + if (i < arguments->size() - 1) + { + out << ", "; + } + } - 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"; - } + out << ");\n"; - mCurrentFunctionMetadata = nullptr; + // 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; + } - bool needsLod0 = mASTMetadataList[index].mNeedsLod0; - if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER) - { - ASSERT(name != "main"); - mOutputLod0Function = true; - node->traverse(this); - mOutputLod0Function = false; - } + return false; +} - return false; - } - break; - case EOpFunctionCall: +bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) +{ + TInfoSinkBase &out = getInfoSink(); + + switch (node->getOp()) + { + case EOpCallBuiltInFunction: + case EOpCallFunctionInAST: + case EOpCallInternalRawFunction: { TIntermSequence *arguments = node->getSequence(); bool lod0 = mInsideDiscontinuousLoop || mOutputLod0Function; - if (node->isUserDefined()) + if (node->getOp() == EOpCallFunctionInAST) { if (node->isArray()) { UNIMPLEMENTED(); } - size_t index = mCallDag.findIndex(node); + size_t index = mCallDag.findIndex(node->getFunctionSymbolInfo()); ASSERT(index != CallDAG::InvalidIndex); lod0 &= mASTMetadataList[index].mNeedsLod0; - out << DecorateFunctionIfNeeded(node->getNameObj()) << (lod0 ? "Lod0(" : "("); + out << DecorateFunctionIfNeeded(node->getFunctionSymbolInfo()->getNameObj()); + out << DisambiguateFunctionName(node->getSequence()); + out << (lod0 ? "Lod0(" : "("); + } + else if (node->getOp() == EOpCallInternalRawFunction) + { + // 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()) << "("; + } + else if (node->getFunctionSymbolInfo()->isImageFunction()) + { + TString name = node->getFunctionSymbolInfo()->getName(); + TType type = (*arguments)[0]->getAsTyped()->getType(); + TString imageFunctionName = mImageFunctionHLSL->useImageFunction( + name, type.getBasicType(), type.getLayoutQualifier().imageInternalFormat, + type.getMemoryQualifier().readonly); + out << imageFunctionName << "("; } else { - TString name = TFunction::unmangleName(node->getNameObj().getString()); + const TString &name = node->getFunctionSymbolInfo()->getName(); TBasicType samplerType = (*arguments)[0]->getAsTyped()->getType().getBasicType(); - - TextureFunction textureFunction; - textureFunction.sampler = samplerType; - textureFunction.coords = (*arguments)[1]->getAsTyped()->getNominalSize(); - textureFunction.method = TextureFunction::IMPLICIT; - textureFunction.proj = false; - textureFunction.offset = false; - - if (name == "texture2D" || name == "textureCube" || name == "texture") - { - textureFunction.method = TextureFunction::IMPLICIT; - } - else if (name == "texture2DProj" || name == "textureProj") - { - textureFunction.method = TextureFunction::IMPLICIT; - textureFunction.proj = true; - } - else if (name == "texture2DLod" || name == "textureCubeLod" || name == "textureLod" || - name == "texture2DLodEXT" || name == "textureCubeLodEXT") - { - textureFunction.method = TextureFunction::LOD; - } - else if (name == "texture2DProjLod" || name == "textureProjLod" || name == "texture2DProjLodEXT") - { - textureFunction.method = TextureFunction::LOD; - textureFunction.proj = true; - } - else if (name == "textureSize") - { - textureFunction.method = TextureFunction::SIZE; - } - else if (name == "textureOffset") - { - textureFunction.method = TextureFunction::IMPLICIT; - textureFunction.offset = true; - } - else if (name == "textureProjOffset") - { - textureFunction.method = TextureFunction::IMPLICIT; - textureFunction.offset = true; - textureFunction.proj = true; - } - else if (name == "textureLodOffset") - { - textureFunction.method = TextureFunction::LOD; - textureFunction.offset = true; - } - else if (name == "textureProjLodOffset") - { - textureFunction.method = TextureFunction::LOD; - textureFunction.proj = true; - textureFunction.offset = true; - } - else if (name == "texelFetch") + int coords = 0; // textureSize(gsampler2DMS) doesn't have a second argument. + if (arguments->size() > 1) { - textureFunction.method = TextureFunction::FETCH; + coords = (*arguments)[1]->getAsTyped()->getNominalSize(); } - else if (name == "texelFetchOffset") - { - textureFunction.method = TextureFunction::FETCH; - textureFunction.offset = true; - } - else if (name == "textureGrad" || name == "texture2DGradEXT") - { - textureFunction.method = TextureFunction::GRAD; - } - else if (name == "textureGradOffset") - { - textureFunction.method = TextureFunction::GRAD; - textureFunction.offset = true; - } - else if (name == "textureProjGrad" || name == "texture2DProjGradEXT" || name == "textureCubeGradEXT") - { - textureFunction.method = TextureFunction::GRAD; - textureFunction.proj = true; - } - else if (name == "textureProjGradOffset") - { - textureFunction.method = TextureFunction::GRAD; - textureFunction.proj = true; - textureFunction.offset = true; - } - else UNREACHABLE(); + TString textureFunctionName = mTextureFunctionHLSL->useTextureFunction( + name, samplerType, coords, arguments->size(), lod0, mShaderType); + out << textureFunctionName << "("; + } - if (textureFunction.method == TextureFunction::IMPLICIT) // Could require lod 0 or have a bias argument + for (TIntermSequence::iterator arg = arguments->begin(); arg != arguments->end(); arg++) + { + TIntermTyped *typedArg = (*arg)->getAsTyped(); + if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT && IsSampler(typedArg->getBasicType())) { - unsigned int mandatoryArgumentCount = 2; // All functions have sampler and coordinate arguments - - if (textureFunction.offset) - { - mandatoryArgumentCount++; - } + out << "texture_"; + (*arg)->traverse(this); + out << ", sampler_"; + } - bool bias = (arguments->size() > mandatoryArgumentCount); // Bias argument is optional + (*arg)->traverse(this); - if (lod0 || mShaderType == GL_VERTEX_SHADER) + if (typedArg->getType().isStructureContainingSamplers()) + { + const TType &argType = typedArg->getType(); + TVector<TIntermSymbol *> samplerSymbols; + TString structName = samplerNamePrefixFromStruct(typedArg); + argType.createSamplerSymbols("angle_" + structName, "", &samplerSymbols, + nullptr, mSymbolTable); + for (const TIntermSymbol *sampler : samplerSymbols) { - if (bias) + if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) { - textureFunction.method = TextureFunction::LOD0BIAS; + out << ", texture_" << sampler->getSymbol(); + out << ", sampler_" << sampler->getSymbol(); } else { - textureFunction.method = TextureFunction::LOD0; + // In case of HLSL 4.1+, this symbol is the sampler index, and in case + // of D3D9, it's the sampler variable. + out << ", " + sampler->getSymbol(); } } - else if (bias) - { - textureFunction.method = TextureFunction::BIAS; - } - } - - mUsesTexture.insert(textureFunction); - - out << textureFunction.name(); - } - - for (TIntermSequence::iterator arg = arguments->begin(); arg != arguments->end(); arg++) - { - if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT && - IsSampler((*arg)->getAsTyped()->getBasicType())) - { - out << "texture_"; - (*arg)->traverse(this); - out << ", sampler_"; } - (*arg)->traverse(this); - if (arg < arguments->end() - 1) { out << ", "; @@ -2479,160 +1995,79 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) return false; } - break; - case EOpParameters: - outputTriplet(out, visit, "(", ", ", ")\n{\n"); - break; - case EOpConstructFloat: - outputConstructor(out, visit, node->getType(), "vec1", node->getSequence()); - break; - case EOpConstructVec2: - outputConstructor(out, visit, node->getType(), "vec2", node->getSequence()); - break; - case EOpConstructVec3: - outputConstructor(out, visit, node->getType(), "vec3", node->getSequence()); - break; - case EOpConstructVec4: - outputConstructor(out, visit, node->getType(), "vec4", node->getSequence()); - break; - case EOpConstructBool: - outputConstructor(out, visit, node->getType(), "bvec1", node->getSequence()); - break; - case EOpConstructBVec2: - outputConstructor(out, visit, node->getType(), "bvec2", node->getSequence()); - break; - case EOpConstructBVec3: - outputConstructor(out, visit, node->getType(), "bvec3", node->getSequence()); - break; - case EOpConstructBVec4: - outputConstructor(out, visit, node->getType(), "bvec4", node->getSequence()); - break; - case EOpConstructInt: - outputConstructor(out, visit, node->getType(), "ivec1", node->getSequence()); - break; - case EOpConstructIVec2: - outputConstructor(out, visit, node->getType(), "ivec2", node->getSequence()); - break; - case EOpConstructIVec3: - outputConstructor(out, visit, node->getType(), "ivec3", node->getSequence()); - break; - case EOpConstructIVec4: - outputConstructor(out, visit, node->getType(), "ivec4", node->getSequence()); - break; - case EOpConstructUInt: - outputConstructor(out, visit, node->getType(), "uvec1", node->getSequence()); - break; - case EOpConstructUVec2: - outputConstructor(out, visit, node->getType(), "uvec2", node->getSequence()); - break; - case EOpConstructUVec3: - outputConstructor(out, visit, node->getType(), "uvec3", node->getSequence()); + case EOpConstruct: + outputConstructor(out, visit, node); break; - case EOpConstructUVec4: - outputConstructor(out, visit, node->getType(), "uvec4", node->getSequence()); - break; - case EOpConstructMat2: - outputConstructor(out, visit, node->getType(), "mat2", node->getSequence()); - break; - case EOpConstructMat2x3: - outputConstructor(out, visit, node->getType(), "mat2x3", node->getSequence()); - break; - case EOpConstructMat2x4: - outputConstructor(out, visit, node->getType(), "mat2x4", node->getSequence()); + case EOpEqualComponentWise: + outputTriplet(out, visit, "(", " == ", ")"); break; - case EOpConstructMat3x2: - outputConstructor(out, visit, node->getType(), "mat3x2", node->getSequence()); + case EOpNotEqualComponentWise: + outputTriplet(out, visit, "(", " != ", ")"); break; - case EOpConstructMat3: - outputConstructor(out, visit, node->getType(), "mat3", node->getSequence()); + case EOpLessThanComponentWise: + outputTriplet(out, visit, "(", " < ", ")"); break; - case EOpConstructMat3x4: - outputConstructor(out, visit, node->getType(), "mat3x4", node->getSequence()); + case EOpGreaterThanComponentWise: + outputTriplet(out, visit, "(", " > ", ")"); break; - case EOpConstructMat4x2: - outputConstructor(out, visit, node->getType(), "mat4x2", node->getSequence()); + case EOpLessThanEqualComponentWise: + outputTriplet(out, visit, "(", " <= ", ")"); break; - case EOpConstructMat4x3: - outputConstructor(out, visit, node->getType(), "mat4x3", node->getSequence()); + case EOpGreaterThanEqualComponentWise: + outputTriplet(out, visit, "(", " >= ", ")"); break; - case EOpConstructMat4: - outputConstructor(out, visit, node->getType(), "mat4", node->getSequence()); + case EOpMod: + ASSERT(node->getUseEmulatedFunction()); + writeEmulatedFunctionTriplet(out, visit, node->getOp()); break; - case EOpConstructStruct: - { - if (node->getType().isArray()) - { - UNIMPLEMENTED(); - } - const TString &structName = StructNameString(*node->getType().getStruct()); - mStructureHLSL->addConstructor(node->getType(), structName, node->getSequence()); - outputTriplet(out, visit, (structName + "_ctor(").c_str(), ", ", ")"); - } - break; - case EOpLessThan: - outputTriplet(out, visit, "(", " < ", ")"); + case EOpModf: + outputTriplet(out, visit, "modf(", ", ", ")"); break; - case EOpGreaterThan: - outputTriplet(out, visit, "(", " > ", ")"); + case EOpPow: + outputTriplet(out, visit, "pow(", ", ", ")"); break; - case EOpLessThanEqual: - outputTriplet(out, visit, "(", " <= ", ")"); + case EOpAtan: + ASSERT(node->getSequence()->size() == 2); // atan(x) is a unary operator + ASSERT(node->getUseEmulatedFunction()); + writeEmulatedFunctionTriplet(out, visit, node->getOp()); break; - case EOpGreaterThanEqual: - outputTriplet(out, visit, "(", " >= ", ")"); + case EOpMin: + outputTriplet(out, visit, "min(", ", ", ")"); break; - case EOpVectorEqual: - outputTriplet(out, visit, "(", " == ", ")"); + case EOpMax: + outputTriplet(out, visit, "max(", ", ", ")"); break; - case EOpVectorNotEqual: - outputTriplet(out, visit, "(", " != ", ")"); + case EOpClamp: + outputTriplet(out, visit, "clamp(", ", ", ")"); 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 EOpMix: { TIntermTyped *lastParamNode = (*(node->getSequence()))[2]->getAsTyped(); if (lastParamNode->getType().getBasicType() == EbtBool) { - // There is no HLSL equivalent for ESSL3 built-in "genType mix (genType x, genType y, genBType a)", + // There is no HLSL equivalent for ESSL3 built-in "genType mix (genType x, genType + // y, genBType a)", // so use emulated version. ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(out, visit, "mix("); + writeEmulatedFunctionTriplet(out, visit, node->getOp()); } else { outputTriplet(out, visit, "lerp(", ", ", ")"); } + break; } - break; case EOpStep: outputTriplet(out, visit, "step(", ", ", ")"); break; case EOpSmoothStep: outputTriplet(out, visit, "smoothstep(", ", ", ")"); break; + case EOpFrexp: + case EOpLdexp: + ASSERT(node->getUseEmulatedFunction()); + writeEmulatedFunctionTriplet(out, visit, node->getOp()); + break; case EOpDistance: outputTriplet(out, visit, "distance(", ", ", ")"); break; @@ -2642,30 +2077,40 @@ 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, node->getOp()); + break; + case EOpReflect: + outputTriplet(out, visit, "reflect(", ", ", ")"); + break; + case EOpRefract: + outputTriplet(out, visit, "refract(", ", ", ")"); + break; + case EOpOuterProduct: + ASSERT(node->getUseEmulatedFunction()); + writeEmulatedFunctionTriplet(out, visit, node->getOp()); + break; + case EOpMulMatrixComponentWise: + outputTriplet(out, visit, "(", " * ", ")"); + break; + case EOpBitfieldExtract: + case EOpBitfieldInsert: + case EOpUaddCarry: + case EOpUsubBorrow: + case EOpUmulExtended: + case EOpImulExtended: + ASSERT(node->getUseEmulatedFunction()); + writeEmulatedFunctionTriplet(out, visit, node->getOp()); + break; + default: + UNREACHABLE(); } return true; } -void OutputHLSL::writeSelection(TInfoSinkBase &out, TIntermSelection *node) +void OutputHLSL::writeIfElse(TInfoSinkBase &out, TIntermIfElse *node) { out << "if ("; @@ -2680,8 +2125,6 @@ void OutputHLSL::writeSelection(TInfoSinkBase &out, TIntermSelection *node) if (node->getTrueBlock()) { // The trueBlock child node will output braces. - ASSERT(IsSequence(node->getTrueBlock())); - node->getTrueBlock()->traverse(this); // Detect true discard @@ -2702,9 +2145,7 @@ void OutputHLSL::writeSelection(TInfoSinkBase &out, TIntermSelection *node) outputLineDirective(out, node->getFalseBlock()->getLine().first_line); - // Either this is "else if" or the falseBlock child node will output braces. - ASSERT(IsSequence(node->getFalseBlock()) || node->getFalseBlock()->getAsSelectionNode() != nullptr); - + // The falseBlock child node will output braces. node->getFalseBlock()->traverse(this); outputLineDirective(out, node->getFalseBlock()->getLine().first_line); @@ -2720,18 +2161,19 @@ void OutputHLSL::writeSelection(TInfoSinkBase &out, TIntermSelection *node) } } -bool OutputHLSL::visitSelection(Visit visit, TIntermSelection *node) +bool OutputHLSL::visitTernary(Visit, TIntermTernary *) { - TInfoSinkBase &out = getInfoSink(); + // 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; +} - ASSERT(!node->usesTernaryOperator()); +bool OutputHLSL::visitIfElse(Visit visit, TIntermIfElse *node) +{ + TInfoSinkBase &out = getInfoSink(); - if (!mInsideFunction) - { - // This is part of unfolded global initialization. - mDeferredGlobalInitializers.push_back(node); - return false; - } + ASSERT(mInsideFunction); // D3D errors when there is a gradient operation in a loop in an unflattened if. if (mShaderType == GL_FRAGMENT_SHADER && mCurrentFunctionMetadata->hasGradientLoop(node)) @@ -2739,7 +2181,7 @@ bool OutputHLSL::visitSelection(Visit visit, TIntermSelection *node) out << "FLATTEN "; } - writeSelection(out, node); + writeIfElse(out, node); return false; } @@ -2748,17 +2190,13 @@ bool OutputHLSL::visitSwitch(Visit visit, TIntermSwitch *node) { TInfoSinkBase &out = getInfoSink(); - if (node->getStatementList()) - { - node->setStatementList(RemoveSwitchFallThrough::removeFallThrough(node->getStatementList())); - outputTriplet(out, visit, "switch (", ") ", ""); - // The curly braces get written when visiting the statementList aggregate - } - else + ASSERT(node->getStatementList()); + if (visit == PreVisit) { - // No statementList, so it won't output curly braces - outputTriplet(out, visit, "switch (", ") {", "}\n"); + node->setStatementList(RemoveSwitchFallThrough(node->getStatementList(), mPerfDiagnostics)); } + outputTriplet(out, visit, "switch (", ") ", ""); + // The curly braces get written when visiting the statementList block. return true; } @@ -2789,8 +2227,8 @@ bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node) mNestedLoopDepth++; bool wasDiscontinuous = mInsideDiscontinuousLoop; - mInsideDiscontinuousLoop = mInsideDiscontinuousLoop || - mCurrentFunctionMetadata->mDiscontinuousLoops.count(node) > 0; + mInsideDiscontinuousLoop = + mInsideDiscontinuousLoop || mCurrentFunctionMetadata->mDiscontinuousLoops.count(node) > 0; TInfoSinkBase &out = getInfoSink(); @@ -2843,7 +2281,6 @@ 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 @@ -2858,11 +2295,11 @@ bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node) if (node->getType() == ELoopDoWhile) { outputLineDirective(out, node->getCondition()->getLine().first_line); - out << "while(\n"; + out << "while ("; node->getCondition()->traverse(this); - out << ");"; + out << ");\n"; } out << "}\n"; @@ -2875,89 +2312,47 @@ bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node) bool OutputHLSL::visitBranch(Visit visit, TIntermBranch *node) { - TInfoSinkBase &out = getInfoSink(); - - switch (node->getFlowOp()) + if (visit == PreVisit) { - case EOpKill: - outputTriplet(out, visit, "discard;\n", "", ""); - break; - case EOpBreak: - if (visit == PreVisit) - { - if (mNestedLoopDepth > 1) - { - mUsesNestedBreak = true; - } - - if (mExcessiveLoopIndex) - { - out << "{Break"; - mExcessiveLoopIndex->traverse(this); - out << " = true; break;}\n"; - } - else - { - out << "break;\n"; - } - } - break; - case EOpContinue: - outputTriplet(out, visit, "continue;\n", "", ""); - break; - case EOpReturn: - if (visit == PreVisit) - { - if (node->getExpression()) - { - out << "return "; - } - else - { - out << "return;\n"; - } - } - else if (visit == PostVisit) - { - if (node->getExpression()) - { - out << ";\n"; - } - } - break; - default: UNREACHABLE(); - } - - return true; -} + TInfoSinkBase &out = getInfoSink(); -bool OutputHLSL::isSingleStatement(TIntermNode *node) -{ - TIntermAggregate *aggregate = node->getAsAggregate(); - - if (aggregate) - { - if (aggregate->getOp() == EOpSequence) + switch (node->getFlowOp()) { - 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)) + case EOpKill: + out << "discard"; + break; + case EOpBreak: + if (mNestedLoopDepth > 1) { - return false; + mUsesNestedBreak = true; } - } - return true; + if (mExcessiveLoopIndex) + { + out << "{Break"; + mExcessiveLoopIndex->traverse(this); + out << " = true; break;}\n"; + } + else + { + out << "break"; + } + break; + case EOpContinue: + out << "continue"; + break; + case EOpReturn: + if (node->getExpression()) + { + out << "return "; + } + else + { + out << "return"; + } + break; + default: + UNREACHABLE(); } } @@ -2965,28 +2360,29 @@ bool OutputHLSL::isSingleStatement(TIntermNode *node) } // 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). +// (The D3D documentation says 255 iterations, but the compiler complains at anything more than +// 254). bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node) { const int MAX_LOOP_ITERATIONS = 254; // Parse loops of the form: // for(int index = initial; index [comparator] limit; index += increment) - TIntermSymbol *index = NULL; + TIntermSymbol *index = nullptr; TOperator comparator = EOpNull; - int initial = 0; - int limit = 0; - int increment = 0; + int initial = 0; + int limit = 0; + int increment = 0; // Parse index name and intial value if (node->getInit()) { - TIntermAggregate *init = node->getInit()->getAsAggregate(); + TIntermDeclaration *init = node->getInit()->getAsDeclarationNode(); if (init) { TIntermSequence *sequence = init->getSequence(); - TIntermTyped *variable = (*sequence)[0]->getAsTyped(); + TIntermTyped *variable = (*sequence)[0]->getAsTyped(); if (variable && variable->getQualifier() == EvqTemporary) { @@ -2994,14 +2390,14 @@ bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node) if (assign->getOp() == EOpInitialize) { - TIntermSymbol *symbol = assign->getLeft()->getAsSymbolNode(); + TIntermSymbol *symbol = assign->getLeft()->getAsSymbolNode(); TIntermConstantUnion *constant = assign->getRight()->getAsConstantUnion(); if (symbol && constant) { if (constant->getBasicType() == EbtInt && constant->isScalar()) { - index = symbol; + index = symbol; initial = constant->getIConst(0); } } @@ -3011,7 +2407,7 @@ bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node) } // Parse comparator and limit value - if (index != NULL && node->getCondition()) + if (index != nullptr && node->getCondition()) { TIntermBinary *test = node->getCondition()->getAsBinaryNode(); @@ -3024,21 +2420,21 @@ bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node) if (constant->getBasicType() == EbtInt && constant->isScalar()) { comparator = test->getOp(); - limit = constant->getIConst(0); + limit = constant->getIConst(0); } } } } // Parse increment - if (index != NULL && comparator != EOpNull && node->getExpression()) + if (index != nullptr && comparator != EOpNull && node->getExpression()) { TIntermBinary *binaryTerminal = node->getExpression()->getAsBinaryNode(); - TIntermUnary *unaryTerminal = node->getExpression()->getAsUnaryNode(); + TIntermUnary *unaryTerminal = node->getExpression()->getAsUnaryNode(); if (binaryTerminal) { - TOperator op = binaryTerminal->getOp(); + TOperator op = binaryTerminal->getOp(); TIntermConstantUnion *constant = binaryTerminal->getRight()->getAsConstantUnion(); if (constant) @@ -3049,9 +2445,14 @@ bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node) switch (op) { - case EOpAddAssign: increment = value; break; - case EOpSubAssign: increment = -value; break; - default: UNIMPLEMENTED(); + case EOpAddAssign: + increment = value; + break; + case EOpSubAssign: + increment = -value; + break; + default: + UNIMPLEMENTED(); } } } @@ -3062,16 +2463,25 @@ bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node) switch (op) { - case EOpPostIncrement: increment = 1; break; - case EOpPostDecrement: increment = -1; break; - case EOpPreIncrement: increment = 1; break; - case EOpPreDecrement: increment = -1; break; - default: UNIMPLEMENTED(); + case EOpPostIncrement: + increment = 1; + break; + case EOpPostDecrement: + increment = -1; + break; + case EOpPreIncrement: + increment = 1; + break; + case EOpPreDecrement: + increment = -1; + break; + default: + UNIMPLEMENTED(); } } } - if (index != NULL && comparator != EOpNull && increment != 0) + if (index != nullptr && comparator != EOpNull && increment != 0) { if (comparator == EOpLessThanEqual) { @@ -3085,11 +2495,11 @@ bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node) if (iterations <= MAX_LOOP_ITERATIONS) { - return false; // Not an excessive loop + return false; // Not an excessive loop } TIntermSymbol *restoreIndex = mExcessiveLoopIndex; - mExcessiveLoopIndex = index; + mExcessiveLoopIndex = index; out << "{int "; index->traverse(this); @@ -3111,13 +2521,14 @@ bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node) out << ") {\n"; } - if (iterations <= MAX_LOOP_ITERATIONS) // Last loop fragment + if (iterations <= MAX_LOOP_ITERATIONS) // Last loop fragment { - mExcessiveLoopIndex = NULL; // Stops setting the Break flag + mExcessiveLoopIndex = nullptr; // Stops setting the Break flag } // for(int index = initial; index < clampedLimit; index += increment) - const char *unroll = mCurrentFunctionMetadata->hasGradientInCallGraph(node) ? "LOOP" : ""; + const char *unroll = + mCurrentFunctionMetadata->hasGradientInCallGraph(node) ? "LOOP" : ""; out << unroll << " for("; index->traverse(this); @@ -3163,10 +2574,11 @@ bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node) return true; } - else UNIMPLEMENTED(); + else + UNIMPLEMENTED(); } - return false; // Not handled as an excessive loop + return false; // Not handled as an excessive loop } void OutputHLSL::outputTriplet(TInfoSinkBase &out, @@ -3218,7 +2630,7 @@ TString OutputHLSL::argumentString(const TIntermSymbol *symbol) } else { - nameStr = DecorateIfNeeded(name); + nameStr = DecorateVariableIfNeeded(name); } if (IsSampler(type.getBasicType())) @@ -3238,7 +2650,44 @@ TString OutputHLSL::argumentString(const TIntermSymbol *symbol) } } - return QualifierString(qualifier) + " " + TypeString(type) + " " + nameStr + ArrayString(type); + TStringStream argString; + argString << QualifierString(qualifier) << " " << TypeString(type) << " " << nameStr + << ArrayString(type); + + // If the structure parameter contains samplers, they need to be passed into the function as + // separate parameters. HLSL doesn't natively support samplers in structs. + if (type.isStructureContainingSamplers()) + { + ASSERT(qualifier != EvqOut && qualifier != EvqInOut); + TVector<TIntermSymbol *> samplerSymbols; + type.createSamplerSymbols("angle" + nameStr, "", &samplerSymbols, nullptr, mSymbolTable); + for (const TIntermSymbol *sampler : samplerSymbols) + { + const TType &samplerType = sampler->getType(); + if (mOutputType == SH_HLSL_4_1_OUTPUT) + { + argString << ", const uint " << sampler->getSymbol() << ArrayString(samplerType); + } + else if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) + { + ASSERT(IsSampler(samplerType.getBasicType())); + argString << ", " << QualifierString(qualifier) << " " + << TextureString(samplerType.getBasicType()) << " texture_" + << sampler->getSymbol() << ArrayString(samplerType) << ", " + << QualifierString(qualifier) << " " + << SamplerString(samplerType.getBasicType()) << " sampler_" + << sampler->getSymbol() << ArrayString(samplerType); + } + else + { + ASSERT(IsSampler(samplerType.getBasicType())); + argString << ", " << QualifierString(qualifier) << " " << TypeString(samplerType) + << " " << sampler->getSymbol() << ArrayString(samplerType); + } + } + } + + return argString.str(); } TString OutputHLSL::initializer(const TType &type) @@ -3259,22 +2708,24 @@ TString OutputHLSL::initializer(const TType &type) return "{" + string + "}"; } -void OutputHLSL::outputConstructor(TInfoSinkBase &out, - Visit visit, - const TType &type, - const char *name, - const TIntermSequence *parameters) +void OutputHLSL::outputConstructor(TInfoSinkBase &out, Visit visit, TIntermAggregate *node) { - if (type.isArray()) - { - UNIMPLEMENTED(); - } + // Array constructors should have been already pruned from the code. + ASSERT(!node->getType().isArray()); if (visit == PreVisit) { - mStructureHLSL->addConstructor(type, name, parameters); - - out << name << "("; + TString constructorName; + if (node->getBasicType() == EbtStruct) + { + constructorName = mStructureHLSL->addStructConstructor(*node->getType().getStruct()); + } + else + { + constructorName = + mStructureHLSL->addBuiltInConstructor(node->getType(), node->getSequence()); + } + out << constructorName << "("; } else if (visit == InVisit) { @@ -3292,12 +2743,12 @@ const TConstantUnion *OutputHLSL::writeConstantUnion(TInfoSinkBase &out, { const TConstantUnion *constUnionIterated = constUnion; - const TStructure* structure = type.getStruct(); + const TStructure *structure = type.getStruct(); if (structure) { - out << StructNameString(*structure) + "_ctor("; + out << mStructureHLSL->addStructConstructor(*structure) << "("; - const TFieldList& fields = structure->fields(); + const TFieldList &fields = structure->fields(); for (size_t i = 0; i < fields.size(); i++) { @@ -3314,14 +2765,14 @@ const TConstantUnion *OutputHLSL::writeConstantUnion(TInfoSinkBase &out, } else { - size_t size = type.getObjectSize(); + size_t size = type.getObjectSize(); bool writeType = size > 1; if (writeType) { out << TypeString(type) << "("; } - constUnionIterated = WriteConstantUnionArray(out, constUnionIterated, size); + constUnionIterated = writeConstantUnionArray(out, constUnionIterated, size); if (writeType) { out << ")"; @@ -3331,13 +2782,23 @@ const TConstantUnion *OutputHLSL::writeConstantUnion(TInfoSinkBase &out, return constUnionIterated; } -void OutputHLSL::writeEmulatedFunctionTriplet(TInfoSinkBase &out, Visit visit, const char *preStr) +void OutputHLSL::writeEmulatedFunctionTriplet(TInfoSinkBase &out, Visit visit, TOperator op) { - TString preString = BuiltInFunctionEmulator::GetEmulatedFunctionName(preStr); - outputTriplet(out, visit, preString.c_str(), ", ", ")"); + if (visit == PreVisit) + { + const char *opStr = GetOperatorString(op); + BuiltInFunctionEmulator::WriteEmulatedFunctionName(out, opStr); + out << "("; + } + else + { + outputTriplet(out, visit, nullptr, ", ", ")"); + } } -bool OutputHLSL::writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *symbolNode, TIntermTyped *expression) +bool OutputHLSL::writeSameSymbolInitializer(TInfoSinkBase &out, + TIntermSymbol *symbolNode, + TIntermTyped *expression) { sh::SearchSymbol searchSymbol(symbolNode->getSymbol()); expression->traverse(&searchSymbol); @@ -3362,52 +2823,40 @@ bool OutputHLSL::canWriteAsHLSLLiteral(TIntermTyped *expression) { // We support writing constant unions and constructors that only take constant unions as // parameters as HLSL literals. - if (expression->getAsConstantUnion()) - { - return true; - } - if (expression->getQualifier() != EvqConst || !expression->getAsAggregate() || - !expression->getAsAggregate()->isConstructor()) - { - return false; - } - TIntermAggregate *constructor = expression->getAsAggregate(); - for (TIntermNode *&node : *constructor->getSequence()) - { - if (!node->getAsConstantUnion()) - return false; - } - return true; + return !expression->getType().isArrayOfArrays() && + (expression->getAsConstantUnion() || + expression->isConstructorWithOnlyConstantUnionParameters()); } bool OutputHLSL::writeConstantInitialization(TInfoSinkBase &out, TIntermSymbol *symbolNode, - TIntermTyped *expression) + TIntermTyped *initializer) { - if (canWriteAsHLSLLiteral(expression)) + if (canWriteAsHLSLLiteral(initializer)) { symbolNode->traverse(this); - if (expression->getType().isArray()) + ASSERT(!symbolNode->getType().isArrayOfArrays()); + if (symbolNode->getType().isArray()) { - out << "[" << expression->getType().getArraySize() << "]"; + out << "[" << symbolNode->getType().getOutermostArraySize() << "]"; } out << " = {"; - if (expression->getAsConstantUnion()) + if (initializer->getAsConstantUnion()) { - TIntermConstantUnion *nodeConst = expression->getAsConstantUnion(); + TIntermConstantUnion *nodeConst = initializer->getAsConstantUnion(); const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer(); - WriteConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize()); + writeConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize()); } else { - TIntermAggregate *constructor = expression->getAsAggregate(); + TIntermAggregate *constructor = initializer->getAsAggregate(); ASSERT(constructor != nullptr); for (TIntermNode *&node : *constructor->getSequence()) { TIntermConstantUnion *nodeConst = node->getAsConstantUnion(); ASSERT(nodeConst); const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer(); - WriteConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize()); + writeConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize()); if (node != constructor->getSequence()->back()) { out << ", "; @@ -3420,47 +2869,6 @@ bool OutputHLSL::writeConstantInitialization(TInfoSinkBase &out, return false; } -void OutputHLSL::writeDeferredGlobalInitializers(TInfoSinkBase &out) -{ - out << "#define ANGLE_USES_DEFERRED_INIT\n" - << "\n" - << "void initializeDeferredGlobals()\n" - << "{\n"; - - for (const auto &deferredGlobal : mDeferredGlobalInitializers) - { - TIntermBinary *binary = deferredGlobal->getAsBinaryNode(); - TIntermSelection *selection = deferredGlobal->getAsSelectionNode(); - if (binary != nullptr) - { - TIntermSymbol *symbol = binary->getLeft()->getAsSymbolNode(); - TIntermTyped *expression = binary->getRight(); - ASSERT(symbol); - ASSERT(symbol->getQualifier() == EvqGlobal && expression->getQualifier() != EvqConst); - - out << " " << Decorate(symbol->getSymbol()) << " = "; - - if (!writeSameSymbolInitializer(out, symbol, expression)) - { - ASSERT(mInfoSinkStack.top() == &out); - expression->traverse(this); - } - out << ";\n"; - } - else if (selection != nullptr) - { - writeSelection(out, selection); - } - else - { - UNREACHABLE(); - } - } - - out << "}\n" - << "\n"; -} - TString OutputHLSL::addStructEqualityFunction(const TStructure &structure) { const TFieldList &fields = structure.fields(); @@ -3476,18 +2884,19 @@ TString OutputHLSL::addStructEqualityFunction(const TStructure &structure) const TString &structNameString = StructNameString(structure); StructEqualityFunction *function = new StructEqualityFunction(); - function->structure = &structure; - function->functionName = "angle_eq_" + structNameString; + function->structure = &structure; + function->functionName = "angle_eq_" + structNameString; TInfoSinkBase fnOut; - fnOut << "bool " << function->functionName << "(" << structNameString << " a, " << structNameString + " b)\n" + fnOut << "bool " << function->functionName << "(" << structNameString << " a, " + << structNameString + " b)\n" << "{\n" " return "; for (size_t i = 0; i < fields.size(); i++) { - const TField *field = fields[i]; + const TField *field = fields[i]; const TType *fieldType = field->type(); const TString &fieldNameA = "a." + Decorate(field->name()); @@ -3507,7 +2916,8 @@ TString OutputHLSL::addStructEqualityFunction(const TStructure &structure) fnOut << ")"; } - fnOut << ";\n" << "}\n"; + fnOut << ";\n" + << "}\n"; function->functionDefinition = fnOut.c_str(); @@ -3517,7 +2927,7 @@ TString OutputHLSL::addStructEqualityFunction(const TStructure &structure) return function->functionName; } -TString OutputHLSL::addArrayEqualityFunction(const TType& type) +TString OutputHLSL::addArrayEqualityFunction(const TType &type) { for (const auto &eqFunction : mArrayEqualityFunctions) { @@ -3527,33 +2937,31 @@ TString OutputHLSL::addArrayEqualityFunction(const TType& type) } } - const TString &typeName = TypeString(type); + TType elementType(type); + elementType.toArrayElementType(); ArrayHelperFunction *function = new ArrayHelperFunction(); - function->type = type; - - TInfoSinkBase fnNameOut; - fnNameOut << "angle_eq_" << type.getArraySize() << "_" << typeName; - function->functionName = fnNameOut.c_str(); + function->type = type; - TType nonArrayType = type; - nonArrayType.clearArrayness(); + function->functionName = ArrayHelperFunctionName("angle_eq", type); TInfoSinkBase fnOut; - fnOut << "bool " << function->functionName << "(" - << typeName << " a[" << type.getArraySize() << "], " - << typeName << " b[" << type.getArraySize() << "])\n" + const TString &typeName = TypeString(type); + fnOut << "bool " << function->functionName << "(" << typeName << " a" << ArrayString(type) + << ", " << typeName << " b" << ArrayString(type) << ")\n" << "{\n" - " for (int i = 0; i < " << type.getArraySize() << "; ++i)\n" + " for (int i = 0; i < " + << type.getOutermostArraySize() + << "; ++i)\n" " {\n" " if ("; - outputEqual(PreVisit, nonArrayType, EOpNotEqual, fnOut); + outputEqual(PreVisit, elementType, EOpNotEqual, fnOut); fnOut << "a[i]"; - outputEqual(InVisit, nonArrayType, EOpNotEqual, fnOut); + outputEqual(InVisit, elementType, EOpNotEqual, fnOut); fnOut << "b[i]"; - outputEqual(PostVisit, nonArrayType, EOpNotEqual, fnOut); + outputEqual(PostVisit, elementType, EOpNotEqual, fnOut); fnOut << ") { return false; }\n" " }\n" @@ -3568,7 +2976,7 @@ TString OutputHLSL::addArrayEqualityFunction(const TType& type) return function->functionName; } -TString OutputHLSL::addArrayAssignmentFunction(const TType& type) +TString OutputHLSL::addArrayAssignmentFunction(const TType &type) { for (const auto &assignFunction : mArrayAssignmentFunctions) { @@ -3578,26 +2986,35 @@ TString OutputHLSL::addArrayAssignmentFunction(const TType& type) } } - const TString &typeName = TypeString(type); + TType elementType(type); + elementType.toArrayElementType(); ArrayHelperFunction function; function.type = type; - TInfoSinkBase fnNameOut; - fnNameOut << "angle_assign_" << type.getArraySize() << "_" << typeName; - function.functionName = fnNameOut.c_str(); + function.functionName = ArrayHelperFunctionName("angle_assign", type); TInfoSinkBase fnOut; - fnOut << "void " << function.functionName << "(out " - << typeName << " a[" << type.getArraySize() << "], " - << typeName << " b[" << type.getArraySize() << "])\n" - << "{\n" - " for (int i = 0; i < " << type.getArraySize() << "; ++i)\n" - " {\n" - " a[i] = b[i];\n" - " }\n" - "}\n"; + const TString &typeName = TypeString(type); + fnOut << "void " << function.functionName << "(out " << typeName << " a" << ArrayString(type) + << ", " << typeName << " b" << ArrayString(type) << ")\n" + << "{\n" + " for (int i = 0; i < " + << type.getOutermostArraySize() + << "; ++i)\n" + " {\n" + " "; + + outputAssign(PreVisit, elementType, fnOut); + fnOut << "a[i]"; + outputAssign(InVisit, elementType, fnOut); + fnOut << "b[i]"; + outputAssign(PostVisit, elementType, fnOut); + + fnOut << ";\n" + " }\n" + "}\n"; function.functionDefinition = fnOut.c_str(); @@ -3606,7 +3023,7 @@ TString OutputHLSL::addArrayAssignmentFunction(const TType& type) return function.functionName; } -TString OutputHLSL::addArrayConstructIntoFunction(const TType& type) +TString OutputHLSL::addArrayConstructIntoFunction(const TType &type) { for (const auto &constructIntoFunction : mArrayConstructIntoFunctions) { @@ -3616,29 +3033,34 @@ TString OutputHLSL::addArrayConstructIntoFunction(const TType& type) } } - const TString &typeName = TypeString(type); + TType elementType(type); + elementType.toArrayElementType(); ArrayHelperFunction function; function.type = type; - TInfoSinkBase fnNameOut; - fnNameOut << "angle_construct_into_" << type.getArraySize() << "_" << typeName; - function.functionName = fnNameOut.c_str(); + function.functionName = ArrayHelperFunctionName("angle_construct_into", type); TInfoSinkBase fnOut; - fnOut << "void " << function.functionName << "(out " - << typeName << " a[" << type.getArraySize() << "]"; - for (int i = 0; i < type.getArraySize(); ++i) + const TString &typeName = TypeString(type); + fnOut << "void " << function.functionName << "(out " << typeName << " a" << ArrayString(type); + for (unsigned int i = 0u; i < type.getOutermostArraySize(); ++i) { - fnOut << ", " << typeName << " b" << i; + fnOut << ", " << typeName << " b" << i << ArrayString(elementType); } fnOut << ")\n" "{\n"; - for (int i = 0; i < type.getArraySize(); ++i) + for (unsigned int i = 0u; i < type.getOutermostArraySize(); ++i) { - fnOut << " a[" << i << "] = b" << i << ";\n"; + fnOut << " "; + outputAssign(PreVisit, elementType, fnOut); + fnOut << "a[" << i << "]"; + outputAssign(InVisit, elementType, fnOut); + fnOut << "b" << i; + outputAssign(PostVisit, elementType, fnOut); + fnOut << ";\n"; } fnOut << "}\n"; @@ -3651,14 +3073,12 @@ TString OutputHLSL::addArrayConstructIntoFunction(const TType& type) void OutputHLSL::ensureStructDefined(const TType &type) { - TStructure *structure = type.getStruct(); - + const TStructure *structure = type.getStruct(); if (structure) { - mStructureHLSL->addConstructor(type, StructNameString(*structure), nullptr); + ASSERT(type.getBasicType() == EbtStruct); + mStructureHLSL->ensureStructDefined(*structure); } } - - -} +} // namespace sh |