Edit

kc3-lang/angle/src/libANGLE/renderer/d3d/DynamicImage2DHLSL.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/libANGLE/renderer/d3d/DynamicImage2DHLSL.cpp
  • //
    // Copyright 2019 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.
    //
    // DynamicImage2DHLSL.cpp: Implementation for link and run-time HLSL generation
    //
    
    #include "libANGLE/renderer/d3d/DynamicImage2DHLSL.h"
    
    #include "common/string_utils.h"
    #include "libANGLE/renderer/d3d/ProgramD3D.h"
    #include "libANGLE/renderer/d3d/ShaderD3D.h"
    
    using namespace gl;
    
    namespace rx
    {
    
    namespace
    {
    
    enum Image2DHLSLGroup
    {
        IMAGE2D_R_FLOAT4,
        IMAGE2D_MIN = IMAGE2D_R_FLOAT4,
        IMAGE2D_R_UNORM,
        IMAGE2D_R_SNORM,
        IMAGE2D_R_UINT4,
        IMAGE2D_R_INT4,
        IMAGE2D_W_FLOAT4,
        IMAGE2D_W_UNORM,
        IMAGE2D_W_SNORM,
        IMAGE2D_W_UINT4,
        IMAGE2D_W_INT4,
        IMAGE2D_UNKNOWN,
        IMAGE2D_MAX = IMAGE2D_UNKNOWN
    };
    
    enum Image2DMethod
    {
        IMAGE2DSIZE,
        IMAGE2DLOAD,
        IMAGE2DSTORE
    };
    
    Image2DHLSLGroup image2DHLSLGroup(const sh::ShaderVariable &uniform)
    {
        GLenum format = uniform.imageUnitFormat;
        bool readonly = uniform.readonly;
        switch (uniform.type)
        {
            case GL_IMAGE_2D:
            {
                switch (format)
                {
                    case GL_RGBA32F:
                    case GL_RGBA16F:
                    case GL_R32F:
                        return readonly ? IMAGE2D_R_FLOAT4 : IMAGE2D_W_FLOAT4;
                    case GL_RGBA8:
                        return readonly ? IMAGE2D_R_UNORM : IMAGE2D_W_UNORM;
                    case GL_RGBA8_SNORM:
                        return readonly ? IMAGE2D_R_SNORM : IMAGE2D_W_SNORM;
                    default:
                        UNREACHABLE();
                        return IMAGE2D_UNKNOWN;
                }
            }
            case GL_INT_IMAGE_2D:
            {
                switch (format)
                {
                    case GL_RGBA32I:
                    case GL_RGBA16I:
                    case GL_RGBA8I:
                    case GL_R32I:
                        return readonly ? IMAGE2D_R_INT4 : IMAGE2D_W_INT4;
                    default:
                        UNREACHABLE();
                        return IMAGE2D_UNKNOWN;
                }
            }
            case GL_UNSIGNED_INT_IMAGE_2D:
            {
                switch (format)
                {
                    case GL_RGBA32UI:
                    case GL_RGBA16UI:
                    case GL_RGBA8UI:
                    case GL_R32UI:
                        return readonly ? IMAGE2D_R_UINT4 : IMAGE2D_W_UINT4;
                    default:
                        UNREACHABLE();
                        return IMAGE2D_UNKNOWN;
                }
            }
            default:
                UNREACHABLE();
                return IMAGE2D_UNKNOWN;
        }
    }
    
    std::string Image2DHLSLGroupSuffix(Image2DHLSLGroup group)
    {
        switch (group)
        {
            case IMAGE2D_R_FLOAT4:
                return "2D";
            case IMAGE2D_R_UNORM:
                return "2D_unorm_float4_";
            case IMAGE2D_R_SNORM:
                return "2D_snorm_float4_";
            case IMAGE2D_R_UINT4:
                return "2D_uint4_";
            case IMAGE2D_R_INT4:
                return "2D_int4_";
            case IMAGE2D_W_FLOAT4:
                return "RW2D_float4_";
            case IMAGE2D_W_UNORM:
                return "RW2D_unorm_float4_";
            case IMAGE2D_W_SNORM:
                return "RW2D_snorm_float4_";
            case IMAGE2D_W_UINT4:
                return "RW2D_uint4_";
            case IMAGE2D_W_INT4:
                return "RW2D_int4_";
            default:
                UNREACHABLE();
        }
    
        return "<unknown group type>";
    }
    
    std::string Image2DHLSLTextureString(Image2DHLSLGroup group, gl::TextureType type)
    {
        std::string textureString;
        switch (group)
        {
            case IMAGE2D_R_FLOAT4:
            case IMAGE2D_R_UNORM:
            case IMAGE2D_R_SNORM:
            case IMAGE2D_R_UINT4:
            case IMAGE2D_R_INT4:
                break;
            case IMAGE2D_W_FLOAT4:
            case IMAGE2D_W_UNORM:
            case IMAGE2D_W_SNORM:
            case IMAGE2D_W_UINT4:
            case IMAGE2D_W_INT4:
                textureString += "RW";
                break;
            default:
                UNREACHABLE();
        }
    
        textureString += "Texture";
    
        switch (type)
        {
            case gl::TextureType::_2D:
                textureString += "2D";
                break;
            case gl::TextureType::_3D:
                textureString += "3D";
                break;
            case gl::TextureType::_2DArray:
                textureString += "2DArray";
                break;
            default:
                UNREACHABLE();
        }
    
        switch (group)
        {
            case IMAGE2D_R_FLOAT4:
            case IMAGE2D_W_FLOAT4:
                textureString += "<float4>";
                break;
            case IMAGE2D_R_UNORM:
            case IMAGE2D_W_UNORM:
                textureString += "<unorm float4>";
                break;
            case IMAGE2D_R_SNORM:
            case IMAGE2D_W_SNORM:
                textureString += "<snorm float4>";
                break;
            case IMAGE2D_R_UINT4:
            case IMAGE2D_W_UINT4:
                textureString += "<uint4>";
                break;
            case IMAGE2D_R_INT4:
            case IMAGE2D_W_INT4:
                textureString += "<int4>";
                break;
            default:
                UNREACHABLE();
        }
    
        return textureString;
    }
    
    std::string Image2DHLSLGroupOffsetPrefix(Image2DHLSLGroup group)
    {
        switch (group)
        {
            case IMAGE2D_R_FLOAT4:
            case IMAGE2D_R_UNORM:
            case IMAGE2D_R_SNORM:
            case IMAGE2D_R_UINT4:
            case IMAGE2D_R_INT4:
                return "readonlyImageIndexOffset";
            case IMAGE2D_W_FLOAT4:
            case IMAGE2D_W_UNORM:
            case IMAGE2D_W_SNORM:
            case IMAGE2D_W_UINT4:
            case IMAGE2D_W_INT4:
                return "imageIndexOffset";
            default:
                UNREACHABLE();
        }
    
        return "<unknown group type>";
    }
    
    std::string Image2DHLSLGroupDeclarationPrefix(Image2DHLSLGroup group)
    {
        switch (group)
        {
            case IMAGE2D_R_FLOAT4:
            case IMAGE2D_R_UNORM:
            case IMAGE2D_R_SNORM:
            case IMAGE2D_R_UINT4:
            case IMAGE2D_R_INT4:
                return "readonlyImages";
            case IMAGE2D_W_FLOAT4:
            case IMAGE2D_W_UNORM:
            case IMAGE2D_W_SNORM:
            case IMAGE2D_W_UINT4:
            case IMAGE2D_W_INT4:
                return "images";
            default:
                UNREACHABLE();
        }
    
        return "<unknown group type>";
    }
    
    std::string Image2DHLSLGroupRegisterSuffix(Image2DHLSLGroup group)
    {
        switch (group)
        {
            case IMAGE2D_R_FLOAT4:
            case IMAGE2D_R_UNORM:
            case IMAGE2D_R_SNORM:
            case IMAGE2D_R_UINT4:
            case IMAGE2D_R_INT4:
                return "t";
            case IMAGE2D_W_FLOAT4:
            case IMAGE2D_W_UNORM:
            case IMAGE2D_W_SNORM:
            case IMAGE2D_W_UINT4:
            case IMAGE2D_W_INT4:
                return "u";
            default:
                UNREACHABLE();
        }
    
        return "<unknown group type>";
    }
    
    std::string Image2DHLSLGroupFunctionName(Image2DHLSLGroup group, Image2DMethod method)
    {
        std::string name = "gl_image";
        name += Image2DHLSLGroupSuffix(group);
        switch (method)
        {
            case IMAGE2DSIZE:
                name += "Size";
                break;
            case IMAGE2DLOAD:
                name += "Load";
                break;
            case IMAGE2DSTORE:
                name += "Store";
                break;
            default:
                UNREACHABLE();
        }
    
        return name;
    }
    
    std::string getImage2DGroupReturnType(Image2DHLSLGroup group, Image2DMethod method)
    {
        switch (method)
        {
            case IMAGE2DSIZE:
                return "int2";
            case IMAGE2DLOAD:
                switch (group)
                {
                    case IMAGE2D_R_FLOAT4:
                    case IMAGE2D_R_UNORM:
                    case IMAGE2D_R_SNORM:
                    case IMAGE2D_W_FLOAT4:
                    case IMAGE2D_W_UNORM:
                    case IMAGE2D_W_SNORM:
                        return "float4";
                    case IMAGE2D_R_UINT4:
                    case IMAGE2D_W_UINT4:
                        return "uint4";
                    case IMAGE2D_R_INT4:
                    case IMAGE2D_W_INT4:
                        return "int4";
                    default:
                        UNREACHABLE();
                        return "unknown group type";
                }
            case IMAGE2DSTORE:
                return "void";
            default:
                UNREACHABLE();
                return "unknown image method";
        }
    }
    
    std::string getImageMetadata(Image2DHLSLGroup group)
    {
        switch (group)
        {
            case IMAGE2D_R_FLOAT4:
            case IMAGE2D_R_UNORM:
            case IMAGE2D_R_SNORM:
            case IMAGE2D_R_UINT4:
            case IMAGE2D_R_INT4:
                return "readonlyImageMetadata[imageIndex - readonlyImageIndexStart]";
            case IMAGE2D_W_FLOAT4:
            case IMAGE2D_W_UNORM:
            case IMAGE2D_W_SNORM:
            case IMAGE2D_W_UINT4:
            case IMAGE2D_W_INT4:
                return "imageMetadata[imageIndex - imageIndexStart]";
            default:
                UNREACHABLE();
                return "unknown image method";
        }
    }
    
    void OutputImage2DFunctionArgumentList(std::ostringstream &out,
                                           Image2DHLSLGroup group,
                                           Image2DMethod method)
    {
        out << "uint imageIndex";
    
        if (method == IMAGE2DLOAD || method == IMAGE2DSTORE)
        {
            out << ", int2 p";
            if (method == IMAGE2DSTORE)
            {
                switch (group)
                {
                    case IMAGE2D_R_FLOAT4:
                    case IMAGE2D_R_UNORM:
                    case IMAGE2D_R_SNORM:
                    case IMAGE2D_W_FLOAT4:
                    case IMAGE2D_W_UNORM:
                    case IMAGE2D_W_SNORM:
                        out << ", float4 data";
                        break;
                    case IMAGE2D_R_UINT4:
                    case IMAGE2D_W_UINT4:
                        out << ", uint4 data";
                        break;
                    case IMAGE2D_R_INT4:
                    case IMAGE2D_W_INT4:
                        out << ", int4 data";
                        break;
                    default:
                        UNREACHABLE();
                }
            }
        }
    }
    
    void OutputImage2DSizeFunction(std::ostringstream &out,
                                   Image2DHLSLGroup textureGroup,
                                   unsigned int totalCount,
                                   unsigned int texture2DCount,
                                   unsigned int texture3DCount,
                                   unsigned int texture2DArrayCount,
                                   const std::string &offsetStr,
                                   const std::string &declarationStr,
                                   bool getDimensionsIgnoresBaseLevel)
    {
        out << getImage2DGroupReturnType(textureGroup, IMAGE2DSIZE) << " "
            << Image2DHLSLGroupFunctionName(textureGroup, IMAGE2DSIZE) << "(";
        OutputImage2DFunctionArgumentList(out, textureGroup, IMAGE2DSIZE);
        out << ")\n"
               "{\n";
        out << "    uint width, height;\n";
    
        if (texture2DCount > 0)
        {
            if (texture2DCount == totalCount)
            {
                out << "    const uint index = imageIndex -  " << offsetStr << "2D;\n";
                if (getDimensionsIgnoresBaseLevel)
                {
                    out << "    uint levelCount;\n";
                    out << "    const uint level = " << getImageMetadata(textureGroup) << ".level;\n";
                    out << "    " << declarationStr
                        << "2D[index].GetDimensions(level, width, height, levelCount);\n";
                }
                else
                {
                    out << "    " << declarationStr << "2D[index].GetDimensions(width, height);\n";
                }
            }
            else
            {
                out << "    if (imageIndex >= " << offsetStr << "2D && imageIndex < " << offsetStr
                    << "2D + " << texture2DCount << ")\n";
                out << "    {\n";
                out << "        const uint index = imageIndex -  " << offsetStr << "2D;\n";
                out << "        " << declarationStr << "2D[index].GetDimensions(width, height);\n";
                out << "    }\n";
            }
        }
    
        if (texture3DCount > 0)
        {
            if (texture3DCount == totalCount)
            {
                out << "    const uint index = imageIndex -  " << offsetStr << "3D;\n";
                out << "    uint depth;\n";
                out << "    " << declarationStr << "3D[index].GetDimensions(width, height, depth);\n";
            }
            else
            {
                if (texture2DArrayCount == 0)
                {
                    out << "    else\n";
                }
                else
                {
                    if (texture2DCount == 0)
                    {
                        out << "    if ";
                    }
                    else
                    {
                        out << "    else if";
                    }
                    out << "(imageIndex >= " << offsetStr << "3D && imageIndex < " << offsetStr
                        << "3D + " << texture3DCount << ")\n";
                }
                out << "    {\n";
                out << "        const uint index = imageIndex -  " << offsetStr << "3D;\n";
                out << "        uint depth;\n";
                out << "        " << declarationStr
                    << "3D[index].GetDimensions(width, height, depth);\n";
                out << "    }\n";
            }
        }
    
        if (texture2DArrayCount > 0)
        {
            if (texture2DArrayCount == totalCount)
            {
                out << "    const uint index = imageIndex -  " << offsetStr << "2DArray;\n";
                out << "    uint depth;\n";
                out << "    " << declarationStr
                    << "2DArray[index].GetDimensions(width, height, depth);\n";
            }
            else
            {
                out << "    else\n";
                out << "    {\n";
                out << "        const uint index = imageIndex -  " << offsetStr << "2DArray;\n";
                out << "        uint depth;\n";
                out << "        " << declarationStr
                    << "2DArray[index].GetDimensions(width, height, depth);\n";
                out << "    }\n";
            }
        }
        out << "    return int2(width, height);\n";
    
        out << "}\n";
    }
    
    void OutputImage2DLoadFunction(std::ostringstream &out,
                                   Image2DHLSLGroup textureGroup,
                                   unsigned int totalCount,
                                   unsigned int texture2DCount,
                                   unsigned int texture3DCount,
                                   unsigned int texture2DArrayCount,
                                   const std::string &offsetStr,
                                   const std::string &declarationStr)
    {
        out << getImage2DGroupReturnType(textureGroup, IMAGE2DLOAD) << " "
            << Image2DHLSLGroupFunctionName(textureGroup, IMAGE2DLOAD) << "(";
        OutputImage2DFunctionArgumentList(out, textureGroup, IMAGE2DLOAD);
        out << ")\n"
               "{\n";
    
        out << "    " << getImage2DGroupReturnType(textureGroup, IMAGE2DLOAD) << " result;\n";
    
        if (texture2DCount > 0)
        {
            if (texture2DCount == totalCount)
            {
                out << "    const uint index = imageIndex -  " << offsetStr << "2D;\n";
                out << "    result = " << declarationStr << "2D[index][uint2(p.x, p.y)];\n";
            }
            else
            {
                out << "    if (imageIndex >= " << offsetStr << "2D && imageIndex < " << offsetStr
                    << "2D + " << texture2DCount << ")\n";
                out << "    {\n";
                out << "        const uint index = imageIndex -  " << offsetStr << "2D;\n";
                out << "        result = " << declarationStr << "2D[index][uint2(p.x, p.y)];\n";
                out << "    }\n";
            }
        }
    
        if (texture3DCount > 0)
        {
            if (texture3DCount == totalCount)
            {
                out << "    const uint index = imageIndex -  " << offsetStr << "3D;\n";
                out << "    result = " << declarationStr << "3D[index][uint3(p.x, p.y, "
                    << getImageMetadata(textureGroup) << ".layer)];\n";
            }
            else
            {
                if (texture2DArrayCount == 0)
                {
                    out << "    else\n";
                }
                else
                {
                    if (texture2DCount == 0)
                    {
                        out << "    if ";
                    }
                    else
                    {
                        out << "    else if";
                    }
                    out << "(imageIndex >= " << offsetStr << "3D && imageIndex < " << offsetStr
                        << "3D + " << texture3DCount << ")\n";
                }
                out << "    {\n";
                out << "        const uint index = imageIndex -  " << offsetStr << "3D;\n";
                out << "        result = " << declarationStr << "3D[index][uint3(p.x, p.y, "
                    << getImageMetadata(textureGroup) << ".layer)];\n";
                out << "    }\n";
            }
        }
    
        if (texture2DArrayCount > 0)
        {
            if (texture2DArrayCount == totalCount)
            {
                out << "    const uint index = imageIndex -  " << offsetStr << "2DArray;\n";
                out << "    result = " << declarationStr << "2DArray[index][uint3(p.x, p.y, "
                    << getImageMetadata(textureGroup) << ".layer)];\n";
            }
            else
            {
                out << "    else\n";
                out << "    {\n";
                out << "        const uint index = imageIndex -  " << offsetStr << "2DArray;\n";
                out << "        result = " << declarationStr << "2DArray[index][uint3(p.x, p.y, "
                    << getImageMetadata(textureGroup) << ".layer)];\n";
                out << "    }\n";
            }
        }
    
        out << "    return result;\n";
        out << "}\n";
    }
    
    void OutputImage2DStoreFunction(std::ostringstream &out,
                                    Image2DHLSLGroup textureGroup,
                                    unsigned int totalCount,
                                    unsigned int texture2DCount,
                                    unsigned int texture3DCount,
                                    unsigned int texture2DArrayCount,
                                    const std::string &offsetStr,
                                    const std::string &declarationStr)
    {
        out << getImage2DGroupReturnType(textureGroup, IMAGE2DSTORE) << " "
            << Image2DHLSLGroupFunctionName(textureGroup, IMAGE2DSTORE) << "(";
        OutputImage2DFunctionArgumentList(out, textureGroup, IMAGE2DSTORE);
        out << ")\n"
               "{\n";
    
        if (texture2DCount > 0)
        {
            if (texture2DCount == totalCount)
            {
                out << "    const uint index = imageIndex -  " << offsetStr << "2D;\n";
                out << "    " << declarationStr << "2D[index][uint2(p.x, p.y)] = data;\n";
            }
            else
            {
                out << "    if (imageIndex >= " << offsetStr << "2D && imageIndex < " << offsetStr
                    << "2D + " << texture2DCount << ")\n";
                out << "    {\n";
                out << "        const uint index = imageIndex -  " << offsetStr << "2D;\n";
                out << "        " << declarationStr << "2D[index][uint2(p.x, p.y)] = data;\n";
                out << "    }\n";
            }
        }
    
        if (texture3DCount > 0)
        {
            if (texture3DCount == totalCount)
            {
                out << "    const uint index = imageIndex -  " << offsetStr << "3D;\n";
                out << "    " << declarationStr << "3D[index][uint3(p.x, p.y, "
                    << getImageMetadata(textureGroup) << ".layer)] = data;\n";
            }
            else
            {
                if (texture2DArrayCount == 0)
                {
                    out << "    else\n";
                }
                else
                {
                    if (texture2DCount == 0)
                    {
                        out << "    if ";
                    }
                    else
                    {
                        out << "    else if";
                    }
                    out << "(imageIndex >= " << offsetStr << "3D && imageIndex < " << offsetStr
                        << "3D + " << texture3DCount << ")\n";
                }
                out << "    {\n";
                out << "        const uint index = imageIndex -  " << offsetStr << "3D;\n";
                out << "        " << declarationStr << "3D[index][uint3(p.x, p.y, "
                    << getImageMetadata(textureGroup) << ".layer)] = data;\n";
                out << "    }\n";
            }
        }
    
        if (texture2DArrayCount > 0)
        {
            if (texture2DArrayCount == totalCount)
            {
                out << "    const uint index = imageIndex -  " << offsetStr << "2DArray;\n";
                out << "    " << declarationStr << "2DArray[index][uint3(p.x, p.y, "
                    << getImageMetadata(textureGroup) << ".layer)] = data;\n";
            }
            else
            {
                out << "    else\n";
                out << "    {\n";
                out << "        const uint index = imageIndex -  " << offsetStr << "2DArray;\n";
                out << "        " << declarationStr << "2DArray[index][uint3(p.x, p.y, "
                    << getImageMetadata(textureGroup) << ".layer)] = data;\n";
                out << "    }\n";
            }
        }
    
        out << "}\n";
    }
    
    unsigned int *GetImage2DRegisterIndex(Image2DHLSLGroup textureGroup,
                                          unsigned int *groupTextureRegisterIndex,
                                          unsigned int *groupRWTextureRegisterIndex)
    {
        switch (textureGroup)
        {
            case IMAGE2D_R_FLOAT4:
            case IMAGE2D_R_UNORM:
            case IMAGE2D_R_SNORM:
            case IMAGE2D_R_UINT4:
            case IMAGE2D_R_INT4:
                return groupTextureRegisterIndex;
            case IMAGE2D_W_FLOAT4:
            case IMAGE2D_W_UNORM:
            case IMAGE2D_W_SNORM:
            case IMAGE2D_W_UINT4:
            case IMAGE2D_W_INT4:
                return groupRWTextureRegisterIndex;
            default:
                UNREACHABLE();
                return nullptr;
        }
    }
    
    void OutputHLSLImage2DUniformGroup(ProgramD3D &programD3D,
                                       const gl::ProgramState &programData,
                                       gl::ShaderType shaderType,
                                       std::ostringstream &out,
                                       const Image2DHLSLGroup textureGroup,
                                       const std::vector<sh::ShaderVariable> &group,
                                       const gl::ImageUnitTextureTypeMap &image2DBindLayout,
                                       unsigned int *groupTextureRegisterIndex,
                                       unsigned int *groupRWTextureRegisterIndex,
                                       unsigned int *image2DTexture3D,
                                       unsigned int *image2DTexture2DArray,
                                       unsigned int *image2DTexture2D)
    {
        if (group.empty())
        {
            return;
        }
    
        unsigned int texture2DCount = 0, texture3DCount = 0, texture2DArrayCount = 0;
        for (const sh::ShaderVariable &uniform : group)
        {
            if (!programD3D.hasNamedUniform(uniform.name))
            {
                continue;
            }
            for (unsigned int index = 0; index < uniform.getArraySizeProduct(); index++)
            {
                switch (image2DBindLayout.at(uniform.binding + index))
                {
                    case gl::TextureType::_2D:
                        texture2DCount++;
                        break;
                    case gl::TextureType::_3D:
                        texture3DCount++;
                        break;
                    case gl::TextureType::_2DArray:
                    case gl::TextureType::CubeMap:
                        texture2DArrayCount++;
                        break;
                    default:
                        UNREACHABLE();
                }
            }
        }
    
        unsigned int totalCount            = texture2DCount + texture3DCount + texture2DArrayCount;
        unsigned int *image2DRegisterIndex = GetImage2DRegisterIndex(
            textureGroup, groupTextureRegisterIndex, groupRWTextureRegisterIndex);
        unsigned int texture2DRegisterIndex      = *image2DRegisterIndex;
        unsigned int texture3DRegisterIndex      = texture2DRegisterIndex + texture2DCount;
        unsigned int texture2DArrayRegisterIndex = texture3DRegisterIndex + texture3DCount;
        *image2DRegisterIndex += totalCount;
    
        std::string offsetStr =
            Image2DHLSLGroupOffsetPrefix(textureGroup) + Image2DHLSLGroupSuffix(textureGroup);
        std::string declarationStr =
            Image2DHLSLGroupDeclarationPrefix(textureGroup) + Image2DHLSLGroupSuffix(textureGroup);
        std::string registerStr = Image2DHLSLGroupRegisterSuffix(textureGroup);
        if (texture2DCount > 0)
        {
            out << "static const uint " << offsetStr << "2D = " << texture2DRegisterIndex << ";\n";
            out << "uniform " << Image2DHLSLTextureString(textureGroup, gl::TextureType::_2D) << " "
                << declarationStr << "2D[" << texture2DCount << "]"
                << " : register(" << registerStr << texture2DRegisterIndex << ");\n";
        }
        if (texture3DCount > 0)
        {
            out << "static const uint " << offsetStr << "3D = " << texture3DRegisterIndex << ";\n";
            out << "uniform " << Image2DHLSLTextureString(textureGroup, gl::TextureType::_3D) << " "
                << declarationStr << "3D[" << texture3DCount << "]"
                << " : register(" << registerStr << texture3DRegisterIndex << ");\n";
        }
        if (texture2DArrayCount > 0)
        {
            out << "static const uint " << offsetStr << "2DArray = " << texture2DArrayRegisterIndex
                << ";\n";
            out << "uniform " << Image2DHLSLTextureString(textureGroup, gl::TextureType::_2DArray)
                << " " << declarationStr << "2DArray[" << texture2DArrayCount << "]"
                << " : register(" << registerStr << texture2DArrayRegisterIndex << ");\n";
        }
        for (const sh::ShaderVariable &uniform : group)
        {
            if (!programD3D.hasNamedUniform(uniform.name))
            {
                continue;
            }
    
            out << "static const uint " << DecorateVariable(uniform.name)
                << ArrayIndexString(uniform.arraySizes) << " = {";
            for (unsigned int index = 0; index < uniform.getArraySizeProduct(); index++)
            {
                if (index > 0)
                {
                    out << ", ";
                }
                switch (image2DBindLayout.at(uniform.binding + index))
                {
                    case gl::TextureType::_2D:
                    {
                        out << texture2DRegisterIndex;
                        programD3D.assignImage2DRegisters(texture2DRegisterIndex,
                                                          uniform.binding + index, uniform.readonly);
                        texture2DRegisterIndex++;
                        break;
                    }
                    case gl::TextureType::_3D:
                    {
                        out << texture3DRegisterIndex;
                        programD3D.assignImage2DRegisters(texture3DRegisterIndex,
                                                          uniform.binding + index, uniform.readonly);
                        texture3DRegisterIndex++;
                        break;
                    }
                    case gl::TextureType::_2DArray:
                    case gl::TextureType::CubeMap:
                    {
                        out << texture2DArrayRegisterIndex;
                        programD3D.assignImage2DRegisters(texture2DArrayRegisterIndex,
                                                          uniform.binding + index, uniform.readonly);
                        texture2DArrayRegisterIndex++;
                        break;
                    }
                    default:
                        UNREACHABLE();
                }
            }
            out << "};\n";
        }
    
        gl::Shader *shaderGL                     = programData.getAttachedShader(shaderType);
        const ShaderD3D *shaderD3D               = GetImplAs<ShaderD3D>(shaderGL);
        const bool getDimensionsIgnoresBaseLevel = programD3D.usesGetDimensionsIgnoresBaseLevel();
    
        if (shaderD3D->useImage2DFunction(Image2DHLSLGroupFunctionName(textureGroup, IMAGE2DSIZE)))
        {
            OutputImage2DSizeFunction(out, textureGroup, totalCount, texture2DCount, texture3DCount,
                                      texture2DArrayCount, offsetStr, declarationStr,
                                      getDimensionsIgnoresBaseLevel);
        }
        if (shaderD3D->useImage2DFunction(Image2DHLSLGroupFunctionName(textureGroup, IMAGE2DLOAD)))
        {
            OutputImage2DLoadFunction(out, textureGroup, totalCount, texture2DCount, texture3DCount,
                                      texture2DArrayCount, offsetStr, declarationStr);
        }
        if (shaderD3D->useImage2DFunction(Image2DHLSLGroupFunctionName(textureGroup, IMAGE2DSTORE)))
        {
            OutputImage2DStoreFunction(out, textureGroup, totalCount, texture2DCount, texture3DCount,
                                       texture2DArrayCount, offsetStr, declarationStr);
        }
    }
    
    // kImage2DFunctionString must be the same as outputHLSL.
    constexpr const char kImage2DFunctionString[] = "// @@ IMAGE2D DECLARATION FUNCTION STRING @@";
    }  // anonymous namespace
    
    std::string generateShaderForImage2DBindSignature(
        const d3d::Context *context,
        ProgramD3D &programD3D,
        const gl::ProgramState &programData,
        gl::ShaderType shaderType,
        std::vector<sh::ShaderVariable> &image2DUniforms,
        const gl::ImageUnitTextureTypeMap &image2DBindLayout)
    {
        std::vector<std::vector<sh::ShaderVariable>> groupedImage2DUniforms(IMAGE2D_MAX + 1);
        unsigned int image2DTexture2DCount = 0, image2DTexture3DCount = 0,
                     image2DTexture2DArrayCount = 0;
        for (sh::ShaderVariable &image2D : image2DUniforms)
        {
            for (unsigned int index = 0; index < image2D.getArraySizeProduct(); index++)
            {
                // Any image variable declared without a binding qualifier is initially bound to unit
                // zero.
                if (image2D.binding == -1)
                {
                    image2D.binding = 0;
                }
                switch (image2DBindLayout.at(image2D.binding + index))
                {
                    case gl::TextureType::_2D:
                        image2DTexture2DCount++;
                        break;
                    case gl::TextureType::_3D:
                        image2DTexture3DCount++;
                        break;
                    case gl::TextureType::_2DArray:
                    case gl::TextureType::CubeMap:
                        image2DTexture2DArrayCount++;
                        break;
                    default:
                        UNREACHABLE();
                }
            }
            Image2DHLSLGroup group = image2DHLSLGroup(image2D);
            groupedImage2DUniforms[group].push_back(image2D);
        }
    
        gl::Shader *shaderGL                     = programData.getAttachedShader(shaderType);
        const ShaderD3D *shaderD3D               = GetImplAs<ShaderD3D>(shaderGL);
        unsigned int groupTextureRegisterIndex   = shaderD3D->getReadonlyImage2DRegisterIndex();
        unsigned int groupRWTextureRegisterIndex = shaderD3D->getImage2DRegisterIndex();
        unsigned int image2DTexture3DIndex       = 0;
        unsigned int image2DTexture2DArrayIndex  = image2DTexture3DCount;
        unsigned int image2DTexture2DIndex       = image2DTexture3DCount + image2DTexture2DArrayCount;
        std::ostringstream out;
    
        for (int groupId = IMAGE2D_MIN; groupId < IMAGE2D_MAX; ++groupId)
        {
            OutputHLSLImage2DUniformGroup(programD3D, programData, shaderType, out,
                                          Image2DHLSLGroup(groupId), groupedImage2DUniforms[groupId],
                                          image2DBindLayout, &groupTextureRegisterIndex,
                                          &groupRWTextureRegisterIndex, &image2DTexture3DIndex,
                                          &image2DTexture2DArrayIndex, &image2DTexture2DIndex);
        }
    
        std::string shaderHLSL(programData.getAttachedShader(shaderType)->getTranslatedSource());
        bool success = angle::ReplaceSubstring(&shaderHLSL, kImage2DFunctionString, out.str());
        ASSERT(success);
    
        return shaderHLSL;
    }
    
    }  // namespace rx