blob: 2ff01e439eb8e2de57f837bb9d62fd30e393389b [file] [log] [blame]
//
// 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 &block;
}
}
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