blob: 036a097f08501b6c9496d97b1a250983a8d84fdb [file] [log] [blame]
//
// Copyright 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 "GLSLANG/ShaderLang.h"
#include "angle_gl.h"
#include "compiler/translator/Compiler.h"
#include "gtest/gtest.h"
namespace sh
{
TEST(ShaderVariableTest, FindInfoByMappedName)
{
// struct A {
// float x[2];
// vec3 y;
// };
// struct B {
// A a[3];
// };
// B uni[2];
ShaderVariable uni(0, 2);
uni.name = "uni";
uni.mappedName = "m_uni";
uni.structName = "B";
{
ShaderVariable a(0, 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);
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;
ShaderVariable vx_a;
vx_a.name = "uni";
vx_a.mappedName = "m_uni";
vx_a.structName = "A";
{
ShaderVariable x(GL_FLOAT);
x.name = "x";
x.mappedName = "m_x";
vx_a.fields.push_back(x);
ShaderVariable y(GL_FLOAT);
y.name = "y";
y.mappedName = "m_y";
vx_a.fields.push_back(y);
}
// struct A {
// float y;
// float x;
// };
// uniform A uni;
ShaderVariable fx_a;
fx_a.name = "uni";
fx_a.mappedName = "m_uni";
fx_a.structName = "A";
{
ShaderVariable y(GL_FLOAT);
y.name = "y";
y.mappedName = "m_y";
fx_a.fields.push_back(y);
ShaderVariable x(GL_FLOAT);
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;
ShaderVariable vx_a;
vx_a.name = "uni";
vx_a.mappedName = "m_uni";
vx_a.structName = "A";
{
ShaderVariable x(GL_FLOAT);
x.name = "x";
x.mappedName = "m_x";
vx_a.fields.push_back(x);
ShaderVariable y(GL_FLOAT);
y.name = "y";
y.mappedName = "m_y";
vx_a.fields.push_back(y);
}
// struct B {
// float x;
// float y;
// };
// uniform B uni;
ShaderVariable fx_a;
fx_a.name = "uni";
fx_a.mappedName = "m_uni";
{
ShaderVariable x(GL_FLOAT);
x.name = "x";
x.mappedName = "m_x";
fx_a.fields.push_back(x);
ShaderVariable y(GL_FLOAT);
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;
ShaderVariable vx;
vx.type = GL_FLOAT;
vx.precision = GL_MEDIUM_FLOAT;
vx.name = "vary";
vx.mappedName = "m_vary";
vx.staticUse = true;
vx.isInvariant = true;
// varying float vary;
ShaderVariable fx;
fx.type = GL_FLOAT;
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[] = {
R"(void foo()
{
}
varying vec4 v_varying;
invariant v_varying;
void main()
{
foo();
gl_Position = v_varying;
})"};
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::ShaderVariable> *varyings = sh::GetOutputVaryings(compiler);
for (const sh::ShaderVariable &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::ShaderVariable &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::ShaderVariable> *varyings = sh::GetOutputVaryings(compiler);
for (const sh::ShaderVariable &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::ShaderVariable &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::ShaderVariable> *varyings = sh::GetOutputVaryings(compiler);
for (const sh::ShaderVariable &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::ShaderVariable &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;
ShaderVariable vx;
vx.type = GL_FLOAT;
vx.precision = GL_MEDIUM_FLOAT;
vx.name = "vary1";
vx.mappedName = "m_vary1";
vx.staticUse = true;
vx.isInvariant = false;
// Varying float vary2;
ShaderVariable fx;
fx.type = GL_FLOAT;
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