Edit

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

Branch :

  • Show log

    Commit

  • Author : Olli Etuaho
    Date : 2016-12-15 13:30:26
    Hash : 09b04a2f
    Message : Add shader translator support for OVR_multiview The OVR_multiview and OVR_multiview2 extensions add gl_ViewID_OVR to shaders. gl_ViewID_OVR can be translated either as is in GLSL output or as a uniform by setting the SH_TRANSLATE_VIEWID_OVR_AS_UNIFORM compiler flag. If WebGL output is selected, the shaders will be validated according to proposed rules in the WEBGL_multiview spec. BUG=angleproject:1669 TEST=angle_unittests Change-Id: I19ea3a6c8b4edb78be03f1a50a96bfef018870d0 Reviewed-on: https://chromium-review.googlesource.com/422848 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>

  • src/compiler/translator/Compiler.cpp
  • //
    // Copyright (c) 2002-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.
    //
    
    #include "compiler/translator/Compiler.h"
    
    #include <sstream>
    
    #include "angle_gl.h"
    #include "common/utilities.h"
    #include "compiler/translator/AddAndTrueToLoopCondition.h"
    #include "compiler/translator/Cache.h"
    #include "compiler/translator/CallDAG.h"
    #include "compiler/translator/DeferGlobalInitializers.h"
    #include "compiler/translator/EmulateGLFragColorBroadcast.h"
    #include "compiler/translator/EmulatePrecision.h"
    #include "compiler/translator/Initialize.h"
    #include "compiler/translator/InitializeParseContext.h"
    #include "compiler/translator/InitializeVariables.h"
    #include "compiler/translator/ParseContext.h"
    #include "compiler/translator/PruneEmptyDeclarations.h"
    #include "compiler/translator/RegenerateStructNames.h"
    #include "compiler/translator/RemoveInvariantDeclaration.h"
    #include "compiler/translator/RemovePow.h"
    #include "compiler/translator/RewriteDoWhile.h"
    #include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h"
    #include "compiler/translator/UnfoldShortCircuitAST.h"
    #include "compiler/translator/UseInterfaceBlockFields.h"
    #include "compiler/translator/ValidateLimitations.h"
    #include "compiler/translator/ValidateMaxParameters.h"
    #include "compiler/translator/ValidateMultiviewWebGL.h"
    #include "compiler/translator/ValidateOutputs.h"
    #include "compiler/translator/VariablePacker.h"
    #include "third_party/compiler/ArrayBoundsClamper.h"
    
    namespace sh
    {
    
    namespace
    {
    
    #if defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT)
    void DumpFuzzerCase(char const *const *shaderStrings,
                        size_t numStrings,
                        uint32_t type,
                        uint32_t spec,
                        uint32_t output,
                        uint64_t options)
    {
        static int fileIndex = 0;
    
        std::ostringstream o;
        o << "corpus/" << fileIndex++ << ".sample";
        std::string s = o.str();
    
        // Must match the input format of the fuzzer
        FILE *f = fopen(s.c_str(), "w");
        fwrite(&type, sizeof(type), 1, f);
        fwrite(&spec, sizeof(spec), 1, f);
        fwrite(&output, sizeof(output), 1, f);
        fwrite(&options, sizeof(options), 1, f);
    
        char zero[128 - 20] = {0};
        fwrite(&zero, 128 - 20, 1, f);
    
        for (size_t i = 0; i < numStrings; i++)
        {
            fwrite(shaderStrings[i], sizeof(char), strlen(shaderStrings[i]), f);
        }
        fwrite(&zero, 1, 1, f);
    
        fclose(f);
    }
    #endif  // defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT)
    }  // anonymous namespace
    
    bool IsWebGLBasedSpec(ShShaderSpec spec)
    {
        return (spec == SH_WEBGL_SPEC || spec == SH_WEBGL2_SPEC || spec == SH_WEBGL3_SPEC);
    }
    
    bool IsGLSL130OrNewer(ShShaderOutput output)
    {
        return (output == SH_GLSL_130_OUTPUT || output == SH_GLSL_140_OUTPUT ||
                output == SH_GLSL_150_CORE_OUTPUT || output == SH_GLSL_330_CORE_OUTPUT ||
                output == SH_GLSL_400_CORE_OUTPUT || output == SH_GLSL_410_CORE_OUTPUT ||
                output == SH_GLSL_420_CORE_OUTPUT || output == SH_GLSL_430_CORE_OUTPUT ||
                output == SH_GLSL_440_CORE_OUTPUT || output == SH_GLSL_450_CORE_OUTPUT);
    }
    
    bool IsGLSL420OrNewer(ShShaderOutput output)
    {
        return (output == SH_GLSL_420_CORE_OUTPUT || output == SH_GLSL_430_CORE_OUTPUT ||
                output == SH_GLSL_440_CORE_OUTPUT || output == SH_GLSL_450_CORE_OUTPUT);
    }
    
    bool IsGLSL410OrOlder(ShShaderOutput output)
    {
        return (output == SH_GLSL_130_OUTPUT || output == SH_GLSL_140_OUTPUT ||
                output == SH_GLSL_150_CORE_OUTPUT || output == SH_GLSL_330_CORE_OUTPUT ||
                output == SH_GLSL_400_CORE_OUTPUT || output == SH_GLSL_410_CORE_OUTPUT);
    }
    
    bool RemoveInvariant(sh::GLenum shaderType,
                         int shaderVersion,
                         ShShaderOutput outputType,
                         ShCompileOptions compileOptions)
    {
        if ((compileOptions & SH_DONT_REMOVE_INVARIANT_FOR_FRAGMENT_INPUT) == 0 &&
            shaderType == GL_FRAGMENT_SHADER && IsGLSL420OrNewer(outputType))
            return true;
    
        if ((compileOptions & SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3) != 0 &&
            shaderVersion >= 300 && shaderType == GL_VERTEX_SHADER)
            return true;
    
        return false;
    }
    
    size_t GetGlobalMaxTokenSize(ShShaderSpec spec)
    {
        // WebGL defines a max token length of 256, while ES2 leaves max token
        // size undefined. ES3 defines a max size of 1024 characters.
        switch (spec)
        {
            case SH_WEBGL_SPEC:
                return 256;
            default:
                return 1024;
        }
    }
    
    namespace
    {
    
    class TScopedPoolAllocator
    {
      public:
        TScopedPoolAllocator(TPoolAllocator *allocator) : mAllocator(allocator)
        {
            mAllocator->push();
            SetGlobalPoolAllocator(mAllocator);
        }
        ~TScopedPoolAllocator()
        {
            SetGlobalPoolAllocator(NULL);
            mAllocator->pop();
        }
    
      private:
        TPoolAllocator *mAllocator;
    };
    
    class TScopedSymbolTableLevel
    {
      public:
        TScopedSymbolTableLevel(TSymbolTable *table) : mTable(table)
        {
            ASSERT(mTable->atBuiltInLevel());
            mTable->push();
        }
        ~TScopedSymbolTableLevel()
        {
            while (!mTable->atBuiltInLevel())
                mTable->pop();
        }
    
      private:
        TSymbolTable *mTable;
    };
    
    int MapSpecToShaderVersion(ShShaderSpec spec)
    {
        switch (spec)
        {
            case SH_GLES2_SPEC:
            case SH_WEBGL_SPEC:
                return 100;
            case SH_GLES3_SPEC:
            case SH_WEBGL2_SPEC:
                return 300;
            case SH_GLES3_1_SPEC:
            case SH_WEBGL3_SPEC:
                return 310;
            default:
                UNREACHABLE();
                return 0;
        }
    }
    
    }  // namespace
    
    TShHandleBase::TShHandleBase()
    {
        allocator.push();
        SetGlobalPoolAllocator(&allocator);
    }
    
    TShHandleBase::~TShHandleBase()
    {
        SetGlobalPoolAllocator(NULL);
        allocator.popAll();
    }
    
    TCompiler::TCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
        : variablesCollected(false),
          shaderType(type),
          shaderSpec(spec),
          outputType(output),
          maxUniformVectors(0),
          maxExpressionComplexity(0),
          maxCallStackDepth(0),
          maxFunctionParameters(0),
          fragmentPrecisionHigh(false),
          clampingStrategy(SH_CLAMP_WITH_CLAMP_INTRINSIC),
          builtInFunctionEmulator(),
          mDiagnostics(infoSink.info),
          mSourcePath(NULL),
          mComputeShaderLocalSizeDeclared(false),
          mTemporaryIndex(0)
    {
        mComputeShaderLocalSize.fill(1);
    }
    
    TCompiler::~TCompiler()
    {
    }
    
    bool TCompiler::shouldRunLoopAndIndexingValidation(ShCompileOptions compileOptions) const
    {
        // If compiling an ESSL 1.00 shader for WebGL, or if its been requested through the API,
        // validate loop and indexing as well (to verify that the shader only uses minimal functionality
        // of ESSL 1.00 as in Appendix A of the spec).
        return (IsWebGLBasedSpec(shaderSpec) && shaderVersion == 100) ||
               (compileOptions & SH_VALIDATE_LOOP_INDEXING);
    }
    
    bool TCompiler::Init(const ShBuiltInResources &resources)
    {
        shaderVersion     = 100;
        maxUniformVectors = (shaderType == GL_VERTEX_SHADER) ? resources.MaxVertexUniformVectors
                                                             : resources.MaxFragmentUniformVectors;
        maxExpressionComplexity = resources.MaxExpressionComplexity;
        maxCallStackDepth       = resources.MaxCallStackDepth;
        maxFunctionParameters   = resources.MaxFunctionParameters;
    
        SetGlobalPoolAllocator(&allocator);
    
        // Generate built-in symbol table.
        if (!InitBuiltInSymbolTable(resources))
            return false;
        InitExtensionBehavior(resources, extensionBehavior);
        fragmentPrecisionHigh = resources.FragmentPrecisionHigh == 1;
    
        arrayBoundsClamper.SetClampingStrategy(resources.ArrayIndexClampingStrategy);
        clampingStrategy = resources.ArrayIndexClampingStrategy;
    
        hashFunction = resources.HashFunction;
    
        return true;
    }
    
    TIntermBlock *TCompiler::compileTreeForTesting(const char *const shaderStrings[],
                                                   size_t numStrings,
                                                   ShCompileOptions compileOptions)
    {
        return compileTreeImpl(shaderStrings, numStrings, compileOptions);
    }
    
    TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[],
                                             size_t numStrings,
                                             const ShCompileOptions compileOptions)
    {
        clearResults();
    
        ASSERT(numStrings > 0);
        ASSERT(GetGlobalPoolAllocator());
    
        // Reset the extension behavior for each compilation unit.
        ResetExtensionBehavior(extensionBehavior);
    
        // First string is path of source file if flag is set. The actual source follows.
        size_t firstSource = 0;
        if (compileOptions & SH_SOURCE_PATH)
        {
            mSourcePath = shaderStrings[0];
            ++firstSource;
        }
    
        TParseContext parseContext(symbolTable, extensionBehavior, shaderType, shaderSpec,
                                   compileOptions, true, &mDiagnostics, getResources());
    
        parseContext.setFragmentPrecisionHighOnESSL1(fragmentPrecisionHigh);
        SetGlobalParseContext(&parseContext);
    
        // We preserve symbols at the built-in level from compile-to-compile.
        // Start pushing the user-defined symbols at global level.
        TScopedSymbolTableLevel scopedSymbolLevel(&symbolTable);
    
        // Parse shader.
        bool success = (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], nullptr,
                                       &parseContext) == 0) &&
                       (parseContext.getTreeRoot() != nullptr);
    
        shaderVersion = parseContext.getShaderVersion();
        if (success && MapSpecToShaderVersion(shaderSpec) < shaderVersion)
        {
            mDiagnostics.globalError("unsupported shader version");
            success = false;
        }
    
        TIntermBlock *root = nullptr;
    
        if (success)
        {
            mPragma = parseContext.pragma();
            symbolTable.setGlobalInvariant(mPragma.stdgl.invariantAll);
    
            mComputeShaderLocalSizeDeclared = parseContext.isComputeShaderLocalSizeDeclared();
            mComputeShaderLocalSize         = parseContext.getComputeShaderLocalSize();
    
            mNumViews = parseContext.getNumViews();
    
            root = parseContext.getTreeRoot();
    
            // Highp might have been auto-enabled based on shader version
            fragmentPrecisionHigh = parseContext.getFragmentPrecisionHigh();
    
            if (success && (IsWebGLBasedSpec(shaderSpec) &&
                            IsExtensionEnabled(extensionBehavior, "GL_OVR_multiview") &&
                            IsExtensionEnabled(extensionBehavior, "GL_OVR_multiview2")))
            {
                // Can't enable both extensions at the same time.
                mDiagnostics.globalError("Can't enable both OVR_multiview and OVR_multiview2");
                success = false;
            }
    
            // Disallow expressions deemed too complex.
            if (success && (compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY))
                success = limitExpressionComplexity(root);
    
            // Create the function DAG and check there is no recursion
            if (success)
                success = initCallDag(root);
    
            if (success && (compileOptions & SH_LIMIT_CALL_STACK_DEPTH))
                success = checkCallDepth();
    
            // Checks which functions are used and if "main" exists
            if (success)
            {
                functionMetadata.clear();
                functionMetadata.resize(mCallDag.size());
                success = tagUsedFunctions();
            }
    
            if (success && !(compileOptions & SH_DONT_PRUNE_UNUSED_FUNCTIONS))
                success = pruneUnusedFunctions(root);
    
            // Prune empty declarations to work around driver bugs and to keep declaration output
            // simple.
            if (success)
                PruneEmptyDeclarations(root);
    
            if (success && shaderVersion == 300 && shaderType == GL_FRAGMENT_SHADER)
                success = validateOutputs(root);
    
            if (success && shouldRunLoopAndIndexingValidation(compileOptions))
                success = validateLimitations(root);
    
            bool multiview2 = IsExtensionEnabled(extensionBehavior, "GL_OVR_multiview2");
            if (success && compileResources.OVR_multiview && IsWebGLBasedSpec(shaderSpec) &&
                (IsExtensionEnabled(extensionBehavior, "GL_OVR_multiview") || multiview2))
                success = ValidateMultiviewWebGL(root, shaderType, multiview2, &mDiagnostics);
    
            // Fail compilation if precision emulation not supported.
            if (success && getResources().WEBGL_debug_shader_precision &&
                getPragma().debugShaderPrecision)
            {
                if (!EmulatePrecision::SupportedInLanguage(outputType))
                {
                    mDiagnostics.globalError("Precision emulation not supported for this output type.");
                    success = false;
                }
            }
    
            // Built-in function emulation needs to happen after validateLimitations pass.
            if (success)
            {
                // TODO(jmadill): Remove global pool allocator.
                GetGlobalPoolAllocator()->lock();
                initBuiltInFunctionEmulator(&builtInFunctionEmulator, compileOptions);
                GetGlobalPoolAllocator()->unlock();
                builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(root);
            }
    
            // Clamping uniform array bounds needs to happen after validateLimitations pass.
            if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS))
                arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root);
    
            // gl_Position is always written in compatibility output mode
            if (success && shaderType == GL_VERTEX_SHADER &&
                ((compileOptions & SH_INIT_GL_POSITION) ||
                 (outputType == SH_GLSL_COMPATIBILITY_OUTPUT)))
                initializeGLPosition(root);
    
            if (success && RemoveInvariant(shaderType, shaderVersion, outputType, compileOptions))
                sh::RemoveInvariantDeclaration(root);
    
            // This pass might emit short circuits so keep it before the short circuit unfolding
            if (success && (compileOptions & SH_REWRITE_DO_WHILE_LOOPS))
                RewriteDoWhile(root, getTemporaryIndex());
    
            if (success && (compileOptions & SH_ADD_AND_TRUE_TO_LOOP_CONDITION))
                sh::AddAndTrueToLoopCondition(root);
    
            if (success && (compileOptions & SH_UNFOLD_SHORT_CIRCUIT))
            {
                UnfoldShortCircuitAST unfoldShortCircuit;
                root->traverse(&unfoldShortCircuit);
                unfoldShortCircuit.updateTree();
            }
    
            if (success && (compileOptions & SH_REMOVE_POW_WITH_CONSTANT_EXPONENT))
            {
                RemovePow(root);
            }
    
            if (success && shouldCollectVariables(compileOptions))
            {
                collectVariables(root);
                if (compileOptions & SH_USE_UNUSED_STANDARD_SHARED_BLOCKS)
                {
                    useAllMembersInUnusedStandardAndSharedBlocks(root);
                }
                if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS)
                {
                    success = enforcePackingRestrictions();
                    if (!success)
                    {
                        mDiagnostics.globalError("too many uniforms");
                    }
                }
                if (success && (compileOptions & SH_INIT_OUTPUT_VARIABLES))
                {
                    initializeOutputVariables(root);
                }
            }
    
            if (success && (compileOptions & SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS))
            {
                ScalarizeVecAndMatConstructorArgs(root, shaderType, fragmentPrecisionHigh,
                                                  &mTemporaryIndex);
            }
    
            if (success && (compileOptions & SH_REGENERATE_STRUCT_NAMES))
            {
                RegenerateStructNames gen(symbolTable, shaderVersion);
                root->traverse(&gen);
            }
    
            if (success && shaderType == GL_FRAGMENT_SHADER && shaderVersion == 100 &&
                compileResources.EXT_draw_buffers && compileResources.MaxDrawBuffers > 1 &&
                IsExtensionEnabled(extensionBehavior, "GL_EXT_draw_buffers"))
            {
                EmulateGLFragColorBroadcast(root, compileResources.MaxDrawBuffers, &outputVariables);
            }
    
            if (success)
            {
                DeferGlobalInitializers(root);
            }
        }
    
        SetGlobalParseContext(NULL);
        if (success)
            return root;
    
        return NULL;
    }
    
    bool TCompiler::compile(const char *const shaderStrings[],
                            size_t numStrings,
                            ShCompileOptions compileOptionsIn)
    {
    #if defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT)
        DumpFuzzerCase(shaderStrings, numStrings, shaderType, shaderSpec, outputType, compileOptionsIn);
    #endif  // defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT)
    
        if (numStrings == 0)
            return true;
    
        ShCompileOptions compileOptions = compileOptionsIn;
    
        // Apply key workarounds.
        if (shouldFlattenPragmaStdglInvariantAll())
        {
            // This should be harmless to do in all cases, but for the moment, do it only conditionally.
            compileOptions |= SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL;
        }
    
        TScopedPoolAllocator scopedAlloc(&allocator);
        TIntermBlock *root = compileTreeImpl(shaderStrings, numStrings, compileOptions);
    
        if (root)
        {
            if (compileOptions & SH_INTERMEDIATE_TREE)
                TIntermediate::outputTree(root, infoSink.info);
    
            if (compileOptions & SH_OBJECT_CODE)
                translate(root, compileOptions);
    
            // The IntermNode tree doesn't need to be deleted here, since the
            // memory will be freed in a big chunk by the PoolAllocator.
            return true;
        }
        return false;
    }
    
    bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources)
    {
        if (resources.MaxDrawBuffers < 1)
        {
            return false;
        }
        if (resources.EXT_blend_func_extended && resources.MaxDualSourceDrawBuffers < 1)
        {
            return false;
        }
    
        compileResources = resources;
        setResourceString();
    
        assert(symbolTable.isEmpty());
        symbolTable.push();  // COMMON_BUILTINS
        symbolTable.push();  // ESSL1_BUILTINS
        symbolTable.push();  // ESSL3_BUILTINS
        symbolTable.push();  // ESSL3_1_BUILTINS
    
        TPublicType integer;
        integer.initializeBasicType(EbtInt);
    
        TPublicType floatingPoint;
        floatingPoint.initializeBasicType(EbtFloat);
    
        switch (shaderType)
        {
            case GL_FRAGMENT_SHADER:
                symbolTable.setDefaultPrecision(integer, EbpMedium);
                break;
            case GL_VERTEX_SHADER:
                symbolTable.setDefaultPrecision(integer, EbpHigh);
                symbolTable.setDefaultPrecision(floatingPoint, EbpHigh);
                break;
            case GL_COMPUTE_SHADER:
                symbolTable.setDefaultPrecision(integer, EbpHigh);
                symbolTable.setDefaultPrecision(floatingPoint, EbpHigh);
                break;
            default:
                assert(false && "Language not supported");
        }
        // Set defaults for sampler types that have default precision, even those that are
        // only available if an extension exists.
        // New sampler types in ESSL3 don't have default precision. ESSL1 types do.
        initSamplerDefaultPrecision(EbtSampler2D);
        initSamplerDefaultPrecision(EbtSamplerCube);
        // SamplerExternalOES is specified in the extension to have default precision.
        initSamplerDefaultPrecision(EbtSamplerExternalOES);
        // It isn't specified whether Sampler2DRect has default precision.
        initSamplerDefaultPrecision(EbtSampler2DRect);
    
        InsertBuiltInFunctions(shaderType, shaderSpec, resources, symbolTable);
    
        IdentifyBuiltIns(shaderType, shaderSpec, resources, symbolTable);
    
        return true;
    }
    
    void TCompiler::initSamplerDefaultPrecision(TBasicType samplerType)
    {
        ASSERT(samplerType > EbtGuardSamplerBegin && samplerType < EbtGuardSamplerEnd);
        TPublicType sampler;
        sampler.initializeBasicType(samplerType);
        symbolTable.setDefaultPrecision(sampler, EbpLow);
    }
    
    void TCompiler::setResourceString()
    {
        std::ostringstream strstream;
    
        // clang-format off
        strstream << ":MaxVertexAttribs:" << compileResources.MaxVertexAttribs
            << ":MaxVertexUniformVectors:" << compileResources.MaxVertexUniformVectors
            << ":MaxVaryingVectors:" << compileResources.MaxVaryingVectors
            << ":MaxVertexTextureImageUnits:" << compileResources.MaxVertexTextureImageUnits
            << ":MaxCombinedTextureImageUnits:" << compileResources.MaxCombinedTextureImageUnits
            << ":MaxTextureImageUnits:" << compileResources.MaxTextureImageUnits
            << ":MaxFragmentUniformVectors:" << compileResources.MaxFragmentUniformVectors
            << ":MaxDrawBuffers:" << compileResources.MaxDrawBuffers
            << ":OES_standard_derivatives:" << compileResources.OES_standard_derivatives
            << ":OES_EGL_image_external:" << compileResources.OES_EGL_image_external
            << ":OES_EGL_image_external_essl3:" << compileResources.OES_EGL_image_external_essl3
            << ":NV_EGL_stream_consumer_external:" << compileResources.NV_EGL_stream_consumer_external
            << ":ARB_texture_rectangle:" << compileResources.ARB_texture_rectangle
            << ":EXT_draw_buffers:" << compileResources.EXT_draw_buffers
            << ":FragmentPrecisionHigh:" << compileResources.FragmentPrecisionHigh
            << ":MaxExpressionComplexity:" << compileResources.MaxExpressionComplexity
            << ":MaxCallStackDepth:" << compileResources.MaxCallStackDepth
            << ":MaxFunctionParameters:" << compileResources.MaxFunctionParameters
            << ":EXT_blend_func_extended:" << compileResources.EXT_blend_func_extended
            << ":EXT_frag_depth:" << compileResources.EXT_frag_depth
            << ":EXT_shader_texture_lod:" << compileResources.EXT_shader_texture_lod
            << ":EXT_shader_framebuffer_fetch:" << compileResources.EXT_shader_framebuffer_fetch
            << ":NV_shader_framebuffer_fetch:" << compileResources.NV_shader_framebuffer_fetch
            << ":ARM_shader_framebuffer_fetch:" << compileResources.ARM_shader_framebuffer_fetch
            << ":MaxVertexOutputVectors:" << compileResources.MaxVertexOutputVectors
            << ":MaxFragmentInputVectors:" << compileResources.MaxFragmentInputVectors
            << ":MinProgramTexelOffset:" << compileResources.MinProgramTexelOffset
            << ":MaxProgramTexelOffset:" << compileResources.MaxProgramTexelOffset
            << ":MaxDualSourceDrawBuffers:" << compileResources.MaxDualSourceDrawBuffers
            << ":NV_draw_buffers:" << compileResources.NV_draw_buffers
            << ":WEBGL_debug_shader_precision:" << compileResources.WEBGL_debug_shader_precision
            << ":MaxImageUnits:" << compileResources.MaxImageUnits
            << ":MaxVertexImageUniforms:" << compileResources.MaxVertexImageUniforms
            << ":MaxFragmentImageUniforms:" << compileResources.MaxFragmentImageUniforms
            << ":MaxComputeImageUniforms:" << compileResources.MaxComputeImageUniforms
            << ":MaxCombinedImageUniforms:" << compileResources.MaxCombinedImageUniforms
            << ":MaxCombinedShaderOutputResources:" << compileResources.MaxCombinedShaderOutputResources
            << ":MaxComputeWorkGroupCountX:" << compileResources.MaxComputeWorkGroupCount[0]
            << ":MaxComputeWorkGroupCountY:" << compileResources.MaxComputeWorkGroupCount[1]
            << ":MaxComputeWorkGroupCountZ:" << compileResources.MaxComputeWorkGroupCount[2]
            << ":MaxComputeWorkGroupSizeX:" << compileResources.MaxComputeWorkGroupSize[0]
            << ":MaxComputeWorkGroupSizeY:" << compileResources.MaxComputeWorkGroupSize[1]
            << ":MaxComputeWorkGroupSizeZ:" << compileResources.MaxComputeWorkGroupSize[2]
            << ":MaxComputeUniformComponents:" << compileResources.MaxComputeUniformComponents
            << ":MaxComputeTextureImageUnits:" << compileResources.MaxComputeTextureImageUnits
            << ":MaxComputeAtomicCounters:" << compileResources.MaxComputeAtomicCounters
            << ":MaxComputeAtomicCounterBuffers:" << compileResources.MaxComputeAtomicCounterBuffers
            << ":MaxVertexAtomicCounters:" << compileResources.MaxVertexAtomicCounters
            << ":MaxFragmentAtomicCounters:" << compileResources.MaxFragmentAtomicCounters
            << ":MaxCombinedAtomicCounters:" << compileResources.MaxCombinedAtomicCounters
            << ":MaxAtomicCounterBindings:" << compileResources.MaxAtomicCounterBindings
            << ":MaxVertexAtomicCounterBuffers:" << compileResources.MaxVertexAtomicCounterBuffers
            << ":MaxFragmentAtomicCounterBuffers:" << compileResources.MaxFragmentAtomicCounterBuffers
            << ":MaxCombinedAtomicCounterBuffers:" << compileResources.MaxCombinedAtomicCounterBuffers
            << ":MaxAtomicCounterBufferSize:" << compileResources.MaxAtomicCounterBufferSize;
        // clang-format on
    
        builtInResourcesString = strstream.str();
    }
    
    void TCompiler::clearResults()
    {
        arrayBoundsClamper.Cleanup();
        infoSink.info.erase();
        infoSink.obj.erase();
        infoSink.debug.erase();
        mDiagnostics.resetErrorCount();
    
        attributes.clear();
        outputVariables.clear();
        uniforms.clear();
        expandedUniforms.clear();
        varyings.clear();
        interfaceBlocks.clear();
        variablesCollected = false;
    
        mNumViews = -1;
    
        builtInFunctionEmulator.Cleanup();
    
        nameMap.clear();
    
        mSourcePath     = NULL;
        mTemporaryIndex = 0;
    }
    
    bool TCompiler::initCallDag(TIntermNode *root)
    {
        mCallDag.clear();
    
        switch (mCallDag.init(root, &mDiagnostics))
        {
            case CallDAG::INITDAG_SUCCESS:
                return true;
            case CallDAG::INITDAG_RECURSION:
            case CallDAG::INITDAG_UNDEFINED:
                // Error message has already been written out.
                ASSERT(mDiagnostics.numErrors() > 0);
                return false;
        }
    
        UNREACHABLE();
        return true;
    }
    
    bool TCompiler::checkCallDepth()
    {
        std::vector<int> depths(mCallDag.size());
    
        for (size_t i = 0; i < mCallDag.size(); i++)
        {
            int depth    = 0;
            auto &record = mCallDag.getRecordFromIndex(i);
    
            for (auto &calleeIndex : record.callees)
            {
                depth = std::max(depth, depths[calleeIndex] + 1);
            }
    
            depths[i] = depth;
    
            if (depth >= maxCallStackDepth)
            {
                // Trace back the function chain to have a meaningful info log.
                std::stringstream errorStream;
                errorStream << "Call stack too deep (larger than " << maxCallStackDepth
                            << ") with the following call chain: " << record.name;
    
                int currentFunction = static_cast<int>(i);
                int currentDepth    = depth;
    
                while (currentFunction != -1)
                {
                    errorStream << " -> " << mCallDag.getRecordFromIndex(currentFunction).name;
    
                    int nextFunction = -1;
                    for (auto &calleeIndex : mCallDag.getRecordFromIndex(currentFunction).callees)
                    {
                        if (depths[calleeIndex] == currentDepth - 1)
                        {
                            currentDepth--;
                            nextFunction = calleeIndex;
                        }
                    }
    
                    currentFunction = nextFunction;
                }
    
                std::string errorStr = errorStream.str();
                mDiagnostics.globalError(errorStr.c_str());
    
                return false;
            }
        }
    
        return true;
    }
    
    bool TCompiler::tagUsedFunctions()
    {
        // Search from main, starting from the end of the DAG as it usually is the root.
        for (size_t i = mCallDag.size(); i-- > 0;)
        {
            if (mCallDag.getRecordFromIndex(i).name == "main(")
            {
                internalTagUsedFunction(i);
                return true;
            }
        }
    
        mDiagnostics.globalError("Missing main()");
        return false;
    }
    
    void TCompiler::internalTagUsedFunction(size_t index)
    {
        if (functionMetadata[index].used)
        {
            return;
        }
    
        functionMetadata[index].used = true;
    
        for (int calleeIndex : mCallDag.getRecordFromIndex(index).callees)
        {
            internalTagUsedFunction(calleeIndex);
        }
    }
    
    // A predicate for the stl that returns if a top-level node is unused
    class TCompiler::UnusedPredicate
    {
      public:
        UnusedPredicate(const CallDAG *callDag, const std::vector<FunctionMetadata> *metadatas)
            : mCallDag(callDag), mMetadatas(metadatas)
        {
        }
    
        bool operator()(TIntermNode *node)
        {
            const TIntermAggregate *asAggregate         = node->getAsAggregate();
            const TIntermFunctionDefinition *asFunction = node->getAsFunctionDefinition();
    
            const TFunctionSymbolInfo *functionInfo = nullptr;
    
            if (asFunction)
            {
                functionInfo = asFunction->getFunctionSymbolInfo();
            }
            else if (asAggregate)
            {
                if (asAggregate->getOp() == EOpPrototype)
                {
                    functionInfo = asAggregate->getFunctionSymbolInfo();
                }
            }
            if (functionInfo == nullptr)
            {
                return false;
            }
    
            size_t callDagIndex = mCallDag->findIndex(functionInfo);
            if (callDagIndex == CallDAG::InvalidIndex)
            {
                // This happens only for unimplemented prototypes which are thus unused
                ASSERT(asAggregate && asAggregate->getOp() == EOpPrototype);
                return true;
            }
    
            ASSERT(callDagIndex < mMetadatas->size());
            return !(*mMetadatas)[callDagIndex].used;
        }
    
      private:
        const CallDAG *mCallDag;
        const std::vector<FunctionMetadata> *mMetadatas;
    };
    
    bool TCompiler::pruneUnusedFunctions(TIntermBlock *root)
    {
        UnusedPredicate isUnused(&mCallDag, &functionMetadata);
        TIntermSequence *sequence = root->getSequence();
    
        if (!sequence->empty())
        {
            sequence->erase(std::remove_if(sequence->begin(), sequence->end(), isUnused),
                            sequence->end());
        }
    
        return true;
    }
    
    bool TCompiler::validateOutputs(TIntermNode *root)
    {
        ValidateOutputs validateOutputs(getExtensionBehavior(), compileResources.MaxDrawBuffers);
        root->traverse(&validateOutputs);
        validateOutputs.validate(&mDiagnostics);
        return (mDiagnostics.numErrors() == 0);
    }
    
    bool TCompiler::validateLimitations(TIntermNode *root)
    {
        ValidateLimitations validate(shaderType, &mDiagnostics);
        root->traverse(&validate);
        return mDiagnostics.numErrors() == 0;
    }
    
    bool TCompiler::limitExpressionComplexity(TIntermNode *root)
    {
        TMaxDepthTraverser traverser(maxExpressionComplexity + 1);
        root->traverse(&traverser);
    
        if (traverser.getMaxDepth() > maxExpressionComplexity)
        {
            mDiagnostics.globalError("Expression too complex.");
            return false;
        }
    
        if (!ValidateMaxParameters::validate(root, maxFunctionParameters))
        {
            mDiagnostics.globalError("Function has too many parameters.");
            return false;
        }
    
        return true;
    }
    
    void TCompiler::collectVariables(TIntermNode *root)
    {
        if (!variablesCollected)
        {
            sh::CollectVariables collect(&attributes, &outputVariables, &uniforms, &varyings,
                                         &interfaceBlocks, hashFunction, symbolTable,
                                         extensionBehavior);
            root->traverse(&collect);
    
            // This is for enforcePackingRestriction().
            sh::ExpandUniforms(uniforms, &expandedUniforms);
            variablesCollected = true;
        }
    }
    
    bool TCompiler::shouldCollectVariables(ShCompileOptions compileOptions)
    {
        return (compileOptions & SH_VARIABLES) != 0;
    }
    
    bool TCompiler::wereVariablesCollected() const
    {
        return variablesCollected;
    }
    
    bool TCompiler::enforcePackingRestrictions()
    {
        VariablePacker packer;
        return packer.CheckVariablesWithinPackingLimits(maxUniformVectors, expandedUniforms);
    }
    
    void TCompiler::initializeGLPosition(TIntermNode *root)
    {
        InitVariableList list;
        sh::ShaderVariable var(GL_FLOAT_VEC4, 0);
        var.name = "gl_Position";
        list.push_back(var);
        InitializeVariables(root, list, symbolTable);
    }
    
    void TCompiler::useAllMembersInUnusedStandardAndSharedBlocks(TIntermNode *root)
    {
        sh::InterfaceBlockList list;
    
        for (auto block : interfaceBlocks)
        {
            if (!block.staticUse &&
                (block.layout == sh::BLOCKLAYOUT_STANDARD || block.layout == sh::BLOCKLAYOUT_SHARED))
            {
                list.push_back(block);
            }
        }
    
        sh::UseInterfaceBlockFields(root, list, symbolTable);
    }
    
    void TCompiler::initializeOutputVariables(TIntermNode *root)
    {
        InitVariableList list;
        if (shaderType == GL_VERTEX_SHADER)
        {
            for (auto var : varyings)
            {
                list.push_back(var);
            }
        }
        else
        {
            ASSERT(shaderType == GL_FRAGMENT_SHADER);
            for (auto var : outputVariables)
            {
                list.push_back(var);
            }
        }
        InitializeVariables(root, list, symbolTable);
    }
    
    const TExtensionBehavior &TCompiler::getExtensionBehavior() const
    {
        return extensionBehavior;
    }
    
    const char *TCompiler::getSourcePath() const
    {
        return mSourcePath;
    }
    
    const ShBuiltInResources &TCompiler::getResources() const
    {
        return compileResources;
    }
    
    const ArrayBoundsClamper &TCompiler::getArrayBoundsClamper() const
    {
        return arrayBoundsClamper;
    }
    
    ShArrayIndexClampingStrategy TCompiler::getArrayIndexClampingStrategy() const
    {
        return clampingStrategy;
    }
    
    const BuiltInFunctionEmulator &TCompiler::getBuiltInFunctionEmulator() const
    {
        return builtInFunctionEmulator;
    }
    
    void TCompiler::writePragma(ShCompileOptions compileOptions)
    {
        if (!(compileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL))
        {
            TInfoSinkBase &sink = infoSink.obj;
            if (mPragma.stdgl.invariantAll)
                sink << "#pragma STDGL invariant(all)\n";
        }
    }
    
    bool TCompiler::isVaryingDefined(const char *varyingName)
    {
        ASSERT(variablesCollected);
        for (size_t ii = 0; ii < varyings.size(); ++ii)
        {
            if (varyings[ii].name == varyingName)
            {
                return true;
            }
        }
    
        return false;
    }
    
    }  // namespace sh