| // |
| // 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)); |
| } |
| |
| 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::GetVaryings(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::GetVaryings(compiler); |
| for (const sh::Varying &varying : *varyings) |
| { |
| if (varying.name == "v_varying") |
| EXPECT_FALSE(varying.isInvariant); |
| } |
| } |
| |
| 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::GetVaryings(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::GetVaryings(compiler); |
| for (const sh::Varying &varying : *varyings) |
| { |
| if (varying.name == "v_varying") |
| EXPECT_FALSE(varying.isInvariant); |
| } |
| } |
| |
| 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::GetVaryings(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::GetVaryings(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)); |
| } |
| |
| } // namespace sh |