diff options
Diffstat (limited to 'src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp')
-rw-r--r-- | src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp | 1568 |
1 files changed, 1058 insertions, 510 deletions
diff --git a/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp index 94225b81c4..253b96696c 100644 --- a/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp +++ b/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp @@ -11,45 +11,81 @@ #include <stdio.h> #include "common/angleutils.h" +#include "common/debug.h" #include "common/utilities.h" #include "compiler/translator/BuiltInFunctionEmulator.h" #include "compiler/translator/BuiltInFunctionEmulatorHLSL.h" -#include "compiler/translator/DetectDiscontinuity.h" #include "compiler/translator/FlagStd140Structs.h" #include "compiler/translator/InfoSink.h" #include "compiler/translator/NodeSearch.h" #include "compiler/translator/RemoveSwitchFallThrough.h" -#include "compiler/translator/RewriteElseBlocks.h" #include "compiler/translator/SearchSymbol.h" #include "compiler/translator/StructureHLSL.h" #include "compiler/translator/TranslatorHLSL.h" -#include "compiler/translator/UnfoldShortCircuit.h" #include "compiler/translator/UniformHLSL.h" #include "compiler/translator/UtilsHLSL.h" #include "compiler/translator/blocklayout.h" -#include "compiler/translator/compilerdebug.h" #include "compiler/translator/util.h" -namespace sh +namespace { -TString OutputHLSL::TextureFunction::name() const +bool IsSequence(TIntermNode *node) { - TString name = "gl_texture"; + return node->getAsAggregate() != nullptr && node->getAsAggregate()->getOp() == EOpSequence; +} - if (IsSampler2D(sampler)) - { - name += "2D"; - } - else if (IsSampler3D(sampler)) - { - name += "3D"; +void 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())); + break; + case EbtInt: + out << constUnion->getIConst(); + break; + case EbtUInt: + out << constUnion->getUConst(); + break; + case EbtBool: + out << constUnion->getBConst(); + break; + default: + UNREACHABLE(); } - else if (IsSamplerCube(sampler)) +} + +const TConstantUnion *WriteConstantUnionArray(TInfoSinkBase &out, + const TConstantUnion *const constUnion, + const size_t size) +{ + const TConstantUnion *constUnionIterated = constUnion; + for (size_t i = 0; i < size; i++, constUnionIterated++) { - name += "Cube"; + WriteSingleConstant(out, constUnionIterated); + + if (i != size - 1) + { + out << ", "; + } } - else UNREACHABLE(); + 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) { @@ -108,10 +144,10 @@ OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion, mExtensionBehavior(extensionBehavior), mSourcePath(sourcePath), mOutputType(outputType), + mCompileOptions(compileOptions), mNumRenderTargets(numRenderTargets), - mCompileOptions(compileOptions) + mCurrentFunctionMetadata(nullptr) { - mUnfoldShortCircuit = new UnfoldShortCircuit(this); mInsideFunction = false; mUsesFragColor = false; @@ -130,8 +166,6 @@ OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion, mUniqueIndex = 0; - mContainsLoopDiscontinuity = false; - mContainsAnyLoop = false; mOutputLod0Function = false; mInsideDiscontinuousLoop = false; mNestedLoopDepth = 0; @@ -141,7 +175,7 @@ OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion, mStructureHLSL = new StructureHLSL; mUniformHLSL = new UniformHLSL(mStructureHLSL, outputType, uniforms); - if (mOutputType == SH_HLSL9_OUTPUT) + 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. @@ -155,37 +189,33 @@ OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion, OutputHLSL::~OutputHLSL() { - SafeDelete(mUnfoldShortCircuit); SafeDelete(mStructureHLSL); SafeDelete(mUniformHLSL); - for (auto it = mStructEqualityFunctions.begin(); it != mStructEqualityFunctions.end(); ++it) + for (auto &eqFunction : mStructEqualityFunctions) { - SafeDelete(*it); + SafeDelete(eqFunction); } - for (auto it = mArrayEqualityFunctions.begin(); it != mArrayEqualityFunctions.end(); ++it) + for (auto &eqFunction : mArrayEqualityFunctions) { - SafeDelete(*it); + SafeDelete(eqFunction); } } void OutputHLSL::output(TIntermNode *treeRoot, TInfoSinkBase &objSink) { - mContainsLoopDiscontinuity = mShaderType == GL_FRAGMENT_SHADER && containsLoopDiscontinuity(treeRoot); - mContainsAnyLoop = containsAnyLoop(treeRoot); const std::vector<TIntermTyped*> &flaggedStructs = FlagStd140ValueStructs(treeRoot); makeFlaggedStructMaps(flaggedStructs); - // Work around D3D9 bug that would manifest in vertex shaders with selection blocks which - // use a vertex attribute as a condition, and some related computation in the else block. - if (mOutputType == SH_HLSL9_OUTPUT && mShaderType == GL_VERTEX_SHADER) - { - RewriteElseBlocks(treeRoot); - } - BuiltInFunctionEmulator builtInFunctionEmulator; InitBuiltInFunctionEmulatorForHLSL(&builtInFunctionEmulator); builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(treeRoot); + // Now that we are done changing the AST, do the analyses need for HLSL generation + CallDAG::InitResult success = mCallDag.init(treeRoot, &objSink); + ASSERT(success == CallDAG::INITDAG_SUCCESS); + UNUSED_ASSERTION_VARIABLE(success); + mASTMetadataList = CreateASTMetadataHLSL(treeRoot, mCallDag); + // Output the body and footer first to determine what has to go in the header mInfoSinkStack.push(&mBody); treeRoot->traverse(this); @@ -199,7 +229,7 @@ void OutputHLSL::output(TIntermNode *treeRoot, TInfoSinkBase &objSink) mInfoSinkStack.pop(); mInfoSinkStack.push(&mHeader); - header(&builtInFunctionEmulator); + header(mHeader, &builtInFunctionEmulator); mInfoSinkStack.pop(); objSink << mHeader.c_str(); @@ -294,10 +324,8 @@ TString OutputHLSL::structInitializerString(int indent, const TStructure &struct return init; } -void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) +void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *builtInFunctionEmulator) { - TInfoSinkBase &out = getInfoSink(); - TString varyings; TString attributes; TString flaggedStructs; @@ -334,23 +362,31 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << mStructureHLSL->structsHeader(); - out << mUniformHLSL->uniformsHeader(mOutputType, mReferencedUniforms); + mUniformHLSL->uniformsHeader(out, mOutputType, mReferencedUniforms); out << mUniformHLSL->interfaceBlocksHeader(mReferencedInterfaceBlocks); if (!mEqualityFunctions.empty()) { out << "\n// Equality functions\n\n"; - for (auto it = mEqualityFunctions.cbegin(); it != mEqualityFunctions.cend(); ++it) + for (const auto &eqFunction : mEqualityFunctions) { - out << (*it)->functionDefinition << "\n"; + out << eqFunction->functionDefinition << "\n"; } } if (!mArrayAssignmentFunctions.empty()) { out << "\n// Assignment functions\n\n"; - for (auto it = mArrayAssignmentFunctions.cbegin(); it != mArrayAssignmentFunctions.cend(); ++it) + for (const auto &assignmentFunction : mArrayAssignmentFunctions) { - out << it->functionDefinition << "\n"; + out << assignmentFunction.functionDefinition << "\n"; + } + } + if (!mArrayConstructIntoFunctions.empty()) + { + out << "\n// Array constructor functions\n\n"; + for (const auto &constructIntoFunction : mArrayConstructIntoFunctions) + { + out << constructIntoFunction.functionDefinition << "\n"; } } @@ -449,7 +485,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) "\n"; } - if (mOutputType == SH_HLSL11_OUTPUT) + if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) { out << "cbuffer DriverConstants : register(b1)\n" "{\n"; @@ -469,6 +505,13 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " float3 dx_DepthFront : packoffset(c2);\n"; } + if (mUsesFragCoord) + { + // dx_ViewScale is only used in the fragment shader to correct + // the value for glFragCoord if necessary + out << " float2 dx_ViewScale : packoffset(c3);\n"; + } + out << "};\n"; } else @@ -553,7 +596,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) "\n"; } - if (mOutputType == SH_HLSL11_OUTPUT) + if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) { out << "cbuffer DriverConstants : register(b1)\n" "{\n"; @@ -563,11 +606,13 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " float3 dx_DepthRange : packoffset(c0);\n"; } - // dx_ViewAdjust and dx_ViewCoords will only be used in Feature Level 9 shaders. - // However, we declare it for all shaders (including Feature Level 10+). - // The bytecode is the same whether we declare it or not, since D3DCompiler removes it if it's unused. + // dx_ViewAdjust and dx_ViewCoords will only be used in Feature Level 9 + // shaders. However, we declare it for all shaders (including Feature Level 10+). + // The bytecode is the same whether we declare it or not, since D3DCompiler removes it + // if it's unused. out << " float4 dx_ViewAdjust : packoffset(c1);\n"; out << " float2 dx_ViewCoords : packoffset(c2);\n"; + out << " float2 dx_ViewScale : packoffset(c3);\n"; out << "};\n" "\n"; @@ -653,7 +698,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) // Argument list int hlslCoords = 4; - if (mOutputType == SH_HLSL9_OUTPUT) + if (mOutputType == SH_HLSL_3_0_OUTPUT) { switch(textureFunction->sampler) { @@ -672,29 +717,20 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) default: UNREACHABLE(); } } - else if (mOutputType == SH_HLSL11_OUTPUT) + else { - switch(textureFunction->sampler) + hlslCoords = HLSLTextureCoordsCount(textureFunction->sampler); + if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) { - case EbtSampler2D: out << "Texture2D x, SamplerState s"; hlslCoords = 2; break; - case EbtSampler3D: out << "Texture3D x, SamplerState s"; hlslCoords = 3; break; - case EbtSamplerCube: out << "TextureCube x, SamplerState s"; hlslCoords = 3; break; - case EbtSampler2DArray: out << "Texture2DArray x, SamplerState s"; hlslCoords = 3; break; - case EbtISampler2D: out << "Texture2D<int4> x, SamplerState s"; hlslCoords = 2; break; - case EbtISampler3D: out << "Texture3D<int4> x, SamplerState s"; hlslCoords = 3; break; - case EbtISamplerCube: out << "Texture2DArray<int4> x, SamplerState s"; hlslCoords = 3; break; - case EbtISampler2DArray: out << "Texture2DArray<int4> x, SamplerState s"; hlslCoords = 3; break; - case EbtUSampler2D: out << "Texture2D<uint4> x, SamplerState s"; hlslCoords = 2; break; - case EbtUSampler3D: out << "Texture3D<uint4> x, SamplerState s"; hlslCoords = 3; break; - case EbtUSamplerCube: out << "Texture2DArray<uint4> x, SamplerState s"; hlslCoords = 3; break; - case EbtUSampler2DArray: out << "Texture2DArray<uint4> x, SamplerState s"; hlslCoords = 3; break; - case EbtSampler2DShadow: out << "Texture2D x, SamplerComparisonState s"; hlslCoords = 2; break; - case EbtSamplerCubeShadow: out << "TextureCube x, SamplerComparisonState s"; hlslCoords = 3; break; - case EbtSampler2DArrayShadow: out << "Texture2DArray x, SamplerComparisonState s"; hlslCoords = 3; break; - default: UNREACHABLE(); + out << TextureString(textureFunction->sampler) << " x, " + << SamplerString(textureFunction->sampler) << " s"; + } + else + { + ASSERT(mOutputType == SH_HLSL_4_1_OUTPUT); + out << "const uint samplerIndex"; } } - else UNREACHABLE(); if (textureFunction->method == TextureFunction::FETCH) // Integer coordinates { @@ -785,6 +821,31 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) 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) + { + 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]"; + } + } + if (textureFunction->method == TextureFunction::SIZE) { if (IsSampler2D(textureFunction->sampler) || IsSamplerCube(textureFunction->sampler)) @@ -792,18 +853,21 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) if (IsSamplerArray(textureFunction->sampler)) { out << " uint width; uint height; uint layers; uint numberOfLevels;\n" - " x.GetDimensions(lod, width, height, layers, numberOfLevels);\n"; + << " " << textureReference + << ".GetDimensions(lod, width, height, layers, numberOfLevels);\n"; } else { out << " uint width; uint height; uint numberOfLevels;\n" - " x.GetDimensions(lod, width, height, numberOfLevels);\n"; + << " " << textureReference + << ".GetDimensions(lod, width, height, numberOfLevels);\n"; } } else if (IsSampler3D(textureFunction->sampler)) { out << " uint width; uint height; uint depth; uint numberOfLevels;\n" - " x.GetDimensions(lod, width, height, depth, numberOfLevels);\n"; + << " " << textureReference + << ".GetDimensions(lod, width, height, depth, numberOfLevels);\n"; } else UNREACHABLE(); @@ -835,7 +899,8 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " uint mip = 0;\n"; - out << " x.GetDimensions(mip, width, height, layers, levels);\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"; @@ -856,6 +921,18 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) 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) @@ -876,11 +953,13 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) } else { + + out << " " << textureReference + << ".GetDimensions(0, width, height, layers, levels);\n"; if (textureFunction->method == TextureFunction::IMPLICIT || textureFunction->method == TextureFunction::BIAS) { - out << " x.GetDimensions(0, width, height, layers, levels);\n" - " float2 tSized = float2(t.x * width, t.y * height);\n" + 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"; @@ -892,14 +971,14 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) } else if (textureFunction->method == TextureFunction::GRAD) { - out << " x.GetDimensions(0, width, height, layers, levels);\n" - " float lod = log2(max(length(ddx), length(ddy)));\n"; + out << " float lod = log2(max(length(ddx), length(ddy)));\n"; } out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; } - out << " x.GetDimensions(mip, width, height, layers, levels);\n"; + out << " " << textureReference + << ".GetDimensions(mip, width, height, layers, levels);\n"; } else { @@ -915,11 +994,13 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) } else { + out << " " << textureReference + << ".GetDimensions(0, width, height, levels);\n"; + if (textureFunction->method == TextureFunction::IMPLICIT || textureFunction->method == TextureFunction::BIAS) { - out << " x.GetDimensions(0, width, height, levels);\n" - " float2 tSized = float2(t.x * width, t.y * height);\n" + 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"; @@ -929,20 +1010,16 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " lod += bias;\n"; } } - else if (textureFunction->method == TextureFunction::LOD) - { - out << " x.GetDimensions(0, width, height, levels);\n"; - } else if (textureFunction->method == TextureFunction::GRAD) { - out << " x.GetDimensions(0, width, height, levels);\n" - " float lod = log2(max(length(ddx), length(ddy)));\n"; + out << " float lod = log2(max(length(ddx), length(ddy)));\n"; } out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; } - out << " x.GetDimensions(mip, width, height, levels);\n"; + out << " " << textureReference + << ".GetDimensions(mip, width, height, levels);\n"; } } else if (IsSampler3D(textureFunction->sampler)) @@ -959,11 +1036,14 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) } else { + out << " " << textureReference + << ".GetDimensions(0, width, height, depth, levels);\n"; + if (textureFunction->method == TextureFunction::IMPLICIT || textureFunction->method == TextureFunction::BIAS) { - out << " x.GetDimensions(0, width, height, depth, levels);\n" - " float3 tSized = float3(t.x * width, t.y * height, t.z * depth);\n" + 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"; @@ -975,14 +1055,14 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) } else if (textureFunction->method == TextureFunction::GRAD) { - out << " x.GetDimensions(0, width, height, depth, levels);\n" - " float lod = log2(max(length(ddx), length(ddy)));\n"; + out << " float lod = log2(max(length(ddx), length(ddy)));\n"; } out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; } - out << " x.GetDimensions(mip, width, height, depth, levels);\n"; + out << " " << textureReference + << ".GetDimensions(mip, width, height, depth, levels);\n"; } else UNREACHABLE(); } @@ -990,7 +1070,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " return "; // HLSL intrinsic - if (mOutputType == SH_HLSL9_OUTPUT) + if (mOutputType == SH_HLSL_3_0_OUTPUT) { switch(textureFunction->sampler) { @@ -1001,45 +1081,71 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) switch(textureFunction->method) { - case TextureFunction::IMPLICIT: out << "(s, "; break; - case TextureFunction::BIAS: out << "bias(s, "; break; - case TextureFunction::LOD: out << "lod(s, "; break; - case TextureFunction::LOD0: out << "lod(s, "; break; - case TextureFunction::LOD0BIAS: out << "lod(s, "; break; + 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_HLSL11_OUTPUT) + 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 << "x.Load("; + out << "" << textureReference << ".Load("; } else if (IsShadowSampler(textureFunction->sampler)) { - out << "x.SampleCmpLevelZero(s, "; + out << "" << textureReference << ".SampleCmpLevelZero(" << samplerReference + << ", "; } else { - out << "x.SampleGrad(s, "; + out << "" << textureReference << ".SampleGrad(" << samplerReference << ", "; } } else if (IsIntegerSampler(textureFunction->sampler) || textureFunction->method == TextureFunction::FETCH) { - out << "x.Load("; + out << "" << textureReference << ".Load("; } else if (IsShadowSampler(textureFunction->sampler)) { switch(textureFunction->method) { - case TextureFunction::IMPLICIT: out << "x.SampleCmp(s, "; break; - case TextureFunction::BIAS: out << "x.SampleCmp(s, "; break; - case TextureFunction::LOD: out << "x.SampleCmp(s, "; break; - case TextureFunction::LOD0: out << "x.SampleCmpLevelZero(s, "; break; - case TextureFunction::LOD0BIAS: out << "x.SampleCmpLevelZero(s, "; break; + 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(); } } @@ -1047,11 +1153,25 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) { switch(textureFunction->method) { - case TextureFunction::IMPLICIT: out << "x.Sample(s, "; break; - case TextureFunction::BIAS: out << "x.SampleBias(s, "; break; - case TextureFunction::LOD: out << "x.SampleLevel(s, "; break; - case TextureFunction::LOD0: out << "x.SampleLevel(s, "; break; - case TextureFunction::LOD0BIAS: out << "x.SampleLevel(s, "; break; + 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(); } } @@ -1121,7 +1241,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << addressx + ("t.x" + proj) + close + ", " + addressy + ("t.y" + proj) + close; - if (mOutputType == SH_HLSL9_OUTPUT) + if (mOutputType == SH_HLSL_3_0_OUTPUT) { if (hlslCoords >= 3) { @@ -1149,7 +1269,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << "));\n"; } - else if (mOutputType == SH_HLSL11_OUTPUT) + else if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) { if (hlslCoords >= 3) { @@ -1311,8 +1431,8 @@ void OutputHLSL::visitSymbol(TIntermSymbol *node) if (qualifier == EvqUniform) { - const TType& nodeType = node->getType(); - const TInterfaceBlock* interfaceBlock = nodeType.getInterfaceBlock(); + const TType &nodeType = node->getType(); + const TInterfaceBlock *interfaceBlock = nodeType.getInterfaceBlock(); if (interfaceBlock) { @@ -1323,6 +1443,8 @@ void OutputHLSL::visitSymbol(TIntermSymbol *node) mReferencedUniforms[name] = node; } + ensureStructDefined(nodeType); + out << DecorateUniform(name, nodeType); } else if (qualifier == EvqAttribute || qualifier == EvqVertexIn) @@ -1375,18 +1497,14 @@ void OutputHLSL::visitSymbol(TIntermSymbol *node) mUsesInstanceID = true; out << name; } - else if (name == "gl_FragDepthEXT") + else if (name == "gl_FragDepthEXT" || name == "gl_FragDepth") { mUsesFragDepth = true; out << "gl_Depth"; } - else if (qualifier == EvqInternal) - { - out << name; - } else { - out << Decorate(name); + out << DecorateIfNeeded(node->getName()); } } } @@ -1402,11 +1520,11 @@ void OutputHLSL::outputEqual(Visit visit, const TType &type, TOperator op, TInfo { if (op == EOpEqual) { - outputTriplet(visit, "(", " == ", ")", out); + outputTriplet(out, visit, "(", " == ", ")"); } else { - outputTriplet(visit, "(", " != ", ")", out); + outputTriplet(out, visit, "(", " != ", ")"); } } else @@ -1419,18 +1537,18 @@ void OutputHLSL::outputEqual(Visit visit, const TType &type, TOperator op, TInfo if (type.isArray()) { const TString &functionName = addArrayEqualityFunction(type); - outputTriplet(visit, (functionName + "(").c_str(), ", ", ")", out); + outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")"); } else if (type.getBasicType() == EbtStruct) { const TStructure &structure = *type.getStruct(); const TString &functionName = addStructEqualityFunction(structure); - outputTriplet(visit, (functionName + "(").c_str(), ", ", ")", out); + outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")"); } else { ASSERT(type.isMatrix() || type.isVector()); - outputTriplet(visit, "all(", " == ", ")", out); + outputTriplet(out, visit, "all(", " == ", ")"); } } } @@ -1451,12 +1569,30 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) case EOpAssign: if (node->getLeft()->isArray()) { + TIntermAggregate *rightAgg = node->getRight()->getAsAggregate(); + if (rightAgg != nullptr && rightAgg->isConstructor()) + { + const TString &functionName = addArrayConstructIntoFunction(node->getType()); + out << functionName << "("; + node->getLeft()->traverse(this); + TIntermSequence *seq = rightAgg->getSequence(); + for (auto &arrayElement : *seq) + { + out << ", "; + arrayElement->traverse(this); + } + out << ")"; + return false; + } + // ArrayReturnValueToOutParameter should have eliminated expressions where a function call is assigned. + ASSERT(rightAgg == nullptr || rightAgg->getOp() != EOpFunctionCall); + const TString &functionName = addArrayAssignmentFunction(node->getType()); - outputTriplet(visit, (functionName + "(").c_str(), ", ", ")"); + outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")"); } else { - outputTriplet(visit, "(", " = ", ")"); + outputTriplet(out, visit, "(", " = ", ")"); } break; case EOpInitialize: @@ -1475,8 +1611,12 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) if (symbolNode->getQualifier() == EvqGlobal && expression->getQualifier() != EvqConst) { // For variables which are not constant, defer their real initialization until - // after we initialize other globals: uniforms, attributes and varyings. - mDeferredGlobalInitializers.push_back(std::make_pair(symbolNode, expression)); + // 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)); } @@ -1485,17 +1625,31 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) // Skip initializing the rest of the expression return false; } + else if (writeConstantInitialization(out, symbolNode, expression)) + { + return false; + } } else if (visit == InVisit) { out << " = "; } break; - case EOpAddAssign: outputTriplet(visit, "(", " += ", ")"); break; - case EOpSubAssign: outputTriplet(visit, "(", " -= ", ")"); break; - case EOpMulAssign: outputTriplet(visit, "(", " *= ", ")"); break; - case EOpVectorTimesScalarAssign: outputTriplet(visit, "(", " *= ", ")"); break; - case EOpMatrixTimesScalarAssign: outputTriplet(visit, "(", " *= ", ")"); 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) { @@ -1528,13 +1682,27 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) out << "))))"; } break; - case EOpDivAssign: outputTriplet(visit, "(", " /= ", ")"); break; - case EOpIModAssign: outputTriplet(visit, "(", " %= ", ")"); break; - case EOpBitShiftLeftAssign: outputTriplet(visit, "(", " <<= ", ")"); break; - case EOpBitShiftRightAssign: outputTriplet(visit, "(", " >>= ", ")"); break; - case EOpBitwiseAndAssign: outputTriplet(visit, "(", " &= ", ")"); break; - case EOpBitwiseXorAssign: outputTriplet(visit, "(", " ^= ", ")"); break; - case EOpBitwiseOrAssign: outputTriplet(visit, "(", " |= ", ")"); 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(); @@ -1551,14 +1719,14 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) } else { - outputTriplet(visit, "", "[", "]"); + outputTriplet(out, visit, "", "[", "]"); } } break; case EOpIndexIndirect: // We do not currently support indirect references to interface blocks ASSERT(node->getLeft()->getBasicType() != EbtInterfaceBlock); - outputTriplet(visit, "", "[", "]"); + outputTriplet(out, visit, "", "[", "]"); break; case EOpIndexDirectStruct: if (visit == InVisit) @@ -1618,55 +1786,81 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) return false; // Fully processed } break; - case EOpAdd: outputTriplet(visit, "(", " + ", ")"); break; - case EOpSub: outputTriplet(visit, "(", " - ", ")"); break; - case EOpMul: outputTriplet(visit, "(", " * ", ")"); break; - case EOpDiv: outputTriplet(visit, "(", " / ", ")"); break; - case EOpIMod: outputTriplet(visit, "(", " % ", ")"); break; - case EOpBitShiftLeft: outputTriplet(visit, "(", " << ", ")"); break; - case EOpBitShiftRight: outputTriplet(visit, "(", " >> ", ")"); break; - case EOpBitwiseAnd: outputTriplet(visit, "(", " & ", ")"); break; - case EOpBitwiseXor: outputTriplet(visit, "(", " ^ ", ")"); break; - case EOpBitwiseOr: outputTriplet(visit, "(", " | ", ")"); 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(visit, "(", " < ", ")"); break; - case EOpGreaterThan: outputTriplet(visit, "(", " > ", ")"); break; - case EOpLessThanEqual: outputTriplet(visit, "(", " <= ", ")"); break; - case EOpGreaterThanEqual: outputTriplet(visit, "(", " >= ", ")"); break; - case EOpVectorTimesScalar: outputTriplet(visit, "(", " * ", ")"); break; - case EOpMatrixTimesScalar: outputTriplet(visit, "(", " * ", ")"); break; - case EOpVectorTimesMatrix: outputTriplet(visit, "mul(", ", transpose(", "))"); break; - case EOpMatrixTimesVector: outputTriplet(visit, "mul(transpose(", "), ", ")"); break; - case EOpMatrixTimesMatrix: outputTriplet(visit, "transpose(mul(transpose(", "), transpose(", ")))"); 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: - if (node->getRight()->hasSideEffects()) - { - out << "s" << mUnfoldShortCircuit->getNextTemporaryIndex(); - return false; - } - else - { - outputTriplet(visit, "(", " || ", ")"); - return true; - } + // 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(visit, "xor(", ", ", ")"); + outputTriplet(out, visit, "xor(", ", ", ")"); break; case EOpLogicalAnd: - if (node->getRight()->hasSideEffects()) - { - out << "s" << mUnfoldShortCircuit->getNextTemporaryIndex(); - return false; - } - else - { - outputTriplet(visit, "(", " && ", ")"); - return true; - } + // 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(); } @@ -1675,131 +1869,221 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) bool OutputHLSL::visitUnary(Visit visit, TIntermUnary *node) { + TInfoSinkBase &out = getInfoSink(); + switch (node->getOp()) { - case EOpNegative: outputTriplet(visit, "(-", "", ")"); break; - case EOpPositive: outputTriplet(visit, "(+", "", ")"); break; - case EOpVectorLogicalNot: outputTriplet(visit, "(!", "", ")"); break; - case EOpLogicalNot: outputTriplet(visit, "(!", "", ")"); break; - case EOpBitwiseNot: outputTriplet(visit, "(~", "", ")"); break; - case EOpPostIncrement: outputTriplet(visit, "(", "", "++)"); break; - case EOpPostDecrement: outputTriplet(visit, "(", "", "--)"); break; - case EOpPreIncrement: outputTriplet(visit, "(++", "", ")"); break; - case EOpPreDecrement: outputTriplet(visit, "(--", "", ")"); break; - case EOpRadians: outputTriplet(visit, "radians(", "", ")"); break; - case EOpDegrees: outputTriplet(visit, "degrees(", "", ")"); break; - case EOpSin: outputTriplet(visit, "sin(", "", ")"); break; - case EOpCos: outputTriplet(visit, "cos(", "", ")"); break; - case EOpTan: outputTriplet(visit, "tan(", "", ")"); break; - case EOpAsin: outputTriplet(visit, "asin(", "", ")"); break; - case EOpAcos: outputTriplet(visit, "acos(", "", ")"); break; - case EOpAtan: outputTriplet(visit, "atan(", "", ")"); break; - case EOpSinh: outputTriplet(visit, "sinh(", "", ")"); break; - case EOpCosh: outputTriplet(visit, "cosh(", "", ")"); break; - case EOpTanh: outputTriplet(visit, "tanh(", "", ")"); break; + case EOpNegative: + outputTriplet(out, visit, "(-", "", ")"); + break; + case EOpPositive: + outputTriplet(out, visit, "(+", "", ")"); + break; + case EOpVectorLogicalNot: + outputTriplet(out, visit, "(!", "", ")"); + break; + case EOpLogicalNot: + outputTriplet(out, visit, "(!", "", ")"); + break; + case EOpBitwiseNot: + outputTriplet(out, visit, "(~", "", ")"); + break; + case EOpPostIncrement: + outputTriplet(out, visit, "(", "", "++)"); + break; + case EOpPostDecrement: + outputTriplet(out, visit, "(", "", "--)"); + break; + case EOpPreIncrement: + outputTriplet(out, visit, "(++", "", ")"); + break; + case EOpPreDecrement: + outputTriplet(out, visit, "(--", "", ")"); + break; + case EOpRadians: + outputTriplet(out, visit, "radians(", "", ")"); + break; + case EOpDegrees: + outputTriplet(out, visit, "degrees(", "", ")"); + break; + case EOpSin: + outputTriplet(out, visit, "sin(", "", ")"); + break; + case EOpCos: + outputTriplet(out, visit, "cos(", "", ")"); + break; + case EOpTan: + outputTriplet(out, visit, "tan(", "", ")"); + break; + case EOpAsin: + outputTriplet(out, visit, "asin(", "", ")"); + break; + case EOpAcos: + outputTriplet(out, visit, "acos(", "", ")"); + break; + case EOpAtan: + outputTriplet(out, visit, "atan(", "", ")"); + break; + case EOpSinh: + outputTriplet(out, visit, "sinh(", "", ")"); + break; + case EOpCosh: + outputTriplet(out, visit, "cosh(", "", ")"); + break; + case EOpTanh: + outputTriplet(out, visit, "tanh(", "", ")"); + break; case EOpAsinh: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "asinh("); + writeEmulatedFunctionTriplet(out, visit, "asinh("); break; case EOpAcosh: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "acosh("); + writeEmulatedFunctionTriplet(out, visit, "acosh("); break; case EOpAtanh: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "atanh("); + writeEmulatedFunctionTriplet(out, visit, "atanh("); break; - case EOpExp: outputTriplet(visit, "exp(", "", ")"); break; - case EOpLog: outputTriplet(visit, "log(", "", ")"); break; - case EOpExp2: outputTriplet(visit, "exp2(", "", ")"); break; - case EOpLog2: outputTriplet(visit, "log2(", "", ")"); break; - case EOpSqrt: outputTriplet(visit, "sqrt(", "", ")"); break; - case EOpInverseSqrt: outputTriplet(visit, "rsqrt(", "", ")"); break; - case EOpAbs: outputTriplet(visit, "abs(", "", ")"); break; - case EOpSign: outputTriplet(visit, "sign(", "", ")"); break; - case EOpFloor: outputTriplet(visit, "floor(", "", ")"); break; - case EOpTrunc: outputTriplet(visit, "trunc(", "", ")"); break; - case EOpRound: outputTriplet(visit, "round(", "", ")"); 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(visit, "roundEven("); + writeEmulatedFunctionTriplet(out, visit, "roundEven("); break; - case EOpCeil: outputTriplet(visit, "ceil(", "", ")"); break; - case EOpFract: outputTriplet(visit, "frac(", "", ")"); break; + case EOpCeil: + outputTriplet(out, visit, "ceil(", "", ")"); + break; + case EOpFract: + outputTriplet(out, visit, "frac(", "", ")"); + break; case EOpIsNan: - outputTriplet(visit, "isnan(", "", ")"); + outputTriplet(out, visit, "isnan(", "", ")"); mRequiresIEEEStrictCompiling = true; break; - case EOpIsInf: outputTriplet(visit, "isinf(", "", ")"); break; - case EOpFloatBitsToInt: outputTriplet(visit, "asint(", "", ")"); break; - case EOpFloatBitsToUint: outputTriplet(visit, "asuint(", "", ")"); break; - case EOpIntBitsToFloat: outputTriplet(visit, "asfloat(", "", ")"); break; - case EOpUintBitsToFloat: outputTriplet(visit, "asfloat(", "", ")"); 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(visit, "packSnorm2x16("); + writeEmulatedFunctionTriplet(out, visit, "packSnorm2x16("); break; case EOpPackUnorm2x16: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "packUnorm2x16("); + writeEmulatedFunctionTriplet(out, visit, "packUnorm2x16("); break; case EOpPackHalf2x16: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "packHalf2x16("); + writeEmulatedFunctionTriplet(out, visit, "packHalf2x16("); break; case EOpUnpackSnorm2x16: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "unpackSnorm2x16("); + writeEmulatedFunctionTriplet(out, visit, "unpackSnorm2x16("); break; case EOpUnpackUnorm2x16: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "unpackUnorm2x16("); + writeEmulatedFunctionTriplet(out, visit, "unpackUnorm2x16("); break; case EOpUnpackHalf2x16: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "unpackHalf2x16("); + writeEmulatedFunctionTriplet(out, visit, "unpackHalf2x16("); break; - case EOpLength: outputTriplet(visit, "length(", "", ")"); break; - case EOpNormalize: outputTriplet(visit, "normalize(", "", ")"); break; + case EOpLength: + outputTriplet(out, visit, "length(", "", ")"); + break; + case EOpNormalize: + outputTriplet(out, visit, "normalize(", "", ")"); + break; case EOpDFdx: if(mInsideDiscontinuousLoop || mOutputLod0Function) { - outputTriplet(visit, "(", "", ", 0.0)"); + outputTriplet(out, visit, "(", "", ", 0.0)"); } else { - outputTriplet(visit, "ddx(", "", ")"); + outputTriplet(out, visit, "ddx(", "", ")"); } break; case EOpDFdy: if(mInsideDiscontinuousLoop || mOutputLod0Function) { - outputTriplet(visit, "(", "", ", 0.0)"); + outputTriplet(out, visit, "(", "", ", 0.0)"); } else { - outputTriplet(visit, "ddy(", "", ")"); + outputTriplet(out, visit, "ddy(", "", ")"); } break; case EOpFwidth: if(mInsideDiscontinuousLoop || mOutputLod0Function) { - outputTriplet(visit, "(", "", ", 0.0)"); + outputTriplet(out, visit, "(", "", ", 0.0)"); } else { - outputTriplet(visit, "fwidth(", "", ")"); + outputTriplet(out, visit, "fwidth(", "", ")"); } break; - case EOpTranspose: outputTriplet(visit, "transpose(", "", ")"); break; - case EOpDeterminant: outputTriplet(visit, "determinant(transpose(", "", "))"); break; + case EOpTranspose: + outputTriplet(out, visit, "transpose(", "", ")"); + break; + case EOpDeterminant: + outputTriplet(out, visit, "determinant(transpose(", "", "))"); + break; case EOpInverse: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "inverse("); + writeEmulatedFunctionTriplet(out, visit, "inverse("); break; - case EOpAny: outputTriplet(visit, "any(", "", ")"); break; - case EOpAll: outputTriplet(visit, "all(", "", ")"); break; + case EOpAny: + outputTriplet(out, visit, "any(", "", ")"); + break; + case EOpAll: + outputTriplet(out, visit, "all(", "", ")"); + break; default: UNREACHABLE(); } @@ -1816,26 +2100,30 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) { if (mInsideFunction) { - outputLineDirective(node->getLine().first_line); + outputLineDirective(out, node->getLine().first_line); out << "{\n"; } for (TIntermSequence::iterator sit = node->getSequence()->begin(); sit != node->getSequence()->end(); sit++) { - outputLineDirective((*sit)->getLine().first_line); + outputLineDirective(out, (*sit)->getLine().first_line); - traverseStatements(*sit); + (*sit)->traverse(this); // Don't output ; after case labels, they're terminated by : // This is needed especially since outputting a ; after a case statement would turn empty // case statements into non-empty case statements, disallowing fall-through from them. - if ((*sit)->getAsCaseNode() == nullptr) + // 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"; } if (mInsideFunction) { - outputLineDirective(node->getLine().last_line); + outputLineDirective(out, node->getLine().last_line); out << "}\n"; } @@ -1846,50 +2134,34 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) { TIntermSequence *sequence = node->getSequence(); TIntermTyped *variable = (*sequence)[0]->getAsTyped(); + ASSERT(sequence->size() == 1); - if (variable && (variable->getQualifier() == EvqTemporary || variable->getQualifier() == EvqGlobal)) + if (variable && + (variable->getQualifier() == EvqTemporary || + variable->getQualifier() == EvqGlobal || variable->getQualifier() == EvqConst)) { - TStructure *structure = variable->getType().getStruct(); - - if (structure) - { - mStructureHLSL->addConstructor(variable->getType(), StructNameString(*structure), NULL); - } + ensureStructDefined(variable->getType()); if (!variable->getAsSymbolNode() || variable->getAsSymbolNode()->getSymbol() != "") // Variable declaration { - for (auto it = sequence->cbegin(); it != sequence->cend(); ++it) + if (!mInsideFunction) { - const auto &seqElement = *it; - if (isSingleStatement(seqElement)) - { - mUnfoldShortCircuit->traverse(seqElement); - } - - if (!mInsideFunction) - { - out << "static "; - } + out << "static "; + } - out << TypeString(variable->getType()) + " "; + out << TypeString(variable->getType()) + " "; - TIntermSymbol *symbol = seqElement->getAsSymbolNode(); + TIntermSymbol *symbol = variable->getAsSymbolNode(); - if (symbol) - { - symbol->traverse(this); - out << ArrayString(symbol->getType()); - out << " = " + initializer(symbol->getType()); - } - else - { - seqElement->traverse(this); - } - - if (seqElement != sequence->back()) - { - out << ";\n"; - } + 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 @@ -1929,7 +2201,16 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) case EOpPrototype: if (visit == PreVisit) { - out << TypeString(node->getType()) << " " << Decorate(TFunction::unmangleName(node->getName())) << (mOutputLod0Function ? "Lod0(" : "("); + size_t index = mCallDag.findIndex(node); + // Skip the prototype if it is not implemented (and thus not used) + if (index == CallDAG::InvalidIndex) + { + return false; + } + + TString name = DecorateFunctionIfNeeded(node->getNameObj()); + out << TypeString(node->getType()) << " " << name + << (mOutputLod0Function ? "Lod0(" : "("); TIntermSequence *arguments = node->getSequence(); @@ -1952,7 +2233,8 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) out << ");\n"; // Also prototype the Lod0 variant if needed - if (mContainsLoopDiscontinuity && !mOutputLod0Function) + bool needsLod0 = mASTMetadataList[index].mNeedsLod0; + if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER) { mOutputLod0Function = true; node->traverse(this); @@ -1962,10 +2244,17 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) return false; } break; - case EOpComma: outputTriplet(visit, "(", ", ", ")"); break; + case EOpComma: + outputTriplet(out, visit, "(", ", ", ")"); + break; case EOpFunction: { - TString name = TFunction::unmangleName(node->getName()); + ASSERT(mCurrentFunctionMetadata == nullptr); + TString name = TFunction::unmangleName(node->getNameObj().getString()); + + size_t index = mCallDag.findIndex(node); + ASSERT(index != CallDAG::InvalidIndex); + mCurrentFunctionMetadata = &mASTMetadataList[index]; out << TypeString(node->getType()) << " "; @@ -1975,7 +2264,8 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) } else { - out << Decorate(name) << (mOutputLod0Function ? "Lod0(" : "("); + out << DecorateFunctionIfNeeded(node->getNameObj()) + << (mOutputLod0Function ? "Lod0(" : "("); } TIntermSequence *sequence = node->getSequence(); @@ -1987,12 +2277,7 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) if (symbol) { - TStructure *structure = symbol->getType().getStruct(); - - if (structure) - { - mStructureHLSL->addConstructor(symbol->getType(), StructNameString(*structure), NULL); - } + ensureStructDefined(symbol->getType()); out << argumentString(symbol); @@ -2004,26 +2289,31 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) else UNREACHABLE(); } - out << ")\n" - "{\n"; + out << ")\n"; if (sequence->size() > 1) { mInsideFunction = true; - (*sequence)[1]->traverse(this); + 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; - if (mContainsLoopDiscontinuity && !mOutputLod0Function) + bool needsLod0 = mASTMetadataList[index].mNeedsLod0; + if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER) { - if (name != "main") - { - mOutputLod0Function = true; - node->traverse(this); - mOutputLod0Function = false; - } + ASSERT(name != "main"); + mOutputLod0Function = true; + node->traverse(this); + mOutputLod0Function = false; } return false; @@ -2031,16 +2321,24 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) break; case EOpFunctionCall: { - TString name = TFunction::unmangleName(node->getName()); - bool lod0 = mInsideDiscontinuousLoop || mOutputLod0Function; TIntermSequence *arguments = node->getSequence(); + bool lod0 = mInsideDiscontinuousLoop || mOutputLod0Function; if (node->isUserDefined()) { - out << Decorate(name) << (lod0 ? "Lod0(" : "("); + if (node->isArray()) + { + UNIMPLEMENTED(); + } + size_t index = mCallDag.findIndex(node); + ASSERT(index != CallDAG::InvalidIndex); + lod0 &= mASTMetadataList[index].mNeedsLod0; + + out << DecorateFunctionIfNeeded(node->getNameObj()) << (lod0 ? "Lod0(" : "("); } else { + TString name = TFunction::unmangleName(node->getNameObj().getString()); TBasicType samplerType = (*arguments)[0]->getAsTyped()->getType().getBasicType(); TextureFunction textureFunction; @@ -2161,7 +2459,8 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) for (TIntermSequence::iterator arg = arguments->begin(); arg != arguments->end(); arg++) { - if (mOutputType == SH_HLSL11_OUTPUT && IsSampler((*arg)->getAsTyped()->getBasicType())) + if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT && + IsSampler((*arg)->getAsTyped()->getBasicType())) { out << "texture_"; (*arg)->traverse(this); @@ -2181,172 +2480,299 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) return false; } break; - case EOpParameters: outputTriplet(visit, "(", ", ", ")\n{\n"); break; - case EOpConstructFloat: outputConstructor(visit, node->getType(), "vec1", node->getSequence()); break; - case EOpConstructVec2: outputConstructor(visit, node->getType(), "vec2", node->getSequence()); break; - case EOpConstructVec3: outputConstructor(visit, node->getType(), "vec3", node->getSequence()); break; - case EOpConstructVec4: outputConstructor(visit, node->getType(), "vec4", node->getSequence()); break; - case EOpConstructBool: outputConstructor(visit, node->getType(), "bvec1", node->getSequence()); break; - case EOpConstructBVec2: outputConstructor(visit, node->getType(), "bvec2", node->getSequence()); break; - case EOpConstructBVec3: outputConstructor(visit, node->getType(), "bvec3", node->getSequence()); break; - case EOpConstructBVec4: outputConstructor(visit, node->getType(), "bvec4", node->getSequence()); break; - case EOpConstructInt: outputConstructor(visit, node->getType(), "ivec1", node->getSequence()); break; - case EOpConstructIVec2: outputConstructor(visit, node->getType(), "ivec2", node->getSequence()); break; - case EOpConstructIVec3: outputConstructor(visit, node->getType(), "ivec3", node->getSequence()); break; - case EOpConstructIVec4: outputConstructor(visit, node->getType(), "ivec4", node->getSequence()); break; - case EOpConstructUInt: outputConstructor(visit, node->getType(), "uvec1", node->getSequence()); break; - case EOpConstructUVec2: outputConstructor(visit, node->getType(), "uvec2", node->getSequence()); break; - case EOpConstructUVec3: outputConstructor(visit, node->getType(), "uvec3", node->getSequence()); break; - case EOpConstructUVec4: outputConstructor(visit, node->getType(), "uvec4", node->getSequence()); break; - case EOpConstructMat2: outputConstructor(visit, node->getType(), "mat2", node->getSequence()); break; - case EOpConstructMat3: outputConstructor(visit, node->getType(), "mat3", node->getSequence()); break; - case EOpConstructMat4: outputConstructor(visit, node->getType(), "mat4", node->getSequence()); 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()); + 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()); + break; + case EOpConstructMat3x2: + outputConstructor(out, visit, node->getType(), "mat3x2", node->getSequence()); + break; + case EOpConstructMat3: + outputConstructor(out, visit, node->getType(), "mat3", node->getSequence()); + break; + case EOpConstructMat3x4: + outputConstructor(out, visit, node->getType(), "mat3x4", node->getSequence()); + break; + case EOpConstructMat4x2: + outputConstructor(out, visit, node->getType(), "mat4x2", node->getSequence()); + break; + case EOpConstructMat4x3: + outputConstructor(out, visit, node->getType(), "mat4x3", node->getSequence()); + break; + case EOpConstructMat4: + outputConstructor(out, visit, node->getType(), "mat4", node->getSequence()); + break; case EOpConstructStruct: { + if (node->getType().isArray()) + { + UNIMPLEMENTED(); + } const TString &structName = StructNameString(*node->getType().getStruct()); mStructureHLSL->addConstructor(node->getType(), structName, node->getSequence()); - outputTriplet(visit, (structName + "_ctor(").c_str(), ", ", ")"); + outputTriplet(out, visit, (structName + "_ctor(").c_str(), ", ", ")"); } break; - case EOpLessThan: outputTriplet(visit, "(", " < ", ")"); break; - case EOpGreaterThan: outputTriplet(visit, "(", " > ", ")"); break; - case EOpLessThanEqual: outputTriplet(visit, "(", " <= ", ")"); break; - case EOpGreaterThanEqual: outputTriplet(visit, "(", " >= ", ")"); break; - case EOpVectorEqual: outputTriplet(visit, "(", " == ", ")"); break; - case EOpVectorNotEqual: outputTriplet(visit, "(", " != ", ")"); 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 EOpVectorEqual: + outputTriplet(out, visit, "(", " == ", ")"); + break; + case EOpVectorNotEqual: + outputTriplet(out, visit, "(", " != ", ")"); + break; case EOpMod: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "mod("); + writeEmulatedFunctionTriplet(out, visit, "mod("); break; - case EOpModf: outputTriplet(visit, "modf(", ", ", ")"); break; - case EOpPow: outputTriplet(visit, "pow(", ", ", ")"); 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(visit, "atan("); + writeEmulatedFunctionTriplet(out, visit, "atan("); + break; + case EOpMin: + outputTriplet(out, visit, "min(", ", ", ")"); + break; + case EOpMax: + outputTriplet(out, visit, "max(", ", ", ")"); + break; + case EOpClamp: + outputTriplet(out, visit, "clamp(", ", ", ")"); + break; + case EOpMix: + { + TIntermTyped *lastParamNode = (*(node->getSequence()))[2]->getAsTyped(); + if (lastParamNode->getType().getBasicType() == EbtBool) + { + // 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("); + } + else + { + outputTriplet(out, visit, "lerp(", ", ", ")"); + } + } break; - case EOpMin: outputTriplet(visit, "min(", ", ", ")"); break; - case EOpMax: outputTriplet(visit, "max(", ", ", ")"); break; - case EOpClamp: outputTriplet(visit, "clamp(", ", ", ")"); break; - case EOpMix: outputTriplet(visit, "lerp(", ", ", ")"); break; - case EOpStep: outputTriplet(visit, "step(", ", ", ")"); break; - case EOpSmoothStep: outputTriplet(visit, "smoothstep(", ", ", ")"); break; - case EOpDistance: outputTriplet(visit, "distance(", ", ", ")"); break; - case EOpDot: outputTriplet(visit, "dot(", ", ", ")"); break; - case EOpCross: outputTriplet(visit, "cross(", ", ", ")"); break; + case EOpStep: + outputTriplet(out, visit, "step(", ", ", ")"); + break; + case EOpSmoothStep: + outputTriplet(out, visit, "smoothstep(", ", ", ")"); + break; + case EOpDistance: + outputTriplet(out, visit, "distance(", ", ", ")"); + break; + case EOpDot: + outputTriplet(out, visit, "dot(", ", ", ")"); + break; + case EOpCross: + outputTriplet(out, visit, "cross(", ", ", ")"); + break; case EOpFaceForward: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "faceforward("); + writeEmulatedFunctionTriplet(out, visit, "faceforward("); break; - case EOpReflect: outputTriplet(visit, "reflect(", ", ", ")"); break; - case EOpRefract: outputTriplet(visit, "refract(", ", ", ")"); break; + case EOpReflect: + outputTriplet(out, visit, "reflect(", ", ", ")"); + break; + case EOpRefract: + outputTriplet(out, visit, "refract(", ", ", ")"); + break; case EOpOuterProduct: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "outerProduct("); + writeEmulatedFunctionTriplet(out, visit, "outerProduct("); break; - case EOpMul: outputTriplet(visit, "(", " * ", ")"); break; + case EOpMul: + outputTriplet(out, visit, "(", " * ", ")"); + break; default: UNREACHABLE(); } return true; } -bool OutputHLSL::visitSelection(Visit visit, TIntermSelection *node) +void OutputHLSL::writeSelection(TInfoSinkBase &out, TIntermSelection *node) { - TInfoSinkBase &out = getInfoSink(); + out << "if ("; - if (node->usesTernaryOperator()) - { - out << "s" << mUnfoldShortCircuit->getNextTemporaryIndex(); - } - else // if/else statement - { - mUnfoldShortCircuit->traverse(node->getCondition()); + node->getCondition()->traverse(this); - // D3D errors when there is a gradient operation in a loop in an unflattened if - // however flattening all the ifs in branch heavy shaders made D3D error too. - // As a temporary workaround we flatten the ifs only if there is at least a loop - // present somewhere in the shader. - if (mShaderType == GL_FRAGMENT_SHADER && mContainsAnyLoop) - { - out << "FLATTEN "; - } + out << ")\n"; - out << "if ("; + outputLineDirective(out, node->getLine().first_line); - node->getCondition()->traverse(this); + bool discard = false; - out << ")\n"; + if (node->getTrueBlock()) + { + // The trueBlock child node will output braces. + ASSERT(IsSequence(node->getTrueBlock())); - outputLineDirective(node->getLine().first_line); - out << "{\n"; + node->getTrueBlock()->traverse(this); - bool discard = false; + // Detect true discard + discard = (discard || FindDiscard::search(node->getTrueBlock())); + } + else + { + // TODO(oetuaho): Check if the semicolon inside is necessary. + // It's there as a result of conservative refactoring of the output. + out << "{;}\n"; + } - if (node->getTrueBlock()) - { - traverseStatements(node->getTrueBlock()); + outputLineDirective(out, node->getLine().first_line); - // Detect true discard - discard = (discard || FindDiscard::search(node->getTrueBlock())); - } + if (node->getFalseBlock()) + { + out << "else\n"; - outputLineDirective(node->getLine().first_line); - out << ";\n}\n"; + outputLineDirective(out, node->getFalseBlock()->getLine().first_line); - if (node->getFalseBlock()) - { - out << "else\n"; + // Either this is "else if" or the falseBlock child node will output braces. + ASSERT(IsSequence(node->getFalseBlock()) || node->getFalseBlock()->getAsSelectionNode() != nullptr); - outputLineDirective(node->getFalseBlock()->getLine().first_line); - out << "{\n"; + node->getFalseBlock()->traverse(this); - outputLineDirective(node->getFalseBlock()->getLine().first_line); - traverseStatements(node->getFalseBlock()); + outputLineDirective(out, node->getFalseBlock()->getLine().first_line); - outputLineDirective(node->getFalseBlock()->getLine().first_line); - out << ";\n}\n"; + // Detect false discard + discard = (discard || FindDiscard::search(node->getFalseBlock())); + } - // Detect false discard - discard = (discard || FindDiscard::search(node->getFalseBlock())); - } + // ANGLE issue 486: Detect problematic conditional discard + if (discard) + { + mUsesDiscardRewriting = true; + } +} - // ANGLE issue 486: Detect problematic conditional discard - if (discard && FindSideEffectRewriting::search(node)) - { - mUsesDiscardRewriting = true; - } +bool OutputHLSL::visitSelection(Visit visit, TIntermSelection *node) +{ + TInfoSinkBase &out = getInfoSink(); + + ASSERT(!node->usesTernaryOperator()); + + if (!mInsideFunction) + { + // This is part of unfolded global initialization. + mDeferredGlobalInitializers.push_back(node); + return false; } + // D3D errors when there is a gradient operation in a loop in an unflattened if. + if (mShaderType == GL_FRAGMENT_SHADER && mCurrentFunctionMetadata->hasGradientLoop(node)) + { + out << "FLATTEN "; + } + + writeSelection(out, node); + return false; } bool OutputHLSL::visitSwitch(Visit visit, TIntermSwitch *node) { + TInfoSinkBase &out = getInfoSink(); + if (node->getStatementList()) { node->setStatementList(RemoveSwitchFallThrough::removeFallThrough(node->getStatementList())); - outputTriplet(visit, "switch (", ") ", ""); + outputTriplet(out, visit, "switch (", ") ", ""); // The curly braces get written when visiting the statementList aggregate } else { // No statementList, so it won't output curly braces - outputTriplet(visit, "switch (", ") {", "}\n"); + outputTriplet(out, visit, "switch (", ") {", "}\n"); } return true; } bool OutputHLSL::visitCase(Visit visit, TIntermCase *node) { + TInfoSinkBase &out = getInfoSink(); + if (node->hasCondition()) { - outputTriplet(visit, "case (", "", "):\n"); + outputTriplet(out, visit, "case (", "", "):\n"); return true; } else { - TInfoSinkBase &out = getInfoSink(); out << "default:\n"; return false; } @@ -2354,7 +2780,8 @@ bool OutputHLSL::visitCase(Visit visit, TIntermCase *node) void OutputHLSL::visitConstantUnion(TIntermConstantUnion *node) { - writeConstantUnion(node->getType(), node->getUnionArrayPointer()); + TInfoSinkBase &out = getInfoSink(); + writeConstantUnion(out, node->getType(), node->getUnionArrayPointer()); } bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node) @@ -2362,15 +2789,14 @@ bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node) mNestedLoopDepth++; bool wasDiscontinuous = mInsideDiscontinuousLoop; + mInsideDiscontinuousLoop = mInsideDiscontinuousLoop || + mCurrentFunctionMetadata->mDiscontinuousLoops.count(node) > 0; - if (mContainsLoopDiscontinuity && !mInsideDiscontinuousLoop) - { - mInsideDiscontinuousLoop = containsLoopDiscontinuity(node); - } + TInfoSinkBase &out = getInfoSink(); - if (mOutputType == SH_HLSL9_OUTPUT) + if (mOutputType == SH_HLSL_3_0_OUTPUT) { - if (handleExcessiveLoop(node)) + if (handleExcessiveLoop(out, node)) { mInsideDiscontinuousLoop = wasDiscontinuous; mNestedLoopDepth--; @@ -2379,18 +2805,16 @@ bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node) } } - TInfoSinkBase &out = getInfoSink(); - + const char *unroll = mCurrentFunctionMetadata->hasGradientInCallGraph(node) ? "LOOP" : ""; if (node->getType() == ELoopDoWhile) { - out << "{LOOP do\n"; + out << "{" << unroll << " do\n"; - outputLineDirective(node->getLine().first_line); - out << "{\n"; + outputLineDirective(out, node->getLine().first_line); } else { - out << "{LOOP for("; + out << "{" << unroll << " for("; if (node->getInit()) { @@ -2413,21 +2837,27 @@ bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node) out << ")\n"; - outputLineDirective(node->getLine().first_line); - out << "{\n"; + outputLineDirective(out, node->getLine().first_line); } if (node->getBody()) { - traverseStatements(node->getBody()); + // The loop body node will output braces. + ASSERT(IsSequence(node->getBody())); + node->getBody()->traverse(this); + } + else + { + // TODO(oetuaho): Check if the semicolon inside is necessary. + // It's there as a result of conservative refactoring of the output. + out << "{;}\n"; } - outputLineDirective(node->getLine().first_line); - out << ";}\n"; + outputLineDirective(out, node->getLine().first_line); if (node->getType() == ELoopDoWhile) { - outputLineDirective(node->getCondition()->getLine().first_line); + outputLineDirective(out, node->getCondition()->getLine().first_line); out << "while(\n"; node->getCondition()->traverse(this); @@ -2450,7 +2880,7 @@ bool OutputHLSL::visitBranch(Visit visit, TIntermBranch *node) switch (node->getFlowOp()) { case EOpKill: - outputTriplet(visit, "discard;\n", "", ""); + outputTriplet(out, visit, "discard;\n", "", ""); break; case EOpBreak: if (visit == PreVisit) @@ -2472,7 +2902,9 @@ bool OutputHLSL::visitBranch(Visit visit, TIntermBranch *node) } } break; - case EOpContinue: outputTriplet(visit, "continue;\n", "", ""); break; + case EOpContinue: + outputTriplet(out, visit, "continue;\n", "", ""); + break; case EOpReturn: if (visit == PreVisit) { @@ -2499,16 +2931,6 @@ bool OutputHLSL::visitBranch(Visit visit, TIntermBranch *node) return true; } -void OutputHLSL::traverseStatements(TIntermNode *node) -{ - if (isSingleStatement(node)) - { - mUnfoldShortCircuit->traverse(node); - } - - node->traverse(this); -} - bool OutputHLSL::isSingleStatement(TIntermNode *node) { TIntermAggregate *aggregate = node->getAsAggregate(); @@ -2544,10 +2966,9 @@ 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). -bool OutputHLSL::handleExcessiveLoop(TIntermLoop *node) +bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node) { const int MAX_LOOP_ITERATIONS = 254; - TInfoSinkBase &out = getInfoSink(); // Parse loops of the form: // for(int index = initial; index [comparator] limit; index += increment) @@ -2696,8 +3117,9 @@ bool OutputHLSL::handleExcessiveLoop(TIntermLoop *node) } // for(int index = initial; index < clampedLimit; index += increment) + const char *unroll = mCurrentFunctionMetadata->hasGradientInCallGraph(node) ? "LOOP" : ""; - out << "LOOP for("; + out << unroll << " for("; index->traverse(this); out << " = "; out << initial; @@ -2713,7 +3135,7 @@ bool OutputHLSL::handleExcessiveLoop(TIntermLoop *node) out << increment; out << ")\n"; - outputLineDirective(node->getLine().first_line); + outputLineDirective(out, node->getLine().first_line); out << "{\n"; if (node->getBody()) @@ -2721,7 +3143,7 @@ bool OutputHLSL::handleExcessiveLoop(TIntermLoop *node) node->getBody()->traverse(this); } - outputLineDirective(node->getLine().first_line); + outputLineDirective(out, node->getLine().first_line); out << ";}\n"; if (!firstLoopFragment) @@ -2747,7 +3169,11 @@ bool OutputHLSL::handleExcessiveLoop(TIntermLoop *node) return false; // Not handled as an excessive loop } -void OutputHLSL::outputTriplet(Visit visit, const char *preString, const char *inString, const char *postString, TInfoSinkBase &out) +void OutputHLSL::outputTriplet(TInfoSinkBase &out, + Visit visit, + const char *preString, + const char *inString, + const char *postString) { if (visit == PreVisit) { @@ -2763,17 +3189,10 @@ void OutputHLSL::outputTriplet(Visit visit, const char *preString, const char *i } } -void OutputHLSL::outputTriplet(Visit visit, const char *preString, const char *inString, const char *postString) -{ - outputTriplet(visit, preString, inString, postString, getInfoSink()); -} - -void OutputHLSL::outputLineDirective(int line) +void OutputHLSL::outputLineDirective(TInfoSinkBase &out, int line) { if ((mCompileOptions & SH_LINE_DIRECTIVES) && (line > 0)) { - TInfoSinkBase &out = getInfoSink(); - out << "\n"; out << "#line " << line; @@ -2789,25 +3208,37 @@ void OutputHLSL::outputLineDirective(int line) TString OutputHLSL::argumentString(const TIntermSymbol *symbol) { TQualifier qualifier = symbol->getQualifier(); - const TType &type = symbol->getType(); - TString name = symbol->getSymbol(); + const TType &type = symbol->getType(); + const TName &name = symbol->getName(); + TString nameStr; - if (name.empty()) // HLSL demands named arguments, also for prototypes + if (name.getString().empty()) // HLSL demands named arguments, also for prototypes { - name = "x" + str(mUniqueIndex++); + nameStr = "x" + str(mUniqueIndex++); } else { - name = Decorate(name); + nameStr = DecorateIfNeeded(name); } - if (mOutputType == SH_HLSL11_OUTPUT && IsSampler(type.getBasicType())) + if (IsSampler(type.getBasicType())) { - return QualifierString(qualifier) + " " + TextureString(type) + " texture_" + name + ArrayString(type) + ", " + - QualifierString(qualifier) + " " + SamplerString(type) + " sampler_" + name + ArrayString(type); + if (mOutputType == SH_HLSL_4_1_OUTPUT) + { + // Samplers are passed as indices to the sampler array. + ASSERT(qualifier != EvqOut && qualifier != EvqInOut); + return "const uint " + nameStr + ArrayString(type); + } + if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) + { + return QualifierString(qualifier) + " " + TextureString(type.getBasicType()) + + " texture_" + nameStr + ArrayString(type) + ", " + QualifierString(qualifier) + + " " + SamplerString(type.getBasicType()) + " sampler_" + nameStr + + ArrayString(type); + } } - return QualifierString(qualifier) + " " + TypeString(type) + " " + name + ArrayString(type); + return QualifierString(qualifier) + " " + TypeString(type) + " " + nameStr + ArrayString(type); } TString OutputHLSL::initializer(const TType &type) @@ -2828,9 +3259,16 @@ TString OutputHLSL::initializer(const TType &type) return "{" + string + "}"; } -void OutputHLSL::outputConstructor(Visit visit, const TType &type, const char *name, const TIntermSequence *parameters) +void OutputHLSL::outputConstructor(TInfoSinkBase &out, + Visit visit, + const TType &type, + const char *name, + const TIntermSequence *parameters) { - TInfoSinkBase &out = getInfoSink(); + if (type.isArray()) + { + UNIMPLEMENTED(); + } if (visit == PreVisit) { @@ -2848,9 +3286,11 @@ void OutputHLSL::outputConstructor(Visit visit, const TType &type, const char *n } } -const ConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, const ConstantUnion *constUnion) +const TConstantUnion *OutputHLSL::writeConstantUnion(TInfoSinkBase &out, + const TType &type, + const TConstantUnion *const constUnion) { - TInfoSinkBase &out = getInfoSink(); + const TConstantUnion *constUnionIterated = constUnion; const TStructure* structure = type.getStruct(); if (structure) @@ -2862,7 +3302,7 @@ const ConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, const Con for (size_t i = 0; i < fields.size(); i++) { const TType *fieldType = fields[i]->type(); - constUnion = writeConstantUnion(*fieldType, constUnion); + constUnionIterated = writeConstantUnion(out, *fieldType, constUnionIterated); if (i != fields.size() - 1) { @@ -2881,37 +3321,20 @@ const ConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, const Con { out << TypeString(type) << "("; } - - for (size_t i = 0; i < size; i++, constUnion++) - { - switch (constUnion->getType()) - { - case EbtFloat: out << std::min(FLT_MAX, std::max(-FLT_MAX, constUnion->getFConst())); break; - case EbtInt: out << constUnion->getIConst(); break; - case EbtUInt: out << constUnion->getUConst(); break; - case EbtBool: out << constUnion->getBConst(); break; - default: UNREACHABLE(); - } - - if (i != size - 1) - { - out << ", "; - } - } - + constUnionIterated = WriteConstantUnionArray(out, constUnionIterated, size); if (writeType) { out << ")"; } } - return constUnion; + return constUnionIterated; } -void OutputHLSL::writeEmulatedFunctionTriplet(Visit visit, const char *preStr) +void OutputHLSL::writeEmulatedFunctionTriplet(TInfoSinkBase &out, Visit visit, const char *preStr) { TString preString = BuiltInFunctionEmulator::GetEmulatedFunctionName(preStr); - outputTriplet(visit, preString.c_str(), ", ", ")"); + outputTriplet(out, visit, preString.c_str(), ", ", ")"); } bool OutputHLSL::writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *symbolNode, TIntermTyped *expression) @@ -2935,6 +3358,68 @@ bool OutputHLSL::writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *s return false; } +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; +} + +bool OutputHLSL::writeConstantInitialization(TInfoSinkBase &out, + TIntermSymbol *symbolNode, + TIntermTyped *expression) +{ + if (canWriteAsHLSLLiteral(expression)) + { + symbolNode->traverse(this); + if (expression->getType().isArray()) + { + out << "[" << expression->getType().getArraySize() << "]"; + } + out << " = {"; + if (expression->getAsConstantUnion()) + { + TIntermConstantUnion *nodeConst = expression->getAsConstantUnion(); + const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer(); + WriteConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize()); + } + else + { + TIntermAggregate *constructor = expression->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()); + if (node != constructor->getSequence()->back()) + { + out << ", "; + } + } + } + out << "}"; + return true; + } + return false; +} + void OutputHLSL::writeDeferredGlobalInitializers(TInfoSinkBase &out) { out << "#define ANGLE_USES_DEFERRED_INIT\n" @@ -2942,23 +3427,34 @@ void OutputHLSL::writeDeferredGlobalInitializers(TInfoSinkBase &out) << "void initializeDeferredGlobals()\n" << "{\n"; - for (auto it = mDeferredGlobalInitializers.cbegin(); it != mDeferredGlobalInitializers.cend(); ++it) + for (const auto &deferredGlobal : mDeferredGlobalInitializers) { - const auto &deferredGlobal = *it; - TIntermSymbol *symbol = deferredGlobal.first; - TIntermTyped *expression = deferredGlobal.second; - ASSERT(symbol); - ASSERT(symbol->getQualifier() == EvqGlobal && expression->getQualifier() != EvqConst); + 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()) << " = "; + out << " " << Decorate(symbol->getSymbol()) << " = "; - if (!writeSameSymbolInitializer(out, symbol, expression)) + if (!writeSameSymbolInitializer(out, symbol, expression)) + { + ASSERT(mInfoSinkStack.top() == &out); + expression->traverse(this); + } + out << ";\n"; + } + else if (selection != nullptr) { - ASSERT(mInfoSinkStack.top() == &out); - expression->traverse(this); + writeSelection(out, selection); + } + else + { + UNREACHABLE(); } - - out << ";\n"; } out << "}\n" @@ -2969,9 +3465,8 @@ TString OutputHLSL::addStructEqualityFunction(const TStructure &structure) { const TFieldList &fields = structure.fields(); - for (auto it = mStructEqualityFunctions.cbegin(); it != mStructEqualityFunctions.cend(); ++it) + for (const auto &eqFunction : mStructEqualityFunctions) { - auto *eqFunction = *it; if (eqFunction->structure == &structure) { return eqFunction->functionName; @@ -3024,9 +3519,8 @@ TString OutputHLSL::addStructEqualityFunction(const TStructure &structure) TString OutputHLSL::addArrayEqualityFunction(const TType& type) { - for (auto it = mArrayEqualityFunctions.cbegin(); it != mArrayEqualityFunctions.cend(); ++it) + for (const auto &eqFunction : mArrayEqualityFunctions) { - const auto &eqFunction = *it; if (eqFunction->type == type) { return eqFunction->functionName; @@ -3076,9 +3570,8 @@ TString OutputHLSL::addArrayEqualityFunction(const TType& type) TString OutputHLSL::addArrayAssignmentFunction(const TType& type) { - for (auto it = mArrayAssignmentFunctions.cbegin(); it != mArrayAssignmentFunctions.cend(); ++it) + for (const auto &assignFunction : mArrayAssignmentFunctions) { - const auto &assignFunction = *it; if (assignFunction.type == type) { return assignFunction.functionName; @@ -3113,4 +3606,59 @@ TString OutputHLSL::addArrayAssignmentFunction(const TType& type) return function.functionName; } +TString OutputHLSL::addArrayConstructIntoFunction(const TType& type) +{ + for (const auto &constructIntoFunction : mArrayConstructIntoFunctions) + { + if (constructIntoFunction.type == type) + { + return constructIntoFunction.functionName; + } + } + + const TString &typeName = TypeString(type); + + ArrayHelperFunction function; + function.type = type; + + TInfoSinkBase fnNameOut; + fnNameOut << "angle_construct_into_" << type.getArraySize() << "_" << typeName; + function.functionName = fnNameOut.c_str(); + + TInfoSinkBase fnOut; + + fnOut << "void " << function.functionName << "(out " + << typeName << " a[" << type.getArraySize() << "]"; + for (int i = 0; i < type.getArraySize(); ++i) + { + fnOut << ", " << typeName << " b" << i; + } + fnOut << ")\n" + "{\n"; + + for (int i = 0; i < type.getArraySize(); ++i) + { + fnOut << " a[" << i << "] = b" << i << ";\n"; + } + fnOut << "}\n"; + + function.functionDefinition = fnOut.c_str(); + + mArrayConstructIntoFunctions.push_back(function); + + return function.functionName; +} + +void OutputHLSL::ensureStructDefined(const TType &type) +{ + TStructure *structure = type.getStruct(); + + if (structure) + { + mStructureHLSL->addConstructor(type, StructNameString(*structure), nullptr); + } +} + + + } |