• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2018 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 // ShaderStorageBlockOutputHLSL: A traverser to translate a ssbo_access_chain to an offset of
7 // RWByteAddressBuffer.
8 //     //EOpIndexDirectInterfaceBlock
9 //     ssbo_variable :=
10 //       | the name of the SSBO
11 //       | the name of a variable in an SSBO backed interface block
12 
13 //     // EOpIndexInDirect
14 //     // EOpIndexDirect
15 //     ssbo_array_indexing := ssbo_access_chain[expr_no_ssbo]
16 
17 //     // EOpIndexDirectStruct
18 //     ssbo_structure_access := ssbo_access_chain.identifier
19 
20 //     ssbo_access_chain :=
21 //       | ssbo_variable
22 //       | ssbo_array_indexing
23 //       | ssbo_structure_access
24 //
25 
26 #include "compiler/translator/ShaderStorageBlockOutputHLSL.h"
27 
28 #include "compiler/translator/ResourcesHLSL.h"
29 #include "compiler/translator/blocklayoutHLSL.h"
30 #include "compiler/translator/util.h"
31 
32 namespace sh
33 {
34 
35 namespace
36 {
37 
GetBlockLayoutInfo(TIntermTyped * node,bool rowMajorAlreadyAssigned,TLayoutBlockStorage * storage,bool * rowMajor)38 void GetBlockLayoutInfo(TIntermTyped *node,
39                         bool rowMajorAlreadyAssigned,
40                         TLayoutBlockStorage *storage,
41                         bool *rowMajor)
42 {
43     TIntermSwizzle *swizzleNode = node->getAsSwizzleNode();
44     if (swizzleNode)
45     {
46         return GetBlockLayoutInfo(swizzleNode->getOperand(), rowMajorAlreadyAssigned, storage,
47                                   rowMajor);
48     }
49 
50     TIntermBinary *binaryNode = node->getAsBinaryNode();
51     if (binaryNode)
52     {
53         switch (binaryNode->getOp())
54         {
55             case EOpIndexDirectInterfaceBlock:
56             {
57                 // The column_major/row_major qualifier of a field member overrides the interface
58                 // block's row_major/column_major. So we can assign rowMajor here and don't need to
59                 // assign it again. But we still need to call recursively to get the storage's
60                 // value.
61                 const TType &type = node->getType();
62                 *rowMajor         = type.getLayoutQualifier().matrixPacking == EmpRowMajor;
63                 return GetBlockLayoutInfo(binaryNode->getLeft(), true, storage, rowMajor);
64             }
65             case EOpIndexIndirect:
66             case EOpIndexDirect:
67             case EOpIndexDirectStruct:
68                 return GetBlockLayoutInfo(binaryNode->getLeft(), rowMajorAlreadyAssigned, storage,
69                                           rowMajor);
70             default:
71                 UNREACHABLE();
72                 return;
73         }
74     }
75 
76     const TType &type = node->getType();
77     ASSERT(type.getQualifier() == EvqBuffer);
78     const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
79     ASSERT(interfaceBlock);
80     *storage = interfaceBlock->blockStorage();
81     // If the block doesn't have an instance name, rowMajorAlreadyAssigned will be false. In
82     // this situation, we still need to set rowMajor's value.
83     if (!rowMajorAlreadyAssigned)
84     {
85         *rowMajor = type.getLayoutQualifier().matrixPacking == EmpRowMajor;
86     }
87 }
88 
89 // It's possible that the current type has lost the original layout information. So we should pass
90 // the right layout information to GetBlockMemberInfoByType.
GetBlockMemberInfoByType(const TType & type,TLayoutBlockStorage storage,bool rowMajor)91 const BlockMemberInfo GetBlockMemberInfoByType(const TType &type,
92                                                TLayoutBlockStorage storage,
93                                                bool rowMajor)
94 {
95     sh::Std140BlockEncoder std140Encoder;
96     sh::Std430BlockEncoder std430Encoder;
97     sh::HLSLBlockEncoder hlslEncoder(sh::HLSLBlockEncoder::ENCODE_PACKED, false);
98     sh::BlockLayoutEncoder *encoder = nullptr;
99 
100     if (storage == EbsStd140)
101     {
102         encoder = &std140Encoder;
103     }
104     else if (storage == EbsStd430)
105     {
106         encoder = &std430Encoder;
107     }
108     else
109     {
110         encoder = &hlslEncoder;
111     }
112 
113     std::vector<unsigned int> arraySizes;
114     const TSpan<const unsigned int> &typeArraySizes = type.getArraySizes();
115     if (!typeArraySizes.empty())
116     {
117         arraySizes.assign(typeArraySizes.begin(), typeArraySizes.end());
118     }
119     return encoder->encodeType(GLVariableType(type), arraySizes, rowMajor);
120 }
121 
GetFieldMemberInShaderStorageBlock(const TInterfaceBlock * interfaceBlock,const ImmutableString & variableName)122 const TField *GetFieldMemberInShaderStorageBlock(const TInterfaceBlock *interfaceBlock,
123                                                  const ImmutableString &variableName)
124 {
125     for (const TField *field : interfaceBlock->fields())
126     {
127         if (field->name() == variableName)
128         {
129             return field;
130         }
131     }
132     return nullptr;
133 }
134 
FindInterfaceBlock(const TInterfaceBlock * needle,const std::vector<InterfaceBlock> & haystack)135 const InterfaceBlock *FindInterfaceBlock(const TInterfaceBlock *needle,
136                                          const std::vector<InterfaceBlock> &haystack)
137 {
138     for (const InterfaceBlock &block : haystack)
139     {
140         if (strcmp(block.name.c_str(), needle->name().data()) == 0)
141         {
142             ASSERT(block.fields.size() == needle->fields().size());
143             return &block;
144         }
145     }
146 
147     UNREACHABLE();
148     return nullptr;
149 }
150 
StripArrayIndices(const std::string & nameIn)151 std::string StripArrayIndices(const std::string &nameIn)
152 {
153     std::string name = nameIn;
154     size_t pos       = name.find('[');
155     while (pos != std::string::npos)
156     {
157         size_t closePos = name.find(']', pos);
158         ASSERT(closePos != std::string::npos);
159         name.erase(pos, closePos - pos + 1);
160         pos = name.find('[', pos);
161     }
162     ASSERT(name.find(']') == std::string::npos);
163     return name;
164 }
165 
166 // Does not include any array indices.
MapVariableToField(const ShaderVariable & variable,const TField * field,std::string currentName,ShaderVarToFieldMap * shaderVarToFieldMap)167 void MapVariableToField(const ShaderVariable &variable,
168                         const TField *field,
169                         std::string currentName,
170                         ShaderVarToFieldMap *shaderVarToFieldMap)
171 {
172     ASSERT((field->type()->getStruct() == nullptr) == variable.fields.empty());
173     (*shaderVarToFieldMap)[currentName] = field;
174 
175     if (!variable.fields.empty())
176     {
177         const TStructure *subStruct = field->type()->getStruct();
178         ASSERT(variable.fields.size() == subStruct->fields().size());
179 
180         for (size_t index = 0; index < variable.fields.size(); ++index)
181         {
182             const TField *subField            = subStruct->fields()[index];
183             const ShaderVariable &subVariable = variable.fields[index];
184             std::string subName               = currentName + "." + subVariable.name;
185             MapVariableToField(subVariable, subField, subName, shaderVarToFieldMap);
186         }
187     }
188 }
189 
190 class BlockInfoVisitor final : public BlockEncoderVisitor
191 {
192   public:
BlockInfoVisitor(const std::string & prefix,TLayoutBlockStorage storage,const ShaderVarToFieldMap & shaderVarToFieldMap,BlockMemberInfoMap * blockInfoOut)193     BlockInfoVisitor(const std::string &prefix,
194                      TLayoutBlockStorage storage,
195                      const ShaderVarToFieldMap &shaderVarToFieldMap,
196                      BlockMemberInfoMap *blockInfoOut)
197         : BlockEncoderVisitor(prefix, "", getEncoder(storage)),
198           mShaderVarToFieldMap(shaderVarToFieldMap),
199           mBlockInfoOut(blockInfoOut),
200           mHLSLEncoder(HLSLBlockEncoder::ENCODE_PACKED, false),
201           mStorage(storage)
202     {}
203 
getEncoder(TLayoutBlockStorage storage)204     BlockLayoutEncoder *getEncoder(TLayoutBlockStorage storage)
205     {
206         switch (storage)
207         {
208             case EbsStd140:
209                 return &mStd140Encoder;
210             case EbsStd430:
211                 return &mStd430Encoder;
212             default:
213                 return &mHLSLEncoder;
214         }
215     }
216 
enterStructAccess(const ShaderVariable & structVar,bool isRowMajor)217     void enterStructAccess(const ShaderVariable &structVar, bool isRowMajor) override
218     {
219         BlockEncoderVisitor::enterStructAccess(structVar, isRowMajor);
220 
221         std::string variableName = StripArrayIndices(collapseNameStack());
222 
223         // Remove the trailing "."
224         variableName.pop_back();
225 
226         BlockInfoVisitor childVisitor(variableName, mStorage, mShaderVarToFieldMap, mBlockInfoOut);
227         childVisitor.getEncoder(mStorage)->enterAggregateType(structVar);
228         TraverseShaderVariables(structVar.fields, isRowMajor, &childVisitor);
229         childVisitor.getEncoder(mStorage)->exitAggregateType(structVar);
230 
231         int offset      = static_cast<int>(getEncoder(mStorage)->getCurrentOffset());
232         int arrayStride = static_cast<int>(childVisitor.getEncoder(mStorage)->getCurrentOffset());
233 
234         auto iter = mShaderVarToFieldMap.find(variableName);
235         if (iter == mShaderVarToFieldMap.end())
236             return;
237 
238         const TField *structField = iter->second;
239         if (mBlockInfoOut->count(structField) == 0)
240         {
241             mBlockInfoOut->emplace(structField, BlockMemberInfo(offset, arrayStride, -1, false));
242         }
243     }
244 
encodeVariable(const ShaderVariable & variable,const BlockMemberInfo & variableInfo,const std::string & name,const std::string & mappedName)245     void encodeVariable(const ShaderVariable &variable,
246                         const BlockMemberInfo &variableInfo,
247                         const std::string &name,
248                         const std::string &mappedName) override
249     {
250         auto iter = mShaderVarToFieldMap.find(StripArrayIndices(name));
251         if (iter == mShaderVarToFieldMap.end())
252             return;
253 
254         const TField *field = iter->second;
255         if (mBlockInfoOut->count(field) == 0)
256         {
257             mBlockInfoOut->emplace(field, variableInfo);
258         }
259     }
260 
261   private:
262     const ShaderVarToFieldMap &mShaderVarToFieldMap;
263     BlockMemberInfoMap *mBlockInfoOut;
264     Std140BlockEncoder mStd140Encoder;
265     Std430BlockEncoder mStd430Encoder;
266     HLSLBlockEncoder mHLSLEncoder;
267     TLayoutBlockStorage mStorage;
268 };
269 
GetShaderStorageBlockMembersInfo(const TInterfaceBlock * interfaceBlock,const std::vector<InterfaceBlock> & shaderStorageBlocks,BlockMemberInfoMap * blockInfoOut)270 void GetShaderStorageBlockMembersInfo(const TInterfaceBlock *interfaceBlock,
271                                       const std::vector<InterfaceBlock> &shaderStorageBlocks,
272                                       BlockMemberInfoMap *blockInfoOut)
273 {
274     // Find the sh::InterfaceBlock.
275     const InterfaceBlock *block = FindInterfaceBlock(interfaceBlock, shaderStorageBlocks);
276     ASSERT(block);
277 
278     // Map ShaderVariable to TField.
279     ShaderVarToFieldMap shaderVarToFieldMap;
280     for (size_t index = 0; index < block->fields.size(); ++index)
281     {
282         const TField *field            = interfaceBlock->fields()[index];
283         const ShaderVariable &variable = block->fields[index];
284         MapVariableToField(variable, field, variable.name, &shaderVarToFieldMap);
285     }
286 
287     BlockInfoVisitor visitor("", interfaceBlock->blockStorage(), shaderVarToFieldMap, blockInfoOut);
288     TraverseShaderVariables(block->fields, false, &visitor);
289 }
290 
IsInArrayOfArraysChain(TIntermTyped * node)291 bool IsInArrayOfArraysChain(TIntermTyped *node)
292 {
293     if (node->getType().isArrayOfArrays())
294         return true;
295     TIntermBinary *binaryNode = node->getAsBinaryNode();
296     if (binaryNode)
297     {
298         if (binaryNode->getLeft()->getType().isArrayOfArrays())
299             return true;
300     }
301 
302     return false;
303 }
304 }  // anonymous namespace
305 
ShaderStorageBlockOutputHLSL(OutputHLSL * outputHLSL,TSymbolTable * symbolTable,ResourcesHLSL * resourcesHLSL,const std::vector<InterfaceBlock> & shaderStorageBlocks)306 ShaderStorageBlockOutputHLSL::ShaderStorageBlockOutputHLSL(
307     OutputHLSL *outputHLSL,
308     TSymbolTable *symbolTable,
309     ResourcesHLSL *resourcesHLSL,
310     const std::vector<InterfaceBlock> &shaderStorageBlocks)
311     : TIntermTraverser(true, true, true, symbolTable),
312       mMatrixStride(0),
313       mRowMajor(false),
314       mLocationAsTheLastArgument(false),
315       mOutputHLSL(outputHLSL),
316       mResourcesHLSL(resourcesHLSL),
317       mShaderStorageBlocks(shaderStorageBlocks)
318 {
319     mSSBOFunctionHLSL = new ShaderStorageBlockFunctionHLSL;
320 }
321 
~ShaderStorageBlockOutputHLSL()322 ShaderStorageBlockOutputHLSL::~ShaderStorageBlockOutputHLSL()
323 {
324     SafeDelete(mSSBOFunctionHLSL);
325 }
326 
outputStoreFunctionCallPrefix(TIntermTyped * node)327 void ShaderStorageBlockOutputHLSL::outputStoreFunctionCallPrefix(TIntermTyped *node)
328 {
329     mLocationAsTheLastArgument = false;
330     traverseSSBOAccess(node, SSBOMethod::STORE);
331 }
332 
outputLoadFunctionCall(TIntermTyped * node)333 void ShaderStorageBlockOutputHLSL::outputLoadFunctionCall(TIntermTyped *node)
334 {
335     mLocationAsTheLastArgument = true;
336     traverseSSBOAccess(node, SSBOMethod::LOAD);
337 }
338 
outputLengthFunctionCall(TIntermTyped * node)339 void ShaderStorageBlockOutputHLSL::outputLengthFunctionCall(TIntermTyped *node)
340 {
341     mLocationAsTheLastArgument = true;
342     traverseSSBOAccess(node, SSBOMethod::LENGTH);
343 }
344 
outputAtomicMemoryFunctionCallPrefix(TIntermTyped * node,TOperator op)345 void ShaderStorageBlockOutputHLSL::outputAtomicMemoryFunctionCallPrefix(TIntermTyped *node,
346                                                                         TOperator op)
347 {
348     mLocationAsTheLastArgument = false;
349 
350     switch (op)
351     {
352         case EOpAtomicAdd:
353             traverseSSBOAccess(node, SSBOMethod::ATOMIC_ADD);
354             break;
355         case EOpAtomicMin:
356             traverseSSBOAccess(node, SSBOMethod::ATOMIC_MIN);
357             break;
358         case EOpAtomicMax:
359             traverseSSBOAccess(node, SSBOMethod::ATOMIC_MAX);
360             break;
361         case EOpAtomicAnd:
362             traverseSSBOAccess(node, SSBOMethod::ATOMIC_AND);
363             break;
364         case EOpAtomicOr:
365             traverseSSBOAccess(node, SSBOMethod::ATOMIC_OR);
366             break;
367         case EOpAtomicXor:
368             traverseSSBOAccess(node, SSBOMethod::ATOMIC_XOR);
369             break;
370         case EOpAtomicExchange:
371             traverseSSBOAccess(node, SSBOMethod::ATOMIC_EXCHANGE);
372             break;
373         case EOpAtomicCompSwap:
374             traverseSSBOAccess(node, SSBOMethod::ATOMIC_COMPSWAP);
375             break;
376         default:
377             UNREACHABLE();
378             break;
379     }
380 }
381 
382 // Note that we must calculate the matrix stride here instead of ShaderStorageBlockFunctionHLSL.
383 // It's because that if the current node's type is a vector which comes from a matrix, we will
384 // lose the matrix type info once we enter ShaderStorageBlockFunctionHLSL.
setMatrixStride(TIntermTyped * node,TLayoutBlockStorage storage,bool rowMajor)385 void ShaderStorageBlockOutputHLSL::setMatrixStride(TIntermTyped *node,
386                                                    TLayoutBlockStorage storage,
387                                                    bool rowMajor)
388 {
389     if (node->getType().isMatrix())
390     {
391         mMatrixStride = GetBlockMemberInfoByType(node->getType(), storage, rowMajor).matrixStride;
392         mRowMajor     = rowMajor;
393         return;
394     }
395 
396     if (node->getType().isVector())
397     {
398         TIntermBinary *binaryNode = node->getAsBinaryNode();
399         if (binaryNode)
400         {
401             return setMatrixStride(binaryNode->getLeft(), storage, rowMajor);
402         }
403         else
404         {
405             TIntermSwizzle *swizzleNode = node->getAsSwizzleNode();
406             if (swizzleNode)
407             {
408                 return setMatrixStride(swizzleNode->getOperand(), storage, rowMajor);
409             }
410         }
411     }
412 }
413 
traverseSSBOAccess(TIntermTyped * node,SSBOMethod method)414 void ShaderStorageBlockOutputHLSL::traverseSSBOAccess(TIntermTyped *node, SSBOMethod method)
415 {
416     mMatrixStride = 0;
417     mRowMajor     = false;
418 
419     // Note that we don't have correct BlockMemberInfo from mBlockMemberInfoMap at the current
420     // point. But we must use those information to generate the right function name. So here we have
421     // to calculate them again.
422     TLayoutBlockStorage storage;
423     bool rowMajor;
424     GetBlockLayoutInfo(node, false, &storage, &rowMajor);
425     int unsizedArrayStride = 0;
426     if (node->getType().isUnsizedArray())
427     {
428         unsizedArrayStride =
429             GetBlockMemberInfoByType(node->getType(), storage, rowMajor).arrayStride;
430     }
431     setMatrixStride(node, storage, rowMajor);
432 
433     const TString &functionName = mSSBOFunctionHLSL->registerShaderStorageBlockFunction(
434         node->getType(), method, storage, mRowMajor, mMatrixStride, unsizedArrayStride,
435         node->getAsSwizzleNode());
436     TInfoSinkBase &out = mOutputHLSL->getInfoSink();
437     out << functionName;
438     out << "(";
439     node->traverse(this);
440 }
441 
writeShaderStorageBlocksHeader(TInfoSinkBase & out) const442 void ShaderStorageBlockOutputHLSL::writeShaderStorageBlocksHeader(TInfoSinkBase &out) const
443 {
444     out << mResourcesHLSL->shaderStorageBlocksHeader(mReferencedShaderStorageBlocks);
445     mSSBOFunctionHLSL->shaderStorageBlockFunctionHeader(out);
446 }
447 
448 // Check if the current node is the end of the SSBO access chain. If true, we should output ')' for
449 // Load method.
isEndOfSSBOAccessChain()450 bool ShaderStorageBlockOutputHLSL::isEndOfSSBOAccessChain()
451 {
452     TIntermNode *parent = getParentNode();
453     if (parent)
454     {
455         TIntermBinary *parentBinary = parent->getAsBinaryNode();
456         if (parentBinary != nullptr)
457         {
458             switch (parentBinary->getOp())
459             {
460                 case EOpIndexDirectStruct:
461                 case EOpIndexDirect:
462                 case EOpIndexIndirect:
463                 {
464                     return false;
465                 }
466                 default:
467                     return true;
468             }
469         }
470 
471         const TIntermSwizzle *parentSwizzle = parent->getAsSwizzleNode();
472         if (parentSwizzle)
473         {
474             return false;
475         }
476     }
477     return true;
478 }
479 
visitSymbol(TIntermSymbol * node)480 void ShaderStorageBlockOutputHLSL::visitSymbol(TIntermSymbol *node)
481 {
482     TInfoSinkBase &out        = mOutputHLSL->getInfoSink();
483     const TVariable &variable = node->variable();
484     TQualifier qualifier      = variable.getType().getQualifier();
485 
486     if (qualifier == EvqBuffer)
487     {
488         const TType &variableType             = variable.getType();
489         const TInterfaceBlock *interfaceBlock = variableType.getInterfaceBlock();
490         ASSERT(interfaceBlock);
491         if (mReferencedShaderStorageBlocks.count(interfaceBlock->uniqueId().get()) == 0)
492         {
493             const TVariable *instanceVariable = nullptr;
494             if (variableType.isInterfaceBlock())
495             {
496                 instanceVariable = &variable;
497             }
498             mReferencedShaderStorageBlocks[interfaceBlock->uniqueId().get()] =
499                 new TReferencedBlock(interfaceBlock, instanceVariable);
500             GetShaderStorageBlockMembersInfo(interfaceBlock, mShaderStorageBlocks,
501                                              &mBlockMemberInfoMap);
502         }
503         if (variableType.isInterfaceBlock())
504         {
505             out << DecorateVariableIfNeeded(variable);
506         }
507         else
508         {
509             out << Decorate(interfaceBlock->name());
510             out << ", ";
511 
512             const TField *field =
513                 GetFieldMemberInShaderStorageBlock(interfaceBlock, variable.name());
514             writeDotOperatorOutput(out, field);
515         }
516     }
517     else
518     {
519         return mOutputHLSL->visitSymbol(node);
520     }
521 }
522 
visitConstantUnion(TIntermConstantUnion * node)523 void ShaderStorageBlockOutputHLSL::visitConstantUnion(TIntermConstantUnion *node)
524 {
525     mOutputHLSL->visitConstantUnion(node);
526 }
527 
visitAggregate(Visit visit,TIntermAggregate * node)528 bool ShaderStorageBlockOutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node)
529 {
530     return mOutputHLSL->visitAggregate(visit, node);
531 }
532 
visitTernary(Visit visit,TIntermTernary * node)533 bool ShaderStorageBlockOutputHLSL::visitTernary(Visit visit, TIntermTernary *node)
534 {
535     return mOutputHLSL->visitTernary(visit, node);
536 }
537 
visitUnary(Visit visit,TIntermUnary * node)538 bool ShaderStorageBlockOutputHLSL::visitUnary(Visit visit, TIntermUnary *node)
539 {
540     return mOutputHLSL->visitUnary(visit, node);
541 }
542 
visitSwizzle(Visit visit,TIntermSwizzle * node)543 bool ShaderStorageBlockOutputHLSL::visitSwizzle(Visit visit, TIntermSwizzle *node)
544 {
545     if (visit == PostVisit)
546     {
547         if (!IsInShaderStorageBlock(node))
548         {
549             return mOutputHLSL->visitSwizzle(visit, node);
550         }
551 
552         TInfoSinkBase &out = mOutputHLSL->getInfoSink();
553         // TODO(jiajia.qin@intel.com): add swizzle process if the swizzle node is not the last node
554         // of ssbo access chain. Such as, data.xy[0]
555         if (mLocationAsTheLastArgument && isEndOfSSBOAccessChain())
556         {
557             out << ")";
558         }
559     }
560     return true;
561 }
562 
visitBinary(Visit visit,TIntermBinary * node)563 bool ShaderStorageBlockOutputHLSL::visitBinary(Visit visit, TIntermBinary *node)
564 {
565     TInfoSinkBase &out = mOutputHLSL->getInfoSink();
566 
567     switch (node->getOp())
568     {
569         case EOpIndexDirect:
570         {
571             if (!IsInShaderStorageBlock(node->getLeft()))
572             {
573                 return mOutputHLSL->visitBinary(visit, node);
574             }
575 
576             const TType &leftType = node->getLeft()->getType();
577             if (leftType.isInterfaceBlock())
578             {
579                 if (visit == PreVisit)
580                 {
581                     ASSERT(leftType.getQualifier() == EvqBuffer);
582                     TIntermSymbol *instanceArraySymbol    = node->getLeft()->getAsSymbolNode();
583                     const TInterfaceBlock *interfaceBlock = leftType.getInterfaceBlock();
584 
585                     if (mReferencedShaderStorageBlocks.count(interfaceBlock->uniqueId().get()) == 0)
586                     {
587                         mReferencedShaderStorageBlocks[interfaceBlock->uniqueId().get()] =
588                             new TReferencedBlock(interfaceBlock, &instanceArraySymbol->variable());
589                         GetShaderStorageBlockMembersInfo(interfaceBlock, mShaderStorageBlocks,
590                                                          &mBlockMemberInfoMap);
591                     }
592 
593                     const int arrayIndex = node->getRight()->getAsConstantUnion()->getIConst(0);
594                     out << mResourcesHLSL->InterfaceBlockInstanceString(
595                         instanceArraySymbol->getName(), arrayIndex);
596                     return false;
597                 }
598             }
599             else
600             {
601                 writeEOpIndexDirectOrIndirectOutput(out, visit, node);
602             }
603             break;
604         }
605         case EOpIndexIndirect:
606         {
607             if (!IsInShaderStorageBlock(node->getLeft()))
608             {
609                 return mOutputHLSL->visitBinary(visit, node);
610             }
611 
612             // We do not currently support indirect references to interface blocks
613             ASSERT(node->getLeft()->getBasicType() != EbtInterfaceBlock);
614             writeEOpIndexDirectOrIndirectOutput(out, visit, node);
615             break;
616         }
617         case EOpIndexDirectStruct:
618         {
619             if (!IsInShaderStorageBlock(node->getLeft()))
620             {
621                 return mOutputHLSL->visitBinary(visit, node);
622             }
623 
624             if (visit == InVisit)
625             {
626                 ASSERT(IsInShaderStorageBlock(node->getLeft()));
627                 const TStructure *structure       = node->getLeft()->getType().getStruct();
628                 const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion();
629                 const TField *field               = structure->fields()[index->getIConst(0)];
630                 out << " + ";
631                 writeDotOperatorOutput(out, field);
632                 return false;
633             }
634             break;
635         }
636         case EOpIndexDirectInterfaceBlock:
637             if (!IsInShaderStorageBlock(node->getLeft()))
638             {
639                 return mOutputHLSL->visitBinary(visit, node);
640             }
641 
642             if (visit == InVisit)
643             {
644                 ASSERT(IsInShaderStorageBlock(node->getLeft()));
645                 out << ", ";
646                 const TInterfaceBlock *interfaceBlock =
647                     node->getLeft()->getType().getInterfaceBlock();
648                 const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion();
649                 const TField *field               = interfaceBlock->fields()[index->getIConst(0)];
650                 writeDotOperatorOutput(out, field);
651                 return false;
652             }
653             break;
654         default:
655             // It may have other operators in EOpIndexIndirect. Such as buffer.attribs[(y * gridSize
656             // + x) * 6u + 0u]
657             return mOutputHLSL->visitBinary(visit, node);
658     }
659 
660     return true;
661 }
662 
writeEOpIndexDirectOrIndirectOutput(TInfoSinkBase & out,Visit visit,TIntermBinary * node)663 void ShaderStorageBlockOutputHLSL::writeEOpIndexDirectOrIndirectOutput(TInfoSinkBase &out,
664                                                                        Visit visit,
665                                                                        TIntermBinary *node)
666 {
667     ASSERT(IsInShaderStorageBlock(node->getLeft()));
668     if (visit == InVisit)
669     {
670         const TType &type = node->getLeft()->getType();
671         // For array of arrays, we calculate the offset using the formula below:
672         // elementStride * (a3 * a2 * a1 * i0 + a3 * a2 * i1 + a3 * i2 + i3)
673         // Note: assume that there are 4 dimensions.
674         //       a0, a1, a2, a3 is the size of the array in each dimension. (S s[a0][a1][a2][a3])
675         //       i0, i1, i2, i3 is the index of the array in each dimension. (s[i0][i1][i2][i3])
676         if (IsInArrayOfArraysChain(node->getLeft()))
677         {
678             if (type.isArrayOfArrays())
679             {
680                 const TSpan<const unsigned int> &arraySizes = type.getArraySizes();
681                 // Don't need to concern the tail comma which will be used to multiply the index.
682                 for (unsigned int i = 0; i < (arraySizes.size() - 1); i++)
683                 {
684                     out << arraySizes[i];
685                     out << " * ";
686                 }
687             }
688         }
689         else
690         {
691             if (node->getType().isVector() && type.isMatrix())
692             {
693                 if (mRowMajor)
694                 {
695                     out << " + " << str(BlockLayoutEncoder::kBytesPerComponent);
696                 }
697                 else
698                 {
699                     out << " + " << str(mMatrixStride);
700                 }
701             }
702             else if (node->getType().isScalar() && !type.isArray())
703             {
704                 if (mRowMajor)
705                 {
706                     out << " + " << str(mMatrixStride);
707                 }
708                 else
709                 {
710                     out << " + " << str(BlockLayoutEncoder::kBytesPerComponent);
711                 }
712             }
713 
714             out << " * ";
715         }
716     }
717     else if (visit == PostVisit)
718     {
719         // This is used to output the '+' in the array of arrays formula in above.
720         if (node->getType().isArray() && !isEndOfSSBOAccessChain())
721         {
722             out << " + ";
723         }
724         // This corresponds to '(' in writeDotOperatorOutput when fieldType.isArrayOfArrays() is
725         // true.
726         if (IsInArrayOfArraysChain(node->getLeft()) && !node->getType().isArray())
727         {
728             out << ")";
729         }
730         if (mLocationAsTheLastArgument && isEndOfSSBOAccessChain())
731         {
732             out << ")";
733         }
734     }
735 }
736 
writeDotOperatorOutput(TInfoSinkBase & out,const TField * field)737 void ShaderStorageBlockOutputHLSL::writeDotOperatorOutput(TInfoSinkBase &out, const TField *field)
738 {
739     auto fieldInfoIter = mBlockMemberInfoMap.find(field);
740     ASSERT(fieldInfoIter != mBlockMemberInfoMap.end());
741     const BlockMemberInfo &memberInfo = fieldInfoIter->second;
742     mMatrixStride                     = memberInfo.matrixStride;
743     mRowMajor                         = memberInfo.isRowMajorMatrix;
744     out << memberInfo.offset;
745 
746     const TType &fieldType = *field->type();
747     if (fieldType.isArray() && !isEndOfSSBOAccessChain())
748     {
749         out << " + ";
750         out << memberInfo.arrayStride;
751         if (fieldType.isArrayOfArrays())
752         {
753             out << " * (";
754         }
755     }
756     if (mLocationAsTheLastArgument && isEndOfSSBOAccessChain())
757     {
758         out << ")";
759     }
760 }
761 
762 }  // namespace sh
763