blob: 53dd93fe4d59346e3c894f17c4fe8d832ed5c813 [file] [log] [blame]
//
// 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