Edit

kc3-lang/angle/src/compiler/translator/ResourcesHLSL.cpp

Branch :

  • Show log

    Commit

  • Author : Tim Van Patten
    Date : 2019-09-04 15:39:58
    Hash : 90a58622
    Message : Refactor ShaderVariable to Remove Specializations The following structs are being refactored and moved into the parent struct ShaderVariable: VariableWithLocation Uniform Attribute OutputVariable InterfaceBlockField Varying Bug: angleproject:3899 Test: CQ Change-Id: I389eb3ab4ed44a360e09fca75ecc78d64a277f83 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1785877 Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Courtney Goeltzenleuchter <courtneygo@google.com> Commit-Queue: Tim Van Patten <timvp@google.com>

  • src/compiler/translator/ResourcesHLSL.cpp
  • //
    // Copyright 2014 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.
    //
    // ResourcesHLSL.cpp:
    //   Methods for GLSL to HLSL translation for uniforms and interface blocks.
    //
    
    #include "compiler/translator/ResourcesHLSL.h"
    
    #include "common/utilities.h"
    #include "compiler/translator/AtomicCounterFunctionHLSL.h"
    #include "compiler/translator/ImmutableStringBuilder.h"
    #include "compiler/translator/StructureHLSL.h"
    #include "compiler/translator/UtilsHLSL.h"
    #include "compiler/translator/blocklayoutHLSL.h"
    #include "compiler/translator/util.h"
    
    namespace sh
    {
    
    namespace
    {
    
    constexpr const ImmutableString kAngleDecorString("angle_");
    
    static const char *UniformRegisterPrefix(const TType &type)
    {
        if (IsSampler(type.getBasicType()))
        {
            return "s";
        }
        else
        {
            return "c";
        }
    }
    
    static TString InterfaceBlockFieldTypeString(const TField &field, TLayoutBlockStorage blockStorage)
    {
        const TType &fieldType                   = *field.type();
        const TLayoutMatrixPacking matrixPacking = fieldType.getLayoutQualifier().matrixPacking;
        ASSERT(matrixPacking != EmpUnspecified);
        const TStructure *structure = fieldType.getStruct();
    
        if (fieldType.isMatrix())
        {
            // Use HLSL row-major packing for GLSL column-major matrices
            const TString &matrixPackString =
                (matrixPacking == EmpRowMajor ? "column_major" : "row_major");
            return matrixPackString + " " + TypeString(fieldType);
        }
        else if (structure)
        {
            // Use HLSL row-major packing for GLSL column-major matrices
            return QualifiedStructNameString(*structure, matrixPacking == EmpColumnMajor,
                                             blockStorage == EbsStd140);
        }
        else
        {
            return TypeString(fieldType);
        }
    }
    
    static TString InterfaceBlockStructName(const TInterfaceBlock &interfaceBlock)
    {
        return DecoratePrivate(interfaceBlock.name()) + "_type";
    }
    
    void OutputUniformIndexArrayInitializer(TInfoSinkBase &out,
                                            const TType &type,
                                            unsigned int startIndex)
    {
        out << "{";
        TType elementType(type);
        elementType.toArrayElementType();
        for (unsigned int i = 0u; i < type.getOutermostArraySize(); ++i)
        {
            if (i > 0u)
            {
                out << ", ";
            }
            if (elementType.isArray())
            {
                OutputUniformIndexArrayInitializer(out, elementType,
                                                   startIndex + i * elementType.getArraySizeProduct());
            }
            else
            {
                out << (startIndex + i);
            }
        }
        out << "}";
    }
    
    }  // anonymous namespace
    
    ResourcesHLSL::ResourcesHLSL(StructureHLSL *structureHLSL,
                                 ShShaderOutput outputType,
                                 const std::vector<ShaderVariable> &uniforms,
                                 unsigned int firstUniformRegister)
        : mUniformRegister(firstUniformRegister),
          mUniformBlockRegister(0),
          mTextureRegister(0),
          mUAVRegister(0),
          mSamplerCount(0),
          mStructureHLSL(structureHLSL),
          mOutputType(outputType),
          mUniforms(uniforms)
    {}
    
    void ResourcesHLSL::reserveUniformRegisters(unsigned int registerCount)
    {
        mUniformRegister = registerCount;
    }
    
    void ResourcesHLSL::reserveUniformBlockRegisters(unsigned int registerCount)
    {
        mUniformBlockRegister = registerCount;
    }
    
    const ShaderVariable *ResourcesHLSL::findUniformByName(const ImmutableString &name) const
    {
        for (size_t uniformIndex = 0; uniformIndex < mUniforms.size(); ++uniformIndex)
        {
            if (name == mUniforms[uniformIndex].name)
            {
                return &mUniforms[uniformIndex];
            }
        }
    
        return nullptr;
    }
    
    unsigned int ResourcesHLSL::assignUniformRegister(const TType &type,
                                                      const ImmutableString &name,
                                                      unsigned int *outRegisterCount)
    {
        unsigned int registerIndex;
        const ShaderVariable *uniform = findUniformByName(name);
        ASSERT(uniform);
    
        if (IsSampler(type.getBasicType()) ||
            (IsImage(type.getBasicType()) && type.getMemoryQualifier().readonly))
        {
            registerIndex = mTextureRegister;
        }
        else if (IsImage(type.getBasicType()))
        {
            registerIndex = mUAVRegister;
        }
        else
        {
            registerIndex = mUniformRegister;
        }
    
        if (uniform->name == "angle_DrawID" && uniform->mappedName == "angle_DrawID")
        {
            mUniformRegisterMap["gl_DrawID"] = registerIndex;
        }
        else
        {
            mUniformRegisterMap[uniform->name] = registerIndex;
        }
    
        if (uniform->name == "angle_BaseVertex" && uniform->mappedName == "angle_BaseVertex")
        {
            mUniformRegisterMap["gl_BaseVertex"] = registerIndex;
        }
        else
        {
            mUniformRegisterMap[uniform->name] = registerIndex;
        }
    
        if (uniform->name == "angle_BaseInstance" && uniform->mappedName == "angle_BaseInstance")
        {
            mUniformRegisterMap["gl_BaseInstance"] = registerIndex;
        }
        else
        {
            mUniformRegisterMap[uniform->name] = registerIndex;
        }
    
        unsigned int registerCount = HLSLVariableRegisterCount(*uniform, mOutputType);
    
        if (IsSampler(type.getBasicType()) ||
            (IsImage(type.getBasicType()) && type.getMemoryQualifier().readonly))
        {
            mTextureRegister += registerCount;
        }
        else if (IsImage(type.getBasicType()))
        {
            mUAVRegister += registerCount;
        }
        else
        {
            mUniformRegister += registerCount;
        }
        if (outRegisterCount)
        {
            *outRegisterCount = registerCount;
        }
        return registerIndex;
    }
    
    unsigned int ResourcesHLSL::assignSamplerInStructUniformRegister(const TType &type,
                                                                     const TString &name,
                                                                     unsigned int *outRegisterCount)
    {
        // Sampler that is a field of a uniform structure.
        ASSERT(IsSampler(type.getBasicType()));
        unsigned int registerIndex                     = mTextureRegister;
        mUniformRegisterMap[std::string(name.c_str())] = registerIndex;
        unsigned int registerCount = type.isArray() ? type.getArraySizeProduct() : 1u;
        mTextureRegister += registerCount;
        if (outRegisterCount)
        {
            *outRegisterCount = registerCount;
        }
        return registerIndex;
    }
    
    void ResourcesHLSL::outputHLSLSamplerUniformGroup(
        TInfoSinkBase &out,
        const HLSLTextureGroup textureGroup,
        const TVector<const TVariable *> &group,
        const TMap<const TVariable *, TString> &samplerInStructSymbolsToAPINames,
        unsigned int *groupTextureRegisterIndex)
    {
        if (group.empty())
        {
            return;
        }
        unsigned int groupRegisterCount = 0;
        for (const TVariable *uniform : group)
        {
            const TType &type           = uniform->getType();
            const ImmutableString &name = uniform->name();
            unsigned int registerCount;
    
            // The uniform might be just a regular sampler or one extracted from a struct.
            unsigned int samplerArrayIndex = 0u;
            const ShaderVariable *uniformByName = findUniformByName(name);
            if (uniformByName)
            {
                samplerArrayIndex = assignUniformRegister(type, name, &registerCount);
            }
            else
            {
                ASSERT(samplerInStructSymbolsToAPINames.find(uniform) !=
                       samplerInStructSymbolsToAPINames.end());
                samplerArrayIndex = assignSamplerInStructUniformRegister(
                    type, samplerInStructSymbolsToAPINames.at(uniform), &registerCount);
            }
            groupRegisterCount += registerCount;
    
            if (type.isArray())
            {
                out << "static const uint " << DecorateVariableIfNeeded(*uniform) << ArrayString(type)
                    << " = ";
                OutputUniformIndexArrayInitializer(out, type, samplerArrayIndex);
                out << ";\n";
            }
            else
            {
                out << "static const uint " << DecorateVariableIfNeeded(*uniform) << " = "
                    << samplerArrayIndex << ";\n";
            }
        }
        TString suffix = TextureGroupSuffix(textureGroup);
        // Since HLSL_TEXTURE_2D is the first group, it has a fixed offset of zero.
        if (textureGroup != HLSL_TEXTURE_2D)
        {
            out << "static const uint textureIndexOffset" << suffix << " = "
                << (*groupTextureRegisterIndex) << ";\n";
            out << "static const uint samplerIndexOffset" << suffix << " = "
                << (*groupTextureRegisterIndex) << ";\n";
        }
        out << "uniform " << TextureString(textureGroup) << " textures" << suffix << "["
            << groupRegisterCount << "]"
            << " : register(t" << (*groupTextureRegisterIndex) << ");\n";
        out << "uniform " << SamplerString(textureGroup) << " samplers" << suffix << "["
            << groupRegisterCount << "]"
            << " : register(s" << (*groupTextureRegisterIndex) << ");\n";
        *groupTextureRegisterIndex += groupRegisterCount;
    }
    
    void ResourcesHLSL::outputHLSLImageUniformIndices(TInfoSinkBase &out,
                                                      const TVector<const TVariable *> &group,
                                                      unsigned int imageArrayIndex,
                                                      unsigned int *groupRegisterCount)
    {
        for (const TVariable *uniform : group)
        {
            const TType &type           = uniform->getType();
            const ImmutableString &name = uniform->name();
            unsigned int registerCount  = 0;
    
            assignUniformRegister(type, name, &registerCount);
            *groupRegisterCount += registerCount;
    
            if (type.isArray())
            {
                out << "static const uint " << DecorateVariableIfNeeded(*uniform) << ArrayString(type)
                    << " = ";
                OutputUniformIndexArrayInitializer(out, type, imageArrayIndex);
                out << ";\n";
            }
            else
            {
                out << "static const uint " << DecorateVariableIfNeeded(*uniform) << " = "
                    << imageArrayIndex << ";\n";
            }
    
            imageArrayIndex += registerCount;
        }
    }
    
    void ResourcesHLSL::outputHLSLReadonlyImageUniformGroup(TInfoSinkBase &out,
                                                            const HLSLTextureGroup textureGroup,
                                                            const TVector<const TVariable *> &group,
                                                            unsigned int *groupTextureRegisterIndex)
    {
        if (group.empty())
        {
            return;
        }
    
        unsigned int groupRegisterCount = 0;
        outputHLSLImageUniformIndices(out, group, *groupTextureRegisterIndex, &groupRegisterCount);
    
        TString suffix = TextureGroupSuffix(textureGroup);
        out << "static const uint readonlyImageIndexOffset" << suffix << " = "
            << (*groupTextureRegisterIndex) << ";\n";
        out << "uniform " << TextureString(textureGroup) << " readonlyImages" << suffix << "["
            << groupRegisterCount << "]"
            << " : register(t" << (*groupTextureRegisterIndex) << ");\n";
        *groupTextureRegisterIndex += groupRegisterCount;
    }
    
    void ResourcesHLSL::outputHLSLImageUniformGroup(TInfoSinkBase &out,
                                                    const HLSLRWTextureGroup textureGroup,
                                                    const TVector<const TVariable *> &group,
                                                    unsigned int *groupTextureRegisterIndex)
    {
        if (group.empty())
        {
            return;
        }
    
        unsigned int groupRegisterCount = 0;
        outputHLSLImageUniformIndices(out, group, *groupTextureRegisterIndex, &groupRegisterCount);
    
        TString suffix = RWTextureGroupSuffix(textureGroup);
        out << "static const uint imageIndexOffset" << suffix << " = " << (*groupTextureRegisterIndex)
            << ";\n";
        out << "uniform " << RWTextureString(textureGroup) << " images" << suffix << "["
            << groupRegisterCount << "]"
            << " : register(u" << (*groupTextureRegisterIndex) << ");\n";
        *groupTextureRegisterIndex += groupRegisterCount;
    }
    
    void ResourcesHLSL::outputHLSL4_0_FL9_3Sampler(TInfoSinkBase &out,
                                                   const TType &type,
                                                   const TVariable &variable,
                                                   const unsigned int registerIndex)
    {
        out << "uniform " << SamplerString(type.getBasicType()) << " sampler_"
            << DecorateVariableIfNeeded(variable) << ArrayString(type) << " : register(s"
            << str(registerIndex) << ");\n";
        out << "uniform " << TextureString(type.getBasicType()) << " texture_"
            << DecorateVariableIfNeeded(variable) << ArrayString(type) << " : register(t"
            << str(registerIndex) << ");\n";
    }
    
    void ResourcesHLSL::outputUniform(TInfoSinkBase &out,
                                      const TType &type,
                                      const TVariable &variable,
                                      const unsigned int registerIndex)
    {
        const TStructure *structure = type.getStruct();
        // If this is a nameless struct, we need to use its full definition, rather than its (empty)
        // name.
        // TypeString() will invoke defineNameless in this case; qualifier prefixes are unnecessary for
        // nameless structs in ES, as nameless structs cannot be used anywhere that layout qualifiers
        // are permitted.
        const TString &typeName = ((structure && structure->symbolType() != SymbolType::Empty)
                                       ? QualifiedStructNameString(*structure, false, false)
                                       : TypeString(type));
    
        const TString &registerString =
            TString("register(") + UniformRegisterPrefix(type) + str(registerIndex) + ")";
    
        out << "uniform " << typeName << " ";
    
        out << DecorateVariableIfNeeded(variable);
    
        out << ArrayString(type) << " : " << registerString << ";\n";
    }
    
    void ResourcesHLSL::outputAtomicCounterBuffer(TInfoSinkBase &out,
                                                  const int binding,
                                                  const unsigned int registerIndex)
    {
        // Atomic counter memory access is not incoherent
        out << "uniform globallycoherent RWByteAddressBuffer "
            << getAtomicCounterNameForBinding(binding) << " : register(u" << registerIndex << ");\n";
    }
    
    void ResourcesHLSL::uniformsHeader(TInfoSinkBase &out,
                                       ShShaderOutput outputType,
                                       const ReferencedVariables &referencedUniforms,
                                       TSymbolTable *symbolTable)
    {
        if (!referencedUniforms.empty())
        {
            out << "// Uniforms\n\n";
        }
        // In the case of HLSL 4, sampler uniforms need to be grouped by type before the code is
        // written. They are grouped based on the combination of the HLSL texture type and
        // HLSL sampler type, enumerated in HLSLTextureSamplerGroup.
        TVector<TVector<const TVariable *>> groupedSamplerUniforms(HLSL_TEXTURE_MAX + 1);
        TMap<const TVariable *, TString> samplerInStructSymbolsToAPINames;
        TVector<TVector<const TVariable *>> groupedReadonlyImageUniforms(HLSL_TEXTURE_MAX + 1);
        TVector<TVector<const TVariable *>> groupedImageUniforms(HLSL_RWTEXTURE_MAX + 1);
    
        TUnorderedMap<int, unsigned int> assignedAtomicCounterBindings;
        unsigned int reservedReadonlyImageRegisterCount = 0, reservedImageRegisterCount = 0;
        for (auto &uniformIt : referencedUniforms)
        {
            // Output regular uniforms. Group sampler uniforms by type.
            const TVariable &variable = *uniformIt.second;
            const TType &type         = variable.getType();
    
            if (outputType == SH_HLSL_4_1_OUTPUT && IsSampler(type.getBasicType()))
            {
                HLSLTextureGroup group = TextureGroup(type.getBasicType());
                groupedSamplerUniforms[group].push_back(&variable);
            }
            else if (outputType == SH_HLSL_4_0_FL9_3_OUTPUT && IsSampler(type.getBasicType()))
            {
                unsigned int registerIndex = assignUniformRegister(type, variable.name(), nullptr);
                outputHLSL4_0_FL9_3Sampler(out, type, variable, registerIndex);
            }
            else if (outputType == SH_HLSL_4_1_OUTPUT && IsImage(type.getBasicType()))
            {
                if (IsImage2D(type.getBasicType()))
                {
                    const ShaderVariable *uniform = findUniformByName(variable.name());
                    if (type.getMemoryQualifier().readonly)
                    {
                        reservedReadonlyImageRegisterCount +=
                            HLSLVariableRegisterCount(*uniform, mOutputType);
                    }
                    else
                    {
                        reservedImageRegisterCount += HLSLVariableRegisterCount(*uniform, mOutputType);
                    }
                    continue;
                }
                if (type.getMemoryQualifier().readonly)
                {
                    HLSLTextureGroup group = TextureGroup(
                        type.getBasicType(), type.getLayoutQualifier().imageInternalFormat);
                    groupedReadonlyImageUniforms[group].push_back(&variable);
                }
                else
                {
                    HLSLRWTextureGroup group = RWTextureGroup(
                        type.getBasicType(), type.getLayoutQualifier().imageInternalFormat);
                    groupedImageUniforms[group].push_back(&variable);
                }
            }
            else if (outputType == SH_HLSL_4_1_OUTPUT && IsAtomicCounter(type.getBasicType()))
            {
                TLayoutQualifier layout = type.getLayoutQualifier();
                int binding             = layout.binding;
                unsigned int registerIndex;
                if (assignedAtomicCounterBindings.find(binding) == assignedAtomicCounterBindings.end())
                {
                    registerIndex                          = mUAVRegister++;
                    assignedAtomicCounterBindings[binding] = registerIndex;
                    outputAtomicCounterBuffer(out, binding, registerIndex);
                }
                else
                {
                    registerIndex = assignedAtomicCounterBindings[binding];
                }
                const ShaderVariable *uniform      = findUniformByName(variable.name());
                mUniformRegisterMap[uniform->name] = registerIndex;
            }
            else
            {
                if (type.isStructureContainingSamplers())
                {
                    TVector<const TVariable *> samplerSymbols;
                    TMap<const TVariable *, TString> symbolsToAPINames;
                    ImmutableStringBuilder namePrefix(kAngleDecorString.length() +
                                                      variable.name().length());
                    namePrefix << kAngleDecorString;
                    namePrefix << variable.name();
                    type.createSamplerSymbols(namePrefix, TString(variable.name().data()),
                                              &samplerSymbols, &symbolsToAPINames, symbolTable);
                    for (const TVariable *sampler : samplerSymbols)
                    {
                        const TType &samplerType = sampler->getType();
    
                        if (outputType == SH_HLSL_4_1_OUTPUT)
                        {
                            HLSLTextureGroup group = TextureGroup(samplerType.getBasicType());
                            groupedSamplerUniforms[group].push_back(sampler);
                            samplerInStructSymbolsToAPINames[sampler] = symbolsToAPINames[sampler];
                        }
                        else if (outputType == SH_HLSL_4_0_FL9_3_OUTPUT)
                        {
                            unsigned int registerIndex = assignSamplerInStructUniformRegister(
                                samplerType, symbolsToAPINames[sampler], nullptr);
                            outputHLSL4_0_FL9_3Sampler(out, samplerType, *sampler, registerIndex);
                        }
                        else
                        {
                            ASSERT(outputType == SH_HLSL_3_0_OUTPUT);
                            unsigned int registerIndex = assignSamplerInStructUniformRegister(
                                samplerType, symbolsToAPINames[sampler], nullptr);
                            outputUniform(out, samplerType, *sampler, registerIndex);
                        }
                    }
                }
                unsigned int registerIndex = assignUniformRegister(type, variable.name(), nullptr);
                outputUniform(out, type, variable, registerIndex);
            }
        }
    
        if (outputType == SH_HLSL_4_1_OUTPUT)
        {
            unsigned int groupTextureRegisterIndex = 0;
            // Atomic counters and RW texture share the same resources. Therefore, RW texture need to
            // start counting after the last atomic counter.
            unsigned int groupRWTextureRegisterIndex = mUAVRegister;
            // TEXTURE_2D is special, index offset is assumed to be 0 and omitted in that case.
            ASSERT(HLSL_TEXTURE_MIN == HLSL_TEXTURE_2D);
            for (int groupId = HLSL_TEXTURE_MIN; groupId < HLSL_TEXTURE_MAX; ++groupId)
            {
                outputHLSLSamplerUniformGroup(
                    out, HLSLTextureGroup(groupId), groupedSamplerUniforms[groupId],
                    samplerInStructSymbolsToAPINames, &groupTextureRegisterIndex);
            }
            mSamplerCount = groupTextureRegisterIndex;
    
            // Reserve t type register for readonly image2D variables.
            mReadonlyImage2DRegisterIndex = mTextureRegister;
            groupTextureRegisterIndex += reservedReadonlyImageRegisterCount;
            mTextureRegister += reservedReadonlyImageRegisterCount;
    
            for (int groupId = HLSL_TEXTURE_MIN; groupId < HLSL_TEXTURE_MAX; ++groupId)
            {
                outputHLSLReadonlyImageUniformGroup(out, HLSLTextureGroup(groupId),
                                                    groupedReadonlyImageUniforms[groupId],
                                                    &groupTextureRegisterIndex);
            }
            mReadonlyImageCount = groupTextureRegisterIndex - mReadonlyImage2DRegisterIndex;
            if (mReadonlyImageCount)
            {
                out << "static const uint readonlyImageIndexStart = " << mReadonlyImage2DRegisterIndex
                    << ";\n";
            }
    
            // Reserve u type register for writable image2D variables.
            mImage2DRegisterIndex = mUAVRegister;
            groupRWTextureRegisterIndex += reservedImageRegisterCount;
            mUAVRegister += reservedImageRegisterCount;
    
            for (int groupId = HLSL_RWTEXTURE_MIN; groupId < HLSL_RWTEXTURE_MAX; ++groupId)
            {
                outputHLSLImageUniformGroup(out, HLSLRWTextureGroup(groupId),
                                            groupedImageUniforms[groupId],
                                            &groupRWTextureRegisterIndex);
            }
            mImageCount = groupRWTextureRegisterIndex - mImage2DRegisterIndex;
            if (mImageCount)
            {
                out << "static const uint imageIndexStart = " << mImage2DRegisterIndex << ";\n";
            }
        }
    }
    
    void ResourcesHLSL::samplerMetadataUniforms(TInfoSinkBase &out, unsigned int regIndex)
    {
        // If mSamplerCount is 0 the shader doesn't use any textures for samplers.
        if (mSamplerCount > 0)
        {
            out << "    struct SamplerMetadata\n"
                   "    {\n"
                   "        int baseLevel;\n"
                   "        int internalFormatBits;\n"
                   "        int wrapModes;\n"
                   "        int padding;\n"
                   "        int4 intBorderColor;\n"
                   "    };\n"
                   "    SamplerMetadata samplerMetadata["
                << mSamplerCount << "] : packoffset(c" << regIndex << ");\n";
        }
    }
    
    void ResourcesHLSL::imageMetadataUniforms(TInfoSinkBase &out, unsigned int regIndex)
    {
        if (mReadonlyImageCount > 0 || mImageCount > 0)
        {
            out << "    struct ImageMetadata\n"
                   "    {\n"
                   "        int layer;\n"
                   "        uint level;\n"
                   "        int2 padding;\n"
                   "    };\n";
    
            if (mReadonlyImageCount > 0)
            {
                out << "    ImageMetadata readonlyImageMetadata[" << mReadonlyImageCount
                    << "] : packoffset(c" << regIndex << ");\n";
            }
    
            if (mImageCount > 0)
            {
                out << "    ImageMetadata imageMetadata[" << mImageCount << "] : packoffset(c"
                    << regIndex + mReadonlyImageCount << ");\n";
            }
        }
    }
    
    TString ResourcesHLSL::uniformBlocksHeader(
        const ReferencedInterfaceBlocks &referencedInterfaceBlocks)
    {
        TString interfaceBlocks;
    
        for (const auto &blockReference : referencedInterfaceBlocks)
        {
            const TInterfaceBlock &interfaceBlock = *blockReference.second->block;
            const TVariable *instanceVariable     = blockReference.second->instanceVariable;
            if (instanceVariable != nullptr)
            {
                interfaceBlocks += uniformBlockStructString(interfaceBlock);
            }
    
            unsigned int activeRegister                            = mUniformBlockRegister;
            mUniformBlockRegisterMap[interfaceBlock.name().data()] = activeRegister;
    
            if (instanceVariable != nullptr && instanceVariable->getType().isArray())
            {
                unsigned int instanceArraySize = instanceVariable->getType().getOutermostArraySize();
                for (unsigned int arrayIndex = 0; arrayIndex < instanceArraySize; arrayIndex++)
                {
                    interfaceBlocks += uniformBlockString(interfaceBlock, instanceVariable,
                                                          activeRegister + arrayIndex, arrayIndex);
                }
                mUniformBlockRegister += instanceArraySize;
            }
            else
            {
                interfaceBlocks += uniformBlockString(interfaceBlock, instanceVariable, activeRegister,
                                                      GL_INVALID_INDEX);
                mUniformBlockRegister += 1u;
            }
        }
    
        return (interfaceBlocks.empty() ? "" : ("// Uniform Blocks\n\n" + interfaceBlocks));
    }
    
    TString ResourcesHLSL::shaderStorageBlocksHeader(
        const ReferencedInterfaceBlocks &referencedInterfaceBlocks)
    {
        TString interfaceBlocks;
    
        for (const auto &interfaceBlockReference : referencedInterfaceBlocks)
        {
            const TInterfaceBlock &interfaceBlock = *interfaceBlockReference.second->block;
            const TVariable *instanceVariable     = interfaceBlockReference.second->instanceVariable;
    
            unsigned int activeRegister                                  = mUAVRegister;
            mShaderStorageBlockRegisterMap[interfaceBlock.name().data()] = activeRegister;
    
            if (instanceVariable != nullptr && instanceVariable->getType().isArray())
            {
                unsigned int instanceArraySize = instanceVariable->getType().getOutermostArraySize();
                for (unsigned int arrayIndex = 0; arrayIndex < instanceArraySize; arrayIndex++)
                {
                    interfaceBlocks += shaderStorageBlockString(
                        interfaceBlock, instanceVariable, activeRegister + arrayIndex, arrayIndex);
                }
                mUAVRegister += instanceArraySize;
            }
            else
            {
                interfaceBlocks += shaderStorageBlockString(interfaceBlock, instanceVariable,
                                                            activeRegister, GL_INVALID_INDEX);
                mUAVRegister += 1u;
            }
        }
    
        return (interfaceBlocks.empty() ? "" : ("// Shader Storage Blocks\n\n" + interfaceBlocks));
    }
    
    TString ResourcesHLSL::uniformBlockString(const TInterfaceBlock &interfaceBlock,
                                              const TVariable *instanceVariable,
                                              unsigned int registerIndex,
                                              unsigned int arrayIndex)
    {
        const TString &arrayIndexString = (arrayIndex != GL_INVALID_INDEX ? str(arrayIndex) : "");
        const TString &blockName        = TString(interfaceBlock.name().data()) + arrayIndexString;
        TString hlsl;
    
        hlsl += "cbuffer " + blockName + " : register(b" + str(registerIndex) +
                ")\n"
                "{\n";
    
        if (instanceVariable != nullptr)
        {
            hlsl += "    " + InterfaceBlockStructName(interfaceBlock) + " " +
                    InterfaceBlockInstanceString(instanceVariable->name(), arrayIndex) + ";\n";
        }
        else
        {
            const TLayoutBlockStorage blockStorage = interfaceBlock.blockStorage();
            hlsl += uniformBlockMembersString(interfaceBlock, blockStorage);
        }
    
        hlsl += "};\n\n";
    
        return hlsl;
    }
    
    TString ResourcesHLSL::shaderStorageBlockString(const TInterfaceBlock &interfaceBlock,
                                                    const TVariable *instanceVariable,
                                                    unsigned int registerIndex,
                                                    unsigned int arrayIndex)
    {
        TString hlsl;
        if (instanceVariable != nullptr)
        {
            hlsl += "RWByteAddressBuffer " +
                    InterfaceBlockInstanceString(instanceVariable->name(), arrayIndex) +
                    ": register(u" + str(registerIndex) + ");\n";
        }
        else
        {
            hlsl += "RWByteAddressBuffer " + Decorate(interfaceBlock.name()) + ": register(u" +
                    str(registerIndex) + ");\n";
        }
        return hlsl;
    }
    
    TString ResourcesHLSL::InterfaceBlockInstanceString(const ImmutableString &instanceName,
                                                        unsigned int arrayIndex)
    {
        if (arrayIndex != GL_INVALID_INDEX)
        {
            return DecoratePrivate(instanceName) + "_" + str(arrayIndex);
        }
        else
        {
            return Decorate(instanceName);
        }
    }
    
    TString ResourcesHLSL::uniformBlockMembersString(const TInterfaceBlock &interfaceBlock,
                                                     TLayoutBlockStorage blockStorage)
    {
        TString hlsl;
    
        Std140PaddingHelper padHelper = mStructureHLSL->getPaddingHelper();
    
        for (unsigned int typeIndex = 0; typeIndex < interfaceBlock.fields().size(); typeIndex++)
        {
            const TField &field    = *interfaceBlock.fields()[typeIndex];
            const TType &fieldType = *field.type();
    
            if (blockStorage == EbsStd140)
            {
                // 2 and 3 component vector types in some cases need pre-padding
                hlsl += padHelper.prePaddingString(fieldType);
            }
    
            hlsl += "    " + InterfaceBlockFieldTypeString(field, blockStorage) + " " +
                    Decorate(field.name()) + ArrayString(fieldType).data() + ";\n";
    
            // must pad out after matrices and arrays, where HLSL usually allows itself room to pack
            // stuff
            if (blockStorage == EbsStd140)
            {
                const bool useHLSLRowMajorPacking =
                    (fieldType.getLayoutQualifier().matrixPacking == EmpColumnMajor);
                hlsl += padHelper.postPaddingString(fieldType, useHLSLRowMajorPacking);
            }
        }
    
        return hlsl;
    }
    
    TString ResourcesHLSL::uniformBlockStructString(const TInterfaceBlock &interfaceBlock)
    {
        const TLayoutBlockStorage blockStorage = interfaceBlock.blockStorage();
    
        return "struct " + InterfaceBlockStructName(interfaceBlock) +
               "\n"
               "{\n" +
               uniformBlockMembersString(interfaceBlock, blockStorage) + "};\n\n";
    }
    }  // namespace sh