| // |
| // 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 |