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