blob: 30390267727c8dfc00aa26ea22bd3396a4ddfc75 [file] [log] [blame]
//
// Copyright 2024 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.
//
// SeparateDeclarations.cpp:
// Tests that compound declarations are rewritten to type declarations and variable declarations.
//
#include "GLSLANG/ShaderLang.h"
#include "angle_gl.h"
#include "gtest/gtest.h"
#include "tests/test_utils/compiler_test.h"
using namespace sh;
namespace
{
class SeparateDeclarations : public MatchOutputCodeTest
{
public:
SeparateDeclarations() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_ESSL_OUTPUT)
{
ShCompileOptions defaultCompileOptions = {};
defaultCompileOptions.validateAST = true;
setDefaultCompileOptions(defaultCompileOptions);
}
};
class SeparateCompoundStructDeclarations : public MatchOutputCodeTest
{
public:
SeparateCompoundStructDeclarations() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_ESSL_OUTPUT)
{
ShCompileOptions defaultCompileOptions = {};
defaultCompileOptions.validateAST = true;
defaultCompileOptions.separateCompoundStructDeclarations = true;
setDefaultCompileOptions(defaultCompileOptions);
}
};
class SeparateStructFunctionDeclarations : public MatchOutputCodeTest
{
public:
SeparateStructFunctionDeclarations() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_ESSL_OUTPUT)
{
ShCompileOptions defaultCompileOptions = {};
defaultCompileOptions.validateAST = true;
setDefaultCompileOptions(defaultCompileOptions);
}
};
TEST_F(SeparateDeclarations, Arrays)
{
const char kShader[] = R"(#version 300 es
precision highp float;
int a[1] = int[1](1), b[1] = int[1](2);
out vec4 o;
void main() {
if (a[0] == b[0])
o = vec4(1);
})";
const char kExpected[] = R"(#version 300 es
const mediump int sbbd = 1;
mediump int _ua[1] = int[1](sbbd);
const mediump int sbbe = 2;
mediump int _ub[1] = int[1](sbbe);
out highp vec4 _uo;
void main(){
if ((_ua[0] == _ub[0]))
{
(_uo = vec4(1.0, 1.0, 1.0, 1.0));
}
}
)";
compile(kShader);
EXPECT_EQ(kExpected, outputCode(SH_ESSL_OUTPUT));
}
TEST_F(SeparateDeclarations, StructNoChange)
{
const char kShader[] = R"(#version 300 es
precision highp float;
struct S { vec3 d; } a;
out vec4 o;
void main() {
if (a.d == vec3(2))
o = vec4(1);
})";
const char kExpected[] = R"(#version 300 es
struct _uS {
highp vec3 _ud;
} _ua;
out highp vec4 _uo;
void main(){
if ((_ua._ud == vec3(2.0, 2.0, 2.0)))
{
(_uo = vec4(1.0, 1.0, 1.0, 1.0));
}
}
)";
compile(kShader);
EXPECT_EQ(kExpected, outputCode(SH_ESSL_OUTPUT));
}
TEST_F(SeparateDeclarations, Structs)
{
const char kShader[] = R"(#version 300 es
precision highp float;
struct S { vec3 d; } a, b;
out vec4 o;
void main() {
if (a.d == b.d)
o = vec4(1);
})";
const char kExpected[] = R"(#version 300 es
struct _uS {
highp vec3 _ud;
} _ua;
_uS _ub;
out highp vec4 _uo;
void main(){
if ((_ua._ud == _ub._ud))
{
(_uo = vec4(1.0, 1.0, 1.0, 1.0));
}
}
)";
compile(kShader);
EXPECT_EQ(kExpected, outputCode(SH_ESSL_OUTPUT));
}
TEST_F(SeparateDeclarations, AnonymousStructNoChange)
{
const char kShader[] = R"(#version 300 es
precision highp float;
struct { vec3 d; } a;
out vec4 o;
void main() {
if (any(lessThan(a.d, vec3(2))))
o = vec4(1);
})";
const char kExpected[] = R"(#version 300 es
struct {
highp vec3 _ud;
} _ua;
out highp vec4 _uo;
void main(){
if (any(lessThan(_ua._ud, vec3(2.0, 2.0, 2.0))))
{
(_uo = vec4(1.0, 1.0, 1.0, 1.0));
}
}
)";
compile(kShader);
EXPECT_EQ(kExpected, outputCode(SH_ESSL_OUTPUT));
}
TEST_F(SeparateDeclarations, AnonymousStructs)
{
const char kShader[] = R"(#version 300 es
precision highp float;
struct { vec3 d; } a, b;
out vec4 o;
void main() {
if (any(lessThan(a.d, b.d)))
o = vec4(1);
})";
const char kExpected[] = R"(#version 300 es
struct sbbe {
highp vec3 _ud;
} _ua;
sbbe _ub;
out highp vec4 _uo;
void main(){
if (any(lessThan(_ua._ud, _ub._ud)))
{
(_uo = vec4(1.0, 1.0, 1.0, 1.0));
}
}
)";
compile(kShader);
EXPECT_EQ(kExpected, outputCode(SH_ESSL_OUTPUT));
}
TEST_F(SeparateCompoundStructDeclarations, AnonymousStruct)
{
const char kShader[] = R"(#version 300 es
precision highp float;
struct { vec3 d; } a;
out vec4 o;
void main() {
if (any(lessThan(a.d, vec3(2))))
o = vec4(1);
})";
const char kExpected[] = R"(#version 300 es
struct sbbd {
highp vec3 _ud;
};
sbbd _ua;
out highp vec4 _uo;
void main(){
if (any(lessThan(_ua._ud, vec3(2.0, 2.0, 2.0))))
{
(_uo = vec4(1.0, 1.0, 1.0, 1.0));
}
}
)";
compile(kShader);
EXPECT_EQ(kExpected, outputCode(SH_ESSL_OUTPUT));
}
TEST_F(SeparateCompoundStructDeclarations, AnonymousStructs)
{
const char kShader[] = R"(#version 300 es
precision highp float;
struct { vec3 d; } a, b;
out vec4 o;
void main() {
if (any(lessThan(a.d, b.d)))
o = vec4(1);
})";
const char kExpected[] = R"(#version 300 es
struct sbbe {
highp vec3 _ud;
};
sbbe _ua;
sbbe _ub;
out highp vec4 _uo;
void main(){
if (any(lessThan(_ua._ud, _ub._ud)))
{
(_uo = vec4(1.0, 1.0, 1.0, 1.0));
}
}
)";
compile(kShader);
EXPECT_EQ(kExpected, outputCode(SH_ESSL_OUTPUT));
}
TEST_F(SeparateCompoundStructDeclarations, Struct)
{
const char kShader[] = R"(#version 300 es
precision highp float;
struct S { vec3 d; } a, b;
out vec4 o;
void main() {
if (a.d == b.d)
o = vec4(1);
})";
const char kExpected[] = R"(#version 300 es
struct _uS {
highp vec3 _ud;
};
_uS _ua;
_uS _ub;
out highp vec4 _uo;
void main(){
if ((_ua._ud == _ub._ud))
{
(_uo = vec4(1.0, 1.0, 1.0, 1.0));
}
}
)";
compile(kShader);
EXPECT_EQ(kExpected, outputCode(SH_ESSL_OUTPUT));
}
TEST_F(SeparateCompoundStructDeclarations, ConstStruct)
{
const char kShader[] = R"(#version 300 es
precision highp float;
out vec4 o;
void main()
{
const struct s2 {
int i;
} s22 = s2(8);
const struct s1 {
s2 ss;
mat4 m;
} s11 = s1(s22, mat4(5));
if (s11.ss.i > int(o.x))
o = vec4(1);
})";
const char kExpected[] = R"(#version 300 es
out highp vec4 _uo;
void main(){
const mediump int sbc3 = 8;
if ((sbc3 > int(_uo.x)))
{
(_uo = vec4(1.0, 1.0, 1.0, 1.0));
}
}
)";
compile(kShader);
EXPECT_EQ(kExpected, outputCode(SH_ESSL_OUTPUT));
}
// Example of what the const struct does before evaluating constants.
TEST_F(SeparateCompoundStructDeclarations, ConstStructsCrossRef)
{
const char kShader[] = R"(#version 300 es
precision highp float;
out vec4 o;
void main()
{
struct s2 {
int i;
} s22 = s2(8);
struct s1 {
s2 ss;
mat4 m;
} s11 = s1(s22, mat4(5));
if (s11.ss.i > int(o.x))
o = vec4(1);
})";
const char kExpected[] = R"(#version 300 es
out highp vec4 _uo;
void main(){
struct _us2 {
mediump int _ui;
};
_us2 _us22 = _us2(8);
struct _us1 {
_us2 _uss;
highp mat4 _um;
};
_us1 _us11 = _us1(_us22, mat4(5.0, 0.0, 0.0, 0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 5.0));
if ((_us11._uss._ui > int(_uo.x)))
{
(_uo = vec4(1.0, 1.0, 1.0, 1.0));
}
}
)";
compile(kShader);
EXPECT_EQ(kExpected, outputCode(SH_ESSL_OUTPUT));
}
// Test that struct name validation takes into account that intenal symbol namespace
// is different to user namespace. The test should be kept in sync so that struct sbbf is the same
// textual symbol as what the anonymous struct gets.
TEST_F(SeparateCompoundStructDeclarations, InternalSymbolNoCrash)
{
const char kShader[] = R"(
precision highp float;
struct { vec4 e; } g;
struct sbbf { vec4 f; };
void main(){
sbbf s;
gl_FragColor = g.e + s.f;
})";
compile(kShader);
const char kExpected[] = R"(struct sbbf {
highp vec4 _ue;
};
sbbf _ug;
struct _usbbf {
highp vec4 _uf;
};
void main(){
_usbbf _us;
(gl_FragColor = (_ug._ue + _us._uf));
}
)";
EXPECT_EQ(kExpected, outputCode(SH_ESSL_OUTPUT));
}
TEST_F(SeparateStructFunctionDeclarations, StructInStruct)
{
const char kShader[] = R"(#version 300 es
struct S {
int f;
};
struct S2 { S h; } o()
{
return S2(S(1));
}
void main() {
S2 s2 = o();
})";
const char kExpected[] = R"(#version 300 es
struct _uS {
mediump int _uf;
};
struct _uS2 {
_uS _uh;
};
_uS2 _uo(){
return _uS2(_uS(1));
}
void main(){
_uS2 _us2 = _uo();
}
)";
compile(kShader);
EXPECT_EQ(kExpected, outputCode(SH_ESSL_OUTPUT));
}
TEST_F(SeparateStructFunctionDeclarations, StructInAnonymousStruct)
{
const char kShader[] = R"(#version 300 es
struct S {
int f;
};
struct { S h; } o();
void main() {
})";
const char kExpected[] = R"(#version 300 es
void main(){
}
)";
compile(kShader);
EXPECT_EQ(kExpected, outputCode(SH_ESSL_OUTPUT));
}
} // namespace