Edit

kc3-lang/angle/src/tests/compiler_tests/CollectVariables_test.cpp

Branch :

  • Show log

    Commit

  • Author : Jamie Madill
    Date : 2016-02-08 15:05:17
    Hash : 39046169
    Message : CollectVariables: Don't include block name in field name. The spec mandates that the instance name of a block determines how the active uniform name for this field is reported. However, our handling of this was a bit bugged. We would include the proper prefix on the compiler-side, but this mangled the hashing, and was also not strictly needed. We now also expose the instance name, so we can determine the proper prefix for variable linking on the GL-side of things. This also is consistent with how we handle other spec issues, where the GL-side handles the GL-API specific functionality. This also allows us to fix name hashing of instanced uniform blocks, which was previously broken because we would hash the full name of the active uniform, instead of just the field. BUG=angleproject:1306 Change-Id: I06ace6dbc3f75fdd8129677360dcc142aa89136e Reviewed-on: https://chromium-review.googlesource.com/326681 Reviewed-by: Geoff Lang <geofflang@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>

  • src/tests/compiler_tests/CollectVariables_test.cpp
  • //
    // Copyright (c) 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.
    //
    // CollectVariables_test.cpp:
    //   Some tests for shader inspection
    //
    
    #include <memory>
    
    #include "angle_gl.h"
    #include "gtest/gtest.h"
    #include "GLSLANG/ShaderLang.h"
    #include "compiler/translator/TranslatorGLSL.h"
    
    #define EXPECT_GLENUM_EQ(expected, actual) \
        EXPECT_EQ(static_cast<GLenum>(expected), static_cast<GLenum>(actual))
    
    class CollectVariablesTest : public testing::Test
    {
      public:
        CollectVariablesTest(GLenum shaderType) : mShaderType(shaderType) {}
    
      protected:
        void SetUp() override
        {
            ShBuiltInResources resources;
            ShInitBuiltInResources(&resources);
            resources.MaxDrawBuffers = 8;
    
            initTranslator(resources);
        }
    
        void initTranslator(const ShBuiltInResources &resources)
        {
            mTranslator.reset(
                new TranslatorGLSL(mShaderType, SH_GLES3_SPEC, SH_GLSL_COMPATIBILITY_OUTPUT));
            ASSERT_TRUE(mTranslator->Init(resources));
        }
    
        // For use in the gl_DepthRange tests.
        void validateDepthRangeShader(const std::string &shaderString)
        {
            const char *shaderStrings[] = { shaderString.c_str() };
            ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES));
    
            const std::vector<sh::Uniform> &uniforms = mTranslator->getUniforms();
            ASSERT_EQ(1u, uniforms.size());
    
            const sh::Uniform &uniform = uniforms[0];
            EXPECT_EQ("gl_DepthRange", uniform.name);
            ASSERT_TRUE(uniform.isStruct());
            ASSERT_EQ(3u, uniform.fields.size());
    
            bool foundNear = false;
            bool foundFar = false;
            bool foundDiff = false;
    
            for (const auto &field : uniform.fields)
            {
                if (field.name == "near")
                {
                    EXPECT_FALSE(foundNear);
                    foundNear = true;
                }
                else if (field.name == "far")
                {
                    EXPECT_FALSE(foundFar);
                    foundFar = true;
                }
                else
                {
                    ASSERT_EQ("diff", field.name);
                    EXPECT_FALSE(foundDiff);
                    foundDiff = true;
                }
    
                EXPECT_EQ(0u, field.arraySize);
                EXPECT_FALSE(field.isStruct());
                EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision);
                EXPECT_TRUE(field.staticUse);
                EXPECT_GLENUM_EQ(GL_FLOAT, field.type);
            }
    
            EXPECT_TRUE(foundNear && foundFar && foundDiff);
        }
    
        // For use in tests for output varibles.
        void validateOutputVariableForShader(const std::string &shaderString,
                                             unsigned int varIndex,
                                             const char *varName,
                                             const sh::OutputVariable **outResult)
        {
            const char *shaderStrings[] = {shaderString.c_str()};
            ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES))
                << mTranslator->getInfoSink().info.str();
    
            const auto &outputVariables = mTranslator->getOutputVariables();
            ASSERT_LT(varIndex, outputVariables.size());
            const sh::OutputVariable &outputVariable = outputVariables[varIndex];
            EXPECT_EQ(-1, outputVariable.location);
            EXPECT_TRUE(outputVariable.staticUse);
            EXPECT_EQ(varName, outputVariable.name);
            *outResult = &outputVariable;
        }
    
        void compile(const std::string &shaderString)
        {
            const char *shaderStrings[] = {shaderString.c_str()};
            ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES));
        }
    
        GLenum mShaderType;
        std::unique_ptr<TranslatorGLSL> mTranslator;
    };
    
    class CollectVertexVariablesTest : public CollectVariablesTest
    {
      public:
        CollectVertexVariablesTest() : CollectVariablesTest(GL_VERTEX_SHADER) {}
    };
    
    class CollectFragmentVariablesTest : public CollectVariablesTest
    {
      public:
          CollectFragmentVariablesTest() : CollectVariablesTest(GL_FRAGMENT_SHADER) {}
    };
    
    TEST_F(CollectFragmentVariablesTest, SimpleOutputVar)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 out_fragColor;\n"
            "void main() {\n"
            "   out_fragColor = vec4(1.0);\n"
            "}\n";
    
        compile(shaderString);
    
        const auto &outputVariables = mTranslator->getOutputVariables();
        ASSERT_EQ(1u, outputVariables.size());
    
        const sh::OutputVariable &outputVariable = outputVariables[0];
    
        EXPECT_EQ(0u, outputVariable.arraySize);
        EXPECT_EQ(-1, outputVariable.location);
        EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable.precision);
        EXPECT_TRUE(outputVariable.staticUse);
        EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable.type);
        EXPECT_EQ("out_fragColor", outputVariable.name);
    }
    
    TEST_F(CollectFragmentVariablesTest, LocationOutputVar)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "layout(location=5) out vec4 out_fragColor;\n"
            "void main() {\n"
            "   out_fragColor = vec4(1.0);\n"
            "}\n";
    
        compile(shaderString);
    
        const auto &outputVariables = mTranslator->getOutputVariables();
        ASSERT_EQ(1u, outputVariables.size());
    
        const sh::OutputVariable &outputVariable = outputVariables[0];
    
        EXPECT_EQ(0u, outputVariable.arraySize);
        EXPECT_EQ(5, outputVariable.location);
        EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable.precision);
        EXPECT_TRUE(outputVariable.staticUse);
        EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable.type);
        EXPECT_EQ("out_fragColor", outputVariable.name);
    }
    
    TEST_F(CollectVertexVariablesTest, LocationAttribute)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "layout(location=5) in vec4 in_Position;\n"
            "void main() {\n"
            "   gl_Position = in_Position;\n"
            "}\n";
    
        compile(shaderString);
    
        const std::vector<sh::Attribute> &attributes = mTranslator->getAttributes();
        ASSERT_EQ(1u, attributes.size());
    
        const sh::Attribute &attribute = attributes[0];
    
        EXPECT_EQ(0u, attribute.arraySize);
        EXPECT_EQ(5, attribute.location);
        EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, attribute.precision);
        EXPECT_TRUE(attribute.staticUse);
        EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, attribute.type);
        EXPECT_EQ("in_Position", attribute.name);
    }
    
    TEST_F(CollectVertexVariablesTest, SimpleInterfaceBlock)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "uniform b {\n"
            "  float f;\n"
            "};"
            "void main() {\n"
            "   gl_Position = vec4(f, 0.0, 0.0, 1.0);\n"
            "}\n";
    
        compile(shaderString);
    
        const std::vector<sh::InterfaceBlock> &interfaceBlocks = mTranslator->getInterfaceBlocks();
        ASSERT_EQ(1u, interfaceBlocks.size());
    
        const sh::InterfaceBlock &interfaceBlock = interfaceBlocks[0];
    
        EXPECT_EQ(0u, interfaceBlock.arraySize);
        EXPECT_FALSE(interfaceBlock.isRowMajorLayout);
        EXPECT_EQ(sh::BLOCKLAYOUT_SHARED, interfaceBlock.layout);
        EXPECT_EQ("b", interfaceBlock.name);
        EXPECT_TRUE(interfaceBlock.staticUse);
    
        ASSERT_EQ(1u, interfaceBlock.fields.size());
    
        const sh::InterfaceBlockField &field = interfaceBlock.fields[0];
    
        EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision);
        EXPECT_TRUE(field.staticUse);
        EXPECT_GLENUM_EQ(GL_FLOAT, field.type);
        EXPECT_EQ("f", field.name);
        EXPECT_FALSE(field.isRowMajorLayout);
        EXPECT_TRUE(field.fields.empty());
    }
    
    TEST_F(CollectVertexVariablesTest, SimpleInstancedInterfaceBlock)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "uniform b {\n"
            "  float f;\n"
            "} blockInstance;"
            "void main() {\n"
            "   gl_Position = vec4(blockInstance.f, 0.0, 0.0, 1.0);\n"
            "}\n";
    
        compile(shaderString);
    
        const std::vector<sh::InterfaceBlock> &interfaceBlocks = mTranslator->getInterfaceBlocks();
        ASSERT_EQ(1u, interfaceBlocks.size());
    
        const sh::InterfaceBlock &interfaceBlock = interfaceBlocks[0];
    
        EXPECT_EQ(0u, interfaceBlock.arraySize);
        EXPECT_FALSE(interfaceBlock.isRowMajorLayout);
        EXPECT_EQ(sh::BLOCKLAYOUT_SHARED, interfaceBlock.layout);
        EXPECT_EQ("b", interfaceBlock.name);
        EXPECT_EQ("blockInstance", interfaceBlock.instanceName);
        EXPECT_TRUE(interfaceBlock.staticUse);
    
        ASSERT_EQ(1u, interfaceBlock.fields.size());
    
        const sh::InterfaceBlockField &field = interfaceBlock.fields[0];
    
        EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision);
        EXPECT_TRUE(field.staticUse);
        EXPECT_GLENUM_EQ(GL_FLOAT, field.type);
        EXPECT_EQ("f", field.name);
        EXPECT_FALSE(field.isRowMajorLayout);
        EXPECT_TRUE(field.fields.empty());
    }
    
    TEST_F(CollectVertexVariablesTest, StructInterfaceBlock)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "struct st { float f; };"
            "uniform b {\n"
            "  st s;\n"
            "};"
            "void main() {\n"
            "   gl_Position = vec4(s.f, 0.0, 0.0, 1.0);\n"
            "}\n";
    
        compile(shaderString);
    
        const std::vector<sh::InterfaceBlock> &interfaceBlocks = mTranslator->getInterfaceBlocks();
        ASSERT_EQ(1u, interfaceBlocks.size());
    
        const sh::InterfaceBlock &interfaceBlock = interfaceBlocks[0];
    
        EXPECT_EQ(0u, interfaceBlock.arraySize);
        EXPECT_FALSE(interfaceBlock.isRowMajorLayout);
        EXPECT_EQ(sh::BLOCKLAYOUT_SHARED, interfaceBlock.layout);
        EXPECT_EQ("b", interfaceBlock.name);
        EXPECT_TRUE(interfaceBlock.staticUse);
    
        ASSERT_EQ(1u, interfaceBlock.fields.size());
    
        const sh::InterfaceBlockField &field = interfaceBlock.fields[0];
    
        EXPECT_TRUE(field.isStruct());
        EXPECT_TRUE(field.staticUse);
        EXPECT_EQ("s", field.name);
        EXPECT_FALSE(field.isRowMajorLayout);
    
        const sh::ShaderVariable &member = field.fields[0];
    
        // NOTE: we don't currently mark struct members as statically used or not
        EXPECT_FALSE(member.isStruct());
        EXPECT_EQ("f", member.name);
        EXPECT_GLENUM_EQ(GL_FLOAT, member.type);
        EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, member.precision);
    }
    
    TEST_F(CollectVertexVariablesTest, StructInstancedInterfaceBlock)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "struct st { float f; };"
            "uniform b {\n"
            "  st s;\n"
            "} instanceName;"
            "void main() {\n"
            "   gl_Position = vec4(instanceName.s.f, 0.0, 0.0, 1.0);\n"
            "}\n";
    
        compile(shaderString);
    
        const std::vector<sh::InterfaceBlock> &interfaceBlocks = mTranslator->getInterfaceBlocks();
        ASSERT_EQ(1u, interfaceBlocks.size());
    
        const sh::InterfaceBlock &interfaceBlock = interfaceBlocks[0];
    
        EXPECT_EQ(0u, interfaceBlock.arraySize);
        EXPECT_FALSE(interfaceBlock.isRowMajorLayout);
        EXPECT_EQ(sh::BLOCKLAYOUT_SHARED, interfaceBlock.layout);
        EXPECT_EQ("b", interfaceBlock.name);
        EXPECT_EQ("instanceName", interfaceBlock.instanceName);
        EXPECT_TRUE(interfaceBlock.staticUse);
    
        ASSERT_EQ(1u, interfaceBlock.fields.size());
    
        const sh::InterfaceBlockField &field = interfaceBlock.fields[0];
    
        EXPECT_TRUE(field.isStruct());
        EXPECT_TRUE(field.staticUse);
        EXPECT_EQ("s", field.name);
        EXPECT_FALSE(field.isRowMajorLayout);
    
        const sh::ShaderVariable &member = field.fields[0];
    
        // NOTE: we don't currently mark struct members as statically used or not
        EXPECT_FALSE(member.isStruct());
        EXPECT_EQ("f", member.name);
        EXPECT_GLENUM_EQ(GL_FLOAT, member.type);
        EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, member.precision);
    }
    
    TEST_F(CollectVertexVariablesTest, NestedStructRowMajorInterfaceBlock)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "struct st { mat2 m; };"
            "layout(row_major) uniform b {\n"
            "  st s;\n"
            "};"
            "void main() {\n"
            "   gl_Position = vec4(s.m);\n"
            "}\n";
    
        compile(shaderString);
    
        const std::vector<sh::InterfaceBlock> &interfaceBlocks = mTranslator->getInterfaceBlocks();
        ASSERT_EQ(1u, interfaceBlocks.size());
    
        const sh::InterfaceBlock &interfaceBlock = interfaceBlocks[0];
    
        EXPECT_EQ(0u, interfaceBlock.arraySize);
        EXPECT_TRUE(interfaceBlock.isRowMajorLayout);
        EXPECT_EQ(sh::BLOCKLAYOUT_SHARED, interfaceBlock.layout);
        EXPECT_EQ("b", interfaceBlock.name);
        EXPECT_TRUE(interfaceBlock.staticUse);
    
        ASSERT_EQ(1u, interfaceBlock.fields.size());
    
        const sh::InterfaceBlockField &field = interfaceBlock.fields[0];
    
        EXPECT_TRUE(field.isStruct());
        EXPECT_TRUE(field.staticUse);
        EXPECT_EQ("s", field.name);
        EXPECT_TRUE(field.isRowMajorLayout);
    
        const sh::ShaderVariable &member = field.fields[0];
    
        // NOTE: we don't currently mark struct members as statically used or not
        EXPECT_FALSE(member.isStruct());
        EXPECT_EQ("m", member.name);
        EXPECT_GLENUM_EQ(GL_FLOAT_MAT2, member.type);
        EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, member.precision);
    }
    
    TEST_F(CollectVertexVariablesTest, VaryingInterpolation)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "precision mediump float;\n"
            "centroid out float vary;\n"
            "void main() {\n"
            "   gl_Position = vec4(1.0);\n"
            "   vary = 1.0;\n"
            "}\n";
    
        compile(shaderString);
    
        const std::vector<sh::Varying> &varyings = mTranslator->getVaryings();
        ASSERT_EQ(2u, varyings.size());
    
        const sh::Varying *varying = &varyings[0];
    
        if (varying->name == "gl_Position")
        {
            varying = &varyings[1];
        }
    
        EXPECT_EQ(0u, varying->arraySize);
        EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, varying->precision);
        EXPECT_TRUE(varying->staticUse);
        EXPECT_GLENUM_EQ(GL_FLOAT, varying->type);
        EXPECT_EQ("vary", varying->name);
        EXPECT_EQ(sh::INTERPOLATION_CENTROID, varying->interpolation);
    }
    
    // Test for builtin uniform "gl_DepthRange" (Vertex shader)
    TEST_F(CollectVertexVariablesTest, DepthRange)
    {
        const std::string &shaderString =
            "attribute vec4 position;\n"
            "void main() {\n"
            "   gl_Position = position + vec4(gl_DepthRange.near, gl_DepthRange.far, gl_DepthRange.diff, 1.0);\n"
            "}\n";
    
        validateDepthRangeShader(shaderString);
    }
    
    // Test for builtin uniform "gl_DepthRange" (Fragment shader)
    TEST_F(CollectFragmentVariablesTest, DepthRange)
    {
        const std::string &shaderString =
            "precision mediump float;\n"
            "void main() {\n"
            "   gl_FragColor = vec4(gl_DepthRange.near, gl_DepthRange.far, gl_DepthRange.diff, 1.0);\n"
            "}\n";
    
        validateDepthRangeShader(shaderString);
    }
    
    // Test that gl_FragColor built-in usage in ESSL1 fragment shader is reflected in the output
    // variables list.
    TEST_F(CollectFragmentVariablesTest, OutputVarESSL1FragColor)
    {
        const std::string &fragColorShader =
            "precision mediump float;\n"
            "void main() {\n"
            "   gl_FragColor = vec4(1.0);\n"
            "}\n";
    
        const sh::OutputVariable *outputVariable = nullptr;
        validateOutputVariableForShader(fragColorShader, 0u, "gl_FragColor", &outputVariable);
        ASSERT_NE(outputVariable, nullptr);
        EXPECT_EQ(0u, outputVariable->arraySize);
        EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable->type);
        EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable->precision);
    }
    
    // Test that gl_FragData built-in usage in ESSL1 fragment shader is reflected in the output
    // variables list.
    TEST_F(CollectFragmentVariablesTest, OutputVarESSL1FragData)
    {
        const std::string &fragDataShader =
            "#extension GL_EXT_draw_buffers : require\n"
            "precision mediump float;\n"
            "void main() {\n"
            "   gl_FragData[0] = vec4(1.0);\n"
            "   gl_FragData[1] = vec4(0.5);\n"
            "}\n";
    
        ShBuiltInResources resources       = mTranslator->getResources();
        resources.EXT_draw_buffers         = 1;
        const unsigned int kMaxDrawBuffers = 3u;
        resources.MaxDrawBuffers = kMaxDrawBuffers;
        initTranslator(resources);
    
        const sh::OutputVariable *outputVariable = nullptr;
        validateOutputVariableForShader(fragDataShader, 0u, "gl_FragData", &outputVariable);
        ASSERT_NE(outputVariable, nullptr);
        EXPECT_EQ(kMaxDrawBuffers, outputVariable->arraySize);
        EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable->type);
        EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable->precision);
    }
    
    // Test that gl_FragDataEXT built-in usage in ESSL1 fragment shader is reflected in the output
    // variables list. Also test that the precision is mediump.
    TEST_F(CollectFragmentVariablesTest, OutputVarESSL1FragDepthMediump)
    {
        const std::string &fragDepthShader =
            "#extension GL_EXT_frag_depth : require\n"
            "precision mediump float;\n"
            "void main() {\n"
            "   gl_FragDepthEXT = 0.7;"
            "}\n";
    
        ShBuiltInResources resources = mTranslator->getResources();
        resources.EXT_frag_depth = 1;
        initTranslator(resources);
    
        const sh::OutputVariable *outputVariable = nullptr;
        validateOutputVariableForShader(fragDepthShader, 0u, "gl_FragDepthEXT", &outputVariable);
        ASSERT_NE(outputVariable, nullptr);
        EXPECT_EQ(0u, outputVariable->arraySize);
        EXPECT_GLENUM_EQ(GL_FLOAT, outputVariable->type);
        EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable->precision);
    }
    
    // Test that gl_FragDataEXT built-in usage in ESSL1 fragment shader is reflected in the output
    // variables list. Also test that the precision is highp if user requests it.
    TEST_F(CollectFragmentVariablesTest, OutputVarESSL1FragDepthHighp)
    {
        const std::string &fragDepthHighShader =
            "#extension GL_EXT_frag_depth : require\n"
            "void main() {\n"
            "   gl_FragDepthEXT = 0.7;"
            "}\n";
    
        ShBuiltInResources resources    = mTranslator->getResources();
        resources.EXT_frag_depth        = 1;
        resources.FragmentPrecisionHigh = 1;
        initTranslator(resources);
    
        const sh::OutputVariable *outputVariable = nullptr;
        validateOutputVariableForShader(fragDepthHighShader, 0u, "gl_FragDepthEXT", &outputVariable);
        ASSERT_NE(outputVariable, nullptr);
        EXPECT_EQ(0u, outputVariable->arraySize);
        EXPECT_GLENUM_EQ(GL_FLOAT, outputVariable->type);
        EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, outputVariable->precision);
    }
    
    // Test that gl_FragData built-in usage in ESSL3 fragment shader is reflected in the output
    // variables list. Also test that the precision is highp.
    TEST_F(CollectFragmentVariablesTest, OutputVarESSL3FragDepthHighp)
    {
        const std::string &fragDepthHighShader =
            "#version 300 es\n"
            "precision mediump float;\n"
            "void main() {\n"
            "   gl_FragDepth = 0.7;"
            "}\n";
    
        ShBuiltInResources resources = mTranslator->getResources();
        resources.EXT_frag_depth = 1;
        initTranslator(resources);
    
        const sh::OutputVariable *outputVariable = nullptr;
        validateOutputVariableForShader(fragDepthHighShader, 0u, "gl_FragDepth", &outputVariable);
        ASSERT_NE(outputVariable, nullptr);
        EXPECT_EQ(0u, outputVariable->arraySize);
        EXPECT_GLENUM_EQ(GL_FLOAT, outputVariable->type);
        EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, outputVariable->precision);
    }
    
    // Test that gl_SecondaryFragColorEXT built-in usage in ESSL1 fragment shader is reflected in the
    // output variables list.
    TEST_F(CollectFragmentVariablesTest, OutputVarESSL1EXTBlendFuncExtendedSecondaryFragColor)
    {
        const char *secondaryFragColorShader =
            "#extension GL_EXT_blend_func_extended : require\n"
            "precision mediump float;\n"
            "void main() {\n"
            "   gl_FragColor = vec4(1.0);\n"
            "   gl_SecondaryFragColorEXT = vec4(1.0);\n"
            "}\n";
    
        const unsigned int kMaxDrawBuffers = 3u;
        ShBuiltInResources resources       = mTranslator->getResources();
        resources.EXT_blend_func_extended  = 1;
        resources.EXT_draw_buffers         = 1;
        resources.MaxDrawBuffers           = kMaxDrawBuffers;
        resources.MaxDualSourceDrawBuffers = resources.MaxDrawBuffers;
        initTranslator(resources);
    
        const sh::OutputVariable *outputVariable = nullptr;
        validateOutputVariableForShader(secondaryFragColorShader, 0u, "gl_FragColor", &outputVariable);
        ASSERT_NE(outputVariable, nullptr);
        EXPECT_EQ(0u, outputVariable->arraySize);
        EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable->type);
        EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable->precision);
    
        outputVariable = nullptr;
        validateOutputVariableForShader(secondaryFragColorShader, 1u, "gl_SecondaryFragColorEXT",
                                        &outputVariable);
        ASSERT_NE(outputVariable, nullptr);
        EXPECT_EQ(0u, outputVariable->arraySize);
        EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable->type);
        EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable->precision);
    }
    
    // Test that gl_SecondaryFragDataEXT built-in usage in ESSL1 fragment shader is reflected in the
    // output variables list.
    TEST_F(CollectFragmentVariablesTest, OutputVarESSL1EXTBlendFuncExtendedSecondaryFragData)
    {
        const char *secondaryFragDataShader =
            "#extension GL_EXT_blend_func_extended : require\n"
            "#extension GL_EXT_draw_buffers : require\n"
            "precision mediump float;\n"
            "void main() {\n"
            "   gl_FragData[0] = vec4(1.0);\n"
            "   gl_FragData[1] = vec4(0.5);\n"
            "   gl_SecondaryFragDataEXT[0] = vec4(1.0);\n"
            "   gl_SecondaryFragDataEXT[1] = vec4(0.8);\n"
            "}\n";
        const unsigned int kMaxDrawBuffers = 3u;
        ShBuiltInResources resources       = mTranslator->getResources();
        resources.EXT_blend_func_extended  = 1;
        resources.EXT_draw_buffers         = 1;
        resources.MaxDrawBuffers           = kMaxDrawBuffers;
        resources.MaxDualSourceDrawBuffers = resources.MaxDrawBuffers;
        initTranslator(resources);
    
        const sh::OutputVariable *outputVariable = nullptr;
        validateOutputVariableForShader(secondaryFragDataShader, 0u, "gl_FragData", &outputVariable);
        ASSERT_NE(outputVariable, nullptr);
        EXPECT_EQ(kMaxDrawBuffers, outputVariable->arraySize);
        EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable->type);
        EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable->precision);
    
        outputVariable = nullptr;
        validateOutputVariableForShader(secondaryFragDataShader, 1u, "gl_SecondaryFragDataEXT",
                                        &outputVariable);
        ASSERT_NE(outputVariable, nullptr);
        EXPECT_EQ(kMaxDrawBuffers, outputVariable->arraySize);
        EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable->type);
        EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable->precision);
    }
    
    static khronos_uint64_t SimpleTestHash(const char *str, size_t len)
    {
        return static_cast<uint64_t>(len);
    }
    
    class CollectHashedVertexVariablesTest : public CollectVertexVariablesTest
    {
      protected:
        void SetUp() override
        {
            // Initialize the translate with a hash function
            ShBuiltInResources resources;
            ShInitBuiltInResources(&resources);
            resources.HashFunction = SimpleTestHash;
            initTranslator(resources);
        }
    };
    
    TEST_F(CollectHashedVertexVariablesTest, InstancedInterfaceBlock)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "uniform blockName {\n"
            "  float field;\n"
            "} blockInstance;"
            "void main() {\n"
            "   gl_Position = vec4(blockInstance.field, 0.0, 0.0, 1.0);\n"
            "}\n";
    
        compile(shaderString);
    
        const std::vector<sh::InterfaceBlock> &interfaceBlocks = mTranslator->getInterfaceBlocks();
        ASSERT_EQ(1u, interfaceBlocks.size());
    
        const sh::InterfaceBlock &interfaceBlock = interfaceBlocks[0];
    
        EXPECT_EQ(0u, interfaceBlock.arraySize);
        EXPECT_FALSE(interfaceBlock.isRowMajorLayout);
        EXPECT_EQ(sh::BLOCKLAYOUT_SHARED, interfaceBlock.layout);
        EXPECT_EQ("blockName", interfaceBlock.name);
        EXPECT_EQ("blockInstance", interfaceBlock.instanceName);
        EXPECT_EQ("webgl_9", interfaceBlock.mappedName);
        EXPECT_TRUE(interfaceBlock.staticUse);
    
        ASSERT_EQ(1u, interfaceBlock.fields.size());
    
        const sh::InterfaceBlockField &field = interfaceBlock.fields[0];
    
        EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision);
        EXPECT_TRUE(field.staticUse);
        EXPECT_GLENUM_EQ(GL_FLOAT, field.type);
        EXPECT_EQ("field", field.name);
        EXPECT_EQ("webgl_5", field.mappedName);
        EXPECT_FALSE(field.isRowMajorLayout);
        EXPECT_TRUE(field.fields.empty());
    }
    
    TEST_F(CollectHashedVertexVariablesTest, StructUniform)
    {
        const std::string &shaderString =
            "#version 300 es\n"
            "struct sType {\n"
            "  float field;\n"
            "};"
            "uniform sType u;"
            "void main() {\n"
            "   gl_Position = vec4(u.field, 0.0, 0.0, 1.0);\n"
            "}\n";
    
        compile(shaderString);
    
        const auto &uniforms = mTranslator->getUniforms();
        ASSERT_EQ(1u, uniforms.size());
    
        const sh::Uniform &uniform = uniforms[0];
    
        EXPECT_EQ(0u, uniform.arraySize);
        EXPECT_EQ("u", uniform.name);
        EXPECT_EQ("webgl_1", uniform.mappedName);
        EXPECT_TRUE(uniform.staticUse);
    
        ASSERT_EQ(1u, uniform.fields.size());
    
        const sh::ShaderVariable &field = uniform.fields[0];
    
        EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision);
        // EXPECT_TRUE(field.staticUse); // we don't yet support struct static use
        EXPECT_GLENUM_EQ(GL_FLOAT, field.type);
        EXPECT_EQ("field", field.name);
        EXPECT_EQ("webgl_5", field.mappedName);
        EXPECT_TRUE(field.fields.empty());
    }