Edit

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

Branch :

  • Show log

    Commit

  • Author : Jiawei Shao
    Date : 2017-08-31 14:25:54
    Hash : 4cc89e2b
    Message : ES31: Enable 'location' layout qualifier on shader interfaces in compiler This patch enables 'location' layout qualifier for vertex outputs and fragment shader inputs when the shader version is 3.1 in ANGLE GLSL compiler and adds the check on location conflicts for these varyings. According to GLSL ES 3.1 SPEC (Chapter 4.4.1 and Chapter 4.4.2), 'location' layout qualifier is allowed on both inputs and outputs of vertex and fragment shaders. 'location' layout qualifier on shader interfaces is only valid on shaders whose version is 3.1 and above. According to GLSL ES 3.0 SPEC, vertex shader cannot have output layout qualifiers (Chapter 4.3.8.2) and fragment shader cannot have input layout qualifiers (Chapter 4.3.8.1). The 'location' qualifier on varyings is used in the shader interface matching defined in OpenGL ES 3.1. (OpenGL ES 3.1 SPEC Chapter 7.4.1). This new link rule will be added to Program.cpp in another patch. For the OpenGL ES 3.1 extension GL_OES_geometry_shader, according to GL_OES_shader_io_blocks SPEC (Chapter 4.4.1 and Chapter 4.4.2), 'location' layout qualifier is both valid on geometry shader inputs and outputs. This feature will be implemented together with other rules on geometry shader inputs and outputs. BUG=angleproject:2144 TEST=angle_unittests Change-Id: I62d85f7144c177448321c2db36ed7aaeaa1fb205 Reviewed-on: https://chromium-review.googlesource.com/645366 Commit-Queue: Geoff Lang <geofflang@chromium.org> Reviewed-by: Geoff Lang <geofflang@chromium.org>

  • src/tests/compiler_tests/ShaderVariable_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 "angle_gl.h"
    #include "compiler/translator/Compiler.h"
    #include "gtest/gtest.h"
    #include "GLSLANG/ShaderLang.h"
    
    namespace sh
    {
    
    TEST(ShaderVariableTest, FindInfoByMappedName)
    {
        // struct A {
        //   float x[2];
        //   vec3 y;
        // };
        // struct B {
        //   A a[3];
        // };
        // B uni[2];
        ShaderVariable uni;
        uni.arraySize = 2;
        uni.name = "uni";
        uni.mappedName = "m_uni";
        uni.structName = "B";
        {
            ShaderVariable a;
            a.arraySize = 3;
            a.name = "a";
            a.mappedName = "m_a";
            a.structName = "A";
            {
                ShaderVariable x(GL_FLOAT, 2);
                x.name = "x";
                x.mappedName = "m_x";
                a.fields.push_back(x);
    
                ShaderVariable y(GL_FLOAT_VEC3, 0);
                y.name = "y";
                y.mappedName = "m_y";
                a.fields.push_back(y);
            }
            uni.fields.push_back(a);
        }
    
        const ShaderVariable *leafVar = nullptr;
        std::string originalFullName;
    
        std::string mappedFullName = "wrongName";
        EXPECT_FALSE(uni.findInfoByMappedName(
            mappedFullName, &leafVar, &originalFullName));
    
        mappedFullName = "m_uni";
        EXPECT_TRUE(uni.findInfoByMappedName(
            mappedFullName, &leafVar, &originalFullName));
        EXPECT_EQ(&uni, leafVar);
        EXPECT_STREQ("uni", originalFullName.c_str());
    
        mappedFullName = "m_uni[0].m_a[1].wrongName";
        EXPECT_FALSE(uni.findInfoByMappedName(
            mappedFullName, &leafVar, &originalFullName));
    
        mappedFullName = "m_uni[0].m_a[1].m_x";
        EXPECT_TRUE(uni.findInfoByMappedName(
            mappedFullName, &leafVar, &originalFullName));
        EXPECT_EQ(&(uni.fields[0].fields[0]), leafVar);
        EXPECT_STREQ("uni[0].a[1].x", originalFullName.c_str());
    
        mappedFullName = "m_uni[0].m_a[1].m_x[0]";
        EXPECT_TRUE(uni.findInfoByMappedName(
            mappedFullName, &leafVar, &originalFullName));
        EXPECT_EQ(&(uni.fields[0].fields[0]), leafVar);
        EXPECT_STREQ("uni[0].a[1].x[0]", originalFullName.c_str());
    
        mappedFullName = "m_uni[0].m_a[1].m_y";
        EXPECT_TRUE(uni.findInfoByMappedName(
            mappedFullName, &leafVar, &originalFullName));
        EXPECT_EQ(&(uni.fields[0].fields[1]), leafVar);
        EXPECT_STREQ("uni[0].a[1].y", originalFullName.c_str());
    }
    
    TEST(ShaderVariableTest, IsSameUniformWithDifferentFieldOrder)
    {
        // struct A {
        //   float x;
        //   float y;
        // };
        // uniform A uni;
        Uniform vx_a;
        vx_a.arraySize = 0;
        vx_a.name = "uni";
        vx_a.mappedName = "m_uni";
        vx_a.structName = "A";
        {
            ShaderVariable x(GL_FLOAT, 0);
            x.name = "x";
            x.mappedName = "m_x";
            vx_a.fields.push_back(x);
    
            ShaderVariable y(GL_FLOAT, 0);
            y.name = "y";
            y.mappedName = "m_y";
            vx_a.fields.push_back(y);
        }
    
        // struct A {
        //   float y;
        //   float x;
        // };
        // uniform A uni;
        Uniform fx_a;
        fx_a.arraySize = 0;
        fx_a.name = "uni";
        fx_a.mappedName = "m_uni";
        fx_a.structName = "A";
        {
            ShaderVariable y(GL_FLOAT, 0);
            y.name = "y";
            y.mappedName = "m_y";
            fx_a.fields.push_back(y);
    
            ShaderVariable x(GL_FLOAT, 0);
            x.name = "x";
            x.mappedName = "m_x";
            fx_a.fields.push_back(x);
        }
    
        EXPECT_FALSE(vx_a.isSameUniformAtLinkTime(fx_a));
    }
    
    TEST(ShaderVariableTest, IsSameUniformWithDifferentStructNames)
    {
        // struct A {
        //   float x;
        //   float y;
        // };
        // uniform A uni;
        Uniform vx_a;
        vx_a.arraySize = 0;
        vx_a.name = "uni";
        vx_a.mappedName = "m_uni";
        vx_a.structName = "A";
        {
            ShaderVariable x(GL_FLOAT, 0);
            x.name = "x";
            x.mappedName = "m_x";
            vx_a.fields.push_back(x);
    
            ShaderVariable y(GL_FLOAT, 0);
            y.name = "y";
            y.mappedName = "m_y";
            vx_a.fields.push_back(y);
        }
    
        // struct B {
        //   float x;
        //   float y;
        // };
        // uniform B uni;
        Uniform fx_a;
        fx_a.arraySize = 0;
        fx_a.name = "uni";
        fx_a.mappedName = "m_uni";
        {
            ShaderVariable x(GL_FLOAT, 0);
            x.name = "x";
            x.mappedName = "m_x";
            fx_a.fields.push_back(x);
    
            ShaderVariable y(GL_FLOAT, 0);
            y.name = "y";
            y.mappedName = "m_y";
            fx_a.fields.push_back(y);
        }
    
        fx_a.structName = "B";
        EXPECT_FALSE(vx_a.isSameUniformAtLinkTime(fx_a));
    
        fx_a.structName = "A";
        EXPECT_TRUE(vx_a.isSameUniformAtLinkTime(fx_a));
    
        fx_a.structName = "";
        EXPECT_FALSE(vx_a.isSameUniformAtLinkTime(fx_a));
    }
    
    TEST(ShaderVariableTest, IsSameVaryingWithDifferentInvariance)
    {
        // invariant varying float vary;
        Varying vx;
        vx.type = GL_FLOAT;
        vx.arraySize = 0;
        vx.precision = GL_MEDIUM_FLOAT;
        vx.name = "vary";
        vx.mappedName = "m_vary";
        vx.staticUse = true;
        vx.isInvariant = true;
    
        // varying float vary;
        Varying fx;
        fx.type = GL_FLOAT;
        fx.arraySize = 0;
        fx.precision = GL_MEDIUM_FLOAT;
        fx.name = "vary";
        fx.mappedName = "m_vary";
        fx.staticUse = true;
        fx.isInvariant = false;
    
        // Default to ESSL1 behavior: invariance must match
        EXPECT_FALSE(vx.isSameVaryingAtLinkTime(fx));
        EXPECT_FALSE(vx.isSameVaryingAtLinkTime(fx, 100));
        // ESSL3 behavior: invariance doesn't need to match
        EXPECT_TRUE(vx.isSameVaryingAtLinkTime(fx, 300));
    
        // invariant varying float vary;
        fx.isInvariant = true;
        EXPECT_TRUE(vx.isSameVaryingAtLinkTime(fx));
        EXPECT_TRUE(vx.isSameVaryingAtLinkTime(fx, 100));
        EXPECT_TRUE(vx.isSameVaryingAtLinkTime(fx, 300));
    }
    
    // Test that using invariant varyings doesn't trigger a double delete.
    TEST(ShaderVariableTest, InvariantDoubleDeleteBug)
    {
        ShBuiltInResources resources;
        sh::InitBuiltInResources(&resources);
    
        ShHandle compiler = sh::ConstructCompiler(GL_VERTEX_SHADER, SH_GLES2_SPEC,
                                                  SH_GLSL_COMPATIBILITY_OUTPUT, &resources);
        EXPECT_NE(static_cast<ShHandle>(0), compiler);
    
        const char *program[] =
        {
            "attribute vec4 position;\n"
            "varying float v;\n"
            "invariant v;\n"
            "void main() {\n"
            "  v = 1.0;\n"
            "  gl_Position = position;\n"
            "}"
        };
    
        EXPECT_TRUE(sh::Compile(compiler, program, 1, SH_OBJECT_CODE));
        EXPECT_TRUE(sh::Compile(compiler, program, 1, SH_OBJECT_CODE));
        sh::Destruct(compiler);
    }
    
    TEST(ShaderVariableTest, IllegalInvariantVarying)
    {
        ShBuiltInResources resources;
        sh::InitBuiltInResources(&resources);
    
        ShHandle compiler = sh::ConstructCompiler(GL_VERTEX_SHADER, SH_GLES2_SPEC,
                                                  SH_GLSL_COMPATIBILITY_OUTPUT, &resources);
        EXPECT_NE(static_cast<ShHandle>(0), compiler);
    
        const char *program1[] =
        {
            "void foo() {\n"
            "  vec4 v;\n"
            "}\n"
            "varying vec4 v_varying;\n"
            "invariant v_varying;\n"
            "void main() {\n"
            "  foo();\n"
            "  gl_Position = v_varying;\n"
            "}"
        };
        const char *program2[] =
        {
            "varying vec4 v_varying;\n"
            "void foo() {\n"
            "  invariant v_varying;\n"
            "}\n"
            "void main() {\n"
            "  foo();\n"
            "  gl_Position = v_varying;\n"
            "}"
        };
    
        EXPECT_TRUE(sh::Compile(compiler, program1, 1, SH_VARIABLES));
        EXPECT_FALSE(sh::Compile(compiler, program2, 1, SH_VARIABLES));
        sh::Destruct(compiler);
    }
    
    TEST(ShaderVariableTest, InvariantLeakAcrossShaders)
    {
        ShBuiltInResources resources;
        sh::InitBuiltInResources(&resources);
    
        ShHandle compiler = sh::ConstructCompiler(GL_VERTEX_SHADER, SH_GLES2_SPEC,
                                                  SH_GLSL_COMPATIBILITY_OUTPUT, &resources);
        EXPECT_NE(static_cast<ShHandle>(0), compiler);
    
        const char *program1[] =
        {
            "varying vec4 v_varying;\n"
            "invariant v_varying;\n"
            "void main() {\n"
            "  gl_Position = v_varying;\n"
            "}"
        };
        const char *program2[] =
        {
            "varying vec4 v_varying;\n"
            "void main() {\n"
            "  gl_Position = v_varying;\n"
            "}"
        };
    
        EXPECT_TRUE(sh::Compile(compiler, program1, 1, SH_VARIABLES));
        const std::vector<sh::Varying> *varyings = sh::GetOutputVaryings(compiler);
        for (const sh::Varying &varying : *varyings)
        {
            if (varying.name == "v_varying")
                EXPECT_TRUE(varying.isInvariant);
        }
        EXPECT_TRUE(sh::Compile(compiler, program2, 1, SH_VARIABLES));
        varyings = sh::GetOutputVaryings(compiler);
        for (const sh::Varying &varying : *varyings)
        {
            if (varying.name == "v_varying")
                EXPECT_FALSE(varying.isInvariant);
        }
        sh::Destruct(compiler);
    }
    
    TEST(ShaderVariableTest, GlobalInvariantLeakAcrossShaders)
    {
        ShBuiltInResources resources;
        sh::InitBuiltInResources(&resources);
    
        ShHandle compiler = sh::ConstructCompiler(GL_VERTEX_SHADER, SH_GLES2_SPEC,
                                                  SH_GLSL_COMPATIBILITY_OUTPUT, &resources);
        EXPECT_NE(static_cast<ShHandle>(0), compiler);
    
        const char *program1[] =
        {
            "#pragma STDGL invariant(all)\n"
            "varying vec4 v_varying;\n"
            "void main() {\n"
            "  gl_Position = v_varying;\n"
            "}"
        };
        const char *program2[] =
        {
            "varying vec4 v_varying;\n"
            "void main() {\n"
            "  gl_Position = v_varying;\n"
            "}"
        };
    
        EXPECT_TRUE(sh::Compile(compiler, program1, 1, SH_VARIABLES));
        const std::vector<sh::Varying> *varyings = sh::GetOutputVaryings(compiler);
        for (const sh::Varying &varying : *varyings)
        {
            if (varying.name == "v_varying")
                EXPECT_TRUE(varying.isInvariant);
        }
        EXPECT_TRUE(sh::Compile(compiler, program2, 1, SH_VARIABLES));
        varyings = sh::GetOutputVaryings(compiler);
        for (const sh::Varying &varying : *varyings)
        {
            if (varying.name == "v_varying")
                EXPECT_FALSE(varying.isInvariant);
        }
        sh::Destruct(compiler);
    }
    
    TEST(ShaderVariableTest, BuiltinInvariantVarying)
    {
    
        ShBuiltInResources resources;
        sh::InitBuiltInResources(&resources);
    
        ShHandle compiler = sh::ConstructCompiler(GL_VERTEX_SHADER, SH_GLES2_SPEC,
                                                  SH_GLSL_COMPATIBILITY_OUTPUT, &resources);
        EXPECT_NE(static_cast<ShHandle>(0), compiler);
    
        const char *program1[] =
        {
            "invariant gl_Position;\n"
            "void main() {\n"
            "  gl_Position = vec4(0, 0, 0, 0);\n"
            "}"
        };
        const char *program2[] =
        {
            "void main() {\n"
            "  gl_Position = vec4(0, 0, 0, 0);\n"
            "}"
        };
        const char *program3[] =
        {
            "void main() {\n"
            "  invariant gl_Position;\n"
            "  gl_Position = vec4(0, 0, 0, 0);\n"
            "}"
        };
    
        EXPECT_TRUE(sh::Compile(compiler, program1, 1, SH_VARIABLES));
        const std::vector<sh::Varying> *varyings = sh::GetOutputVaryings(compiler);
        for (const sh::Varying &varying : *varyings)
        {
            if (varying.name == "gl_Position")
                EXPECT_TRUE(varying.isInvariant);
        }
        EXPECT_TRUE(sh::Compile(compiler, program2, 1, SH_VARIABLES));
        varyings = sh::GetOutputVaryings(compiler);
        for (const sh::Varying &varying : *varyings)
        {
            if (varying.name == "gl_Position")
                EXPECT_FALSE(varying.isInvariant);
        }
        EXPECT_FALSE(sh::Compile(compiler, program3, 1, SH_VARIABLES));
        sh::Destruct(compiler);
    }
    
    // Verify in ES3.1 two varyings with either same name or same declared location can match.
    TEST(ShaderVariableTest, IsSameVaryingWithDifferentName)
    {
        // Varying float vary1;
        Varying vx;
        vx.type        = GL_FLOAT;
        vx.arraySize   = 0;
        vx.precision   = GL_MEDIUM_FLOAT;
        vx.name        = "vary1";
        vx.mappedName  = "m_vary1";
        vx.staticUse   = true;
        vx.isInvariant = false;
    
        // Varying float vary2;
        Varying fx;
        fx.type        = GL_FLOAT;
        fx.arraySize   = 0;
        fx.precision   = GL_MEDIUM_FLOAT;
        fx.name        = "vary2";
        fx.mappedName  = "m_vary2";
        fx.staticUse   = true;
        fx.isInvariant = false;
    
        // ESSL3 behavior: name must match
        EXPECT_FALSE(vx.isSameVaryingAtLinkTime(fx, 300));
    
        // ESSL3.1 behavior:
        // [OpenGL ES 3.1 SPEC Chapter 7.4.1]
        // An output variable is considered to match an input variable in the subsequent shader if:
        // - the two variables match in name, type, and qualification; or
        // - the two variables are declared with the same location qualifier and match in type and
        //   qualification.
        vx.location = 0;
        fx.location = 0;
        EXPECT_TRUE(vx.isSameVaryingAtLinkTime(fx, 310));
    
        fx.name       = vx.name;
        fx.mappedName = vx.mappedName;
    
        fx.location = -1;
        EXPECT_FALSE(vx.isSameVaryingAtLinkTime(fx, 310));
    
        fx.location = 1;
        EXPECT_FALSE(vx.isSameVaryingAtLinkTime(fx, 310));
    
        fx.location = 0;
        EXPECT_TRUE(vx.isSameVaryingAtLinkTime(fx, 310));
    }
    
    }  // namespace sh