xref: /aosp_15_r20/external/angle/src/compiler/translator/hlsl/TranslatorHLSL.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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