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 █
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