diff options
Diffstat (limited to 'gfx/angle/checkout/src/compiler/translator/ShaderStorageBlockOutputHLSL.cpp')
-rw-r--r-- | gfx/angle/checkout/src/compiler/translator/ShaderStorageBlockOutputHLSL.cpp | 667 |
1 files changed, 667 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/compiler/translator/ShaderStorageBlockOutputHLSL.cpp b/gfx/angle/checkout/src/compiler/translator/ShaderStorageBlockOutputHLSL.cpp new file mode 100644 index 0000000000..2ff01e439e --- /dev/null +++ b/gfx/angle/checkout/src/compiler/translator/ShaderStorageBlockOutputHLSL.cpp @@ -0,0 +1,667 @@ +// +// Copyright 2018 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// ShaderStorageBlockOutputHLSL: A traverser to translate a ssbo_access_chain to an offset of +// RWByteAddressBuffer. +// //EOpIndexDirectInterfaceBlock +// ssbo_variable := +// | the name of the SSBO +// | the name of a variable in an SSBO backed interface block + +// // EOpIndexInDirect +// // EOpIndexDirect +// ssbo_array_indexing := ssbo_access_chain[expr_no_ssbo] + +// // EOpIndexDirectStruct +// ssbo_structure_access := ssbo_access_chain.identifier + +// ssbo_access_chain := +// | ssbo_variable +// | ssbo_array_indexing +// | ssbo_structure_access +// + +#include "compiler/translator/ShaderStorageBlockOutputHLSL.h" + +#include "compiler/translator/ResourcesHLSL.h" +#include "compiler/translator/blocklayoutHLSL.h" +#include "compiler/translator/tree_util/IntermNode_util.h" +#include "compiler/translator/util.h" + +namespace sh +{ + +namespace +{ + +constexpr const char kShaderStorageDeclarationString[] = + "// @@ SHADER STORAGE DECLARATION STRING @@"; + +void GetBlockLayoutInfo(TIntermTyped *node, + bool rowMajorAlreadyAssigned, + TLayoutBlockStorage *storage, + bool *rowMajor) +{ + TIntermSwizzle *swizzleNode = node->getAsSwizzleNode(); + if (swizzleNode) + { + return GetBlockLayoutInfo(swizzleNode->getOperand(), rowMajorAlreadyAssigned, storage, + rowMajor); + } + + TIntermBinary *binaryNode = node->getAsBinaryNode(); + if (binaryNode) + { + switch (binaryNode->getOp()) + { + case EOpIndexDirectInterfaceBlock: + { + // The column_major/row_major qualifier of a field member overrides the interface + // block's row_major/column_major. So we can assign rowMajor here and don't need to + // assign it again. But we still need to call recursively to get the storage's + // value. + const TType &type = node->getType(); + *rowMajor = type.getLayoutQualifier().matrixPacking == EmpRowMajor; + return GetBlockLayoutInfo(binaryNode->getLeft(), true, storage, rowMajor); + } + case EOpIndexIndirect: + case EOpIndexDirect: + case EOpIndexDirectStruct: + return GetBlockLayoutInfo(binaryNode->getLeft(), rowMajorAlreadyAssigned, storage, + rowMajor); + default: + UNREACHABLE(); + return; + } + } + + const TType &type = node->getType(); + ASSERT(type.getQualifier() == EvqBuffer); + const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); + ASSERT(interfaceBlock); + *storage = interfaceBlock->blockStorage(); + // If the block doesn't have an instance name, rowMajorAlreadyAssigned will be false. In + // this situation, we still need to set rowMajor's value. + if (!rowMajorAlreadyAssigned) + { + *rowMajor = type.getLayoutQualifier().matrixPacking == EmpRowMajor; + } +} + +// It's possible that the current type has lost the original layout information. So we should pass +// the right layout information to GetBlockMemberInfoByType. +const BlockMemberInfo GetBlockMemberInfoByType(const TType &type, + TLayoutBlockStorage storage, + bool rowMajor) +{ + sh::Std140BlockEncoder std140Encoder; + sh::Std430BlockEncoder std430Encoder; + sh::HLSLBlockEncoder hlslEncoder(sh::HLSLBlockEncoder::ENCODE_PACKED, false); + sh::BlockLayoutEncoder *encoder = nullptr; + + if (storage == EbsStd140) + { + encoder = &std140Encoder; + } + else if (storage == EbsStd430) + { + encoder = &std430Encoder; + } + else + { + encoder = &hlslEncoder; + } + + std::vector<unsigned int> arraySizes; + const TSpan<const unsigned int> &typeArraySizes = type.getArraySizes(); + if (!typeArraySizes.empty()) + { + arraySizes.assign(typeArraySizes.begin(), typeArraySizes.end()); + } + return encoder->encodeType(GLVariableType(type), arraySizes, rowMajor); +} + +const TField *GetFieldMemberInShaderStorageBlock(const TInterfaceBlock *interfaceBlock, + const ImmutableString &variableName) +{ + for (const TField *field : interfaceBlock->fields()) + { + if (field->name() == variableName) + { + return field; + } + } + return nullptr; +} + +const InterfaceBlock *FindInterfaceBlock(const TInterfaceBlock *needle, + const std::vector<InterfaceBlock> &haystack) +{ + for (const InterfaceBlock &block : haystack) + { + if (strcmp(block.name.c_str(), needle->name().data()) == 0) + { + ASSERT(block.fields.size() == needle->fields().size()); + return █ + } + } + + UNREACHABLE(); + return nullptr; +} + +std::string StripArrayIndices(const std::string &nameIn) +{ + std::string name = nameIn; + size_t pos = name.find('['); + while (pos != std::string::npos) + { + size_t closePos = name.find(']', pos); + ASSERT(closePos != std::string::npos); + name.erase(pos, closePos - pos + 1); + pos = name.find('[', pos); + } + ASSERT(name.find(']') == std::string::npos); + return name; +} + +// Does not include any array indices. +void MapVariableToField(const ShaderVariable &variable, + const TField *field, + std::string currentName, + ShaderVarToFieldMap *shaderVarToFieldMap) +{ + ASSERT((field->type()->getStruct() == nullptr) == variable.fields.empty()); + (*shaderVarToFieldMap)[currentName] = field; + + if (!variable.fields.empty()) + { + const TStructure *subStruct = field->type()->getStruct(); + ASSERT(variable.fields.size() == subStruct->fields().size()); + + for (size_t index = 0; index < variable.fields.size(); ++index) + { + const TField *subField = subStruct->fields()[index]; + const ShaderVariable &subVariable = variable.fields[index]; + std::string subName = currentName + "." + subVariable.name; + MapVariableToField(subVariable, subField, subName, shaderVarToFieldMap); + } + } +} + +class BlockInfoVisitor final : public BlockEncoderVisitor +{ + public: + BlockInfoVisitor(const std::string &prefix, + TLayoutBlockStorage storage, + const ShaderVarToFieldMap &shaderVarToFieldMap, + BlockMemberInfoMap *blockInfoOut) + : BlockEncoderVisitor(prefix, "", getEncoder(storage)), + mShaderVarToFieldMap(shaderVarToFieldMap), + mBlockInfoOut(blockInfoOut), + mHLSLEncoder(HLSLBlockEncoder::ENCODE_PACKED, false), + mStorage(storage) + {} + + BlockLayoutEncoder *getEncoder(TLayoutBlockStorage storage) + { + switch (storage) + { + case EbsStd140: + return &mStd140Encoder; + case EbsStd430: + return &mStd430Encoder; + default: + return &mHLSLEncoder; + } + } + + void enterStructAccess(const ShaderVariable &structVar, bool isRowMajor) override + { + BlockEncoderVisitor::enterStructAccess(structVar, isRowMajor); + + std::string variableName = StripArrayIndices(collapseNameStack()); + + // Remove the trailing "." + variableName.pop_back(); + + BlockInfoVisitor childVisitor(variableName, mStorage, mShaderVarToFieldMap, mBlockInfoOut); + childVisitor.getEncoder(mStorage)->enterAggregateType(structVar); + TraverseShaderVariables(structVar.fields, isRowMajor, &childVisitor); + childVisitor.getEncoder(mStorage)->exitAggregateType(structVar); + + int offset = static_cast<int>(getEncoder(mStorage)->getCurrentOffset()); + int arrayStride = static_cast<int>(childVisitor.getEncoder(mStorage)->getCurrentOffset()); + + auto iter = mShaderVarToFieldMap.find(variableName); + if (iter == mShaderVarToFieldMap.end()) + return; + + const TField *structField = iter->second; + if (mBlockInfoOut->count(structField) == 0) + { + mBlockInfoOut->emplace(structField, BlockMemberInfo(offset, arrayStride, -1, false)); + } + } + + void encodeVariable(const ShaderVariable &variable, + const BlockMemberInfo &variableInfo, + const std::string &name, + const std::string &mappedName) override + { + auto iter = mShaderVarToFieldMap.find(StripArrayIndices(name)); + if (iter == mShaderVarToFieldMap.end()) + return; + + const TField *field = iter->second; + if (mBlockInfoOut->count(field) == 0) + { + mBlockInfoOut->emplace(field, variableInfo); + } + } + + private: + const ShaderVarToFieldMap &mShaderVarToFieldMap; + BlockMemberInfoMap *mBlockInfoOut; + Std140BlockEncoder mStd140Encoder; + Std430BlockEncoder mStd430Encoder; + HLSLBlockEncoder mHLSLEncoder; + TLayoutBlockStorage mStorage; +}; + +void GetShaderStorageBlockMembersInfo(const TInterfaceBlock *interfaceBlock, + const std::vector<InterfaceBlock> &shaderStorageBlocks, + BlockMemberInfoMap *blockInfoOut) +{ + // Find the sh::InterfaceBlock. + const InterfaceBlock *block = FindInterfaceBlock(interfaceBlock, shaderStorageBlocks); + ASSERT(block); + + // Map ShaderVariable to TField. + ShaderVarToFieldMap shaderVarToFieldMap; + for (size_t index = 0; index < block->fields.size(); ++index) + { + const TField *field = interfaceBlock->fields()[index]; + const ShaderVariable &variable = block->fields[index]; + MapVariableToField(variable, field, variable.name, &shaderVarToFieldMap); + } + + BlockInfoVisitor visitor("", interfaceBlock->blockStorage(), shaderVarToFieldMap, blockInfoOut); + TraverseShaderVariables(block->fields, false, &visitor); +} + +TIntermTyped *Mul(TIntermTyped *left, TIntermTyped *right) +{ + return left && right ? new TIntermBinary(EOpMul, left, right) : nullptr; +} + +TIntermTyped *Add(TIntermTyped *left, TIntermTyped *right) +{ + return left ? right ? new TIntermBinary(EOpAdd, left, right) : left : right; +} + +} // anonymous namespace + +ShaderStorageBlockOutputHLSL::ShaderStorageBlockOutputHLSL( + OutputHLSL *outputHLSL, + ResourcesHLSL *resourcesHLSL, + const std::vector<InterfaceBlock> &shaderStorageBlocks) + : mOutputHLSL(outputHLSL), + mResourcesHLSL(resourcesHLSL), + mShaderStorageBlocks(shaderStorageBlocks) +{ + mSSBOFunctionHLSL = new ShaderStorageBlockFunctionHLSL; +} + +ShaderStorageBlockOutputHLSL::~ShaderStorageBlockOutputHLSL() +{ + SafeDelete(mSSBOFunctionHLSL); +} + +void ShaderStorageBlockOutputHLSL::outputStoreFunctionCallPrefix(TIntermTyped *node) +{ + traverseSSBOAccess(node, SSBOMethod::STORE); +} + +void ShaderStorageBlockOutputHLSL::outputLoadFunctionCall(TIntermTyped *node) +{ + traverseSSBOAccess(node, SSBOMethod::LOAD); + mOutputHLSL->getInfoSink() << ")"; +} + +void ShaderStorageBlockOutputHLSL::outputLengthFunctionCall(TIntermTyped *node) +{ + traverseSSBOAccess(node, SSBOMethod::LENGTH); + mOutputHLSL->getInfoSink() << ")"; +} + +void ShaderStorageBlockOutputHLSL::outputAtomicMemoryFunctionCallPrefix(TIntermTyped *node, + TOperator op) +{ + switch (op) + { + case EOpAtomicAdd: + traverseSSBOAccess(node, SSBOMethod::ATOMIC_ADD); + break; + case EOpAtomicMin: + traverseSSBOAccess(node, SSBOMethod::ATOMIC_MIN); + break; + case EOpAtomicMax: + traverseSSBOAccess(node, SSBOMethod::ATOMIC_MAX); + break; + case EOpAtomicAnd: + traverseSSBOAccess(node, SSBOMethod::ATOMIC_AND); + break; + case EOpAtomicOr: + traverseSSBOAccess(node, SSBOMethod::ATOMIC_OR); + break; + case EOpAtomicXor: + traverseSSBOAccess(node, SSBOMethod::ATOMIC_XOR); + break; + case EOpAtomicExchange: + traverseSSBOAccess(node, SSBOMethod::ATOMIC_EXCHANGE); + break; + case EOpAtomicCompSwap: + traverseSSBOAccess(node, SSBOMethod::ATOMIC_COMPSWAP); + break; + default: + UNREACHABLE(); + break; + } +} + +// Note that we must calculate the matrix stride here instead of ShaderStorageBlockFunctionHLSL. +// It's because that if the current node's type is a vector which comes from a matrix, we will +// lose the matrix type info once we enter ShaderStorageBlockFunctionHLSL. +int ShaderStorageBlockOutputHLSL::getMatrixStride(TIntermTyped *node, + TLayoutBlockStorage storage, + bool rowMajor, + bool *isRowMajorMatrix) const +{ + if (node->getType().isMatrix()) + { + *isRowMajorMatrix = rowMajor; + return GetBlockMemberInfoByType(node->getType(), storage, rowMajor).matrixStride; + } + + if (node->getType().isVector()) + { + TIntermBinary *binaryNode = node->getAsBinaryNode(); + if (binaryNode) + { + return getMatrixStride(binaryNode->getLeft(), storage, rowMajor, isRowMajorMatrix); + } + else + { + TIntermSwizzle *swizzleNode = node->getAsSwizzleNode(); + if (swizzleNode) + { + return getMatrixStride(swizzleNode->getOperand(), storage, rowMajor, + isRowMajorMatrix); + } + } + } + return 0; +} + +void ShaderStorageBlockOutputHLSL::collectShaderStorageBlocks(TIntermTyped *node) +{ + TIntermSwizzle *swizzleNode = node->getAsSwizzleNode(); + if (swizzleNode) + { + return collectShaderStorageBlocks(swizzleNode->getOperand()); + } + + TIntermBinary *binaryNode = node->getAsBinaryNode(); + if (binaryNode) + { + switch (binaryNode->getOp()) + { + case EOpIndexDirectInterfaceBlock: + case EOpIndexIndirect: + case EOpIndexDirect: + case EOpIndexDirectStruct: + return collectShaderStorageBlocks(binaryNode->getLeft()); + default: + UNREACHABLE(); + return; + } + } + + const TIntermSymbol *symbolNode = node->getAsSymbolNode(); + const TType &type = symbolNode->getType(); + ASSERT(type.getQualifier() == EvqBuffer); + const TVariable &variable = symbolNode->variable(); + + const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); + ASSERT(interfaceBlock); + if (mReferencedShaderStorageBlocks.count(interfaceBlock->uniqueId().get()) == 0) + { + const TVariable *instanceVariable = nullptr; + if (type.isInterfaceBlock()) + { + instanceVariable = &variable; + } + mReferencedShaderStorageBlocks[interfaceBlock->uniqueId().get()] = + new TReferencedBlock(interfaceBlock, instanceVariable); + GetShaderStorageBlockMembersInfo(interfaceBlock, mShaderStorageBlocks, + &mBlockMemberInfoMap); + } +} + +void ShaderStorageBlockOutputHLSL::traverseSSBOAccess(TIntermTyped *node, SSBOMethod method) +{ + // TODO: Merge collectShaderStorageBlocks and GetBlockLayoutInfo to simplify the code. + collectShaderStorageBlocks(node); + + // Note that we don't have correct BlockMemberInfo from mBlockMemberInfoMap at the current + // point. But we must use those information to generate the right function name. So here we have + // to calculate them again. + TLayoutBlockStorage storage; + bool rowMajor; + GetBlockLayoutInfo(node, false, &storage, &rowMajor); + int unsizedArrayStride = 0; + if (node->getType().isUnsizedArray()) + { + // The unsized array member must be the last member of a shader storage block. + TIntermBinary *binaryNode = node->getAsBinaryNode(); + if (binaryNode) + { + const TInterfaceBlock *interfaceBlock = + binaryNode->getLeft()->getType().getInterfaceBlock(); + ASSERT(interfaceBlock); + const TIntermConstantUnion *index = binaryNode->getRight()->getAsConstantUnion(); + const TField *field = interfaceBlock->fields()[index->getIConst(0)]; + auto fieldInfoIter = mBlockMemberInfoMap.find(field); + ASSERT(fieldInfoIter != mBlockMemberInfoMap.end()); + unsizedArrayStride = fieldInfoIter->second.arrayStride; + } + else + { + const TIntermSymbol *symbolNode = node->getAsSymbolNode(); + const TVariable &variable = symbolNode->variable(); + const TInterfaceBlock *interfaceBlock = symbolNode->getType().getInterfaceBlock(); + ASSERT(interfaceBlock); + const TField *field = + GetFieldMemberInShaderStorageBlock(interfaceBlock, variable.name()); + auto fieldInfoIter = mBlockMemberInfoMap.find(field); + ASSERT(fieldInfoIter != mBlockMemberInfoMap.end()); + unsizedArrayStride = fieldInfoIter->second.arrayStride; + } + } + bool isRowMajorMatrix = false; + int matrixStride = getMatrixStride(node, storage, rowMajor, &isRowMajorMatrix); + + const TString &functionName = mSSBOFunctionHLSL->registerShaderStorageBlockFunction( + node->getType(), method, storage, isRowMajorMatrix, matrixStride, unsizedArrayStride, + node->getAsSwizzleNode()); + TInfoSinkBase &out = mOutputHLSL->getInfoSink(); + out << functionName; + out << "("; + BlockMemberInfo blockMemberInfo; + TIntermNode *loc = traverseNode(out, node, &blockMemberInfo); + out << ", "; + loc->traverse(mOutputHLSL); +} + +void ShaderStorageBlockOutputHLSL::writeShaderStorageBlocksHeader(GLenum shaderType, + TInfoSinkBase &out) const +{ + if (mReferencedShaderStorageBlocks.empty()) + { + return; + } + + mResourcesHLSL->allocateShaderStorageBlockRegisters(mReferencedShaderStorageBlocks); + out << "// Shader Storage Blocks\n\n"; + if (shaderType == GL_COMPUTE_SHADER) + { + out << mResourcesHLSL->shaderStorageBlocksHeader(mReferencedShaderStorageBlocks); + } + else + { + out << kShaderStorageDeclarationString << "\n"; + } + mSSBOFunctionHLSL->shaderStorageBlockFunctionHeader(out); +} + +TIntermTyped *ShaderStorageBlockOutputHLSL::traverseNode(TInfoSinkBase &out, + TIntermTyped *node, + BlockMemberInfo *blockMemberInfo) +{ + if (TIntermSymbol *symbolNode = node->getAsSymbolNode()) + { + const TVariable &variable = symbolNode->variable(); + const TType &type = variable.getType(); + if (type.isInterfaceBlock()) + { + out << DecorateVariableIfNeeded(variable); + } + else + { + const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); + out << Decorate(interfaceBlock->name()); + const TField *field = + GetFieldMemberInShaderStorageBlock(interfaceBlock, variable.name()); + return createFieldOffset(field, blockMemberInfo); + } + } + else if (TIntermSwizzle *swizzleNode = node->getAsSwizzleNode()) + { + return traverseNode(out, swizzleNode->getOperand(), blockMemberInfo); + } + else if (TIntermBinary *binaryNode = node->getAsBinaryNode()) + { + switch (binaryNode->getOp()) + { + case EOpIndexDirect: + { + const TType &leftType = binaryNode->getLeft()->getType(); + if (leftType.isInterfaceBlock()) + { + ASSERT(leftType.getQualifier() == EvqBuffer); + TIntermSymbol *instanceArraySymbol = binaryNode->getLeft()->getAsSymbolNode(); + + const int arrayIndex = + binaryNode->getRight()->getAsConstantUnion()->getIConst(0); + out << mResourcesHLSL->InterfaceBlockInstanceString( + instanceArraySymbol->getName(), arrayIndex); + } + else + { + return writeEOpIndexDirectOrIndirectOutput(out, binaryNode, blockMemberInfo); + } + break; + } + case EOpIndexIndirect: + { + // We do not currently support indirect references to interface blocks + ASSERT(binaryNode->getLeft()->getBasicType() != EbtInterfaceBlock); + return writeEOpIndexDirectOrIndirectOutput(out, binaryNode, blockMemberInfo); + } + case EOpIndexDirectStruct: + { + // We do not currently support direct references to interface blocks + ASSERT(binaryNode->getLeft()->getBasicType() != EbtInterfaceBlock); + TIntermTyped *left = traverseNode(out, binaryNode->getLeft(), blockMemberInfo); + const TStructure *structure = binaryNode->getLeft()->getType().getStruct(); + const TIntermConstantUnion *index = binaryNode->getRight()->getAsConstantUnion(); + const TField *field = structure->fields()[index->getIConst(0)]; + return Add(createFieldOffset(field, blockMemberInfo), left); + } + case EOpIndexDirectInterfaceBlock: + { + ASSERT(IsInShaderStorageBlock(binaryNode->getLeft())); + traverseNode(out, binaryNode->getLeft(), blockMemberInfo); + const TInterfaceBlock *interfaceBlock = + binaryNode->getLeft()->getType().getInterfaceBlock(); + const TIntermConstantUnion *index = binaryNode->getRight()->getAsConstantUnion(); + const TField *field = interfaceBlock->fields()[index->getIConst(0)]; + return createFieldOffset(field, blockMemberInfo); + } + default: + return nullptr; + } + } + return nullptr; +} + +TIntermTyped *ShaderStorageBlockOutputHLSL::writeEOpIndexDirectOrIndirectOutput( + TInfoSinkBase &out, + TIntermBinary *node, + BlockMemberInfo *blockMemberInfo) +{ + ASSERT(IsInShaderStorageBlock(node->getLeft())); + TIntermTyped *left = traverseNode(out, node->getLeft(), blockMemberInfo); + TIntermTyped *right = node->getRight()->deepCopy(); + const TType &type = node->getLeft()->getType(); + TLayoutBlockStorage storage; + bool rowMajor; + GetBlockLayoutInfo(node, false, &storage, &rowMajor); + + if (type.isArray()) + { + const TSpan<const unsigned int> &arraySizes = type.getArraySizes(); + for (unsigned int i = 0; i < arraySizes.size() - 1; i++) + { + right = Mul(CreateUIntNode(arraySizes[i]), right); + } + right = Mul(CreateUIntNode(blockMemberInfo->arrayStride), right); + } + else if (type.isMatrix()) + { + if (rowMajor) + { + right = Mul(CreateUIntNode(BlockLayoutEncoder::kBytesPerComponent), right); + } + else + { + right = Mul(CreateUIntNode(blockMemberInfo->matrixStride), right); + } + } + else if (type.isVector()) + { + if (blockMemberInfo->isRowMajorMatrix) + { + right = Mul(CreateUIntNode(blockMemberInfo->matrixStride), right); + } + else + { + right = Mul(CreateUIntNode(BlockLayoutEncoder::kBytesPerComponent), right); + } + } + return Add(left, right); +} + +TIntermTyped *ShaderStorageBlockOutputHLSL::createFieldOffset(const TField *field, + BlockMemberInfo *blockMemberInfo) +{ + auto fieldInfoIter = mBlockMemberInfoMap.find(field); + ASSERT(fieldInfoIter != mBlockMemberInfoMap.end()); + *blockMemberInfo = fieldInfoIter->second; + return CreateUIntNode(blockMemberInfo->offset); +} + +} // namespace sh |