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