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