1 //
2 // Copyright 2002 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 
7 #include "compiler/translator/TranslatorHLSL.h"
8 
9 #include "compiler/translator/OutputHLSL.h"
10 #include "compiler/translator/tree_ops/AddDefaultReturnStatements.h"
11 #include "compiler/translator/tree_ops/ArrayReturnValueToOutParameter.h"
12 #include "compiler/translator/tree_ops/BreakVariableAliasingInInnerLoops.h"
13 #include "compiler/translator/tree_ops/ExpandIntegerPowExpressions.h"
14 #include "compiler/translator/tree_ops/PruneEmptyCases.h"
15 #include "compiler/translator/tree_ops/RemoveDynamicIndexing.h"
16 #include "compiler/translator/tree_ops/RewriteAtomicFunctionExpressions.h"
17 #include "compiler/translator/tree_ops/RewriteElseBlocks.h"
18 #include "compiler/translator/tree_ops/RewriteExpressionsWithShaderStorageBlock.h"
19 #include "compiler/translator/tree_ops/RewriteTexelFetchOffset.h"
20 #include "compiler/translator/tree_ops/RewriteUnaryMinusOperatorInt.h"
21 #include "compiler/translator/tree_ops/SeparateArrayConstructorStatements.h"
22 #include "compiler/translator/tree_ops/SeparateArrayInitialization.h"
23 #include "compiler/translator/tree_ops/SeparateDeclarations.h"
24 #include "compiler/translator/tree_ops/SeparateExpressionsReturningArrays.h"
25 #include "compiler/translator/tree_ops/SimplifyLoopConditions.h"
26 #include "compiler/translator/tree_ops/SplitSequenceOperator.h"
27 #include "compiler/translator/tree_ops/UnfoldShortCircuitToIf.h"
28 #include "compiler/translator/tree_ops/WrapSwitchStatementsInBlocks.h"
29 #include "compiler/translator/tree_util/IntermNodePatternMatcher.h"
30 
31 namespace sh
32 {
33 
TranslatorHLSL(sh::GLenum type,ShShaderSpec spec,ShShaderOutput output)34 TranslatorHLSL::TranslatorHLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
35     : TCompiler(type, spec, output)
36 {}
37 
translate(TIntermBlock * root,ShCompileOptions compileOptions,PerformanceDiagnostics * perfDiagnostics)38 bool TranslatorHLSL::translate(TIntermBlock *root,
39                                ShCompileOptions compileOptions,
40                                PerformanceDiagnostics *perfDiagnostics)
41 {
42     const ShBuiltInResources &resources = getResources();
43     int numRenderTargets                = resources.EXT_draw_buffers ? resources.MaxDrawBuffers : 1;
44     int maxDualSourceDrawBuffers =
45         resources.EXT_blend_func_extended ? resources.MaxDualSourceDrawBuffers : 0;
46 
47     if (!sh::AddDefaultReturnStatements(this, root))
48     {
49         return false;
50     }
51 
52     // Note that SimplifyLoopConditions needs to be run before any other AST transformations that
53     // may need to generate new statements from loop conditions or loop expressions.
54     // Note that SeparateDeclarations has already been run in TCompiler::compileTreeImpl().
55     if (!SimplifyLoopConditions(
56             this, root,
57             IntermNodePatternMatcher::kExpressionReturningArray |
58                 IntermNodePatternMatcher::kUnfoldedShortCircuitExpression |
59                 IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue,
60             &getSymbolTable()))
61     {
62         return false;
63     }
64 
65     if (!SplitSequenceOperator(
66             this, root,
67             IntermNodePatternMatcher::kExpressionReturningArray |
68                 IntermNodePatternMatcher::kUnfoldedShortCircuitExpression |
69                 IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue,
70             &getSymbolTable()))
71     {
72         return false;
73     }
74 
75     // Note that SeparateDeclarations needs to be run before UnfoldShortCircuitToIf.
76     if (!UnfoldShortCircuitToIf(this, root, &getSymbolTable()))
77     {
78         return false;
79     }
80 
81     if (!SeparateArrayConstructorStatements(this, root))
82     {
83         return false;
84     }
85 
86     if (!SeparateExpressionsReturningArrays(this, root, &getSymbolTable()))
87     {
88         return false;
89     }
90 
91     // Note that SeparateDeclarations needs to be run before SeparateArrayInitialization.
92     if (!SeparateArrayInitialization(this, root))
93     {
94         return false;
95     }
96 
97     // HLSL doesn't support arrays as return values, we'll need to make functions that have an array
98     // as a return value to use an out parameter to transfer the array data instead.
99     if (!ArrayReturnValueToOutParameter(this, root, &getSymbolTable()))
100     {
101         return false;
102     }
103 
104     if (!shouldRunLoopAndIndexingValidation(compileOptions))
105     {
106         // HLSL doesn't support dynamic indexing of vectors and matrices.
107         if (!RemoveDynamicIndexingOfNonSSBOVectorOrMatrix(this, root, &getSymbolTable(),
108                                                           perfDiagnostics))
109         {
110             return false;
111         }
112     }
113 
114     // Work around D3D9 bug that would manifest in vertex shaders with selection blocks which
115     // use a vertex attribute as a condition, and some related computation in the else block.
116     if (getOutputType() == SH_HLSL_3_0_OUTPUT && getShaderType() == GL_VERTEX_SHADER)
117     {
118         if (!sh::RewriteElseBlocks(this, root, &getSymbolTable()))
119         {
120             return false;
121         }
122     }
123 
124     // Work around an HLSL compiler frontend aliasing optimization bug.
125     // TODO(cwallez) The date is 2016-08-25, Microsoft said the bug would be fixed
126     // in the next release of d3dcompiler.dll, it would be nice to detect the DLL
127     // version and only apply the workaround if it is too old.
128     if (!sh::BreakVariableAliasingInInnerLoops(this, root))
129     {
130         return false;
131     }
132 
133     // WrapSwitchStatementsInBlocks should be called after any AST transformations that might
134     // introduce variable declarations inside the main scope of any switch statement. It cannot
135     // result in no-op cases at the end of switch statements, because unreferenced variables
136     // have already been pruned.
137     if (!WrapSwitchStatementsInBlocks(this, root))
138     {
139         return false;
140     }
141 
142     bool precisionEmulation = false;
143     if (!emulatePrecisionIfNeeded(root, getInfoSink().obj, &precisionEmulation, getOutputType()))
144         return false;
145 
146     if ((compileOptions & SH_EXPAND_SELECT_HLSL_INTEGER_POW_EXPRESSIONS) != 0)
147     {
148         if (!sh::ExpandIntegerPowExpressions(this, root, &getSymbolTable()))
149         {
150             return false;
151         }
152     }
153 
154     if ((compileOptions & SH_REWRITE_TEXELFETCHOFFSET_TO_TEXELFETCH) != 0)
155     {
156         if (!sh::RewriteTexelFetchOffset(this, root, getSymbolTable(), getShaderVersion()))
157         {
158             return false;
159         }
160     }
161 
162     if (((compileOptions & SH_REWRITE_INTEGER_UNARY_MINUS_OPERATOR) != 0) &&
163         getShaderType() == GL_VERTEX_SHADER)
164     {
165         if (!sh::RewriteUnaryMinusOperatorInt(this, root))
166         {
167             return false;
168         }
169     }
170 
171     if (getShaderVersion() >= 310)
172     {
173         // Due to ssbo also can be used as the argument of atomic memory functions, we should put
174         // RewriteExpressionsWithShaderStorageBlock before RewriteAtomicFunctionExpressions.
175         if (!sh::RewriteExpressionsWithShaderStorageBlock(this, root, &getSymbolTable()))
176         {
177             return false;
178         }
179         if (!sh::RewriteAtomicFunctionExpressions(this, root, &getSymbolTable(),
180                                                   getShaderVersion()))
181         {
182             return false;
183         }
184     }
185 
186     sh::OutputHLSL outputHLSL(getShaderType(), getShaderSpec(), getShaderVersion(),
187                               getExtensionBehavior(), getSourcePath(), getOutputType(),
188                               numRenderTargets, maxDualSourceDrawBuffers, getUniforms(),
189                               compileOptions, getComputeShaderLocalSize(), &getSymbolTable(),
190                               perfDiagnostics, mShaderStorageBlocks);
191 
192     outputHLSL.output(root, getInfoSink().obj);
193 
194     mShaderStorageBlockRegisterMap      = outputHLSL.getShaderStorageBlockRegisterMap();
195     mUniformBlockRegisterMap            = outputHLSL.getUniformBlockRegisterMap();
196     mUniformBlockUseStructuredBufferMap = outputHLSL.getUniformBlockUseStructuredBufferMap();
197     mUniformRegisterMap                 = outputHLSL.getUniformRegisterMap();
198     mReadonlyImage2DRegisterIndex       = outputHLSL.getReadonlyImage2DRegisterIndex();
199     mImage2DRegisterIndex               = outputHLSL.getImage2DRegisterIndex();
200     mUsedImage2DFunctionNames           = outputHLSL.getUsedImage2DFunctionNames();
201 
202     return true;
203 }
204 
shouldFlattenPragmaStdglInvariantAll()205 bool TranslatorHLSL::shouldFlattenPragmaStdglInvariantAll()
206 {
207     // Not necessary when translating to HLSL.
208     return false;
209 }
210 
hasShaderStorageBlock(const std::string & uniformBlockName) const211 bool TranslatorHLSL::hasShaderStorageBlock(const std::string &uniformBlockName) const
212 {
213     return (mShaderStorageBlockRegisterMap.count(uniformBlockName) > 0);
214 }
215 
getShaderStorageBlockRegister(const std::string & shaderStorageBlockName) const216 unsigned int TranslatorHLSL::getShaderStorageBlockRegister(
217     const std::string &shaderStorageBlockName) const
218 {
219     ASSERT(hasShaderStorageBlock(shaderStorageBlockName));
220     return mShaderStorageBlockRegisterMap.find(shaderStorageBlockName)->second;
221 }
222 
hasUniformBlock(const std::string & uniformBlockName) const223 bool TranslatorHLSL::hasUniformBlock(const std::string &uniformBlockName) const
224 {
225     return (mUniformBlockRegisterMap.count(uniformBlockName) > 0);
226 }
227 
getUniformBlockRegister(const std::string & uniformBlockName) const228 unsigned int TranslatorHLSL::getUniformBlockRegister(const std::string &uniformBlockName) const
229 {
230     ASSERT(hasUniformBlock(uniformBlockName));
231     return mUniformBlockRegisterMap.find(uniformBlockName)->second;
232 }
233 
getUniformRegisterMap() const234 const std::map<std::string, unsigned int> *TranslatorHLSL::getUniformRegisterMap() const
235 {
236     return &mUniformRegisterMap;
237 }
238 
getReadonlyImage2DRegisterIndex() const239 unsigned int TranslatorHLSL::getReadonlyImage2DRegisterIndex() const
240 {
241     return mReadonlyImage2DRegisterIndex;
242 }
243 
getImage2DRegisterIndex() const244 unsigned int TranslatorHLSL::getImage2DRegisterIndex() const
245 {
246     return mImage2DRegisterIndex;
247 }
248 
getUsedImage2DFunctionNames() const249 const std::set<std::string> *TranslatorHLSL::getUsedImage2DFunctionNames() const
250 {
251     return &mUsedImage2DFunctionNames;
252 }
253 
shouldUniformBlockUseStructuredBuffer(const std::string & uniformBlockName) const254 bool TranslatorHLSL::shouldUniformBlockUseStructuredBuffer(
255     const std::string &uniformBlockName) const
256 {
257     auto uniformBlockIter = mUniformBlockUseStructuredBufferMap.find(uniformBlockName);
258     return uniformBlockIter != mUniformBlockUseStructuredBufferMap.end() &&
259            uniformBlockIter->second;
260 }
261 
262 }  // namespace sh
263