1 //
2 // Copyright 2024 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 // WGSLOutput_test.cpp:
7 // Tests for corect WGSL translations.
8 //
9
10 #include <regex>
11
12 #include "GLSLANG/ShaderLang.h"
13 #include "angle_gl.h"
14 #include "gtest/gtest.h"
15 #include "tests/test_utils/compiler_test.h"
16
17 using namespace sh;
18
19 class WGSLVertexOutputTest : public MatchOutputCodeTest
20 {
21 public:
WGSLVertexOutputTest()22 WGSLVertexOutputTest() : MatchOutputCodeTest(GL_VERTEX_SHADER, SH_WGSL_OUTPUT)
23 {
24 ShCompileOptions defaultCompileOptions = {};
25 defaultCompileOptions.validateAST = true;
26 setDefaultCompileOptions(defaultCompileOptions);
27 }
28 };
29
30 class WGSLOutputTest : public MatchOutputCodeTest
31 {
32 public:
WGSLOutputTest()33 WGSLOutputTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_WGSL_OUTPUT)
34 {
35 ShCompileOptions defaultCompileOptions = {};
36 defaultCompileOptions.validateAST = true;
37 setDefaultCompileOptions(defaultCompileOptions);
38 }
39 };
40
TEST_F(WGSLOutputTest,BasicTranslation)41 TEST_F(WGSLOutputTest, BasicTranslation)
42 {
43 const std::string &shaderString =
44 R"(#version 310 es
45 precision highp float;
46
47 out vec4 outColor;
48
49 struct Foo {
50 float x;
51 float y;
52 vec3 multiArray[2][3];
53 mat3 aMatrix;
54 };
55
56 vec4 doFoo(Foo foo, float zw);
57
58 vec4 doFoo(Foo foo, float zw)
59 {
60 // foo.x = foo.y;
61 return vec4(foo.x, foo.y, zw, zw);
62 }
63
64 Foo returnFoo(Foo foo) {
65 return foo;
66 }
67
68 float returnFloat(float x) {
69 return x;
70 }
71
72 float takeArgs(vec2 x, float y) {
73 return y;
74 }
75
76 void main()
77 {
78 Foo foo;
79 // Struct field accesses.
80 foo.x = 2.0;
81 foo.y = 2.0;
82 // Complicated constUnion should be emitted correctly.
83 foo.multiArray = vec3[][](
84 vec3[](
85 vec3(1.0, 2.0, 3.0),
86 vec3(1.0, 2.0, 3.0),
87 vec3(1.0, 2.0, 3.0)),
88 vec3[](
89 vec3(4.0, 5.0, 6.0),
90 vec3(4.0, 5.0, 6.0),
91 vec3(4.0, 5.0, 6.0)
92 )
93 );
94 int arrIndex = 1;
95 // Access an array index with a constant index.
96 float f = foo.multiArray[0][1].x;
97 // Access an array index with a non-const index, should clamp by default.
98 float f2 = foo.multiArray[0][arrIndex].x;
99 gl_FragDepth = f + f2;
100 doFoo(returnFoo(foo), returnFloat(3.0));
101 takeArgs(vec2(1.0, 2.0), foo.x);
102 returnFloat(doFoo(foo, 7.0 + 9.0).x);
103 outColor = vec4(0.0, 0.0, 0.0, 0.0);
104 })";
105 const std::string &outputString =
106 R"(struct ANGLE_Output_Global {
107 outColor : vec4<f32>,
108 gl_FragDepth_ : f32,
109 };
110
111 var<private> ANGLE_output_global : ANGLE_Output_Global;
112
113 struct ANGLE_Output_Annotated {
114 @location(@@@@@@) outColor : vec4<f32>,
115 @builtin(frag_depth) gl_FragDepth_ : f32,
116 };
117
118 ;
119
120 struct _uFoo
121 {
122 _ux : f32,
123 _uy : f32,
124 _umultiArray : array<array<vec3<f32>, 3>, 2>,
125 _uaMatrix : mat3x3<f32>,
126 };
127
128 fn _udoFoo(_ufoo : _uFoo, _uzw : f32) -> vec4<f32>;
129
130 fn _udoFoo(_ufoo : _uFoo, _uzw : f32) -> vec4<f32>
131 {
132 return vec4<f32>((_ufoo)._ux, (_ufoo)._uy, _uzw, _uzw);
133 }
134
135 fn _ureturnFoo(_ufoo : _uFoo) -> _uFoo
136 {
137 return _ufoo;
138 }
139
140 fn _ureturnFloat(_ux : f32) -> f32
141 {
142 return _ux;
143 }
144
145 fn _utakeArgs(_ux : vec2<f32>, _uy : f32) -> f32
146 {
147 return _uy;
148 }
149
150 fn _umain()
151 {
152 var _ufoo : _uFoo;
153 ((_ufoo)._ux) = (2.0f);
154 ((_ufoo)._uy) = (2.0f);
155 ((_ufoo)._umultiArray) = (array<array<vec3<f32>, 3>, 2>(array<vec3<f32>, 3>(vec3<f32>(1.0f, 2.0f, 3.0f), vec3<f32>(1.0f, 2.0f, 3.0f), vec3<f32>(1.0f, 2.0f, 3.0f)), array<vec3<f32>, 3>(vec3<f32>(4.0f, 5.0f, 6.0f), vec3<f32>(4.0f, 5.0f, 6.0f), vec3<f32>(4.0f, 5.0f, 6.0f))));
156 var _uarrIndex : i32 = (1i);
157 var _uf : f32 = (((((_ufoo)._umultiArray)[0i])[1i]).x);
158 var _uf2 : f32 = (((((_ufoo)._umultiArray)[0i])[clamp((_uarrIndex), 0, 2)]).x);
159 (ANGLE_output_global.gl_FragDepth_) = ((_uf) + (_uf2));
160 _udoFoo(_ureturnFoo(_ufoo), _ureturnFloat(3.0f));
161 _utakeArgs(vec2<f32>(1.0f, 2.0f), (_ufoo)._ux);
162 _ureturnFloat((_udoFoo(_ufoo, 16.0f)).x);
163 (ANGLE_output_global.outColor) = (vec4<f32>(0.0f, 0.0f, 0.0f, 0.0f));
164 }
165 @fragment
166 fn wgslMain() -> ANGLE_Output_Annotated
167 {
168 _umain();
169 var ANGLE_output_annotated : ANGLE_Output_Annotated;
170 ANGLE_output_annotated.outColor = ANGLE_output_global.outColor;
171 ANGLE_output_annotated.gl_FragDepth_ = ANGLE_output_global.gl_FragDepth_;
172 return ANGLE_output_annotated;
173 }
174 )";
175 compile(shaderString);
176 EXPECT_TRUE(foundInCode(outputString.c_str()));
177 }
178
TEST_F(WGSLOutputTest,ControlFlow)179 TEST_F(WGSLOutputTest, ControlFlow)
180 {
181 const std::string &shaderString =
182 R"(#version 300 es
183 precision highp float;
184
185 int ifElseDemo() {
186 int x = 5;
187 if (x == 5) {
188 return 6;
189 } else if (x == 6) {
190 return 7;
191 } else {
192 return 8;
193 }
194 }
195
196 void switchDemo() {
197 int x = 5;
198 switch (x) {
199 case 5:
200 case 6:
201 discard;
202 case 7: {
203 return;
204 }
205 case 8:
206 case 9:
207 {
208 x = 7;
209 }
210 return;
211 default:
212 return;
213 }
214 }
215
216 void forLoopDemo() {
217 for (int i = 0; i < 5; i++) {
218 if (i == 4) {
219 break;
220 } else if (i == 5) {
221 continue;
222 }
223 }
224 }
225
226 void whileLoopDemo() {
227 int i = 0;
228 while (i < 5) {
229 i++;
230 }
231
232 do {
233 i++;
234 } while (i < 5);
235 }
236
237 void main()
238 {
239 ifElseDemo();
240 switchDemo();
241 forLoopDemo();
242 whileLoopDemo();
243 })";
244 const std::string &outputString =
245 R"(fn _uifElseDemo() -> i32
246 {
247 var _ux : i32 = (5i);
248 if ((_ux) == (5i))
249 {
250 return 6i;
251 }
252 else
253 {
254 if ((_ux) == (6i))
255 {
256 return 7i;
257 }
258 else
259 {
260 return 8i;
261 }
262 }
263 }
264
265 fn _uswitchDemo()
266 {
267 var _ux : i32 = (5i);
268 switch _ux
269 {
270 case 5i, 6i:
271 {
272 discard;
273 }
274 case 7i:
275 {
276 {
277 return;
278 }
279 }
280 case 8i, 9i:
281 {
282 {
283 (_ux) = (7i);
284 }
285 return;
286 }
287 case default:
288 {
289 return;
290 }
291 }
292 }
293
294 fn _uforLoopDemo()
295 {
296 for (var _ui : i32 = (0i); (_ui) < (5i); (_ui)++)
297 {
298 if ((_ui) == (4i))
299 {
300 break;
301 }
302 else
303 {
304 if ((_ui) == (5i))
305 {
306 continue;
307 }
308 }
309 }
310 }
311
312 fn _uwhileLoopDemo()
313 {
314 var _ui : i32 = (0i);
315 while ((_ui) < (5i))
316 {
317 (_ui)++;
318 }
319 loop {
320 {
321 (_ui)++;
322 }
323 if (!((_ui) < (5i)) { break; }
324 }
325 }
326
327 fn _umain()
328 {
329 _uifElseDemo();
330 _uswitchDemo();
331 _uforLoopDemo();
332 _uwhileLoopDemo();
333 }
334 @fragment
335 fn wgslMain()
336 {
337 _umain();
338 }
339 )";
340 compile(shaderString);
341 EXPECT_TRUE(foundInCode(outputString.c_str()));
342 }
343
TEST_F(WGSLOutputTest,GLFragColorWithUniform)344 TEST_F(WGSLOutputTest, GLFragColorWithUniform)
345 {
346 const std::string &shaderString =
347 R"(
348 uniform mediump vec4 u_color;
349 void main(void)
350 {
351 gl_FragColor = u_color;
352 })";
353 const std::string &outputString =
354 R"(struct ANGLE_Output_Global {
355 gl_FragColor_ : vec4<f32>,
356 };
357
358 var<private> ANGLE_output_global : ANGLE_Output_Global;
359
360 struct ANGLE_Output_Annotated {
361 @location(0) gl_FragColor_ : vec4<f32>,
362 };
363
364 struct ANGLE_DefaultUniformBlock {
365 u_color : vec4<f32>,
366 };
367
368 @group(0) @binding(1) var<uniform> ANGLE_defaultUniformBlock : ANGLE_DefaultUniformBlock;
369 ;
370
371 fn _umain()
372 {
373 (ANGLE_output_global.gl_FragColor_) = (ANGLE_defaultUniformBlock.u_color);
374 }
375 @fragment
376 fn wgslMain() -> ANGLE_Output_Annotated
377 {
378 _umain();
379 var ANGLE_output_annotated : ANGLE_Output_Annotated;
380 ANGLE_output_annotated.gl_FragColor_ = ANGLE_output_global.gl_FragColor_;
381 return ANGLE_output_annotated;
382 }
383 )";
384 compile(shaderString);
385 EXPECT_TRUE(foundInCode(outputString.c_str()));
386 }
387
TEST_F(WGSLOutputTest,UniformsWithNestedStructs)388 TEST_F(WGSLOutputTest, UniformsWithNestedStructs)
389 {
390 const std::string &shaderString =
391 R"(precision mediump float;
392 struct NestedUniforms {
393 float x;
394 };
395 struct Uniforms {
396 NestedUniforms a;
397 float b;
398 float c;
399 float[5] d;
400 float e;
401 };
402 uniform Uniforms unis;
403 void main() {
404 gl_FragColor = vec4(unis.a.x, unis.b, unis.c, 1.0);
405 })";
406 const std::string &outputString =
407 R"(Output_Global {
408 gl_FragColor_ : vec4<f32>,
409 };
410
411 var<private> ANGLE_output_global : ANGLE_Output_Global;
412
413 struct ANGLE_Output_Annotated {
414 @location(0) gl_FragColor_ : vec4<f32>,
415 };
416
417 struct ANGLE_DefaultUniformBlock {
418 unis : _uUniforms,
419 };
420
421 @group(0) @binding(1) var<uniform> ANGLE_defaultUniformBlock : ANGLE_DefaultUniformBlock;
422 struct _uNestedUniforms
423 {
424 @align(16) _ux : f32,
425 };
426
427 struct _uUniforms
428 {
429 @align(16) _ua : _uNestedUniforms,
430 @align(16) _ub : f32,
431 _uc : f32,
432 @align(16) _ud : array<f32, 5>,
433 _ue : f32,
434 };
435
436 ;
437
438 fn _umain()
439 {
440 (ANGLE_output_global.gl_FragColor_) = (vec4<f32>(((ANGLE_defaultUniformBlock.unis)._ua)._ux, (ANGLE_defaultUniformBlock.unis)._ub, (ANGLE_defaultUniformBlock.unis)._uc, 1.0f));
441 }
442 @fragment
443 fn wgslMain() -> ANGLE_Output_Annotated
444 {
445 _umain();
446 var ANGLE_output_annotated : ANGLE_Output_Annotated;
447 ANGLE_output_annotated.gl_FragColor_ = ANGLE_output_global.gl_FragColor_;
448 return ANGLE_output_annotated;
449 })";
450 compile(shaderString);
451 EXPECT_TRUE(foundInCode(outputString.c_str()));
452 }
453