TranslatorHLSL.cpp (12249B)
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/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/d3d/AddDefaultReturnStatements.h" 15 #include "compiler/translator/tree_ops/d3d/AggregateAssignArraysInSSBOs.h" 16 #include "compiler/translator/tree_ops/d3d/AggregateAssignStructsInSSBOs.h" 17 #include "compiler/translator/tree_ops/d3d/ArrayReturnValueToOutParameter.h" 18 #include "compiler/translator/tree_ops/d3d/BreakVariableAliasingInInnerLoops.h" 19 #include "compiler/translator/tree_ops/d3d/ExpandIntegerPowExpressions.h" 20 #include "compiler/translator/tree_ops/d3d/RecordUniformBlocksWithLargeArrayMember.h" 21 #include "compiler/translator/tree_ops/d3d/RewriteAtomicFunctionExpressions.h" 22 #include "compiler/translator/tree_ops/d3d/RewriteElseBlocks.h" 23 #include "compiler/translator/tree_ops/d3d/RewriteExpressionsWithShaderStorageBlock.h" 24 #include "compiler/translator/tree_ops/d3d/RewriteUnaryMinusOperatorInt.h" 25 #include "compiler/translator/tree_ops/d3d/SeparateArrayConstructorStatements.h" 26 #include "compiler/translator/tree_ops/d3d/SeparateArrayInitialization.h" 27 #include "compiler/translator/tree_ops/d3d/SeparateExpressionsReturningArrays.h" 28 #include "compiler/translator/tree_ops/d3d/UnfoldShortCircuitToIf.h" 29 #include "compiler/translator/tree_ops/d3d/WrapSwitchStatementsInBlocks.h" 30 #include "compiler/translator/tree_util/IntermNodePatternMatcher.h" 31 32 namespace sh 33 { 34 35 TranslatorHLSL::TranslatorHLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output) 36 : TCompiler(type, spec, output) 37 {} 38 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, isEarlyFragmentTestsSpecified()); 235 236 outputHLSL.output(root, getInfoSink().obj); 237 238 mShaderStorageBlockRegisterMap = outputHLSL.getShaderStorageBlockRegisterMap(); 239 mUniformBlockRegisterMap = outputHLSL.getUniformBlockRegisterMap(); 240 mUniformBlockUseStructuredBufferMap = outputHLSL.getUniformBlockUseStructuredBufferMap(); 241 mUniformRegisterMap = outputHLSL.getUniformRegisterMap(); 242 mReadonlyImage2DRegisterIndex = outputHLSL.getReadonlyImage2DRegisterIndex(); 243 mImage2DRegisterIndex = outputHLSL.getImage2DRegisterIndex(); 244 mUsedImage2DFunctionNames = outputHLSL.getUsedImage2DFunctionNames(); 245 246 return true; 247 } 248 249 bool TranslatorHLSL::shouldFlattenPragmaStdglInvariantAll() 250 { 251 // Not necessary when translating to HLSL. 252 return false; 253 } 254 255 bool TranslatorHLSL::hasShaderStorageBlock(const std::string &uniformBlockName) const 256 { 257 return (mShaderStorageBlockRegisterMap.count(uniformBlockName) > 0); 258 } 259 260 unsigned int TranslatorHLSL::getShaderStorageBlockRegister( 261 const std::string &shaderStorageBlockName) const 262 { 263 ASSERT(hasShaderStorageBlock(shaderStorageBlockName)); 264 return mShaderStorageBlockRegisterMap.find(shaderStorageBlockName)->second; 265 } 266 267 bool TranslatorHLSL::hasUniformBlock(const std::string &uniformBlockName) const 268 { 269 return (mUniformBlockRegisterMap.count(uniformBlockName) > 0); 270 } 271 272 unsigned int TranslatorHLSL::getUniformBlockRegister(const std::string &uniformBlockName) const 273 { 274 ASSERT(hasUniformBlock(uniformBlockName)); 275 return mUniformBlockRegisterMap.find(uniformBlockName)->second; 276 } 277 278 const std::map<std::string, unsigned int> *TranslatorHLSL::getUniformRegisterMap() const 279 { 280 return &mUniformRegisterMap; 281 } 282 283 const std::set<std::string> *TranslatorHLSL::getSlowCompilingUniformBlockSet() const 284 { 285 return &mSlowCompilingUniformBlockSet; 286 } 287 288 unsigned int TranslatorHLSL::getReadonlyImage2DRegisterIndex() const 289 { 290 return mReadonlyImage2DRegisterIndex; 291 } 292 293 unsigned int TranslatorHLSL::getImage2DRegisterIndex() const 294 { 295 return mImage2DRegisterIndex; 296 } 297 298 const std::set<std::string> *TranslatorHLSL::getUsedImage2DFunctionNames() const 299 { 300 return &mUsedImage2DFunctionNames; 301 } 302 303 bool TranslatorHLSL::shouldUniformBlockUseStructuredBuffer( 304 const std::string &uniformBlockName) const 305 { 306 auto uniformBlockIter = mUniformBlockUseStructuredBufferMap.find(uniformBlockName); 307 return uniformBlockIter != mUniformBlockUseStructuredBufferMap.end() && 308 uniformBlockIter->second; 309 } 310 311 } // namespace sh