1 //
2 // Copyright 2020 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 // RecordUniformBlocksWithLargeArrayMember.h:
7 // Collect all uniform blocks which have one or more large array members,
8 // and the array sizes are greater than or equal to 50. If some of them
9 // satify some conditions, we will translate them to StructuredBuffers
10 // on Direct3D backend.
11 //
12
13 #include "compiler/translator/tree_ops/d3d/RecordUniformBlocksWithLargeArrayMember.h"
14
15 #include "compiler/translator/Compiler.h"
16 #include "compiler/translator/tree_util/IntermNode_util.h"
17 #include "compiler/translator/tree_util/IntermTraverse.h"
18
19 namespace sh
20 {
21
22 namespace
23 {
24 // Only when a uniform block member's array size is greater than or equal to
25 // kMinArraySizeUseStructuredBuffer, then we may translate the uniform block
26 // to a StructuredBuffer on Direct3D backend.
27 const unsigned int kMinArraySizeUseStructuredBuffer = 50u;
28
29 // There is a maximum of D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT(128) slots that are
30 // available for shader resources on Direct3D 11. When shader version is 300, we only use
31 // D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT(16) slots for texture units. We allow StructuredBuffer
32 // to use the maximum of 60 slots, that is enough here.
33 const unsigned int kMaxAllowToUseRegisterCount = 60u;
34
35 // Traverser that all uniform blocks which have one or more large array members, and the array
36 // sizes are greater than or equal to 50.
37 class UniformBlocksWithLargeArrayMemberTraverser : public TIntermTraverser
38 {
39 public:
40 UniformBlocksWithLargeArrayMemberTraverser();
41
42 void visitSymbol(TIntermSymbol *node) override;
43 bool visitBinary(Visit visit, TIntermBinary *node) override;
getUniformBlockMayTranslation()44 std::map<int, const TInterfaceBlock *> &getUniformBlockMayTranslation()
45 {
46 return mUniformBlockMayTranslation;
47 }
getUniformBlockNotAllowTranslation()48 std::map<int, const TInterfaceBlock *> &getUniformBlockNotAllowTranslation()
49 {
50 return mUniformBlockNotAllowTranslation;
51 }
getUniformBlockUsedRegisterCount()52 std::map<int, unsigned int> &getUniformBlockUsedRegisterCount()
53 {
54 return mUniformBlockUsedRegisterCount;
55 }
getUniformBlockWithLargeArrayMember()56 std::map<int, const TInterfaceBlock *> &getUniformBlockWithLargeArrayMember()
57 {
58 return mUniformBlockWithLargeArrayMember;
59 }
60
61 private:
62 std::map<int, const TInterfaceBlock *> mUniformBlockMayTranslation;
63 std::map<int, const TInterfaceBlock *> mUniformBlockNotAllowTranslation;
64 std::map<int, unsigned int> mUniformBlockUsedRegisterCount;
65 std::map<int, const TInterfaceBlock *> mUniformBlockWithLargeArrayMember;
66 };
67
UniformBlocksWithLargeArrayMemberTraverser()68 UniformBlocksWithLargeArrayMemberTraverser::UniformBlocksWithLargeArrayMemberTraverser()
69 : TIntermTraverser(true, true, false)
70 {}
71
IsSupportedTypeForStructuredBuffer(const TType & type)72 static bool IsSupportedTypeForStructuredBuffer(const TType &type)
73 {
74 const TStructure *structure = type.getStruct();
75 const TLayoutMatrixPacking matrixPacking = type.getLayoutQualifier().matrixPacking;
76 if (structure)
77 {
78 const TFieldList &fields = structure->fields();
79 for (size_t i = 0; i < fields.size(); i++)
80 {
81 const TType &fieldType = *fields[i]->type();
82 // Do not allow the structure's member is array or structure.
83 if (!fieldType.isArray() && !fieldType.getStruct() &&
84 (fieldType.isScalar() || fieldType.isVector() ||
85 (fieldType.isMatrix() &&
86 ((matrixPacking != EmpRowMajor && fieldType.getRows() == 4) ||
87 (matrixPacking == EmpRowMajor && fieldType.getCols() == 4)))))
88 {
89 return true;
90 }
91 }
92 return false;
93 }
94 else if (type.isMatrix())
95 {
96 // Only supports the matrix types that we do not need to pad in a structure or an array
97 // explicitly.
98 return (matrixPacking != EmpRowMajor && type.getRows() == 4) ||
99 (matrixPacking == EmpRowMajor && type.getCols() == 4);
100 }
101 else
102 {
103 // Supports vector and scalar types in a structure or an array.
104 return true;
105 }
106 }
107
CanTranslateUniformBlockToStructuredBuffer(const TInterfaceBlock & interfaceBlock)108 static bool CanTranslateUniformBlockToStructuredBuffer(const TInterfaceBlock &interfaceBlock)
109 {
110 const TLayoutBlockStorage blockStorage = interfaceBlock.blockStorage();
111
112 if (blockStorage == EbsStd140 && interfaceBlock.fields().size() == 1u)
113 {
114 const TType &fieldType = *interfaceBlock.fields()[0]->type();
115 if (fieldType.getNumArraySizes() == 1u &&
116 fieldType.getOutermostArraySize() >= kMinArraySizeUseStructuredBuffer)
117 {
118 return IsSupportedTypeForStructuredBuffer(fieldType);
119 }
120 }
121
122 return false;
123 }
124
FieldIsOrHasLargeArrayField(const TField & field)125 static bool FieldIsOrHasLargeArrayField(const TField &field)
126 {
127 const TType *type = field.type();
128 if (type->getArraySizeProduct() >= kMinArraySizeUseStructuredBuffer)
129 {
130 return true;
131 }
132
133 const TStructure *structure = type->getStruct();
134 if (structure)
135 {
136 const TFieldList &fields = structure->fields();
137 bool hasLargeArrayField = false;
138 for (size_t i = 0; i < fields.size(); i++)
139 {
140 hasLargeArrayField = FieldIsOrHasLargeArrayField(*fields[i]);
141 if (hasLargeArrayField)
142 {
143 break;
144 }
145 }
146 return hasLargeArrayField;
147 }
148
149 return false;
150 }
151
IsInterfaceBlockWithLargeArrayField(const TInterfaceBlock & interfaceBlock)152 static bool IsInterfaceBlockWithLargeArrayField(const TInterfaceBlock &interfaceBlock)
153 {
154 const TFieldList &fields = interfaceBlock.fields();
155 bool isLargeArrayField = false;
156 for (size_t i = 0; i < fields.size(); i++)
157 {
158 isLargeArrayField = FieldIsOrHasLargeArrayField(*fields[i]);
159 if (isLargeArrayField)
160 {
161 break;
162 }
163 }
164
165 return isLargeArrayField;
166 }
167
visitSymbol(TIntermSymbol * node)168 void UniformBlocksWithLargeArrayMemberTraverser::visitSymbol(TIntermSymbol *node)
169 {
170 const TVariable &variable = node->variable();
171 const TType &variableType = variable.getType();
172 TQualifier qualifier = variable.getType().getQualifier();
173
174 if (qualifier == EvqUniform)
175 {
176 const TInterfaceBlock *interfaceBlock = variableType.getInterfaceBlock();
177 if (interfaceBlock)
178 {
179 if (CanTranslateUniformBlockToStructuredBuffer(*interfaceBlock))
180 {
181 if (mUniformBlockMayTranslation.count(interfaceBlock->uniqueId().get()) == 0)
182 {
183 mUniformBlockMayTranslation[interfaceBlock->uniqueId().get()] = interfaceBlock;
184 }
185
186 if (!variableType.isInterfaceBlock())
187 {
188 TIntermNode *accessor = getAncestorNode(0);
189 TIntermBinary *accessorAsBinary = accessor->getAsBinaryNode();
190 // The uniform block variable is array type, only indexing operator is allowed
191 // to operate on the variable, otherwise do not translate the uniform block to
192 // HLSL StructuredBuffer.
193 if (!accessorAsBinary ||
194 !(accessorAsBinary && (accessorAsBinary->getOp() == EOpIndexDirect ||
195 accessorAsBinary->getOp() == EOpIndexIndirect)))
196 {
197 if (mUniformBlockNotAllowTranslation.count(
198 interfaceBlock->uniqueId().get()) == 0)
199 {
200 mUniformBlockNotAllowTranslation[interfaceBlock->uniqueId().get()] =
201 interfaceBlock;
202 }
203 }
204 else
205 {
206 if (mUniformBlockUsedRegisterCount.count(
207 interfaceBlock->uniqueId().get()) == 0)
208 {
209 // The uniform block is not an instanced one, so it only uses one
210 // register.
211 mUniformBlockUsedRegisterCount[interfaceBlock->uniqueId().get()] = 1;
212 }
213 }
214 }
215 else
216 {
217 if (mUniformBlockUsedRegisterCount.count(interfaceBlock->uniqueId().get()) == 0)
218 {
219 // The uniform block is an instanced one, the count of used registers
220 // depends on the array size of variable.
221 mUniformBlockUsedRegisterCount[interfaceBlock->uniqueId().get()] =
222 variableType.isArray() ? variableType.getOutermostArraySize() : 1;
223 }
224 }
225 }
226
227 if (interfaceBlock->blockStorage() == EbsStd140 &&
228 IsInterfaceBlockWithLargeArrayField(*interfaceBlock))
229 {
230 if (!variableType.isInterfaceBlock())
231 {
232 TIntermNode *accessor = getAncestorNode(0);
233 TIntermBinary *accessorAsBinary = accessor->getAsBinaryNode();
234 if (accessorAsBinary && (accessorAsBinary->getOp() == EOpIndexDirect ||
235 accessorAsBinary->getOp() == EOpIndexIndirect))
236 {
237 if (mUniformBlockWithLargeArrayMember.count(
238 interfaceBlock->uniqueId().get()) == 0)
239 {
240 mUniformBlockWithLargeArrayMember[interfaceBlock->uniqueId().get()] =
241 interfaceBlock;
242 }
243 }
244 }
245 }
246 }
247 }
248 }
249
visitBinary(Visit visit,TIntermBinary * node)250 bool UniformBlocksWithLargeArrayMemberTraverser::visitBinary(Visit visit, TIntermBinary *node)
251 {
252 switch (node->getOp())
253 {
254 case EOpIndexDirect:
255 {
256 if (visit == PreVisit)
257 {
258 const TType &leftType = node->getLeft()->getType();
259 if (leftType.isInterfaceBlock())
260 {
261 const TInterfaceBlock *interfaceBlock = leftType.getInterfaceBlock();
262 if (CanTranslateUniformBlockToStructuredBuffer(*interfaceBlock) &&
263 mUniformBlockMayTranslation.count(interfaceBlock->uniqueId().get()) == 0)
264 {
265 mUniformBlockMayTranslation[interfaceBlock->uniqueId().get()] =
266 interfaceBlock;
267 if (mUniformBlockUsedRegisterCount.count(
268 interfaceBlock->uniqueId().get()) == 0)
269 {
270 // The uniform block is an instanced one, the count of used registers
271 // depends on the array size of variable.
272 mUniformBlockUsedRegisterCount[interfaceBlock->uniqueId().get()] =
273 leftType.isArray() ? leftType.getOutermostArraySize() : 1;
274 }
275 return false;
276 }
277
278 if (interfaceBlock->blockStorage() == EbsStd140 &&
279 IsInterfaceBlockWithLargeArrayField(*interfaceBlock))
280 {
281 if (mUniformBlockWithLargeArrayMember.count(
282 interfaceBlock->uniqueId().get()) == 0)
283 {
284 mUniformBlockWithLargeArrayMember[interfaceBlock->uniqueId().get()] =
285 interfaceBlock;
286 }
287 }
288 }
289 }
290 break;
291 }
292 case EOpIndexDirectInterfaceBlock:
293 {
294 if (visit == InVisit)
295 {
296 const TInterfaceBlock *interfaceBlock =
297 node->getLeft()->getType().getInterfaceBlock();
298 if (CanTranslateUniformBlockToStructuredBuffer(*interfaceBlock))
299 {
300 TIntermNode *accessor = getAncestorNode(0);
301 TIntermBinary *accessorAsBinary = accessor->getAsBinaryNode();
302 // The uniform block variable is array type, only indexing operator is allowed
303 // to operate on the variable, otherwise do not translate the uniform block to
304 // HLSL StructuredBuffer.
305 if ((!accessorAsBinary ||
306 !(accessorAsBinary && (accessorAsBinary->getOp() == EOpIndexDirect ||
307 accessorAsBinary->getOp() == EOpIndexIndirect))) &&
308 mUniformBlockNotAllowTranslation.count(interfaceBlock->uniqueId().get()) ==
309 0)
310 {
311 mUniformBlockNotAllowTranslation[interfaceBlock->uniqueId().get()] =
312 interfaceBlock;
313 return false;
314 }
315 }
316
317 if (interfaceBlock->blockStorage() == EbsStd140 &&
318 IsInterfaceBlockWithLargeArrayField(*interfaceBlock))
319 {
320 TIntermNode *accessor = getAncestorNode(0);
321 TIntermBinary *accessorAsBinary = accessor->getAsBinaryNode();
322 if (accessorAsBinary && (accessorAsBinary->getOp() == EOpIndexDirect ||
323 accessorAsBinary->getOp() == EOpIndexIndirect))
324 {
325 if (mUniformBlockWithLargeArrayMember.count(
326 interfaceBlock->uniqueId().get()) == 0)
327 {
328 mUniformBlockWithLargeArrayMember[interfaceBlock->uniqueId().get()] =
329 interfaceBlock;
330 }
331 }
332 }
333 }
334 break;
335 }
336 default:
337 break;
338 }
339
340 return true;
341 }
342 } // namespace
343
RecordUniformBlocksWithLargeArrayMember(TIntermNode * root,std::map<int,const TInterfaceBlock * > & uniformBlockOptimizedMap,std::set<std::string> & slowCompilingUniformBlockSet)344 bool RecordUniformBlocksWithLargeArrayMember(
345 TIntermNode *root,
346 std::map<int, const TInterfaceBlock *> &uniformBlockOptimizedMap,
347 std::set<std::string> &slowCompilingUniformBlockSet)
348 {
349 UniformBlocksWithLargeArrayMemberTraverser traverser;
350 root->traverse(&traverser);
351 std::map<int, const TInterfaceBlock *> &uniformBlockMayTranslation =
352 traverser.getUniformBlockMayTranslation();
353 std::map<int, const TInterfaceBlock *> &uniformBlockNotAllowTranslation =
354 traverser.getUniformBlockNotAllowTranslation();
355 std::map<int, unsigned int> &uniformBlockUsedRegisterCount =
356 traverser.getUniformBlockUsedRegisterCount();
357 std::map<int, const TInterfaceBlock *> &uniformBlockWithLargeArrayMember =
358 traverser.getUniformBlockWithLargeArrayMember();
359
360 unsigned int usedRegisterCount = 0;
361 for (auto &uniformBlock : uniformBlockMayTranslation)
362 {
363 if (uniformBlockNotAllowTranslation.count(uniformBlock.first) == 0)
364 {
365 usedRegisterCount += uniformBlockUsedRegisterCount[uniformBlock.first];
366 if (usedRegisterCount > kMaxAllowToUseRegisterCount)
367 {
368 break;
369 }
370 uniformBlockOptimizedMap[uniformBlock.first] = uniformBlock.second;
371 }
372 }
373
374 for (auto &uniformBlock : uniformBlockWithLargeArrayMember)
375 {
376 if (uniformBlockOptimizedMap.count(uniformBlock.first) == 0)
377 {
378 slowCompilingUniformBlockSet.insert(uniformBlock.second->name().data());
379 }
380 }
381
382 return true;
383 }
384
385 } // namespace sh
386