• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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