1 //
2 // Copyright 2014 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 // CollectVariables_test.cpp:
7 // Some tests for shader inspection
8 //
9
10 #include "GLSLANG/ShaderLang.h"
11 #include "angle_gl.h"
12 #include "compiler/translator/Compiler.h"
13 #include "gtest/gtest.h"
14
15 namespace sh
16 {
17
18 class ShaderVariableTest : public testing::Test
19 {
20 public:
ShaderVariableTest()21 ShaderVariableTest() : mVariablesCompileOptions{}, mObjectCodeCompileOptions{}
22 {
23 mObjectCodeCompileOptions.objectCode = true;
24 }
25
26 protected:
27 ShCompileOptions mVariablesCompileOptions;
28 ShCompileOptions mObjectCodeCompileOptions;
29 };
30
TEST_F(ShaderVariableTest,FindInfoByMappedName)31 TEST_F(ShaderVariableTest, FindInfoByMappedName)
32 {
33 // struct A {
34 // float x[2];
35 // vec3 y;
36 // };
37 // struct B {
38 // A a[3];
39 // };
40 // B uni[2];
41 ShaderVariable uni(0, 2);
42 uni.name = "uni";
43 uni.mappedName = "m_uni";
44 uni.structOrBlockName = "B";
45 {
46 ShaderVariable a(0, 3);
47 a.name = "a";
48 a.mappedName = "m_a";
49 a.structOrBlockName = "A";
50 {
51 ShaderVariable x(GL_FLOAT, 2);
52 x.name = "x";
53 x.mappedName = "m_x";
54 a.fields.push_back(x);
55
56 ShaderVariable y(GL_FLOAT_VEC3);
57 y.name = "y";
58 y.mappedName = "m_y";
59 a.fields.push_back(y);
60 }
61 uni.fields.push_back(a);
62 }
63
64 const ShaderVariable *leafVar = nullptr;
65 std::string originalFullName;
66
67 std::string mappedFullName = "wrongName";
68 EXPECT_FALSE(uni.findInfoByMappedName(mappedFullName, &leafVar, &originalFullName));
69
70 mappedFullName = "m_uni";
71 EXPECT_TRUE(uni.findInfoByMappedName(mappedFullName, &leafVar, &originalFullName));
72 EXPECT_EQ(&uni, leafVar);
73 EXPECT_STREQ("uni", originalFullName.c_str());
74
75 mappedFullName = "m_uni[0].m_a[1].wrongName";
76 EXPECT_FALSE(uni.findInfoByMappedName(mappedFullName, &leafVar, &originalFullName));
77
78 mappedFullName = "m_uni[0].m_a[1].m_x";
79 EXPECT_TRUE(uni.findInfoByMappedName(mappedFullName, &leafVar, &originalFullName));
80 EXPECT_EQ(&(uni.fields[0].fields[0]), leafVar);
81 EXPECT_STREQ("uni[0].a[1].x", originalFullName.c_str());
82
83 mappedFullName = "m_uni[0].m_a[1].m_x[0]";
84 EXPECT_TRUE(uni.findInfoByMappedName(mappedFullName, &leafVar, &originalFullName));
85 EXPECT_EQ(&(uni.fields[0].fields[0]), leafVar);
86 EXPECT_STREQ("uni[0].a[1].x[0]", originalFullName.c_str());
87
88 mappedFullName = "m_uni[0].m_a[1].m_y";
89 EXPECT_TRUE(uni.findInfoByMappedName(mappedFullName, &leafVar, &originalFullName));
90 EXPECT_EQ(&(uni.fields[0].fields[1]), leafVar);
91 EXPECT_STREQ("uni[0].a[1].y", originalFullName.c_str());
92 }
93
TEST_F(ShaderVariableTest,IsSameUniformWithDifferentFieldOrder)94 TEST_F(ShaderVariableTest, IsSameUniformWithDifferentFieldOrder)
95 {
96 // struct A {
97 // float x;
98 // float y;
99 // };
100 // uniform A uni;
101 ShaderVariable vx_a;
102 vx_a.name = "uni";
103 vx_a.mappedName = "m_uni";
104 vx_a.structOrBlockName = "A";
105 {
106 ShaderVariable x(GL_FLOAT);
107 x.name = "x";
108 x.mappedName = "m_x";
109 vx_a.fields.push_back(x);
110
111 ShaderVariable y(GL_FLOAT);
112 y.name = "y";
113 y.mappedName = "m_y";
114 vx_a.fields.push_back(y);
115 }
116
117 // struct A {
118 // float y;
119 // float x;
120 // };
121 // uniform A uni;
122 ShaderVariable fx_a;
123 fx_a.name = "uni";
124 fx_a.mappedName = "m_uni";
125 fx_a.structOrBlockName = "A";
126 {
127 ShaderVariable y(GL_FLOAT);
128 y.name = "y";
129 y.mappedName = "m_y";
130 fx_a.fields.push_back(y);
131
132 ShaderVariable x(GL_FLOAT);
133 x.name = "x";
134 x.mappedName = "m_x";
135 fx_a.fields.push_back(x);
136 }
137
138 EXPECT_FALSE(vx_a.isSameUniformAtLinkTime(fx_a));
139 }
140
TEST_F(ShaderVariableTest,IsSameUniformWithDifferentStructNames)141 TEST_F(ShaderVariableTest, IsSameUniformWithDifferentStructNames)
142 {
143 // struct A {
144 // float x;
145 // float y;
146 // };
147 // uniform A uni;
148 ShaderVariable vx_a;
149 vx_a.name = "uni";
150 vx_a.mappedName = "m_uni";
151 vx_a.structOrBlockName = "A";
152 {
153 ShaderVariable x(GL_FLOAT);
154 x.name = "x";
155 x.mappedName = "m_x";
156 vx_a.fields.push_back(x);
157
158 ShaderVariable y(GL_FLOAT);
159 y.name = "y";
160 y.mappedName = "m_y";
161 vx_a.fields.push_back(y);
162 }
163
164 // struct B {
165 // float x;
166 // float y;
167 // };
168 // uniform B uni;
169 ShaderVariable fx_a;
170 fx_a.name = "uni";
171 fx_a.mappedName = "m_uni";
172 {
173 ShaderVariable x(GL_FLOAT);
174 x.name = "x";
175 x.mappedName = "m_x";
176 fx_a.fields.push_back(x);
177
178 ShaderVariable y(GL_FLOAT);
179 y.name = "y";
180 y.mappedName = "m_y";
181 fx_a.fields.push_back(y);
182 }
183
184 fx_a.structOrBlockName = "B";
185 EXPECT_FALSE(vx_a.isSameUniformAtLinkTime(fx_a));
186
187 fx_a.structOrBlockName = "A";
188 EXPECT_TRUE(vx_a.isSameUniformAtLinkTime(fx_a));
189
190 fx_a.structOrBlockName = "";
191 EXPECT_FALSE(vx_a.isSameUniformAtLinkTime(fx_a));
192 }
193
TEST_F(ShaderVariableTest,IsSameVaryingWithDifferentInvariance)194 TEST_F(ShaderVariableTest, IsSameVaryingWithDifferentInvariance)
195 {
196 // invariant varying float vary;
197 ShaderVariable vx;
198 vx.type = GL_FLOAT;
199 vx.precision = GL_MEDIUM_FLOAT;
200 vx.name = "vary";
201 vx.mappedName = "m_vary";
202 vx.staticUse = true;
203 vx.isInvariant = true;
204
205 // varying float vary;
206 ShaderVariable fx;
207 fx.type = GL_FLOAT;
208 fx.precision = GL_MEDIUM_FLOAT;
209 fx.name = "vary";
210 fx.mappedName = "m_vary";
211 fx.staticUse = true;
212 fx.isInvariant = false;
213
214 // Default to ESSL1 behavior: invariance must match
215 EXPECT_FALSE(vx.isSameVaryingAtLinkTime(fx));
216 EXPECT_FALSE(vx.isSameVaryingAtLinkTime(fx, 100));
217 // ESSL3 behavior: invariance doesn't need to match
218 EXPECT_TRUE(vx.isSameVaryingAtLinkTime(fx, 300));
219
220 // invariant varying float vary;
221 fx.isInvariant = true;
222 EXPECT_TRUE(vx.isSameVaryingAtLinkTime(fx));
223 EXPECT_TRUE(vx.isSameVaryingAtLinkTime(fx, 100));
224 EXPECT_TRUE(vx.isSameVaryingAtLinkTime(fx, 300));
225 }
226
227 // Test that using invariant varyings doesn't trigger a double delete.
TEST_F(ShaderVariableTest,InvariantDoubleDeleteBug)228 TEST_F(ShaderVariableTest, InvariantDoubleDeleteBug)
229 {
230 ShBuiltInResources resources;
231 sh::InitBuiltInResources(&resources);
232
233 ShHandle compiler = sh::ConstructCompiler(GL_VERTEX_SHADER, SH_GLES2_SPEC,
234 SH_GLSL_COMPATIBILITY_OUTPUT, &resources);
235 EXPECT_NE(static_cast<ShHandle>(0), compiler);
236
237 const char *program[] = {
238 "attribute vec4 position;\n"
239 "varying float v;\n"
240 "invariant v;\n"
241 "void main() {\n"
242 " v = 1.0;\n"
243 " gl_Position = position;\n"
244 "}"};
245
246 EXPECT_TRUE(sh::Compile(compiler, program, 1, mObjectCodeCompileOptions));
247 EXPECT_TRUE(sh::Compile(compiler, program, 1, mObjectCodeCompileOptions));
248 sh::Destruct(compiler);
249 }
250
TEST_F(ShaderVariableTest,IllegalInvariantVarying)251 TEST_F(ShaderVariableTest, IllegalInvariantVarying)
252 {
253 ShBuiltInResources resources;
254 sh::InitBuiltInResources(&resources);
255
256 ShHandle compiler = sh::ConstructCompiler(GL_VERTEX_SHADER, SH_GLES2_SPEC,
257 SH_GLSL_COMPATIBILITY_OUTPUT, &resources);
258 EXPECT_NE(static_cast<ShHandle>(0), compiler);
259
260 const char *program1[] = {
261 R"(void foo()
262 {
263 }
264 varying vec4 v_varying;
265 invariant v_varying;
266 void main()
267 {
268 foo();
269 gl_Position = v_varying;
270 })"};
271 const char *program2[] = {
272 "varying vec4 v_varying;\n"
273 "void foo() {\n"
274 " invariant v_varying;\n"
275 "}\n"
276 "void main() {\n"
277 " foo();\n"
278 " gl_Position = v_varying;\n"
279 "}"};
280
281 EXPECT_TRUE(sh::Compile(compiler, program1, 1, mVariablesCompileOptions));
282 EXPECT_FALSE(sh::Compile(compiler, program2, 1, mVariablesCompileOptions));
283 sh::Destruct(compiler);
284 }
285
TEST_F(ShaderVariableTest,InvariantLeakAcrossShaders)286 TEST_F(ShaderVariableTest, InvariantLeakAcrossShaders)
287 {
288 ShBuiltInResources resources;
289 sh::InitBuiltInResources(&resources);
290
291 ShHandle compiler = sh::ConstructCompiler(GL_VERTEX_SHADER, SH_GLES2_SPEC,
292 SH_GLSL_COMPATIBILITY_OUTPUT, &resources);
293 EXPECT_NE(static_cast<ShHandle>(0), compiler);
294
295 const char *program1[] = {
296 "varying vec4 v_varying;\n"
297 "invariant v_varying;\n"
298 "void main() {\n"
299 " gl_Position = v_varying;\n"
300 "}"};
301 const char *program2[] = {
302 "varying vec4 v_varying;\n"
303 "void main() {\n"
304 " gl_Position = v_varying;\n"
305 "}"};
306
307 EXPECT_TRUE(sh::Compile(compiler, program1, 1, mVariablesCompileOptions));
308 const std::vector<sh::ShaderVariable> *varyings = sh::GetOutputVaryings(compiler);
309 for (const sh::ShaderVariable &varying : *varyings)
310 {
311 if (varying.name == "v_varying")
312 {
313 EXPECT_TRUE(varying.isInvariant);
314 }
315 }
316 EXPECT_TRUE(sh::Compile(compiler, program2, 1, mVariablesCompileOptions));
317 varyings = sh::GetOutputVaryings(compiler);
318 for (const sh::ShaderVariable &varying : *varyings)
319 {
320 if (varying.name == "v_varying")
321 {
322 EXPECT_FALSE(varying.isInvariant);
323 }
324 }
325 sh::Destruct(compiler);
326 }
327
TEST_F(ShaderVariableTest,GlobalInvariantLeakAcrossShaders)328 TEST_F(ShaderVariableTest, GlobalInvariantLeakAcrossShaders)
329 {
330 ShBuiltInResources resources;
331 sh::InitBuiltInResources(&resources);
332
333 ShHandle compiler = sh::ConstructCompiler(GL_VERTEX_SHADER, SH_GLES2_SPEC,
334 SH_GLSL_COMPATIBILITY_OUTPUT, &resources);
335 EXPECT_NE(static_cast<ShHandle>(0), compiler);
336
337 const char *program1[] = {
338 "#pragma STDGL invariant(all)\n"
339 "varying vec4 v_varying;\n"
340 "void main() {\n"
341 " gl_Position = v_varying;\n"
342 "}"};
343 const char *program2[] = {
344 "varying vec4 v_varying;\n"
345 "void main() {\n"
346 " gl_Position = v_varying;\n"
347 "}"};
348
349 EXPECT_TRUE(sh::Compile(compiler, program1, 1, mVariablesCompileOptions));
350 const std::vector<sh::ShaderVariable> *varyings = sh::GetOutputVaryings(compiler);
351 for (const sh::ShaderVariable &varying : *varyings)
352 {
353 if (varying.name == "v_varying")
354 {
355 EXPECT_TRUE(varying.isInvariant);
356 }
357 }
358 EXPECT_TRUE(sh::Compile(compiler, program2, 1, mVariablesCompileOptions));
359 varyings = sh::GetOutputVaryings(compiler);
360 for (const sh::ShaderVariable &varying : *varyings)
361 {
362 if (varying.name == "v_varying")
363 {
364 EXPECT_FALSE(varying.isInvariant);
365 }
366 }
367 sh::Destruct(compiler);
368 }
369
TEST_F(ShaderVariableTest,BuiltinInvariantVarying)370 TEST_F(ShaderVariableTest, BuiltinInvariantVarying)
371 {
372 ShBuiltInResources resources;
373 sh::InitBuiltInResources(&resources);
374
375 ShHandle compiler = sh::ConstructCompiler(GL_VERTEX_SHADER, SH_GLES2_SPEC,
376 SH_GLSL_COMPATIBILITY_OUTPUT, &resources);
377 EXPECT_NE(static_cast<ShHandle>(0), compiler);
378
379 const char *program1[] = {
380 "invariant gl_Position;\n"
381 "void main() {\n"
382 " gl_Position = vec4(0, 0, 0, 0);\n"
383 "}"};
384 const char *program2[] = {
385 "void main() {\n"
386 " gl_Position = vec4(0, 0, 0, 0);\n"
387 "}"};
388 const char *program3[] = {
389 "void main() {\n"
390 " invariant gl_Position;\n"
391 " gl_Position = vec4(0, 0, 0, 0);\n"
392 "}"};
393
394 EXPECT_TRUE(sh::Compile(compiler, program1, 1, mVariablesCompileOptions));
395 const std::vector<sh::ShaderVariable> *varyings = sh::GetOutputVaryings(compiler);
396 for (const sh::ShaderVariable &varying : *varyings)
397 {
398 if (varying.name == "gl_Position")
399 {
400 EXPECT_TRUE(varying.isInvariant);
401 }
402 }
403 EXPECT_TRUE(sh::Compile(compiler, program2, 1, mVariablesCompileOptions));
404 varyings = sh::GetOutputVaryings(compiler);
405 for (const sh::ShaderVariable &varying : *varyings)
406 {
407 if (varying.name == "gl_Position")
408 {
409 EXPECT_FALSE(varying.isInvariant);
410 }
411 }
412 EXPECT_FALSE(sh::Compile(compiler, program3, 1, mVariablesCompileOptions));
413 sh::Destruct(compiler);
414 }
415
416 // Verify in ES3.1 two varyings with either same name or same declared location can match.
TEST_F(ShaderVariableTest,IsSameVaryingWithDifferentName)417 TEST_F(ShaderVariableTest, IsSameVaryingWithDifferentName)
418 {
419 // Varying float vary1;
420 ShaderVariable vx;
421 vx.type = GL_FLOAT;
422 vx.precision = GL_MEDIUM_FLOAT;
423 vx.name = "vary1";
424 vx.mappedName = "m_vary1";
425 vx.staticUse = true;
426 vx.isInvariant = false;
427
428 // Varying float vary2;
429 ShaderVariable fx;
430 fx.type = GL_FLOAT;
431 fx.precision = GL_MEDIUM_FLOAT;
432 fx.name = "vary2";
433 fx.mappedName = "m_vary2";
434 fx.staticUse = true;
435 fx.isInvariant = false;
436
437 // ESSL3 behavior: name must match
438 EXPECT_FALSE(vx.isSameVaryingAtLinkTime(fx, 300));
439
440 // ESSL3.1 behavior:
441 // [OpenGL ES 3.1 SPEC Chapter 7.4.1]
442 // An output variable is considered to match an input variable in the subsequent shader if:
443 // - the two variables match in name, type, and qualification; or
444 // - the two variables are declared with the same location qualifier and match in type and
445 // qualification.
446 vx.location = 0;
447 fx.location = 0;
448 EXPECT_TRUE(vx.isSameVaryingAtLinkTime(fx, 310));
449
450 fx.name = vx.name;
451 fx.mappedName = vx.mappedName;
452
453 fx.location = -1;
454 EXPECT_FALSE(vx.isSameVaryingAtLinkTime(fx, 310));
455
456 fx.location = 1;
457 EXPECT_FALSE(vx.isSameVaryingAtLinkTime(fx, 310));
458
459 fx.location = 0;
460 EXPECT_TRUE(vx.isSameVaryingAtLinkTime(fx, 310));
461 }
462
463 // Test that using two consecutive underscores (__) can be used for declaring an identifier
TEST_F(ShaderVariableTest,DoubleUnderscoresForIdentifier)464 TEST_F(ShaderVariableTest, DoubleUnderscoresForIdentifier)
465 {
466 ShBuiltInResources resources;
467 sh::InitBuiltInResources(&resources);
468
469 ShHandle compiler = sh::ConstructCompiler(GL_VERTEX_SHADER, SH_GLES3_SPEC,
470 SH_GLSL_COMPATIBILITY_OUTPUT, &resources);
471 EXPECT_NE(static_cast<ShHandle>(0), compiler);
472
473 const char *front_underscores[] = {
474 "#version 300 es\n"
475 "in vec4 __position;\n"
476 "out float v;\n"
477 "void main() {\n"
478 " v = 1.0;\n"
479 " gl_Position = __position;\n"
480 "}"};
481 EXPECT_TRUE(sh::Compile(compiler, front_underscores, 1, mObjectCodeCompileOptions));
482
483 const char *middle_underscores[] = {
484 "#version 300 es\n"
485 "in vec4 position__in;\n"
486 "out float v;\n"
487 "void main() {\n"
488 " v = 1.0;\n"
489 " gl_Position = position__in;\n"
490 "}"};
491 EXPECT_TRUE(sh::Compile(compiler, middle_underscores, 1, mObjectCodeCompileOptions));
492
493 const char *end_underscores[] = {
494 "#version 300 es\n"
495 "in vec4 position__;\n"
496 "out float v;\n"
497 "void main() {\n"
498 " v = 1.0;\n"
499 " gl_Position = position__;\n"
500 "}"};
501 EXPECT_TRUE(sh::Compile(compiler, end_underscores, 1, mObjectCodeCompileOptions));
502
503 sh::Destruct(compiler);
504 }
505
506 } // namespace sh
507