Edit

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

Branch :

  • Show log

    Commit

  • Author : Xinghua Cao
    Date : 2020-06-23 16:57:05
    Hash : d1bf23d7
    Message : Restrict member types for StructuredBuffer Std140 storage layout has some rules for uniform block, must pad StructuredBuffer element type explicitly to match std140 layout rules when translating std140 uniform block to StructuredBuffer. In this CL, restrict member types which are not needed to add paddings according std140 layout rules. Bug: angleproject:4783 Change-Id: I50130da64ad6865083849bf55b76525f3d2835a8 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2259226 Commit-Queue: Xinghua Cao <xinghua.cao@intel.com> 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_");
    // D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT = 128;
    const unsigned int kMaxInputResourceSlotCount = 128u;
    // If uniform block member's array size is larger than kMinArraySizeUseStructuredBuffer,
    // then we translate uniform block to StructuredBuffer for compiling performance.
    const unsigned int kMinArraySizeUseStructuredBuffer = 50u;
    
    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 << "}";
    }
    
    // Check whether all fields match std140 storage layout, and do not need to add paddings
    // when translating std140 uniform block to StructuredBuffer.
    static bool ShouldPadUniformBlockMemberForStructuredBuffer(const TType &type)
    {
        const TStructure *structure = type.getStruct();
        if (structure)
        {
            const TFieldList &fields = structure->fields();
            for (size_t i = 0; i < fields.size(); i++)
            {
                if (ShouldPadUniformBlockMemberForStructuredBuffer(*fields[i]->type()))
                {
                    return true;
                }
            }
            return false;
        }
        else if (type.isMatrix())
        {
            if ((type.getLayoutQualifier().matrixPacking != EmpRowMajor && type.getRows() == 4) ||
                (type.getLayoutQualifier().matrixPacking == EmpRowMajor && type.getCols() == 4))
            {
                return false;
            }
        }
        else
        {
            if (type.isVector() && type.getNominalSize() == 4)
            {
                return false;
            }
        }
    
        return true;
    }
    }  // anonymous namespace
    
    ResourcesHLSL::ResourcesHLSL(StructureHLSL *structureHLSL,
                                 ShShaderOutput outputType,
                                 ShCompileOptions compileOptions,
                                 const std::vector<ShaderVariable> &uniforms,
                                 unsigned int firstUniformRegister)
        : mUniformRegister(firstUniformRegister),
          mUniformBlockRegister(0),
          mSRVRegister(0),
          mUAVRegister(0),
          mSamplerCount(0),
          mStructureHLSL(structureHLSL),
          mOutputType(outputType),
          mCompileOptions(compileOptions),
          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)
    {
        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 (shouldTranslateUniformBlockToStructuredBuffer(interfaceBlock))
            {
                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 matrix" + Decorate(field.name()) + " { " + typeString + " _matrix_" +
                        Decorate(field.name()) + "; };\n";
            }
            typeString = "matrix" + Decorate(field.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);
            }
    
            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";
    }
    
    bool ResourcesHLSL::shouldTranslateUniformBlockToStructuredBuffer(
        const TInterfaceBlock &interfaceBlock)
    {
        const TType &fieldType = *interfaceBlock.fields()[0]->type();
        // Restrict field and sub-fields types match std140 storage layout rules, even the uniform
        // does not use std140 qualifier.
        bool shouldPadUniformBlockMember = ShouldPadUniformBlockMemberForStructuredBuffer(fieldType);
    
        return (mCompileOptions & SH_DONT_TRANSLATE_UNIFORM_BLOCK_TO_STRUCTUREDBUFFER) == 0 &&
               mSRVRegister < kMaxInputResourceSlotCount && interfaceBlock.fields().size() == 1u &&
               !shouldPadUniformBlockMember && fieldType.getNumArraySizes() == 1u &&
               fieldType.getOutermostArraySize() >= kMinArraySizeUseStructuredBuffer;
    }
    }  // namespace sh