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/hlsl/TranslatorHLSL.h"
8
9 #include "compiler/translator/hlsl/OutputHLSL.h"
10 #include "compiler/translator/tree_ops/RemoveDynamicIndexing.h"
11 #include "compiler/translator/tree_ops/RewriteTexelFetchOffset.h"
12 #include "compiler/translator/tree_ops/SimplifyLoopConditions.h"
13 #include "compiler/translator/tree_ops/SplitSequenceOperator.h"
14 #include "compiler/translator/tree_ops/hlsl/AddDefaultReturnStatements.h"
15 #include "compiler/translator/tree_ops/hlsl/AggregateAssignArraysInSSBOs.h"
16 #include "compiler/translator/tree_ops/hlsl/AggregateAssignStructsInSSBOs.h"
17 #include "compiler/translator/tree_ops/hlsl/ArrayReturnValueToOutParameter.h"
18 #include "compiler/translator/tree_ops/hlsl/BreakVariableAliasingInInnerLoops.h"
19 #include "compiler/translator/tree_ops/hlsl/ExpandIntegerPowExpressions.h"
20 #include "compiler/translator/tree_ops/hlsl/RecordUniformBlocksWithLargeArrayMember.h"
21 #include "compiler/translator/tree_ops/hlsl/RewriteAtomicFunctionExpressions.h"
22 #include "compiler/translator/tree_ops/hlsl/RewriteElseBlocks.h"
23 #include "compiler/translator/tree_ops/hlsl/RewriteExpressionsWithShaderStorageBlock.h"
24 #include "compiler/translator/tree_ops/hlsl/RewriteUnaryMinusOperatorInt.h"
25 #include "compiler/translator/tree_ops/hlsl/SeparateArrayConstructorStatements.h"
26 #include "compiler/translator/tree_ops/hlsl/SeparateArrayInitialization.h"
27 #include "compiler/translator/tree_ops/hlsl/SeparateExpressionsReturningArrays.h"
28 #include "compiler/translator/tree_ops/hlsl/UnfoldShortCircuitToIf.h"
29 #include "compiler/translator/tree_ops/hlsl/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,const ShCompileOptions & compileOptions,PerformanceDiagnostics * perfDiagnostics)39 bool TranslatorHLSL::translate(TIntermBlock *root,
40 const ShCompileOptions &compileOptions,
41 PerformanceDiagnostics *perfDiagnostics)
42 {
43 // A few transformations leave the tree in an inconsistent state. For example, when unfolding
44 // the short-circuit in the following function:
45 //
46 // mediump float f(float a) { return a > 0 ? 0.0 : 1.0; }
47 //
48 // a temp variable is created to hold the result of the expression. Currently the precision of
49 // the return value of the function is not propagated to its return expressions. Additionally,
50 // an expression such as
51 //
52 // cond ? gl_NumWorkGroups.x : gl_NumWorkGroups.y
53 //
54 // does not have a precision as the built-in does not specify a precision.
55 //
56 // Precision is not applicable to HLSL so fixing these issues are deferred.
57 mValidateASTOptions.validatePrecision = false;
58
59 const ShBuiltInResources &resources = getResources();
60 int numRenderTargets = resources.EXT_draw_buffers ? resources.MaxDrawBuffers : 1;
61 int maxDualSourceDrawBuffers =
62 resources.EXT_blend_func_extended ? resources.MaxDualSourceDrawBuffers : 0;
63
64 if (!sh::AddDefaultReturnStatements(this, root))
65 {
66 return false;
67 }
68
69 // Note that SimplifyLoopConditions needs to be run before any other AST transformations that
70 // may need to generate new statements from loop conditions or loop expressions.
71 // Note that SeparateDeclarations has already been run in TCompiler::compileTreeImpl().
72 if (!SimplifyLoopConditions(
73 this, root,
74 IntermNodePatternMatcher::kExpressionReturningArray |
75 IntermNodePatternMatcher::kUnfoldedShortCircuitExpression |
76 IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue,
77 &getSymbolTable()))
78 {
79 return false;
80 }
81
82 if (!SplitSequenceOperator(
83 this, root,
84 IntermNodePatternMatcher::kExpressionReturningArray |
85 IntermNodePatternMatcher::kUnfoldedShortCircuitExpression |
86 IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue,
87 &getSymbolTable()))
88 {
89 return false;
90 }
91
92 // Note that SeparateDeclarations needs to be run before UnfoldShortCircuitToIf.
93 if (!UnfoldShortCircuitToIf(this, root, &getSymbolTable()))
94 {
95 return false;
96 }
97
98 if (!SeparateArrayConstructorStatements(this, root))
99 {
100 return false;
101 }
102
103 if (getShaderVersion() >= 310)
104 {
105 // Do element-by-element assignments of arrays in SSBOs. This allows the D3D backend to use
106 // RWByteAddressBuffer.Load() and .Store(), which only operate on values up to 16 bytes in
107 // size. Note that this must be done before SeparateExpressionsReturningArrays.
108 if (!sh::AggregateAssignArraysInSSBOs(this, root, &getSymbolTable()))
109 {
110 return false;
111 }
112 // Do field-by-field assignment of structs in SSBOs. This allows the D3D backend to use
113 // RWByteAddressBuffer.Load() and .Store(), which only operate on values up to 16 bytes in
114 // size.
115 if (!sh::AggregateAssignStructsInSSBOs(this, root, &getSymbolTable()))
116 {
117 return false;
118 }
119 }
120
121 if (!SeparateExpressionsReturningArrays(this, root, &getSymbolTable()))
122 {
123 return false;
124 }
125
126 // Note that SeparateDeclarations needs to be run before SeparateArrayInitialization.
127 if (!SeparateArrayInitialization(this, root))
128 {
129 return false;
130 }
131
132 // HLSL doesn't support arrays as return values, we'll need to make functions that have an array
133 // as a return value to use an out parameter to transfer the array data instead.
134 if (!ArrayReturnValueToOutParameter(this, root, &getSymbolTable()))
135 {
136 return false;
137 }
138
139 if (!shouldRunLoopAndIndexingValidation(compileOptions))
140 {
141 // HLSL doesn't support dynamic indexing of vectors and matrices.
142 if (!RemoveDynamicIndexingOfNonSSBOVectorOrMatrix(this, root, &getSymbolTable(),
143 perfDiagnostics))
144 {
145 return false;
146 }
147 }
148
149 // Work around D3D9 bug that would manifest in vertex shaders with selection blocks which
150 // use a vertex attribute as a condition, and some related computation in the else block.
151 if (getOutputType() == SH_HLSL_3_0_OUTPUT && getShaderType() == GL_VERTEX_SHADER)
152 {
153 if (!sh::RewriteElseBlocks(this, root, &getSymbolTable()))
154 {
155 return false;
156 }
157 }
158
159 // Work around an HLSL compiler frontend aliasing optimization bug.
160 // TODO(cwallez) The date is 2016-08-25, Microsoft said the bug would be fixed
161 // in the next release of d3dcompiler.dll, it would be nice to detect the DLL
162 // version and only apply the workaround if it is too old.
163 if (!sh::BreakVariableAliasingInInnerLoops(this, root))
164 {
165 return false;
166 }
167
168 // WrapSwitchStatementsInBlocks should be called after any AST transformations that might
169 // introduce variable declarations inside the main scope of any switch statement. It cannot
170 // result in no-op cases at the end of switch statements, because unreferenced variables
171 // have already been pruned.
172 if (!WrapSwitchStatementsInBlocks(this, root))
173 {
174 return false;
175 }
176
177 if (compileOptions.expandSelectHLSLIntegerPowExpressions)
178 {
179 if (!sh::ExpandIntegerPowExpressions(this, root, &getSymbolTable()))
180 {
181 return false;
182 }
183 }
184
185 if (compileOptions.rewriteTexelFetchOffsetToTexelFetch)
186 {
187 if (!sh::RewriteTexelFetchOffset(this, root, getSymbolTable(), getShaderVersion()))
188 {
189 return false;
190 }
191 }
192
193 if (compileOptions.rewriteIntegerUnaryMinusOperator && getShaderType() == GL_VERTEX_SHADER)
194 {
195 if (!sh::RewriteUnaryMinusOperatorInt(this, root))
196 {
197 return false;
198 }
199 }
200
201 if (getShaderVersion() >= 310)
202 {
203 // Due to ssbo also can be used as the argument of atomic memory functions, we should put
204 // RewriteExpressionsWithShaderStorageBlock before RewriteAtomicFunctionExpressions.
205 if (!sh::RewriteExpressionsWithShaderStorageBlock(this, root, &getSymbolTable()))
206 {
207 return false;
208 }
209 if (!sh::RewriteAtomicFunctionExpressions(this, root, &getSymbolTable(),
210 getShaderVersion()))
211 {
212 return false;
213 }
214 }
215
216 mUniformBlockOptimizedMap.clear();
217 mSlowCompilingUniformBlockSet.clear();
218 // In order to get the exact maximum of slots are available for shader resources, which would
219 // been bound with StructuredBuffer, we only translate uniform block with a large array member
220 // into StructuredBuffer when shader version is 300.
221 if (getShaderVersion() == 300 && compileOptions.allowTranslateUniformBlockToStructuredBuffer)
222 {
223 if (!sh::RecordUniformBlocksWithLargeArrayMember(root, mUniformBlockOptimizedMap,
224 mSlowCompilingUniformBlockSet))
225 {
226 return false;
227 }
228 }
229
230 sh::OutputHLSL outputHLSL(
231 getShaderType(), getShaderSpec(), getShaderVersion(), getExtensionBehavior(),
232 getSourcePath(), getOutputType(), numRenderTargets, maxDualSourceDrawBuffers, getUniforms(),
233 compileOptions, getComputeShaderLocalSize(), &getSymbolTable(), perfDiagnostics,
234 mUniformBlockOptimizedMap, mShaderStorageBlocks, getClipDistanceArraySize(),
235 getCullDistanceArraySize(), isEarlyFragmentTestsSpecified());
236
237 outputHLSL.output(root, getInfoSink().obj);
238
239 mShaderStorageBlockRegisterMap = outputHLSL.getShaderStorageBlockRegisterMap();
240 mUniformBlockRegisterMap = outputHLSL.getUniformBlockRegisterMap();
241 mUniformBlockUseStructuredBufferMap = outputHLSL.getUniformBlockUseStructuredBufferMap();
242 mUniformRegisterMap = outputHLSL.getUniformRegisterMap();
243 mReadonlyImage2DRegisterIndex = outputHLSL.getReadonlyImage2DRegisterIndex();
244 mImage2DRegisterIndex = outputHLSL.getImage2DRegisterIndex();
245 mUsedImage2DFunctionNames = outputHLSL.getUsedImage2DFunctionNames();
246
247 return true;
248 }
249
shouldFlattenPragmaStdglInvariantAll()250 bool TranslatorHLSL::shouldFlattenPragmaStdglInvariantAll()
251 {
252 // Not necessary when translating to HLSL.
253 return false;
254 }
255
hasShaderStorageBlock(const std::string & uniformBlockName) const256 bool TranslatorHLSL::hasShaderStorageBlock(const std::string &uniformBlockName) const
257 {
258 return (mShaderStorageBlockRegisterMap.count(uniformBlockName) > 0);
259 }
260
getShaderStorageBlockRegister(const std::string & shaderStorageBlockName) const261 unsigned int TranslatorHLSL::getShaderStorageBlockRegister(
262 const std::string &shaderStorageBlockName) const
263 {
264 ASSERT(hasShaderStorageBlock(shaderStorageBlockName));
265 return mShaderStorageBlockRegisterMap.find(shaderStorageBlockName)->second;
266 }
267
hasUniformBlock(const std::string & uniformBlockName) const268 bool TranslatorHLSL::hasUniformBlock(const std::string &uniformBlockName) const
269 {
270 return (mUniformBlockRegisterMap.count(uniformBlockName) > 0);
271 }
272
getUniformBlockRegister(const std::string & uniformBlockName) const273 unsigned int TranslatorHLSL::getUniformBlockRegister(const std::string &uniformBlockName) const
274 {
275 ASSERT(hasUniformBlock(uniformBlockName));
276 return mUniformBlockRegisterMap.find(uniformBlockName)->second;
277 }
278
getUniformRegisterMap() const279 const std::map<std::string, unsigned int> *TranslatorHLSL::getUniformRegisterMap() const
280 {
281 return &mUniformRegisterMap;
282 }
283
getSlowCompilingUniformBlockSet() const284 const std::set<std::string> *TranslatorHLSL::getSlowCompilingUniformBlockSet() const
285 {
286 return &mSlowCompilingUniformBlockSet;
287 }
288
getReadonlyImage2DRegisterIndex() const289 unsigned int TranslatorHLSL::getReadonlyImage2DRegisterIndex() const
290 {
291 return mReadonlyImage2DRegisterIndex;
292 }
293
getImage2DRegisterIndex() const294 unsigned int TranslatorHLSL::getImage2DRegisterIndex() const
295 {
296 return mImage2DRegisterIndex;
297 }
298
getUsedImage2DFunctionNames() const299 const std::set<std::string> *TranslatorHLSL::getUsedImage2DFunctionNames() const
300 {
301 return &mUsedImage2DFunctionNames;
302 }
303
shouldUniformBlockUseStructuredBuffer(const std::string & uniformBlockName) const304 bool TranslatorHLSL::shouldUniformBlockUseStructuredBuffer(
305 const std::string &uniformBlockName) const
306 {
307 auto uniformBlockIter = mUniformBlockUseStructuredBufferMap.find(uniformBlockName);
308 return uniformBlockIter != mUniformBlockUseStructuredBufferMap.end() &&
309 uniformBlockIter->second;
310 }
311
312 } // namespace sh
313