| // | |
| // Copyright 2002 The ANGLE Project Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // | |
| #include "compiler/translator/TranslatorHLSL.h" | |
| #include "compiler/translator/OutputHLSL.h" | |
| #include "compiler/translator/tree_ops/AddDefaultReturnStatements.h" | |
| #include "compiler/translator/tree_ops/ArrayReturnValueToOutParameter.h" | |
| #include "compiler/translator/tree_ops/BreakVariableAliasingInInnerLoops.h" | |
| #include "compiler/translator/tree_ops/EmulatePrecision.h" | |
| #include "compiler/translator/tree_ops/ExpandIntegerPowExpressions.h" | |
| #include "compiler/translator/tree_ops/PruneEmptyCases.h" | |
| #include "compiler/translator/tree_ops/RemoveDynamicIndexing.h" | |
| #include "compiler/translator/tree_ops/RewriteAtomicFunctionExpressions.h" | |
| #include "compiler/translator/tree_ops/RewriteElseBlocks.h" | |
| #include "compiler/translator/tree_ops/RewriteExpressionsWithShaderStorageBlock.h" | |
| #include "compiler/translator/tree_ops/RewriteTexelFetchOffset.h" | |
| #include "compiler/translator/tree_ops/RewriteUnaryMinusOperatorInt.h" | |
| #include "compiler/translator/tree_ops/SeparateArrayConstructorStatements.h" | |
| #include "compiler/translator/tree_ops/SeparateArrayInitialization.h" | |
| #include "compiler/translator/tree_ops/SeparateDeclarations.h" | |
| #include "compiler/translator/tree_ops/SeparateExpressionsReturningArrays.h" | |
| #include "compiler/translator/tree_ops/SimplifyLoopConditions.h" | |
| #include "compiler/translator/tree_ops/SplitSequenceOperator.h" | |
| #include "compiler/translator/tree_ops/UnfoldShortCircuitToIf.h" | |
| #include "compiler/translator/tree_ops/WrapSwitchStatementsInBlocks.h" | |
| #include "compiler/translator/tree_util/IntermNodePatternMatcher.h" | |
| namespace sh | |
| { | |
| TranslatorHLSL::TranslatorHLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output) | |
| : TCompiler(type, spec, output) | |
| {} | |
| bool TranslatorHLSL::translate(TIntermBlock *root, | |
| ShCompileOptions compileOptions, | |
| PerformanceDiagnostics *perfDiagnostics) | |
| { | |
| const ShBuiltInResources &resources = getResources(); | |
| int numRenderTargets = resources.EXT_draw_buffers ? resources.MaxDrawBuffers : 1; | |
| int maxDualSourceDrawBuffers = | |
| resources.EXT_blend_func_extended ? resources.MaxDualSourceDrawBuffers : 0; | |
| if (!sh::AddDefaultReturnStatements(this, root)) | |
| { | |
| return false; | |
| } | |
| // Note that SimplifyLoopConditions needs to be run before any other AST transformations that | |
| // may need to generate new statements from loop conditions or loop expressions. | |
| // Note that SeparateDeclarations has already been run in TCompiler::compileTreeImpl(). | |
| if (!SimplifyLoopConditions( | |
| this, root, | |
| IntermNodePatternMatcher::kExpressionReturningArray | | |
| IntermNodePatternMatcher::kUnfoldedShortCircuitExpression | | |
| IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue, | |
| &getSymbolTable())) | |
| { | |
| return false; | |
| } | |
| if (!SplitSequenceOperator( | |
| this, root, | |
| IntermNodePatternMatcher::kExpressionReturningArray | | |
| IntermNodePatternMatcher::kUnfoldedShortCircuitExpression | | |
| IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue, | |
| &getSymbolTable())) | |
| { | |
| return false; | |
| } | |
| // Note that SeparateDeclarations needs to be run before UnfoldShortCircuitToIf. | |
| if (!UnfoldShortCircuitToIf(this, root, &getSymbolTable())) | |
| { | |
| return false; | |
| } | |
| if (!SeparateArrayConstructorStatements(this, root)) | |
| { | |
| return false; | |
| } | |
| if (!SeparateExpressionsReturningArrays(this, root, &getSymbolTable())) | |
| { | |
| return false; | |
| } | |
| // Note that SeparateDeclarations needs to be run before SeparateArrayInitialization. | |
| if (!SeparateArrayInitialization(this, root)) | |
| { | |
| return false; | |
| } | |
| // HLSL doesn't support arrays as return values, we'll need to make functions that have an array | |
| // as a return value to use an out parameter to transfer the array data instead. | |
| if (!ArrayReturnValueToOutParameter(this, root, &getSymbolTable())) | |
| { | |
| return false; | |
| } | |
| if (!shouldRunLoopAndIndexingValidation(compileOptions)) | |
| { | |
| // HLSL doesn't support dynamic indexing of vectors and matrices. | |
| if (!RemoveDynamicIndexingOfNonSSBOVectorOrMatrix(this, root, &getSymbolTable(), | |
| perfDiagnostics)) | |
| { | |
| return false; | |
| } | |
| } | |
| // 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 (getOutputType() == SH_HLSL_3_0_OUTPUT && getShaderType() == GL_VERTEX_SHADER) | |
| { | |
| if (!sh::RewriteElseBlocks(this, root, &getSymbolTable())) | |
| { | |
| return false; | |
| } | |
| } | |
| // Work around an HLSL compiler frontend aliasing optimization bug. | |
| // TODO(cwallez) The date is 2016-08-25, Microsoft said the bug would be fixed | |
| // in the next release of d3dcompiler.dll, it would be nice to detect the DLL | |
| // version and only apply the workaround if it is too old. | |
| if (!sh::BreakVariableAliasingInInnerLoops(this, root)) | |
| { | |
| return false; | |
| } | |
| // WrapSwitchStatementsInBlocks should be called after any AST transformations that might | |
| // introduce variable declarations inside the main scope of any switch statement. It cannot | |
| // result in no-op cases at the end of switch statements, because unreferenced variables | |
| // have already been pruned. | |
| if (!WrapSwitchStatementsInBlocks(this, root)) | |
| { | |
| return false; | |
| } | |
| bool precisionEmulation = | |
| getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision; | |
| if (precisionEmulation) | |
| { | |
| EmulatePrecision emulatePrecision(&getSymbolTable()); | |
| root->traverse(&emulatePrecision); | |
| if (!emulatePrecision.updateTree(this, root)) | |
| { | |
| return false; | |
| } | |
| emulatePrecision.writeEmulationHelpers(getInfoSink().obj, getShaderVersion(), | |
| getOutputType()); | |
| } | |
| if ((compileOptions & SH_EXPAND_SELECT_HLSL_INTEGER_POW_EXPRESSIONS) != 0) | |
| { | |
| if (!sh::ExpandIntegerPowExpressions(this, root, &getSymbolTable())) | |
| { | |
| return false; | |
| } | |
| } | |
| if ((compileOptions & SH_REWRITE_TEXELFETCHOFFSET_TO_TEXELFETCH) != 0) | |
| { | |
| if (!sh::RewriteTexelFetchOffset(this, root, getSymbolTable(), getShaderVersion())) | |
| { | |
| return false; | |
| } | |
| } | |
| if (((compileOptions & SH_REWRITE_INTEGER_UNARY_MINUS_OPERATOR) != 0) && | |
| getShaderType() == GL_VERTEX_SHADER) | |
| { | |
| if (!sh::RewriteUnaryMinusOperatorInt(this, root)) | |
| { | |
| return false; | |
| } | |
| } | |
| if (getShaderVersion() >= 310) | |
| { | |
| // Due to ssbo also can be used as the argument of atomic memory functions, we should put | |
| // RewriteExpressionsWithShaderStorageBlock before RewriteAtomicFunctionExpressions. | |
| if (!sh::RewriteExpressionsWithShaderStorageBlock(this, root, &getSymbolTable())) | |
| { | |
| return false; | |
| } | |
| if (!sh::RewriteAtomicFunctionExpressions(this, root, &getSymbolTable(), | |
| getShaderVersion())) | |
| { | |
| return false; | |
| } | |
| } | |
| sh::OutputHLSL outputHLSL(getShaderType(), getShaderSpec(), getShaderVersion(), | |
| getExtensionBehavior(), getSourcePath(), getOutputType(), | |
| numRenderTargets, maxDualSourceDrawBuffers, getUniforms(), | |
| compileOptions, getComputeShaderLocalSize(), &getSymbolTable(), | |
| perfDiagnostics, mShaderStorageBlocks); | |
| outputHLSL.output(root, getInfoSink().obj); | |
| mShaderStorageBlockRegisterMap = outputHLSL.getShaderStorageBlockRegisterMap(); | |
| mUniformBlockRegisterMap = outputHLSL.getUniformBlockRegisterMap(); | |
| mUniformRegisterMap = outputHLSL.getUniformRegisterMap(); | |
| mReadonlyImage2DRegisterIndex = outputHLSL.getReadonlyImage2DRegisterIndex(); | |
| mImage2DRegisterIndex = outputHLSL.getImage2DRegisterIndex(); | |
| mUsedImage2DFunctionNames = outputHLSL.getUsedImage2DFunctionNames(); | |
| return true; | |
| } | |
| bool TranslatorHLSL::shouldFlattenPragmaStdglInvariantAll() | |
| { | |
| // Not necessary when translating to HLSL. | |
| return false; | |
| } | |
| bool TranslatorHLSL::hasShaderStorageBlock(const std::string &uniformBlockName) const | |
| { | |
| return (mShaderStorageBlockRegisterMap.count(uniformBlockName) > 0); | |
| } | |
| unsigned int TranslatorHLSL::getShaderStorageBlockRegister( | |
| const std::string &shaderStorageBlockName) const | |
| { | |
| ASSERT(hasShaderStorageBlock(shaderStorageBlockName)); | |
| return mShaderStorageBlockRegisterMap.find(shaderStorageBlockName)->second; | |
| } | |
| bool TranslatorHLSL::hasUniformBlock(const std::string &uniformBlockName) const | |
| { | |
| return (mUniformBlockRegisterMap.count(uniformBlockName) > 0); | |
| } | |
| unsigned int TranslatorHLSL::getUniformBlockRegister(const std::string &uniformBlockName) const | |
| { | |
| ASSERT(hasUniformBlock(uniformBlockName)); | |
| return mUniformBlockRegisterMap.find(uniformBlockName)->second; | |
| } | |
| const std::map<std::string, unsigned int> *TranslatorHLSL::getUniformRegisterMap() const | |
| { | |
| return &mUniformRegisterMap; | |
| } | |
| unsigned int TranslatorHLSL::getReadonlyImage2DRegisterIndex() const | |
| { | |
| return mReadonlyImage2DRegisterIndex; | |
| } | |
| unsigned int TranslatorHLSL::getImage2DRegisterIndex() const | |
| { | |
| return mImage2DRegisterIndex; | |
| } | |
| const std::set<std::string> *TranslatorHLSL::getUsedImage2DFunctionNames() const | |
| { | |
| return &mUsedImage2DFunctionNames; | |
| } | |
| } // namespace sh |