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