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