Edit

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

Branch :

  • Show log

    Commit

  • Author : Xinghua Cao
    Date : 2020-08-12 13:30:26
    Hash : dc1c1cb5
    Message : Restrict to translate uniform block to StructuredBuffer We had translated an uniform block only containing a large array member into StructuredBuffer instead of cbuffer on D3D backend for slow fxc compile performance issue with dynamic uniform indexing. Now we add more conditions to restrict the translation. Only indexing operator is allowed to operate on this uniform block variable. And we also restrict the types of uniform block's member. Bug: angleproject:3682 Change-Id: I992b7890d84fcaa6169722af6d7e14785526d48a Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2351728 Commit-Queue: Xinghua Cao <xinghua.cao@intel.com> Reviewed-by: Geoff Lang <geofflang@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Jiajia Qin <jiajia.qin@intel.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,
                                                 bool usedStructuredbuffer)
    {
        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)
        {
            // If uniform block's layout is std140 and translating it to StructuredBuffer,
            // should pack structure in the end, in order to fit API buffer.
            bool forcePackingEnd = usedStructuredbuffer && (blockStorage == EbsStd140);
            // Use HLSL row-major packing for GLSL column-major matrices
            return QualifiedStructNameString(*structure, matrixPacking == EmpColumnMajor,
                                             blockStorage == EbsStd140, forcePackingEnd);
        }
        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 << "}";
    }
    
    static TString InterfaceBlockScalarVectorFieldPaddingString(const TType &type)
    {
        switch (type.getBasicType())
        {
            case EbtFloat:
                switch (type.getNominalSize())
                {
                    case 1:
                        return "float3 padding;";
                    case 2:
                        return "float2 padding;";
                    case 3:
                        return "float padding;";
                    default:
                        break;
                }
                break;
            case EbtInt:
                switch (type.getNominalSize())
                {
                    case 1:
                        return "int3 padding;";
                    case 2:
                        return "int2 padding;";
                    case 3:
                        return "int padding";
                    default:
                        break;
                }
                break;
            case EbtUInt:
                switch (type.getNominalSize())
                {
                    case 1:
                        return "uint3 padding;";
                    case 2:
                        return "uint2 padding;";
                    case 3:
                        return "uint padding;";
                    default:
                        break;
                }
                break;
            case EbtBool:
                switch (type.getNominalSize())
                {
                    case 1:
                        return "bool3 padding;";
                    case 2:
                        return "bool2 padding;";
                    case 3:
                        return "bool padding;";
                    default:
                        break;
                }
                break;
            default:
                break;
        }
        return "";
    }
    
    }  // anonymous namespace
    
    ResourcesHLSL::ResourcesHLSL(StructureHLSL *structureHLSL,
                                 ShShaderOutput outputType,
                                 const std::vector<ShaderVariable> &uniforms,
                                 unsigned int firstUniformRegister)
        : mUniformRegister(firstUniformRegister),
          mUniformBlockRegister(0),
          mSRVRegister(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 = mSRVRegister;
        }
        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))
        {
            mSRVRegister += 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                     = mSRVRegister;
        mUniformRegisterMap[std::string(name.c_str())] = registerIndex;
        unsigned int registerCount = type.isArray() ? type.getArraySizeProduct() : 1u;
        mSRVRegister += 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, 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 = mSRVRegister;
            groupTextureRegisterIndex += reservedReadonlyImageRegisterCount;
            mSRVRegister += 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,
        const std::map<int, const TInterfaceBlock *> &uniformBlockTranslatedToStructuredBuffer)
    {
        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);
            }
    
            // In order to avoid compile performance issue, translate uniform block to structured
            // buffer. anglebug.com/3682.
            if (uniformBlockTranslatedToStructuredBuffer.count(interfaceBlock.uniqueId().get()) != 0)
            {
                unsigned int structuredBufferRegister = mSRVRegister;
                if (instanceVariable != nullptr && instanceVariable->getType().isArray())
                {
                    unsigned int instanceArraySize =
                        instanceVariable->getType().getOutermostArraySize();
                    for (unsigned int arrayIndex = 0; arrayIndex < instanceArraySize; arrayIndex++)
                    {
                        interfaceBlocks += uniformBlockWithOneLargeArrayMemberString(
                            interfaceBlock, instanceVariable, structuredBufferRegister + arrayIndex,
                            arrayIndex);
                    }
                    mSRVRegister += instanceArraySize;
                }
                else
                {
                    interfaceBlocks += uniformBlockWithOneLargeArrayMemberString(
                        interfaceBlock, instanceVariable, structuredBufferRegister, GL_INVALID_INDEX);
                    mSRVRegister += 1u;
                }
                mUniformBlockRegisterMap[interfaceBlock.name().data()] = structuredBufferRegister;
                mUniformBlockUseStructuredBufferMap[interfaceBlock.name().data()] = true;
                continue;
            }
    
            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::uniformBlockWithOneLargeArrayMemberString(
        const TInterfaceBlock &interfaceBlock,
        const TVariable *instanceVariable,
        unsigned int registerIndex,
        unsigned int arrayIndex)
    {
        TString hlsl, typeString;
    
        const TField &field                    = *interfaceBlock.fields()[0];
        const TLayoutBlockStorage blockStorage = interfaceBlock.blockStorage();
        typeString             = InterfaceBlockFieldTypeString(field, blockStorage, true);
        const TType &fieldType = *field.type();
        if (fieldType.isMatrix())
        {
            if (arrayIndex == GL_INVALID_INDEX || arrayIndex == 0)
            {
                hlsl += "struct pack" + Decorate(interfaceBlock.name()) + " { " + typeString + " " +
                        Decorate(field.name()) + "; };\n";
            }
            typeString = "pack" + Decorate(interfaceBlock.name());
        }
        else if (fieldType.isVectorArray() || fieldType.isScalarArray())
        {
            // If the member is an array of scalars or vectors, std140 rules require the base array
            // stride are rounded up to the base alignment of a vec4.
            if (arrayIndex == GL_INVALID_INDEX || arrayIndex == 0)
            {
                hlsl += "struct pack" + Decorate(interfaceBlock.name()) + " { " + typeString + " " +
                        Decorate(field.name()) + ";\n";
                hlsl += InterfaceBlockScalarVectorFieldPaddingString(fieldType) + " };\n";
            }
            typeString = "pack" + Decorate(interfaceBlock.name());
        }
    
        if (instanceVariable != nullptr)
        {
    
            hlsl += "StructuredBuffer <" + typeString + "> " +
                    InterfaceBlockInstanceString(instanceVariable->name(), arrayIndex) + "_" +
                    Decorate(field.name()) + +" : register(t" + str(registerIndex) + ");\n";
        }
        else
        {
            hlsl += "StructuredBuffer <" + typeString + "> " + Decorate(field.name()) +
                    " : register(t" + str(registerIndex) + ");\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, false);
            }
    
            hlsl += "    " + InterfaceBlockFieldTypeString(field, blockStorage, false) + " " +
                    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, false);
            }
        }
    
        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