xref: /aosp_15_r20/external/angle/src/tests/compiler_tests/UnrollFlatten_test.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2015 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // UnrollFlatten_test.cpp:
7 //   Test for the outputting of [[unroll]] and [[flatten]] for the D3D compiler.
8 //   This test can only be enabled when HLSL support is enabled.
9 //
10 
11 #include "GLSLANG/ShaderLang.h"
12 #include "angle_gl.h"
13 #include "common/angleutils.h"
14 #include "gtest/gtest.h"
15 #include "tests/test_utils/compiler_test.h"
16 
17 using namespace sh;
18 
19 namespace
20 {
21 
22 class UnrollFlattenTest : public testing::Test
23 {
24   public:
UnrollFlattenTest()25     UnrollFlattenTest() : mInputSpec(SH_GLES2_SPEC) {}
UnrollFlattenTest(ShShaderSpec inputSpec)26     UnrollFlattenTest(ShShaderSpec inputSpec) : mInputSpec(inputSpec) {}
27 
28   protected:
compile(const std::string & shaderString)29     void compile(const std::string &shaderString)
30     {
31         ShCompileOptions compileOptions = {};
32 
33         std::string infoLog;
34         bool compilationSuccess =
35             compileTestShader(GL_FRAGMENT_SHADER, mInputSpec, SH_HLSL_4_1_OUTPUT, shaderString,
36                               compileOptions, &mTranslatedSource, &infoLog);
37         if (!compilationSuccess)
38         {
39             FAIL() << "Shader compilation failed " << infoLog;
40         }
41         // Ignore the beginning of the shader to avoid the definitions of LOOP and FLATTEN
42         mCurrentPosition = static_cast<int>(mTranslatedSource.find("cbuffer DriverConstants"));
43     }
44 
expect(const char * patterns[],size_t count)45     void expect(const char *patterns[], size_t count)
46     {
47         const char *badPatterns[] = {UNROLL, FLATTEN};
48         for (size_t i = 0; i < count; i++)
49         {
50             const char *pattern = patterns[i];
51             auto position       = mTranslatedSource.find(pattern, mCurrentPosition);
52             if (position == std::string::npos)
53             {
54                 FAIL() << "Couldn't find '" << pattern << "' after expectations '"
55                        << mExpectationList << "' in translated source:\n"
56                        << mTranslatedSource;
57             }
58 
59             for (size_t j = 0; j < ArraySize(badPatterns); j++)
60             {
61                 const char *badPattern = badPatterns[j];
62                 if (pattern != badPattern &&
63                     mTranslatedSource.find(badPattern, mCurrentPosition) < position)
64                 {
65                     FAIL() << "Found '" << badPattern << "' before '" << pattern
66                            << "' after expectations '" << mExpectationList
67                            << "' in translated source:\n"
68                            << mTranslatedSource;
69                 }
70             }
71             mExpectationList += " - " + std::string(pattern);
72             mCurrentPosition = static_cast<int>(position) + 1;
73         }
74     }
75 
76     static const char *UNROLL;
77     static const char *FLATTEN;
78 
79   private:
80     ShShaderSpec mInputSpec;
81     std::string mTranslatedSource;
82 
83     int mCurrentPosition;
84     std::string mExpectationList;
85 };
86 
87 const char *UnrollFlattenTest::UNROLL  = "LOOP";
88 const char *UnrollFlattenTest::FLATTEN = "FLATTEN";
89 
90 // Check that the nothing is added if there is no gradient operation
91 // even when there is ifs and discontinuous loops
TEST_F(UnrollFlattenTest,NoGradient)92 TEST_F(UnrollFlattenTest, NoGradient)
93 {
94     const std::string &shaderString =
95         "precision mediump float;\n"
96         "uniform float f;\n"
97         "float fun(float a){\n"           // 1
98         "    if (a > 1.0) {return f;}\n"  // 2
99         "    else {return a + 1.0;}\n"
100         "}\n"
101         "float fun2(float a){\n"                // 3
102         "    for (int i = 0; i < 10; i++) {\n"  // 4
103         "        if (a > 1.0) {break;}\n"       // 5
104         "        a = fun(a);\n"                 // 6
105         "    }\n"
106         "    return a;\n"
107         "}\n"
108         "void main() {\n"
109         "    float accum = 0.0;\n"
110         "    if (f < 5.0) {accum = fun2(accum);}\n"  // 7
111         "    gl_FragColor = vec4(accum);\n"
112         "}\n";
113     compile(shaderString);
114     // 1 - shouldn't get a Lod0 version generated
115     // 2 - no FLATTEN because does not contain discont loop
116     // 3 - shouldn't get a Lod0 version generated
117     // 4 - no LOOP because discont, and also no gradient
118     // 5 - no FLATTEN because does not contain loop with a gradient
119     // 6 - call non-Lod0 version
120     // 7 - no FLATTEN
121     const char *expectations[] = {"fun(",  "if",   "fun2(", "for", "if",
122                                   "break", "fun(", "main(", "if",  "fun2("};
123     expect(expectations, ArraySize(expectations));
124 }
125 
126 // Check that when we have a gradient in a non-discontinuous loop
127 // we use the regular version of the functions. Also checks that
128 // LOOP is generated for the loop containing the gradient.
TEST_F(UnrollFlattenTest,GradientNotInDiscont)129 TEST_F(UnrollFlattenTest, GradientNotInDiscont)
130 {
131     const std::string &shaderString =
132         "precision mediump float;\n"
133         "uniform float f;\n"
134         "uniform sampler2D tex;"
135         "float fun(float a){\n"                         // 1
136         "    return texture2D(tex, vec2(0.5, f)).x;\n"  // 2
137         "}\n"
138         "float fun2(float a){\n"                        // 3
139         "    for (int i = 0; i < 10; i++) {\n"          // 4
140         "        if (a > 1.0) {}\n"                     // 5
141         "        a = fun(a);\n"                         // 6
142         "        a += texture2D(tex, vec2(a, 0.0)).x;"  // 7
143         "    }\n"
144         "    return a;\n"
145         "}\n"
146         "void main() {\n"
147         "    float accum = 0.0;\n"
148         "    if (f < 5.0) {accum = fun2(accum);}\n"  // 8
149         "    gl_FragColor = vec4(accum);\n"
150         "}\n";
151     // 1 - shouldn't get a Lod0 version generated
152     // 2 - no Lod0 version generated
153     // 3 - shouldn't get a Lod0 version generated (not in discont loop)
154     // 4 - should have LOOP because it contains a gradient operation (even if Lod0)
155     // 5 - no FLATTEN because doesn't contain loop with a gradient
156     // 6 - call non-Lod0 version
157     // 7 - call non-Lod0 version
158     // 8 - FLATTEN because it contains a loop with a gradient
159     compile(shaderString);
160     const char *expectations[] = {"fun(", "texture2D(", "fun2(", "LOOP",    "for", "if",
161                                   "fun(", "texture2D(", "main(", "FLATTEN", "if",  "fun2("};
162     expect(expectations, ArraySize(expectations));
163 }
164 
165 // Check that when we have a gradient in a discontinuous loop
166 // we use the Lod0 version of the functions.
TEST_F(UnrollFlattenTest,GradientInDiscont)167 TEST_F(UnrollFlattenTest, GradientInDiscont)
168 {
169     const std::string &shaderString =
170         "precision mediump float;\n"
171         "uniform float f;\n"
172         "uniform sampler2D tex;"
173         "float fun(float a){\n"                         // 1
174         "    return texture2D(tex, vec2(0.5, f)).x;\n"  // 2
175         "}\n"
176         "float fun2(float a){\n"                        // 3
177         "    for (int i = 0; i < 10; i++) {\n"          // 4
178         "        if (a > 1.0) {break;}\n"               // 5
179         "        a = fun(a);\n"                         // 6
180         "        a += texture2D(tex, vec2(a, 0.0)).x;"  // 7
181         "    }\n"
182         "    return a;\n"
183         "}\n"
184         "void main() {\n"
185         "    float accum = 0.0;\n"
186         "    if (f < 5.0) {accum = fun2(accum);}\n"  // 8
187         "    gl_FragColor = vec4(accum);\n"
188         "}\n";
189     // 1 - should get a Lod0 version generated (gradient + discont loop)
190     // 2 - will get the Lod0 if in funLod0
191     // 3 - shouldn't get a Lod0 version generated (not in discont loop)
192     // 4 - should have LOOP because it contains a gradient operation (even if Lod0)
193     // 5 - no FLATTEN because doesn't contain a loop with a gradient
194     // 6 - call Lod0 version
195     // 7 - call Lod0 version
196     // 8 - FLATTEN because it contains a loop with a gradient
197     compile(shaderString);
198     const char *expectations[] = {
199         "fun(",  "texture2D(", "funLod0(",      "texture2DLod0(", "fun2(",   "LOOP", "for",  "if",
200         "break", "funLod0(",   "texture2DLod0", "main(",          "FLATTEN", "if",   "fun2("};
201     expect(expectations, ArraySize(expectations));
202 }
203 
204 class UnrollFlattenTestES3 : public UnrollFlattenTest
205 {
206   public:
UnrollFlattenTestES3()207     UnrollFlattenTestES3() : UnrollFlattenTest(SH_GLES3_SPEC) {}
208 };
209 
210 // Check that we correctly detect the ES3 builtin "texture" function as a gradient operation.
TEST_F(UnrollFlattenTestES3,TextureBuiltin)211 TEST_F(UnrollFlattenTestES3, TextureBuiltin)
212 {
213     const std::string &shaderString =
214         "#version 300 es\n"
215         "precision mediump float;\n"
216         "uniform sampler2D tex;"
217         "out float fragColor;\n"
218         "void main() {\n"
219         "    float a = 0.0;"
220         "    for (int i = 0; i < 10; i++) {\n"
221         "        if (a > 1.0) {break;}\n"
222         "        a += texture(tex, vec2(a, 0.0)).x;"
223         "    }\n"
224         "    fragColor = a;\n"
225         "}\n";
226 
227     compile(shaderString);
228     const char *expectations[] = {"main(", "LOOP", "Lod0("};
229     expect(expectations, ArraySize(expectations));
230 }
231 }  // namespace
232