• 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/hlsl/ShaderStorageBlockOutputHLSL.h"
27 
28 #include "common/span.h"
29 #include "compiler/translator/hlsl/ResourcesHLSL.h"
30 #include "compiler/translator/hlsl/blocklayoutHLSL.h"
31 #include "compiler/translator/tree_util/IntermNode_util.h"
32 #include "compiler/translator/util.h"
33 
34 namespace sh
35 {
36 
37 namespace
38 {
39 
40 constexpr const char kShaderStorageDeclarationString[] =
41     "// @@ SHADER STORAGE DECLARATION STRING @@";
42 
GetBlockLayoutInfo(TIntermTyped * node,bool rowMajorAlreadyAssigned,TLayoutBlockStorage * storage,bool * rowMajor)43 void GetBlockLayoutInfo(TIntermTyped *node,
44                         bool rowMajorAlreadyAssigned,
45                         TLayoutBlockStorage *storage,
46                         bool *rowMajor)
47 {
48     TIntermSwizzle *swizzleNode = node->getAsSwizzleNode();
49     if (swizzleNode)
50     {
51         return GetBlockLayoutInfo(swizzleNode->getOperand(), rowMajorAlreadyAssigned, storage,
52                                   rowMajor);
53     }
54 
55     TIntermBinary *binaryNode = node->getAsBinaryNode();
56     if (binaryNode)
57     {
58         switch (binaryNode->getOp())
59         {
60             case EOpIndexDirectInterfaceBlock:
61             {
62                 // The column_major/row_major qualifier of a field member overrides the interface
63                 // block's row_major/column_major. So we can assign rowMajor here and don't need to
64                 // assign it again. But we still need to call recursively to get the storage's
65                 // value.
66                 const TType &type = node->getType();
67                 *rowMajor         = type.getLayoutQualifier().matrixPacking == EmpRowMajor;
68                 return GetBlockLayoutInfo(binaryNode->getLeft(), true, storage, rowMajor);
69             }
70             case EOpIndexIndirect:
71             case EOpIndexDirect:
72             case EOpIndexDirectStruct:
73                 return GetBlockLayoutInfo(binaryNode->getLeft(), rowMajorAlreadyAssigned, storage,
74                                           rowMajor);
75             default:
76                 UNREACHABLE();
77                 return;
78         }
79     }
80 
81     const TType &type = node->getType();
82     ASSERT(type.getQualifier() == EvqBuffer);
83     const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
84     ASSERT(interfaceBlock);
85     *storage = interfaceBlock->blockStorage();
86     // If the block doesn't have an instance name, rowMajorAlreadyAssigned will be false. In
87     // this situation, we still need to set rowMajor's value.
88     if (!rowMajorAlreadyAssigned)
89     {
90         *rowMajor = type.getLayoutQualifier().matrixPacking == EmpRowMajor;
91     }
92 }
93 
94 // It's possible that the current type has lost the original layout information. So we should pass
95 // the right layout information to GetBlockMemberInfoByType.
GetBlockMemberInfoByType(const TType & type,TLayoutBlockStorage storage,bool rowMajor)96 const BlockMemberInfo GetBlockMemberInfoByType(const TType &type,
97                                                TLayoutBlockStorage storage,
98                                                bool rowMajor)
99 {
100     sh::Std140BlockEncoder std140Encoder;
101     sh::Std430BlockEncoder std430Encoder;
102     sh::HLSLBlockEncoder hlslEncoder(sh::HLSLBlockEncoder::ENCODE_PACKED, false);
103     sh::BlockLayoutEncoder *encoder = nullptr;
104 
105     if (storage == EbsStd140)
106     {
107         encoder = &std140Encoder;
108     }
109     else if (storage == EbsStd430)
110     {
111         encoder = &std430Encoder;
112     }
113     else
114     {
115         encoder = &hlslEncoder;
116     }
117 
118     std::vector<unsigned int> arraySizes;
119     const angle::Span<const unsigned int> &typeArraySizes = type.getArraySizes();
120     if (!typeArraySizes.empty())
121     {
122         arraySizes.assign(typeArraySizes.begin(), typeArraySizes.end());
123     }
124     return encoder->encodeType(GLVariableType(type), arraySizes, rowMajor);
125 }
126 
GetFieldMemberInShaderStorageBlock(const TInterfaceBlock * interfaceBlock,const ImmutableString & variableName)127 const TField *GetFieldMemberInShaderStorageBlock(const TInterfaceBlock *interfaceBlock,
128                                                  const ImmutableString &variableName)
129 {
130     for (const TField *field : interfaceBlock->fields())
131     {
132         if (field->name() == variableName)
133         {
134             return field;
135         }
136     }
137     return nullptr;
138 }
139 
FindInterfaceBlock(const TInterfaceBlock * needle,const std::vector<InterfaceBlock> & haystack)140 const InterfaceBlock *FindInterfaceBlock(const TInterfaceBlock *needle,
141                                          const std::vector<InterfaceBlock> &haystack)
142 {
143     for (const InterfaceBlock &block : haystack)
144     {
145         if (strcmp(block.name.c_str(), needle->name().data()) == 0)
146         {
147             ASSERT(block.fields.size() == needle->fields().size());
148             return &block;
149         }
150     }
151 
152     UNREACHABLE();
153     return nullptr;
154 }
155 
StripArrayIndices(const std::string & nameIn)156 std::string StripArrayIndices(const std::string &nameIn)
157 {
158     std::string name = nameIn;
159     size_t pos       = name.find('[');
160     while (pos != std::string::npos)
161     {
162         size_t closePos = name.find(']', pos);
163         ASSERT(closePos != std::string::npos);
164         name.erase(pos, closePos - pos + 1);
165         pos = name.find('[', pos);
166     }
167     ASSERT(name.find(']') == std::string::npos);
168     return name;
169 }
170 
171 // Does not include any array indices.
MapVariableToField(const ShaderVariable & variable,const TField * field,std::string currentName,ShaderVarToFieldMap * shaderVarToFieldMap)172 void MapVariableToField(const ShaderVariable &variable,
173                         const TField *field,
174                         std::string currentName,
175                         ShaderVarToFieldMap *shaderVarToFieldMap)
176 {
177     ASSERT((field->type()->getStruct() == nullptr) == variable.fields.empty());
178     (*shaderVarToFieldMap)[currentName] = field;
179 
180     if (!variable.fields.empty())
181     {
182         const TStructure *subStruct = field->type()->getStruct();
183         ASSERT(variable.fields.size() == subStruct->fields().size());
184 
185         for (size_t index = 0; index < variable.fields.size(); ++index)
186         {
187             const TField *subField            = subStruct->fields()[index];
188             const ShaderVariable &subVariable = variable.fields[index];
189             std::string subName               = currentName + "." + subVariable.name;
190             MapVariableToField(subVariable, subField, subName, shaderVarToFieldMap);
191         }
192     }
193 }
194 
195 class BlockInfoVisitor final : public BlockEncoderVisitor
196 {
197   public:
BlockInfoVisitor(const std::string & prefix,TLayoutBlockStorage storage,const ShaderVarToFieldMap & shaderVarToFieldMap,BlockMemberInfoMap * blockInfoOut)198     BlockInfoVisitor(const std::string &prefix,
199                      TLayoutBlockStorage storage,
200                      const ShaderVarToFieldMap &shaderVarToFieldMap,
201                      BlockMemberInfoMap *blockInfoOut)
202         : BlockEncoderVisitor(prefix, "", getEncoder(storage)),
203           mShaderVarToFieldMap(shaderVarToFieldMap),
204           mBlockInfoOut(blockInfoOut),
205           mHLSLEncoder(HLSLBlockEncoder::ENCODE_PACKED, false),
206           mStorage(storage)
207     {}
208 
getEncoder(TLayoutBlockStorage storage)209     BlockLayoutEncoder *getEncoder(TLayoutBlockStorage storage)
210     {
211         switch (storage)
212         {
213             case EbsStd140:
214                 return &mStd140Encoder;
215             case EbsStd430:
216                 return &mStd430Encoder;
217             default:
218                 return &mHLSLEncoder;
219         }
220     }
221 
enterStructAccess(const ShaderVariable & structVar,bool isRowMajor)222     void enterStructAccess(const ShaderVariable &structVar, bool isRowMajor) override
223     {
224         BlockEncoderVisitor::enterStructAccess(structVar, isRowMajor);
225 
226         std::string variableName = StripArrayIndices(collapseNameStack());
227 
228         // Remove the trailing "."
229         variableName.pop_back();
230 
231         BlockInfoVisitor childVisitor(variableName, mStorage, mShaderVarToFieldMap, mBlockInfoOut);
232         childVisitor.getEncoder(mStorage)->enterAggregateType(structVar);
233         TraverseShaderVariables(structVar.fields, isRowMajor, &childVisitor);
234         childVisitor.getEncoder(mStorage)->exitAggregateType(structVar);
235 
236         int offset      = static_cast<int>(getEncoder(mStorage)->getCurrentOffset());
237         int arrayStride = static_cast<int>(childVisitor.getEncoder(mStorage)->getCurrentOffset());
238 
239         auto iter = mShaderVarToFieldMap.find(variableName);
240         if (iter == mShaderVarToFieldMap.end())
241             return;
242 
243         const TField *structField = iter->second;
244         if (mBlockInfoOut->count(structField) == 0)
245         {
246             mBlockInfoOut->emplace(structField, BlockMemberInfo(offset, arrayStride, -1, false));
247         }
248     }
249 
encodeVariable(const ShaderVariable & variable,const BlockMemberInfo & variableInfo,const std::string & name,const std::string & mappedName)250     void encodeVariable(const ShaderVariable &variable,
251                         const BlockMemberInfo &variableInfo,
252                         const std::string &name,
253                         const std::string &mappedName) override
254     {
255         auto iter = mShaderVarToFieldMap.find(StripArrayIndices(name));
256         if (iter == mShaderVarToFieldMap.end())
257             return;
258 
259         const TField *field = iter->second;
260         if (mBlockInfoOut->count(field) == 0)
261         {
262             mBlockInfoOut->emplace(field, variableInfo);
263         }
264     }
265 
266   private:
267     const ShaderVarToFieldMap &mShaderVarToFieldMap;
268     BlockMemberInfoMap *mBlockInfoOut;
269     Std140BlockEncoder mStd140Encoder;
270     Std430BlockEncoder mStd430Encoder;
271     HLSLBlockEncoder mHLSLEncoder;
272     TLayoutBlockStorage mStorage;
273 };
274 
GetShaderStorageBlockMembersInfo(const TInterfaceBlock * interfaceBlock,const std::vector<InterfaceBlock> & shaderStorageBlocks,BlockMemberInfoMap * blockInfoOut)275 void GetShaderStorageBlockMembersInfo(const TInterfaceBlock *interfaceBlock,
276                                       const std::vector<InterfaceBlock> &shaderStorageBlocks,
277                                       BlockMemberInfoMap *blockInfoOut)
278 {
279     // Find the sh::InterfaceBlock.
280     const InterfaceBlock *block = FindInterfaceBlock(interfaceBlock, shaderStorageBlocks);
281     ASSERT(block);
282 
283     // Map ShaderVariable to TField.
284     ShaderVarToFieldMap shaderVarToFieldMap;
285     for (size_t index = 0; index < block->fields.size(); ++index)
286     {
287         const TField *field            = interfaceBlock->fields()[index];
288         const ShaderVariable &variable = block->fields[index];
289         MapVariableToField(variable, field, variable.name, &shaderVarToFieldMap);
290     }
291 
292     BlockInfoVisitor visitor("", interfaceBlock->blockStorage(), shaderVarToFieldMap, blockInfoOut);
293     TraverseShaderVariables(block->fields, false, &visitor);
294 }
295 
Mul(TIntermTyped * left,TIntermTyped * right)296 TIntermTyped *Mul(TIntermTyped *left, TIntermTyped *right)
297 {
298     return left && right ? new TIntermBinary(EOpMul, left, right) : nullptr;
299 }
300 
Add(TIntermTyped * left,TIntermTyped * right)301 TIntermTyped *Add(TIntermTyped *left, TIntermTyped *right)
302 {
303     return left ? right ? new TIntermBinary(EOpAdd, left, right) : left : right;
304 }
305 
306 }  // anonymous namespace
307 
ShaderStorageBlockOutputHLSL(OutputHLSL * outputHLSL,ResourcesHLSL * resourcesHLSL,const std::vector<InterfaceBlock> & shaderStorageBlocks)308 ShaderStorageBlockOutputHLSL::ShaderStorageBlockOutputHLSL(
309     OutputHLSL *outputHLSL,
310     ResourcesHLSL *resourcesHLSL,
311     const std::vector<InterfaceBlock> &shaderStorageBlocks)
312     : mOutputHLSL(outputHLSL),
313       mResourcesHLSL(resourcesHLSL),
314       mShaderStorageBlocks(shaderStorageBlocks)
315 {
316     mSSBOFunctionHLSL = new ShaderStorageBlockFunctionHLSL;
317 }
318 
~ShaderStorageBlockOutputHLSL()319 ShaderStorageBlockOutputHLSL::~ShaderStorageBlockOutputHLSL()
320 {
321     SafeDelete(mSSBOFunctionHLSL);
322 }
323 
outputStoreFunctionCallPrefix(TIntermTyped * node)324 void ShaderStorageBlockOutputHLSL::outputStoreFunctionCallPrefix(TIntermTyped *node)
325 {
326     traverseSSBOAccess(node, SSBOMethod::STORE);
327 }
328 
outputLoadFunctionCall(TIntermTyped * node)329 void ShaderStorageBlockOutputHLSL::outputLoadFunctionCall(TIntermTyped *node)
330 {
331     traverseSSBOAccess(node, SSBOMethod::LOAD);
332     mOutputHLSL->getInfoSink() << ")";
333 }
334 
outputLengthFunctionCall(TIntermTyped * node)335 void ShaderStorageBlockOutputHLSL::outputLengthFunctionCall(TIntermTyped *node)
336 {
337     traverseSSBOAccess(node, SSBOMethod::LENGTH);
338     mOutputHLSL->getInfoSink() << ")";
339 }
340 
outputAtomicMemoryFunctionCallPrefix(TIntermTyped * node,TOperator op)341 void ShaderStorageBlockOutputHLSL::outputAtomicMemoryFunctionCallPrefix(TIntermTyped *node,
342                                                                         TOperator op)
343 {
344     switch (op)
345     {
346         case EOpAtomicAdd:
347             traverseSSBOAccess(node, SSBOMethod::ATOMIC_ADD);
348             break;
349         case EOpAtomicMin:
350             traverseSSBOAccess(node, SSBOMethod::ATOMIC_MIN);
351             break;
352         case EOpAtomicMax:
353             traverseSSBOAccess(node, SSBOMethod::ATOMIC_MAX);
354             break;
355         case EOpAtomicAnd:
356             traverseSSBOAccess(node, SSBOMethod::ATOMIC_AND);
357             break;
358         case EOpAtomicOr:
359             traverseSSBOAccess(node, SSBOMethod::ATOMIC_OR);
360             break;
361         case EOpAtomicXor:
362             traverseSSBOAccess(node, SSBOMethod::ATOMIC_XOR);
363             break;
364         case EOpAtomicExchange:
365             traverseSSBOAccess(node, SSBOMethod::ATOMIC_EXCHANGE);
366             break;
367         case EOpAtomicCompSwap:
368             traverseSSBOAccess(node, SSBOMethod::ATOMIC_COMPSWAP);
369             break;
370         default:
371             UNREACHABLE();
372             break;
373     }
374 }
375 
376 // Note that we must calculate the matrix stride here instead of ShaderStorageBlockFunctionHLSL.
377 // It's because that if the current node's type is a vector which comes from a matrix, we will
378 // lose the matrix type info once we enter ShaderStorageBlockFunctionHLSL.
getMatrixStride(TIntermTyped * node,TLayoutBlockStorage storage,bool rowMajor,bool * isRowMajorMatrix) const379 int ShaderStorageBlockOutputHLSL::getMatrixStride(TIntermTyped *node,
380                                                   TLayoutBlockStorage storage,
381                                                   bool rowMajor,
382                                                   bool *isRowMajorMatrix) const
383 {
384     if (node->getType().isMatrix())
385     {
386         *isRowMajorMatrix = rowMajor;
387         return GetBlockMemberInfoByType(node->getType(), storage, rowMajor).matrixStride;
388     }
389 
390     if (node->getType().isVector())
391     {
392         TIntermBinary *binaryNode = node->getAsBinaryNode();
393         if (binaryNode)
394         {
395             return getMatrixStride(binaryNode->getLeft(), storage, rowMajor, isRowMajorMatrix);
396         }
397         else
398         {
399             TIntermSwizzle *swizzleNode = node->getAsSwizzleNode();
400             if (swizzleNode)
401             {
402                 return getMatrixStride(swizzleNode->getOperand(), storage, rowMajor,
403                                        isRowMajorMatrix);
404             }
405         }
406     }
407     return 0;
408 }
409 
collectShaderStorageBlocks(TIntermTyped * node)410 void ShaderStorageBlockOutputHLSL::collectShaderStorageBlocks(TIntermTyped *node)
411 {
412     TIntermSwizzle *swizzleNode = node->getAsSwizzleNode();
413     if (swizzleNode)
414     {
415         return collectShaderStorageBlocks(swizzleNode->getOperand());
416     }
417 
418     TIntermBinary *binaryNode = node->getAsBinaryNode();
419     if (binaryNode)
420     {
421         switch (binaryNode->getOp())
422         {
423             case EOpIndexDirectInterfaceBlock:
424             case EOpIndexIndirect:
425             case EOpIndexDirect:
426             case EOpIndexDirectStruct:
427                 return collectShaderStorageBlocks(binaryNode->getLeft());
428             default:
429                 UNREACHABLE();
430                 return;
431         }
432     }
433 
434     const TIntermSymbol *symbolNode = node->getAsSymbolNode();
435     const TType &type               = symbolNode->getType();
436     ASSERT(type.getQualifier() == EvqBuffer);
437     const TVariable &variable = symbolNode->variable();
438 
439     const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
440     ASSERT(interfaceBlock);
441     if (mReferencedShaderStorageBlocks.count(interfaceBlock->uniqueId().get()) == 0)
442     {
443         const TVariable *instanceVariable = nullptr;
444         if (type.isInterfaceBlock())
445         {
446             instanceVariable = &variable;
447         }
448         mReferencedShaderStorageBlocks[interfaceBlock->uniqueId().get()] =
449             new TReferencedBlock(interfaceBlock, instanceVariable);
450         GetShaderStorageBlockMembersInfo(interfaceBlock, mShaderStorageBlocks,
451                                          &mBlockMemberInfoMap);
452     }
453 }
454 
traverseSSBOAccess(TIntermTyped * node,SSBOMethod method)455 void ShaderStorageBlockOutputHLSL::traverseSSBOAccess(TIntermTyped *node, SSBOMethod method)
456 {
457     // TODO: Merge collectShaderStorageBlocks and GetBlockLayoutInfo to simplify the code.
458     collectShaderStorageBlocks(node);
459 
460     // Note that we don't have correct BlockMemberInfo from mBlockMemberInfoMap at the current
461     // point. But we must use those information to generate the right function name. So here we have
462     // to calculate them again.
463     TLayoutBlockStorage storage;
464     bool rowMajor;
465     GetBlockLayoutInfo(node, false, &storage, &rowMajor);
466     int unsizedArrayStride = 0;
467     if (node->getType().isUnsizedArray())
468     {
469         // The unsized array member must be the last member of a shader storage block.
470         TIntermBinary *binaryNode = node->getAsBinaryNode();
471         if (binaryNode)
472         {
473             const TInterfaceBlock *interfaceBlock =
474                 binaryNode->getLeft()->getType().getInterfaceBlock();
475             ASSERT(interfaceBlock);
476             const TIntermConstantUnion *index = binaryNode->getRight()->getAsConstantUnion();
477             const TField *field               = interfaceBlock->fields()[index->getIConst(0)];
478             auto fieldInfoIter                = mBlockMemberInfoMap.find(field);
479             ASSERT(fieldInfoIter != mBlockMemberInfoMap.end());
480             unsizedArrayStride = fieldInfoIter->second.arrayStride;
481         }
482         else
483         {
484             const TIntermSymbol *symbolNode       = node->getAsSymbolNode();
485             const TVariable &variable             = symbolNode->variable();
486             const TInterfaceBlock *interfaceBlock = symbolNode->getType().getInterfaceBlock();
487             ASSERT(interfaceBlock);
488             const TField *field =
489                 GetFieldMemberInShaderStorageBlock(interfaceBlock, variable.name());
490             auto fieldInfoIter = mBlockMemberInfoMap.find(field);
491             ASSERT(fieldInfoIter != mBlockMemberInfoMap.end());
492             unsizedArrayStride = fieldInfoIter->second.arrayStride;
493         }
494     }
495     bool isRowMajorMatrix = false;
496     int matrixStride      = getMatrixStride(node, storage, rowMajor, &isRowMajorMatrix);
497 
498     const TString &functionName = mSSBOFunctionHLSL->registerShaderStorageBlockFunction(
499         node->getType(), method, storage, isRowMajorMatrix, matrixStride, unsizedArrayStride,
500         node->getAsSwizzleNode());
501     TInfoSinkBase &out = mOutputHLSL->getInfoSink();
502     out << functionName;
503     out << "(";
504     BlockMemberInfo blockMemberInfo;
505     TIntermNode *loc = traverseNode(out, node, &blockMemberInfo);
506     out << ", ";
507     loc->traverse(mOutputHLSL);
508 }
509 
writeShaderStorageBlocksHeader(GLenum shaderType,TInfoSinkBase & out) const510 void ShaderStorageBlockOutputHLSL::writeShaderStorageBlocksHeader(GLenum shaderType,
511                                                                   TInfoSinkBase &out) const
512 {
513     if (mReferencedShaderStorageBlocks.empty())
514     {
515         return;
516     }
517 
518     mResourcesHLSL->allocateShaderStorageBlockRegisters(mReferencedShaderStorageBlocks);
519     out << "// Shader Storage Blocks\n\n";
520     if (shaderType == GL_COMPUTE_SHADER)
521     {
522         out << mResourcesHLSL->shaderStorageBlocksHeader(mReferencedShaderStorageBlocks);
523     }
524     else
525     {
526         out << kShaderStorageDeclarationString << "\n";
527     }
528     mSSBOFunctionHLSL->shaderStorageBlockFunctionHeader(out);
529 }
530 
traverseNode(TInfoSinkBase & out,TIntermTyped * node,BlockMemberInfo * blockMemberInfo)531 TIntermTyped *ShaderStorageBlockOutputHLSL::traverseNode(TInfoSinkBase &out,
532                                                          TIntermTyped *node,
533                                                          BlockMemberInfo *blockMemberInfo)
534 {
535     if (TIntermSymbol *symbolNode = node->getAsSymbolNode())
536     {
537         const TVariable &variable = symbolNode->variable();
538         const TType &type         = variable.getType();
539         if (type.isInterfaceBlock())
540         {
541             out << DecorateVariableIfNeeded(variable);
542         }
543         else
544         {
545             const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
546             out << Decorate(interfaceBlock->name());
547             const TField *field =
548                 GetFieldMemberInShaderStorageBlock(interfaceBlock, variable.name());
549             return createFieldOffset(field, blockMemberInfo);
550         }
551     }
552     else if (TIntermSwizzle *swizzleNode = node->getAsSwizzleNode())
553     {
554         return traverseNode(out, swizzleNode->getOperand(), blockMemberInfo);
555     }
556     else if (TIntermBinary *binaryNode = node->getAsBinaryNode())
557     {
558         switch (binaryNode->getOp())
559         {
560             case EOpIndexDirect:
561             {
562                 const TType &leftType = binaryNode->getLeft()->getType();
563                 if (leftType.isInterfaceBlock())
564                 {
565                     ASSERT(leftType.getQualifier() == EvqBuffer);
566                     TIntermSymbol *instanceArraySymbol = binaryNode->getLeft()->getAsSymbolNode();
567 
568                     const int arrayIndex =
569                         binaryNode->getRight()->getAsConstantUnion()->getIConst(0);
570                     out << mResourcesHLSL->InterfaceBlockInstanceString(
571                         instanceArraySymbol->getName(), arrayIndex);
572                 }
573                 else
574                 {
575                     return writeEOpIndexDirectOrIndirectOutput(out, binaryNode, blockMemberInfo);
576                 }
577                 break;
578             }
579             case EOpIndexIndirect:
580             {
581                 // We do not currently support indirect references to interface blocks
582                 ASSERT(binaryNode->getLeft()->getBasicType() != EbtInterfaceBlock);
583                 return writeEOpIndexDirectOrIndirectOutput(out, binaryNode, blockMemberInfo);
584             }
585             case EOpIndexDirectStruct:
586             {
587                 // We do not currently support direct references to interface blocks
588                 ASSERT(binaryNode->getLeft()->getBasicType() != EbtInterfaceBlock);
589                 TIntermTyped *left = traverseNode(out, binaryNode->getLeft(), blockMemberInfo);
590                 const TStructure *structure       = binaryNode->getLeft()->getType().getStruct();
591                 const TIntermConstantUnion *index = binaryNode->getRight()->getAsConstantUnion();
592                 const TField *field               = structure->fields()[index->getIConst(0)];
593                 return Add(createFieldOffset(field, blockMemberInfo), left);
594             }
595             case EOpIndexDirectInterfaceBlock:
596             {
597                 ASSERT(IsInShaderStorageBlock(binaryNode->getLeft()));
598                 traverseNode(out, binaryNode->getLeft(), blockMemberInfo);
599                 const TInterfaceBlock *interfaceBlock =
600                     binaryNode->getLeft()->getType().getInterfaceBlock();
601                 const TIntermConstantUnion *index = binaryNode->getRight()->getAsConstantUnion();
602                 const TField *field               = interfaceBlock->fields()[index->getIConst(0)];
603                 return createFieldOffset(field, blockMemberInfo);
604             }
605             default:
606                 return nullptr;
607         }
608     }
609     return nullptr;
610 }
611 
writeEOpIndexDirectOrIndirectOutput(TInfoSinkBase & out,TIntermBinary * node,BlockMemberInfo * blockMemberInfo)612 TIntermTyped *ShaderStorageBlockOutputHLSL::writeEOpIndexDirectOrIndirectOutput(
613     TInfoSinkBase &out,
614     TIntermBinary *node,
615     BlockMemberInfo *blockMemberInfo)
616 {
617     ASSERT(IsInShaderStorageBlock(node->getLeft()));
618     TIntermTyped *left  = traverseNode(out, node->getLeft(), blockMemberInfo);
619     TIntermTyped *right = node->getRight()->deepCopy();
620     const TType &type   = node->getLeft()->getType();
621     TLayoutBlockStorage storage;
622     bool rowMajor;
623     GetBlockLayoutInfo(node, false, &storage, &rowMajor);
624 
625     if (type.isArray())
626     {
627         const angle::Span<const unsigned int> &arraySizes = type.getArraySizes();
628         for (unsigned int i = 0; i < arraySizes.size() - 1; i++)
629         {
630             right = Mul(CreateUIntNode(arraySizes[i]), right);
631         }
632         right = Mul(CreateUIntNode(blockMemberInfo->arrayStride), right);
633     }
634     else if (type.isMatrix())
635     {
636         if (rowMajor)
637         {
638             right = Mul(CreateUIntNode(BlockLayoutEncoder::kBytesPerComponent), right);
639         }
640         else
641         {
642             right = Mul(CreateUIntNode(blockMemberInfo->matrixStride), right);
643         }
644     }
645     else if (type.isVector())
646     {
647         if (blockMemberInfo->isRowMajorMatrix)
648         {
649             right = Mul(CreateUIntNode(blockMemberInfo->matrixStride), right);
650         }
651         else
652         {
653             right = Mul(CreateUIntNode(BlockLayoutEncoder::kBytesPerComponent), right);
654         }
655     }
656     return Add(left, right);
657 }
658 
createFieldOffset(const TField * field,BlockMemberInfo * blockMemberInfo)659 TIntermTyped *ShaderStorageBlockOutputHLSL::createFieldOffset(const TField *field,
660                                                               BlockMemberInfo *blockMemberInfo)
661 {
662     auto fieldInfoIter = mBlockMemberInfoMap.find(field);
663     ASSERT(fieldInfoIter != mBlockMemberInfoMap.end());
664     *blockMemberInfo = fieldInfoIter->second;
665     return CreateUIntNode(blockMemberInfo->offset);
666 }
667 
668 }  // namespace sh
669