tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ShaderStorageBlockOutputHLSL.cpp (24552B)


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