1 //
2 // Copyright 2017 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 // HLSLOutput_test.cpp:
7 // Tests for HLSL output.
8 //
9
10 #include <regex>
11 #include "GLSLANG/ShaderLang.h"
12 #include "angle_gl.h"
13 #include "gtest/gtest.h"
14 #include "tests/test_utils/compiler_test.h"
15
16 using namespace sh;
17
18 class HLSLOutputTest : public MatchOutputCodeTest
19 {
20 public:
HLSLOutputTest()21 HLSLOutputTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_HLSL_4_1_OUTPUT) {}
22 };
23
24 class HLSL30VertexOutputTest : public MatchOutputCodeTest
25 {
26 public:
HLSL30VertexOutputTest()27 HLSL30VertexOutputTest() : MatchOutputCodeTest(GL_VERTEX_SHADER, SH_HLSL_3_0_OUTPUT) {}
28 };
29
30 class HLSL41VertexOutputTest : public MatchOutputCodeTest
31 {
32 public:
HLSL41VertexOutputTest()33 HLSL41VertexOutputTest() : MatchOutputCodeTest(GL_VERTEX_SHADER, SH_HLSL_4_1_OUTPUT) {}
34 };
35
36 // Test that having dynamic indexing of a vector inside the right hand side of logical or doesn't
37 // trigger asserts in HLSL output.
TEST_F(HLSLOutputTest,DynamicIndexingOfVectorOnRightSideOfLogicalOr)38 TEST_F(HLSLOutputTest, DynamicIndexingOfVectorOnRightSideOfLogicalOr)
39 {
40 const std::string &shaderString =
41 "#version 300 es\n"
42 "precision highp float;\n"
43 "out vec4 my_FragColor;\n"
44 "uniform int u1;\n"
45 "void main() {\n"
46 " bvec4 v = bvec4(true, true, true, false);\n"
47 " my_FragColor = vec4(v[u1 + 1] || v[u1]);\n"
48 "}\n";
49 compile(shaderString);
50 }
51
52 // Test that rewriting else blocks in a function that returns a struct doesn't use the struct name
53 // without a prefix.
TEST_F(HLSL30VertexOutputTest,RewriteElseBlockReturningStruct)54 TEST_F(HLSL30VertexOutputTest, RewriteElseBlockReturningStruct)
55 {
56 const std::string &shaderString =
57 "struct foo\n"
58 "{\n"
59 " float member;\n"
60 "};\n"
61 "uniform bool b;\n"
62 "foo getFoo()\n"
63 "{\n"
64 " if (b)\n"
65 " {\n"
66 " return foo(0.0);\n"
67 " }\n"
68 " else\n"
69 " {\n"
70 " return foo(1.0);\n"
71 " }\n"
72 "}\n"
73 "void main()\n"
74 "{\n"
75 " gl_Position = vec4(getFoo().member);\n"
76 "}\n";
77 compile(shaderString);
78 EXPECT_TRUE(foundInCode("_foo"));
79 EXPECT_FALSE(foundInCode("(foo)"));
80 EXPECT_FALSE(foundInCode(" foo"));
81 }
82
83 // Regression test for RemoveDynamicIndexing transformation producing invalid AST, based on fuzzer
84 // test.
TEST_F(HLSL41VertexOutputTest,RemoveDynamicingIndexIndexPrecisionBug)85 TEST_F(HLSL41VertexOutputTest, RemoveDynamicingIndexIndexPrecisionBug)
86 {
87 const char shaderString[] = R"(void main()
88 {
89 mat3 tmp;
90 vec3 res = vec3(0);
91 for (int i = 0; res += 0., ivec3(0)[i], ivec3(tmp)[i], i < 0;);
92 })";
93 compile(shaderString);
94 }
95
96 // Test that having an array constructor as a statement doesn't trigger an assert in HLSL output.
97 // This test has a constant array constructor statement.
TEST_F(HLSLOutputTest,ConstArrayConstructorStatement)98 TEST_F(HLSLOutputTest, ConstArrayConstructorStatement)
99 {
100 const std::string &shaderString =
101 R"(#version 300 es
102 void main()
103 {
104 int[1](0);
105 })";
106 compile(shaderString);
107 }
108
109 // Test that having an array constructor as a statement doesn't trigger an assert in HLSL output.
TEST_F(HLSLOutputTest,ArrayConstructorStatement)110 TEST_F(HLSLOutputTest, ArrayConstructorStatement)
111 {
112 const std::string &shaderString =
113 R"(#version 300 es
114 precision mediump float;
115 out vec4 outColor;
116 void main()
117 {
118 outColor = vec4(0.0, 0.0, 0.0, 1.0);
119 float[1](outColor[1]++);
120 })";
121 compile(shaderString);
122 }
123
124 // Test an array of arrays constructor as a statement.
TEST_F(HLSLOutputTest,ArrayOfArraysStatement)125 TEST_F(HLSLOutputTest, ArrayOfArraysStatement)
126 {
127 const std::string &shaderString =
128 R"(#version 310 es
129 precision mediump float;
130 out vec4 outColor;
131 void main()
132 {
133 outColor = vec4(0.0, 0.0, 0.0, 1.0);
134 float[2][2](float[2](outColor[1]++, 0.0), float[2](1.0, 2.0));
135 })";
136 compile(shaderString);
137 }
138
139 // Test dynamic indexing of a vector. This makes sure that helper functions added for dynamic
140 // indexing have correct data that subsequent traversal steps rely on.
TEST_F(HLSLOutputTest,VectorDynamicIndexing)141 TEST_F(HLSLOutputTest, VectorDynamicIndexing)
142 {
143 const std::string &shaderString =
144 R"(#version 300 es
145 precision mediump float;
146 out vec4 outColor;
147 uniform int i;
148 void main()
149 {
150 vec4 foo = vec4(0.0, 0.0, 0.0, 1.0);
151 foo[i] = foo[i + 1];
152 outColor = foo;
153 })";
154 compile(shaderString);
155 }
156
157 // Test returning an array from a user-defined function. This makes sure that function symbols are
158 // changed consistently when the user-defined function is changed to have an array out parameter.
TEST_F(HLSLOutputTest,ArrayReturnValue)159 TEST_F(HLSLOutputTest, ArrayReturnValue)
160 {
161 const std::string &shaderString =
162 R"(#version 300 es
163 precision mediump float;
164 uniform float u;
165 out vec4 outColor;
166
167 float[2] getArray(float f)
168 {
169 return float[2](f, f + 1.0);
170 }
171
172 void main()
173 {
174 float[2] arr = getArray(u);
175 outColor = vec4(arr[0], arr[1], 0.0, 1.0);
176 })";
177 compile(shaderString);
178 }
179
180 // Test that writing parameters without a name doesn't assert.
TEST_F(HLSLOutputTest,ParameterWithNoName)181 TEST_F(HLSLOutputTest, ParameterWithNoName)
182 {
183 const std::string &shaderString =
184 R"(precision mediump float;
185
186 uniform vec4 v;
187
188 vec4 s(vec4)
189 {
190 return v;
191 }
192 void main()
193 {
194 gl_FragColor = s(v);
195 })";
196 compile(shaderString);
197 }
198
199 // Test that array dimensions are written out correctly.
TEST_F(HLSLOutputTest,Array)200 TEST_F(HLSLOutputTest, Array)
201 {
202 const std::string &shaderString =
203 R"(#version 300 es
204 precision mediump float;
205
206 uniform float uf;
207
208 out vec4 my_FragColor;
209
210 void main()
211 {
212 my_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
213 float arr[2];
214 for (int i = 0; i < 2; ++i) {
215 arr[i] = uf * 2.0;
216 my_FragColor.x += arr[i];
217 }
218 })";
219 compile(shaderString);
220 EXPECT_TRUE(foundInCodeRegex(std::regex("_arr(\\d)*\\[2\\]")));
221 }
222
223 // Test that initializing array with previously declared array will not be overwritten
TEST_F(HLSLOutputTest,SameNameArray)224 TEST_F(HLSLOutputTest, SameNameArray)
225 {
226 const std::string &shaderString =
227 R"(#version 300 es
228 precision highp float;
229 out vec4 my_FragColor;
230
231 void main()
232 {
233 float arr[2] = float[2](1.0, 1.0);
234 {
235 float arr[2] = arr;
236 my_FragColor = vec4(0.0, arr[0], 0.0, arr[1]);
237 }
238 })";
239 compile(shaderString);
240 // There should be two different arr defined, e.g. _arr1000 and _arr1001
241 // Use Workaround for now.
242 // Once the build team fixes libc++ we could use the following one line solution instead.
243 // EXPECT_TRUE(foundInCodeRegex(std::regex("_arr(\\d*)\\[2\\](.|\\r|\\n)*_arr(?!\\1)\\d*\\[2\\]")));
244 std::smatch m;
245 EXPECT_TRUE(foundInCodeRegex(std::regex("_arr(\\d)*\\[2\\]"), &m));
246 EXPECT_TRUE(m.size() == 2);
247 EXPECT_TRUE(m[0].str() != m[1].str());
248 }
249
250 // Test that passing a non-struct member of a std140 structure to a function won't trigger the
251 // struct mapping.
TEST_F(HLSLOutputTest,NonStructMemberAsFunctionArgument)252 TEST_F(HLSLOutputTest, NonStructMemberAsFunctionArgument)
253 {
254 constexpr char shaderString[] = R"(#version 300 es
255 precision highp float;
256 out vec4 my_FragColor;
257
258 struct InstancingData
259 {
260 vec4 data;
261 };
262
263 layout(std140) uniform InstanceBlock
264 {
265 InstancingData instances[8];
266 };
267
268 void main()
269 {
270 int index = int(gl_FragCoord.x);
271 float result = dot(instances[index].data, vec4(1.0, 1.0, 1.0, 1.0));
272 my_FragColor = vec4(result, 0.0, 0.0, 1.0);
273 })";
274
275 compile(shaderString);
276 EXPECT_FALSE(foundInCode("map_instances"));
277 }
278