Edit

kc3-lang/angle/samples/shader_translator

Branch :

  • Show log

    Commit

  • Author : Shahbaz Youssefi
    Date : 2021-02-26 00:00:57
    Hash : 776c6015
    Message : Vulkan: Call glslang at compile time With this change, the ANGLE translator immediately compiles the generated GLSL into SPIR-V with glslang and discards the source. This is in preparation for generating SPIR-V directly, by making the frontend and backend already able to digest it. This change also allows the expensive glslang calls to be parallelized, improving the following perf test by about 20%: LinkProgramBenchmark.Run/vulkan_compile_and_link_multi_thread Previously, the test was run as such in the Vulkan backend: Main Thread 1 Thread 2 Compile1 ---> Compile2 ---------------------> Translator Translator <--- <--------------------- Link glslang for shader1 glslang for shader2 Done With this change, it is run as such: Main Thread 1 Thread 2 Compile1 ---> Compile2 ---------------------> Translator Translator glslang glslang <--- <--------------------- Link Done glslang_wrapper_utils no longer interacts with glslang! A rename will follow. Bug: angleproject:4889 Change-Id: If4303e8ba0ba43b1a2f47f8c0a9133d0bee1a19a Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2721195 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Tim Van Patten <timvp@google.com> Reviewed-by: Jamie Madill <jmadill@chromium.org>

  • shader_translator.cpp
  • //
    // Copyright 2002 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.
    //
    
    #include "GLSLANG/ShaderLang.h"
    
    #include <assert.h>
    #include <math.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sstream>
    #include <vector>
    #include "angle_gl.h"
    
    #if defined(ANGLE_ENABLE_VULKAN)
    // SPIR-V tools include for disassembly.
    #    include <spirv-tools/libspirv.hpp>
    #endif
    
    //
    // Return codes from main.
    //
    enum TFailCode
    {
        ESuccess = 0,
        EFailUsage,
        EFailCompile,
        EFailCompilerCreate,
    };
    
    static void usage();
    static sh::GLenum FindShaderType(const char *fileName);
    static bool CompileFile(char *fileName, ShHandle compiler, ShCompileOptions compileOptions);
    static void LogMsg(const char *msg, const char *name, const int num, const char *logName);
    static void PrintVariable(const std::string &prefix, size_t index, const sh::ShaderVariable &var);
    static void PrintActiveVariables(ShHandle compiler);
    
    // If NUM_SOURCE_STRINGS is set to a value > 1, the input file data is
    // broken into that many chunks. This will affect file/line numbering in
    // the preprocessor.
    const unsigned int NUM_SOURCE_STRINGS = 1;
    typedef std::vector<char *> ShaderSource;
    static bool ReadShaderSource(const char *fileName, ShaderSource &source);
    static void FreeShaderSource(ShaderSource &source);
    
    static bool ParseGLSLOutputVersion(const std::string &, ShShaderOutput *outResult);
    static bool ParseIntValue(const std::string &, int emptyDefault, int *outValue);
    
    static void PrintSpirv(const sh::BinaryBlob &blob);
    
    //
    // Set up the per compile resources
    //
    void GenerateResources(ShBuiltInResources *resources)
    {
        sh::InitBuiltInResources(resources);
    
        resources->MaxVertexAttribs             = 8;
        resources->MaxVertexUniformVectors      = 128;
        resources->MaxVaryingVectors            = 8;
        resources->MaxVertexTextureImageUnits   = 0;
        resources->MaxCombinedTextureImageUnits = 8;
        resources->MaxTextureImageUnits         = 8;
        resources->MaxFragmentUniformVectors    = 16;
        resources->MaxDrawBuffers               = 1;
        resources->MaxDualSourceDrawBuffers     = 1;
    
        resources->OES_standard_derivatives  = 0;
        resources->OES_EGL_image_external    = 0;
        resources->EXT_geometry_shader       = 1;
        resources->ANGLE_texture_multisample = 0;
        resources->APPLE_clip_distance       = 0;
    }
    
    int main(int argc, char *argv[])
    {
        TFailCode failCode = ESuccess;
    
        ShCompileOptions compileOptions = 0;
        int numCompiles                 = 0;
        ShHandle vertexCompiler         = 0;
        ShHandle fragmentCompiler       = 0;
        ShHandle computeCompiler        = 0;
        ShHandle geometryCompiler       = 0;
        ShShaderSpec spec               = SH_GLES2_SPEC;
        ShShaderOutput output           = SH_ESSL_OUTPUT;
    
    #if defined(ANGLE_ENABLE_VULKAN)
        sh::InitializeGlslang();
    #endif
        sh::Initialize();
    
        ShBuiltInResources resources;
        GenerateResources(&resources);
    
        argc--;
        argv++;
        for (; (argc >= 1) && (failCode == ESuccess); argc--, argv++)
        {
            if (argv[0][0] == '-')
            {
                switch (argv[0][1])
                {
                    case 'i':
                        compileOptions |= SH_INTERMEDIATE_TREE;
                        break;
                    case 'o':
                        compileOptions |= SH_OBJECT_CODE;
                        break;
                    case 'u':
                        compileOptions |= SH_VARIABLES;
                        break;
                    case 'p':
                        resources.WEBGL_debug_shader_precision = 1;
                        break;
                    case 's':
                        if (argv[0][2] == '=')
                        {
                            switch (argv[0][3])
                            {
                                case 'e':
                                    if (argv[0][4] == '3')
                                    {
                                        if (argv[0][5] == '1')
                                        {
                                            spec = SH_GLES3_1_SPEC;
                                        }
                                        else
                                        {
                                            spec = SH_GLES3_SPEC;
                                        }
                                    }
                                    else
                                    {
                                        spec = SH_GLES2_SPEC;
                                    }
                                    break;
                                case 'w':
                                    if (argv[0][4] == '3')
                                    {
                                        spec = SH_WEBGL3_SPEC;
                                    }
                                    else if (argv[0][4] == '2')
                                    {
                                        spec = SH_WEBGL2_SPEC;
                                    }
                                    else if (argv[0][4] == 'n')
                                    {
                                        spec = SH_WEBGL_SPEC;
                                    }
                                    else
                                    {
                                        spec                            = SH_WEBGL_SPEC;
                                        resources.FragmentPrecisionHigh = 1;
                                    }
                                    break;
                                case 'd':
                                    if (argv[0][4] == 'c')
                                    {
                                        spec = SH_GL_COMPATIBILITY_SPEC;
                                    }
                                    else
                                    {
                                        spec = SH_GL_CORE_SPEC;
                                    }
                                    break;
                                default:
                                    failCode = EFailUsage;
                            }
                        }
                        else
                        {
                            failCode = EFailUsage;
                        }
                        break;
                    case 'b':
                        if (argv[0][2] == '=')
                        {
                            switch (argv[0][3])
                            {
                                case 'e':
                                    output = SH_ESSL_OUTPUT;
                                    compileOptions |= SH_INITIALIZE_UNINITIALIZED_LOCALS;
                                    break;
                                case 'g':
                                    if (!ParseGLSLOutputVersion(&argv[0][sizeof("-b=g") - 1], &output))
                                    {
                                        failCode = EFailUsage;
                                    }
                                    compileOptions |= SH_INITIALIZE_UNINITIALIZED_LOCALS;
                                    break;
                                case 'v':
                                    output = SH_SPIRV_VULKAN_OUTPUT;
                                    compileOptions |= SH_INITIALIZE_UNINITIALIZED_LOCALS;
                                    break;
                                case 'h':
                                    if (argv[0][4] == '1' && argv[0][5] == '1')
                                    {
                                        output = SH_HLSL_4_1_OUTPUT;
                                    }
                                    else
                                    {
                                        output = SH_HLSL_3_0_OUTPUT;
                                    }
                                    break;
                                default:
                                    failCode = EFailUsage;
                            }
                        }
                        else
                        {
                            failCode = EFailUsage;
                        }
                        break;
                    case 'x':
                        if (argv[0][2] == '=')
                        {
                            // clang-format off
                        switch (argv[0][3])
                        {
                          case 'i': resources.OES_EGL_image_external = 1; break;
                          case 'd': resources.OES_standard_derivatives = 1; break;
                          case 'r': resources.ARB_texture_rectangle = 1; break;
                          case 'b':
                              if (ParseIntValue(&argv[0][sizeof("-x=b") - 1], 1,
                                                &resources.MaxDualSourceDrawBuffers))
                              {
                                  resources.EXT_blend_func_extended = 1;
                              }
                              else
                              {
                                  failCode = EFailUsage;
                              }
                              break;
                          case 'w':
                              if (ParseIntValue(&argv[0][sizeof("-x=w") - 1], 1,
                                                &resources.MaxDrawBuffers))
                              {
                                  resources.EXT_draw_buffers = 1;
                              }
                              else
                              {
                                  failCode = EFailUsage;
                              }
                              break;
                          case 'g': resources.EXT_frag_depth = 1; break;
                          case 'l': resources.EXT_shader_texture_lod = 1; break;
                          case 'f': resources.EXT_shader_framebuffer_fetch = 1; break;
                          case 'n': resources.NV_shader_framebuffer_fetch = 1; break;
                          case 'a': resources.ARM_shader_framebuffer_fetch = 1; break;
                          case 'm':
                              resources.OVR_multiview2 = 1;
                              resources.OVR_multiview = 1;
                              compileOptions |= SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW;
                              compileOptions |= SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER;
                              break;
                          case 'y': resources.EXT_YUV_target = 1; break;
                          case 's': resources.OES_sample_variables = 1; break;
                          default: failCode = EFailUsage;
                        }
                            // clang-format on
                        }
                        else
                        {
                            failCode = EFailUsage;
                        }
                        break;
                    default:
                        failCode = EFailUsage;
                }
            }
            else
            {
                if (spec != SH_GLES2_SPEC && spec != SH_WEBGL_SPEC)
                {
                    resources.MaxDrawBuffers             = 8;
                    resources.MaxVertexTextureImageUnits = 16;
                    resources.MaxTextureImageUnits       = 16;
                }
                ShHandle compiler = 0;
                switch (FindShaderType(argv[0]))
                {
                    case GL_VERTEX_SHADER:
                        if (vertexCompiler == 0)
                        {
                            vertexCompiler =
                                sh::ConstructCompiler(GL_VERTEX_SHADER, spec, output, &resources);
                        }
                        compiler = vertexCompiler;
                        break;
                    case GL_FRAGMENT_SHADER:
                        if (fragmentCompiler == 0)
                        {
                            fragmentCompiler =
                                sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
                        }
                        compiler = fragmentCompiler;
                        break;
                    case GL_COMPUTE_SHADER:
                        if (computeCompiler == 0)
                        {
                            computeCompiler =
                                sh::ConstructCompiler(GL_COMPUTE_SHADER, spec, output, &resources);
                        }
                        compiler = computeCompiler;
                        break;
                    case GL_GEOMETRY_SHADER_EXT:
                        if (geometryCompiler == 0)
                        {
                            geometryCompiler =
                                sh::ConstructCompiler(GL_GEOMETRY_SHADER_EXT, spec, output, &resources);
                        }
                        compiler = geometryCompiler;
                        break;
                    default:
                        break;
                }
                if (compiler)
                {
                    switch (output)
                    {
                        case SH_HLSL_3_0_OUTPUT:
                        case SH_HLSL_4_1_OUTPUT:
                        case SH_HLSL_4_0_FL9_3_OUTPUT:
                            compileOptions &= ~SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER;
                            break;
                        default:
                            break;
                    }
    
                    bool compiled = CompileFile(argv[0], compiler, compileOptions);
    
                    LogMsg("BEGIN", "COMPILER", numCompiles, "INFO LOG");
                    std::string log = sh::GetInfoLog(compiler);
                    puts(log.c_str());
                    LogMsg("END", "COMPILER", numCompiles, "INFO LOG");
                    printf("\n\n");
    
                    if (compiled && (compileOptions & SH_OBJECT_CODE))
                    {
                        LogMsg("BEGIN", "COMPILER", numCompiles, "OBJ CODE");
                        if (output != SH_SPIRV_VULKAN_OUTPUT)
                        {
                            const std::string &code = sh::GetObjectCode(compiler);
                            puts(code.c_str());
                        }
                        else
                        {
                            const sh::BinaryBlob &blob = sh::GetObjectBinaryBlob(compiler);
                            PrintSpirv(blob);
                        }
                        LogMsg("END", "COMPILER", numCompiles, "OBJ CODE");
                        printf("\n\n");
                    }
                    if (compiled && (compileOptions & SH_VARIABLES))
                    {
                        LogMsg("BEGIN", "COMPILER", numCompiles, "VARIABLES");
                        PrintActiveVariables(compiler);
                        LogMsg("END", "COMPILER", numCompiles, "VARIABLES");
                        printf("\n\n");
                    }
                    if (!compiled)
                        failCode = EFailCompile;
                    ++numCompiles;
                }
                else
                {
                    failCode = EFailCompilerCreate;
                }
            }
        }
    
        if ((vertexCompiler == 0) && (fragmentCompiler == 0) && (computeCompiler == 0) &&
            (geometryCompiler == 0))
            failCode = EFailUsage;
        if (failCode == EFailUsage)
            usage();
    
        if (vertexCompiler)
            sh::Destruct(vertexCompiler);
        if (fragmentCompiler)
            sh::Destruct(fragmentCompiler);
        if (computeCompiler)
            sh::Destruct(computeCompiler);
        if (geometryCompiler)
            sh::Destruct(geometryCompiler);
    
        sh::Finalize();
    #if defined(ANGLE_ENABLE_VULKAN)
        sh::FinalizeGlslang();
    #endif
    
        return failCode;
    }
    
    //
    //   print usage to stdout
    //
    void usage()
    {
        // clang-format off
        printf(
            "Usage: translate [-i -o -u -l -p -b=e -b=g -b=h9 -x=i -x=d] file1 file2 ...\n"
            "Where: filename : filename ending in .frag or .vert\n"
            "       -i       : print intermediate tree\n"
            "       -o       : print translated code\n"
            "       -u       : print active attribs, uniforms, varyings and program outputs\n"
            "       -p       : use precision emulation\n"
            "       -s=e2    : use GLES2 spec (this is by default)\n"
            "       -s=e3    : use GLES3 spec\n"
            "       -s=e31   : use GLES31 spec (in development)\n"
            "       -s=w     : use WebGL 1.0 spec\n"
            "       -s=wn    : use WebGL 1.0 spec with no highp support in fragment shaders\n"
            "       -s=w2    : use WebGL 2.0 spec\n"
            "       -s=d     : use Desktop Core spec (in development)\n"
            "       -s=dc    : use Desktop Compatibility spec (in development)\n"
            "       -b=e     : output GLSL ES code (this is by default)\n"
            "       -b=g     : output GLSL code (compatibility profile)\n"
            "       -b=g[NUM]: output GLSL code (NUM can be 130, 140, 150, 330, 400, 410, 420, 430, "
            "440, 450)\n"
            "       -b=v     : output Vulkan SPIR-V code\n"
            "       -b=h9    : output HLSL9 code\n"
            "       -b=h11   : output HLSL11 code\n"
            "       -x=i     : enable GL_OES_EGL_image_external\n"
            "       -x=d     : enable GL_OES_EGL_standard_derivatives\n"
            "       -x=r     : enable ARB_texture_rectangle\n"
            "       -x=b[NUM]: enable EXT_blend_func_extended (NUM default 1)\n"
            "       -x=w[NUM]: enable EXT_draw_buffers (NUM default 1)\n"
            "       -x=g     : enable EXT_frag_depth\n"
            "       -x=l     : enable EXT_shader_texture_lod\n"
            "       -x=f     : enable EXT_shader_framebuffer_fetch\n"
            "       -x=n     : enable NV_shader_framebuffer_fetch\n"
            "       -x=a     : enable ARM_shader_framebuffer_fetch\n"
            "       -x=m     : enable OVR_multiview\n"
            "       -x=y     : enable YUV_target\n"
            "       -x=s     : enable OES_sample_variables\n");
        // clang-format on
    }
    
    //
    //   Deduce the shader type from the filename.  Files must end in one of the
    //   following extensions:
    //
    //   .frag*    = fragment shader
    //   .vert*    = vertex shader
    //
    sh::GLenum FindShaderType(const char *fileName)
    {
        assert(fileName);
    
        const char *ext = strrchr(fileName, '.');
    
        if (ext && strcmp(ext, ".sl") == 0)
            for (; ext > fileName && ext[0] != '.'; ext--)
                ;
    
        ext = strrchr(fileName, '.');
        if (ext)
        {
            if (strncmp(ext, ".frag", 5) == 0)
                return GL_FRAGMENT_SHADER;
            if (strncmp(ext, ".vert", 5) == 0)
                return GL_VERTEX_SHADER;
            if (strncmp(ext, ".comp", 5) == 0)
                return GL_COMPUTE_SHADER;
            if (strncmp(ext, ".geom", 5) == 0)
                return GL_GEOMETRY_SHADER_EXT;
        }
    
        return GL_FRAGMENT_SHADER;
    }
    
    //
    //   Read a file's data into a string, and compile it using sh::Compile
    //
    bool CompileFile(char *fileName, ShHandle compiler, ShCompileOptions compileOptions)
    {
        ShaderSource source;
        if (!ReadShaderSource(fileName, source))
            return false;
    
        int ret = sh::Compile(compiler, &source[0], source.size(), compileOptions);
    
        FreeShaderSource(source);
        return ret ? true : false;
    }
    
    void LogMsg(const char *msg, const char *name, const int num, const char *logName)
    {
        printf("#### %s %s %d %s ####\n", msg, name, num, logName);
    }
    
    void PrintVariable(const std::string &prefix, size_t index, const sh::ShaderVariable &var)
    {
        std::string typeName;
        switch (var.type)
        {
            case GL_FLOAT:
                typeName = "GL_FLOAT";
                break;
            case GL_FLOAT_VEC2:
                typeName = "GL_FLOAT_VEC2";
                break;
            case GL_FLOAT_VEC3:
                typeName = "GL_FLOAT_VEC3";
                break;
            case GL_FLOAT_VEC4:
                typeName = "GL_FLOAT_VEC4";
                break;
            case GL_INT:
                typeName = "GL_INT";
                break;
            case GL_INT_VEC2:
                typeName = "GL_INT_VEC2";
                break;
            case GL_INT_VEC3:
                typeName = "GL_INT_VEC3";
                break;
            case GL_INT_VEC4:
                typeName = "GL_INT_VEC4";
                break;
            case GL_UNSIGNED_INT:
                typeName = "GL_UNSIGNED_INT";
                break;
            case GL_UNSIGNED_INT_VEC2:
                typeName = "GL_UNSIGNED_INT_VEC2";
                break;
            case GL_UNSIGNED_INT_VEC3:
                typeName = "GL_UNSIGNED_INT_VEC3";
                break;
            case GL_UNSIGNED_INT_VEC4:
                typeName = "GL_UNSIGNED_INT_VEC4";
                break;
            case GL_BOOL:
                typeName = "GL_BOOL";
                break;
            case GL_BOOL_VEC2:
                typeName = "GL_BOOL_VEC2";
                break;
            case GL_BOOL_VEC3:
                typeName = "GL_BOOL_VEC3";
                break;
            case GL_BOOL_VEC4:
                typeName = "GL_BOOL_VEC4";
                break;
            case GL_FLOAT_MAT2:
                typeName = "GL_FLOAT_MAT2";
                break;
            case GL_FLOAT_MAT3:
                typeName = "GL_FLOAT_MAT3";
                break;
            case GL_FLOAT_MAT4:
                typeName = "GL_FLOAT_MAT4";
                break;
            case GL_FLOAT_MAT2x3:
                typeName = "GL_FLOAT_MAT2x3";
                break;
            case GL_FLOAT_MAT3x2:
                typeName = "GL_FLOAT_MAT3x2";
                break;
            case GL_FLOAT_MAT4x2:
                typeName = "GL_FLOAT_MAT4x2";
                break;
            case GL_FLOAT_MAT2x4:
                typeName = "GL_FLOAT_MAT2x4";
                break;
            case GL_FLOAT_MAT3x4:
                typeName = "GL_FLOAT_MAT3x4";
                break;
            case GL_FLOAT_MAT4x3:
                typeName = "GL_FLOAT_MAT4x3";
                break;
    
            case GL_SAMPLER_2D:
                typeName = "GL_SAMPLER_2D";
                break;
            case GL_SAMPLER_3D:
                typeName = "GL_SAMPLER_3D";
                break;
            case GL_SAMPLER_CUBE:
                typeName = "GL_SAMPLER_CUBE";
                break;
            case GL_SAMPLER_CUBE_SHADOW:
                typeName = "GL_SAMPLER_CUBE_SHADOW";
                break;
            case GL_SAMPLER_2D_SHADOW:
                typeName = "GL_SAMPLER_2D_ARRAY_SHADOW";
                break;
            case GL_SAMPLER_2D_ARRAY:
                typeName = "GL_SAMPLER_2D_ARRAY";
                break;
            case GL_SAMPLER_2D_ARRAY_SHADOW:
                typeName = "GL_SAMPLER_2D_ARRAY_SHADOW";
                break;
            case GL_SAMPLER_2D_MULTISAMPLE:
                typeName = "GL_SAMPLER_2D_MULTISAMPLE";
                break;
            case GL_IMAGE_2D:
                typeName = "GL_IMAGE_2D";
                break;
            case GL_IMAGE_3D:
                typeName = "GL_IMAGE_3D";
                break;
            case GL_IMAGE_CUBE:
                typeName = "GL_IMAGE_CUBE";
                break;
            case GL_IMAGE_2D_ARRAY:
                typeName = "GL_IMAGE_2D_ARRAY";
                break;
    
            case GL_INT_SAMPLER_2D:
                typeName = "GL_INT_SAMPLER_2D";
                break;
            case GL_INT_SAMPLER_3D:
                typeName = "GL_INT_SAMPLER_3D";
                break;
            case GL_INT_SAMPLER_CUBE:
                typeName = "GL_INT_SAMPLER_CUBE";
                break;
            case GL_INT_SAMPLER_2D_ARRAY:
                typeName = "GL_INT_SAMPLER_2D_ARRAY";
                break;
            case GL_INT_SAMPLER_2D_MULTISAMPLE:
                typeName = "GL_INT_SAMPLER_2D_MULTISAMPLE";
                break;
            case GL_INT_IMAGE_2D:
                typeName = "GL_INT_IMAGE_2D";
                break;
            case GL_INT_IMAGE_3D:
                typeName = "GL_INT_IMAGE_3D";
                break;
            case GL_INT_IMAGE_CUBE:
                typeName = "GL_INT_IMAGE_CUBE";
                break;
            case GL_INT_IMAGE_2D_ARRAY:
                typeName = "GL_INT_IMAGE_2D_ARRAY";
                break;
    
            case GL_UNSIGNED_INT_SAMPLER_2D:
                typeName = "GL_UNSIGNED_INT_SAMPLER_2D";
                break;
            case GL_UNSIGNED_INT_SAMPLER_3D:
                typeName = "GL_UNSIGNED_INT_SAMPLER_3D";
                break;
            case GL_UNSIGNED_INT_SAMPLER_CUBE:
                typeName = "GL_UNSIGNED_INT_SAMPLER_CUBE";
                break;
            case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
                typeName = "GL_UNSIGNED_INT_SAMPLER_2D_ARRAY";
                break;
            case GL_UNSIGNED_INT_ATOMIC_COUNTER:
                typeName = "GL_UNSIGNED_INT_ATOMIC_COUNTER";
                break;
            case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
                typeName = "GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE";
                break;
            case GL_UNSIGNED_INT_IMAGE_2D:
                typeName = "GL_UNSIGNED_INT_IMAGE_2D";
                break;
            case GL_UNSIGNED_INT_IMAGE_3D:
                typeName = "GL_UNSIGNED_INT_IMAGE_3D";
                break;
            case GL_UNSIGNED_INT_IMAGE_CUBE:
                typeName = "GL_UNSIGNED_INT_IMAGE_CUBE";
                break;
            case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
                typeName = "GL_UNSIGNED_INT_IMAGE_2D_ARRAY";
                break;
    
            case GL_SAMPLER_EXTERNAL_OES:
                typeName = "GL_SAMPLER_EXTERNAL_OES";
                break;
            case GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT:
                typeName = "GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT";
                break;
            default:
                typeName = "UNKNOWN";
                break;
        }
    
        printf("%s %u : name=%s, mappedName=%s, type=%s, arraySizes=", prefix.c_str(),
               static_cast<unsigned int>(index), var.name.c_str(), var.mappedName.c_str(),
               typeName.c_str());
        for (unsigned int arraySize : var.arraySizes)
        {
            printf("%u ", arraySize);
        }
        printf("\n");
        if (var.fields.size())
        {
            std::string structPrefix;
            for (size_t i = 0; i < prefix.size(); ++i)
                structPrefix += ' ';
            printf("%s  struct %s\n", structPrefix.c_str(), var.structOrBlockName.c_str());
            structPrefix += "    field";
            for (size_t i = 0; i < var.fields.size(); ++i)
                PrintVariable(structPrefix, i, var.fields[i]);
        }
    }
    
    static void PrintActiveVariables(ShHandle compiler)
    {
        const std::vector<sh::ShaderVariable> *uniforms       = sh::GetUniforms(compiler);
        const std::vector<sh::ShaderVariable> *inputVaryings  = sh::GetInputVaryings(compiler);
        const std::vector<sh::ShaderVariable> *outputVaryings = sh::GetOutputVaryings(compiler);
        const std::vector<sh::ShaderVariable> *attributes     = sh::GetAttributes(compiler);
        const std::vector<sh::ShaderVariable> *outputs        = sh::GetOutputVariables(compiler);
        for (size_t varCategory = 0; varCategory < 5; ++varCategory)
        {
            size_t numVars = 0;
            std::string varCategoryName;
            if (varCategory == 0)
            {
                numVars         = uniforms->size();
                varCategoryName = "uniform";
            }
            else if (varCategory == 1)
            {
                numVars         = inputVaryings->size();
                varCategoryName = "input varying";
            }
            else if (varCategory == 2)
            {
                numVars         = outputVaryings->size();
                varCategoryName = "output varying";
            }
            else if (varCategory == 3)
            {
                numVars         = attributes->size();
                varCategoryName = "attribute";
            }
            else
            {
                numVars         = outputs->size();
                varCategoryName = "output";
            }
    
            for (size_t i = 0; i < numVars; ++i)
            {
                const sh::ShaderVariable *var;
                if (varCategory == 0)
                    var = &((*uniforms)[i]);
                else if (varCategory == 1)
                    var = &((*inputVaryings)[i]);
                else if (varCategory == 2)
                    var = &((*outputVaryings)[i]);
                else if (varCategory == 3)
                    var = &((*attributes)[i]);
                else
                    var = &((*outputs)[i]);
    
                PrintVariable(varCategoryName, i, *var);
            }
            printf("\n");
        }
    }
    
    static bool ReadShaderSource(const char *fileName, ShaderSource &source)
    {
        FILE *in = fopen(fileName, "rb");
        if (!in)
        {
            printf("Error: unable to open input file: %s\n", fileName);
            return false;
        }
    
        // Obtain file size.
        fseek(in, 0, SEEK_END);
        size_t count = ftell(in);
        rewind(in);
    
        int len = (int)ceil((float)count / (float)NUM_SOURCE_STRINGS);
        source.reserve(NUM_SOURCE_STRINGS);
        // Notice the usage of do-while instead of a while loop here.
        // It is there to handle empty files in which case a single empty
        // string is added to vector.
        do
        {
            char *data   = new char[len + 1];
            size_t nread = fread(data, 1, len, in);
            data[nread]  = '\0';
            source.push_back(data);
    
            count -= nread;
        } while (count > 0);
    
        fclose(in);
        return true;
    }
    
    static void FreeShaderSource(ShaderSource &source)
    {
        for (ShaderSource::size_type i = 0; i < source.size(); ++i)
        {
            delete[] source[i];
        }
        source.clear();
    }
    
    static bool ParseGLSLOutputVersion(const std::string &num, ShShaderOutput *outResult)
    {
        if (num.length() == 0)
        {
            *outResult = SH_GLSL_COMPATIBILITY_OUTPUT;
            return true;
        }
        std::istringstream input(num);
        int value;
        if (!(input >> value && input.eof()))
        {
            return false;
        }
    
        switch (value)
        {
            case 130:
                *outResult = SH_GLSL_130_OUTPUT;
                return true;
            case 140:
                *outResult = SH_GLSL_140_OUTPUT;
                return true;
            case 150:
                *outResult = SH_GLSL_150_CORE_OUTPUT;
                return true;
            case 330:
                *outResult = SH_GLSL_330_CORE_OUTPUT;
                return true;
            case 400:
                *outResult = SH_GLSL_400_CORE_OUTPUT;
                return true;
            case 410:
                *outResult = SH_GLSL_410_CORE_OUTPUT;
                return true;
            case 420:
                *outResult = SH_GLSL_420_CORE_OUTPUT;
                return true;
            case 430:
                *outResult = SH_GLSL_430_CORE_OUTPUT;
                return true;
            case 440:
                *outResult = SH_GLSL_440_CORE_OUTPUT;
                return true;
            case 450:
                *outResult = SH_GLSL_450_CORE_OUTPUT;
                return true;
            default:
                break;
        }
        return false;
    }
    
    static bool ParseIntValue(const std::string &num, int emptyDefault, int *outValue)
    {
        if (num.length() == 0)
        {
            *outValue = emptyDefault;
            return true;
        }
    
        std::istringstream input(num);
        int value;
        if (!(input >> value && input.eof()))
        {
            return false;
        }
        *outValue = value;
        return true;
    }
    
    static void PrintSpirv(const sh::BinaryBlob &blob)
    {
    #if defined(ANGLE_ENABLE_VULKAN)
        spvtools::SpirvTools spirvTools(SPV_ENV_VULKAN_1_1);
    
        std::string readableSpirv;
        spirvTools.Disassemble(blob, &readableSpirv, 0);
    
        puts(readableSpirv.c_str());
    #endif
    }