blob: 2ae0de3112379b41513479bf01d08d8075d85f96 [file] [log] [blame]
Jamie Madilleb1a0102013-07-08 13:31:38 -04001//
2// Copyright (c) 2002-2013 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#include <sstream>
7#include <string>
8#include <vector>
9#include "GLSLANG/ShaderLang.h"
10#include "gtest/gtest.h"
11
12#define SHADER(Src) #Src
13
14class ExpressionLimitTest : public testing::Test {
15protected:
16 static const int kMaxExpressionComplexity = 16;
17 static const int kMaxCallStackDepth = 16;
18 static const char* kExpressionTooComplex;
19 static const char* kCallStackTooDeep;
20 static const char* kHasRecursion;
21
22 virtual void SetUp()
23 {
24 memset(&resources, 0, sizeof(resources));
25
Jamie Madilleb1a0102013-07-08 13:31:38 -040026 GenerateResources(&resources);
27 }
28
Jamie Madilleb1a0102013-07-08 13:31:38 -040029 // Set up the per compile resources
30 void GenerateResources(ShBuiltInResources* resources)
31 {
32 ShInitBuiltInResources(resources);
33
34 resources->MaxVertexAttribs = 8;
35 resources->MaxVertexUniformVectors = 128;
36 resources->MaxVaryingVectors = 8;
37 resources->MaxVertexTextureImageUnits = 0;
38 resources->MaxCombinedTextureImageUnits = 8;
39 resources->MaxTextureImageUnits = 8;
40 resources->MaxFragmentUniformVectors = 16;
41 resources->MaxDrawBuffers = 1;
42
43 resources->OES_standard_derivatives = 0;
44 resources->OES_EGL_image_external = 0;
45
46 resources->MaxExpressionComplexity = kMaxExpressionComplexity;
47 resources->MaxCallStackDepth = kMaxCallStackDepth;
48 }
49
50 void GenerateLongExpression(int length, std::stringstream* ss)
51 {
52 for (int ii = 0; ii < length; ++ii) {
53 *ss << "+ vec4(" << ii << ")";
54 }
55 }
56
57 std::string GenerateShaderWithLongExpression(int length)
58 {
59 static const char* shaderStart = SHADER(
60 precision mediump float;
61 uniform vec4 u_color;
62 void main()
63 {
64 gl_FragColor = u_color
65 );
66
67 std::stringstream ss;
68 ss << shaderStart;
69 GenerateLongExpression(length, &ss);
70 ss << "; }";
71
72 return ss.str();
73 }
74
75 std::string GenerateShaderWithUnusedLongExpression(int length)
76 {
77 static const char* shaderStart = SHADER(
78 precision mediump float;
79 uniform vec4 u_color;
80 void main()
81 {
82 gl_FragColor = u_color;
83 }
84 vec4 someFunction() {
85 return u_color
86 );
87
88 std::stringstream ss;
89
90 ss << shaderStart;
91 GenerateLongExpression(length, &ss);
92 ss << "; }";
93
94 return ss.str();
95 }
96
97 void GenerateDeepFunctionStack(int length, std::stringstream* ss)
98 {
99 static const char* shaderStart = SHADER(
100 precision mediump float;
101 uniform vec4 u_color;
102 vec4 function0() {
103 return u_color;
104 }
105 );
106
107 *ss << shaderStart;
108 for (int ii = 0; ii < length; ++ii) {
109 *ss << "vec4 function" << (ii + 1) << "() {\n"
110 << " return function" << ii << "();\n"
111 << "}\n";
112 }
113 }
114
115 std::string GenerateShaderWithDeepFunctionStack(int length)
116 {
117 std::stringstream ss;
118
119 GenerateDeepFunctionStack(length, &ss);
120
121 ss << "void main() {\n"
122 << " gl_FragColor = function" << length << "();\n"
123 << "}";
124
125 return ss.str();
126 }
127
128 std::string GenerateShaderWithUnusedDeepFunctionStack(int length)
129 {
130 std::stringstream ss;
131
132 GenerateDeepFunctionStack(length, &ss);
133
134 ss << "void main() {\n"
135 << " gl_FragColor = vec4(0,0,0,0);\n"
136 << "}";
137
138
139 return ss.str();
140 }
141
142 // Compiles a shader and if there's an error checks for a specific
143 // substring in the error log. This way we know the error is specific
144 // to the issue we are testing.
145 bool CheckShaderCompilation(ShHandle compiler,
146 const char* source,
147 int compileOptions,
148 const char* expected_error) {
Geoff Lang0049e432013-10-07 17:07:33 -0400149 bool success = ShCompile(compiler, &source, 1, compileOptions) != 0;
Jamie Madilleb1a0102013-07-08 13:31:38 -0400150 if (success) {
151 success = !expected_error;
152 } else {
153 size_t bufferLen = 0;
154 ShGetInfo(compiler, SH_INFO_LOG_LENGTH, &bufferLen);
155 char* buffer(new char [bufferLen]);
156 ShGetInfoLog(compiler, buffer);
157 std::string log(buffer, buffer + bufferLen);
158 delete [] buffer;
159 if (expected_error)
160 success = log.find(expected_error) != std::string::npos;
161
162 EXPECT_TRUE(success) << log << "\n----shader----\n" << source;
163 }
164 return success;
165 }
166
167 ShBuiltInResources resources;
168};
169
170const char* ExpressionLimitTest::kExpressionTooComplex =
171 "Expression too complex";
172const char* ExpressionLimitTest::kCallStackTooDeep =
173 "call stack too deep";
174const char* ExpressionLimitTest::kHasRecursion =
175 "Function recursion detected";
176
177TEST_F(ExpressionLimitTest, ExpressionComplexity)
178{
179 ShShaderSpec spec = SH_WEBGL_SPEC;
180 ShShaderOutput output = SH_ESSL_OUTPUT;
181 ShHandle vertexCompiler = ShConstructCompiler(
182 SH_FRAGMENT_SHADER, spec, output, &resources);
183 int compileOptions = SH_LIMIT_EXPRESSION_COMPLEXITY;
184
185 // Test expression under the limit passes.
186 EXPECT_TRUE(CheckShaderCompilation(
187 vertexCompiler,
188 GenerateShaderWithLongExpression(
189 kMaxExpressionComplexity - 10).c_str(),
190 compileOptions, NULL));
191 // Test expression over the limit fails.
192 EXPECT_TRUE(CheckShaderCompilation(
193 vertexCompiler,
194 GenerateShaderWithLongExpression(
195 kMaxExpressionComplexity + 10).c_str(),
196 compileOptions, kExpressionTooComplex));
197 // Test expression over the limit without a limit does not fail.
198 EXPECT_TRUE(CheckShaderCompilation(
199 vertexCompiler,
200 GenerateShaderWithLongExpression(
201 kMaxExpressionComplexity + 10).c_str(),
202 compileOptions & ~SH_LIMIT_EXPRESSION_COMPLEXITY, NULL));
203}
204
205TEST_F(ExpressionLimitTest, UnusedExpressionComplexity)
206{
207 ShShaderSpec spec = SH_WEBGL_SPEC;
208 ShShaderOutput output = SH_ESSL_OUTPUT;
209 ShHandle vertexCompiler = ShConstructCompiler(
210 SH_FRAGMENT_SHADER, spec, output, &resources);
211 int compileOptions = SH_LIMIT_EXPRESSION_COMPLEXITY;
212
213 // Test expression under the limit passes.
214 EXPECT_TRUE(CheckShaderCompilation(
215 vertexCompiler,
216 GenerateShaderWithUnusedLongExpression(
217 kMaxExpressionComplexity - 10).c_str(),
218 compileOptions, NULL));
219 // Test expression over the limit fails.
220 EXPECT_TRUE(CheckShaderCompilation(
221 vertexCompiler,
222 GenerateShaderWithUnusedLongExpression(
223 kMaxExpressionComplexity + 10).c_str(),
224 compileOptions, kExpressionTooComplex));
225 // Test expression over the limit without a limit does not fail.
226 EXPECT_TRUE(CheckShaderCompilation(
227 vertexCompiler,
228 GenerateShaderWithUnusedLongExpression(
229 kMaxExpressionComplexity + 10).c_str(),
230 compileOptions & ~SH_LIMIT_EXPRESSION_COMPLEXITY, NULL));
231}
232
233TEST_F(ExpressionLimitTest, CallStackDepth)
234{
235 ShShaderSpec spec = SH_WEBGL_SPEC;
236 ShShaderOutput output = SH_ESSL_OUTPUT;
237 ShHandle vertexCompiler = ShConstructCompiler(
238 SH_FRAGMENT_SHADER, spec, output, &resources);
239 int compileOptions = SH_LIMIT_CALL_STACK_DEPTH;
240
241 // Test call stack under the limit passes.
242 EXPECT_TRUE(CheckShaderCompilation(
243 vertexCompiler,
244 GenerateShaderWithDeepFunctionStack(
245 kMaxCallStackDepth - 10).c_str(),
246 compileOptions, NULL));
247 // Test call stack over the limit fails.
248 EXPECT_TRUE(CheckShaderCompilation(
249 vertexCompiler,
250 GenerateShaderWithDeepFunctionStack(
251 kMaxCallStackDepth + 10).c_str(),
252 compileOptions, kCallStackTooDeep));
253 // Test call stack over the limit without limit does not fail.
254 EXPECT_TRUE(CheckShaderCompilation(
255 vertexCompiler,
256 GenerateShaderWithDeepFunctionStack(
257 kMaxCallStackDepth + 10).c_str(),
258 compileOptions & ~SH_LIMIT_CALL_STACK_DEPTH, NULL));
259}
260
261TEST_F(ExpressionLimitTest, UnusedCallStackDepth)
262{
263 ShShaderSpec spec = SH_WEBGL_SPEC;
264 ShShaderOutput output = SH_ESSL_OUTPUT;
265 ShHandle vertexCompiler = ShConstructCompiler(
266 SH_FRAGMENT_SHADER, spec, output, &resources);
267 int compileOptions = SH_LIMIT_CALL_STACK_DEPTH;
268
269 // Test call stack under the limit passes.
270 EXPECT_TRUE(CheckShaderCompilation(
271 vertexCompiler,
272 GenerateShaderWithUnusedDeepFunctionStack(
273 kMaxCallStackDepth - 10).c_str(),
274 compileOptions, NULL));
275 // Test call stack over the limit fails.
276 EXPECT_TRUE(CheckShaderCompilation(
277 vertexCompiler,
278 GenerateShaderWithUnusedDeepFunctionStack(
279 kMaxCallStackDepth + 10).c_str(),
280 compileOptions, kCallStackTooDeep));
281 // Test call stack over the limit without limit does not fail.
282 EXPECT_TRUE(CheckShaderCompilation(
283 vertexCompiler,
284 GenerateShaderWithUnusedDeepFunctionStack(
285 kMaxCallStackDepth + 10).c_str(),
286 compileOptions & ~SH_LIMIT_CALL_STACK_DEPTH, NULL));
287}
288
289TEST_F(ExpressionLimitTest, Recursion)
290{
291 ShShaderSpec spec = SH_WEBGL_SPEC;
292 ShShaderOutput output = SH_ESSL_OUTPUT;
293 ShHandle vertexCompiler = ShConstructCompiler(
294 SH_FRAGMENT_SHADER, spec, output, &resources);
295 int compileOptions = 0;
296
297 static const char* shaderWithRecursion0 = SHADER(
298 precision mediump float;
299 uniform vec4 u_color;
300 vec4 someFunc() {
301 return someFunc();
302 }
303
304 void main() {
305 gl_FragColor = u_color * someFunc();
306 }
307 );
308
309 static const char* shaderWithRecursion1 = SHADER(
310 precision mediump float;
311 uniform vec4 u_color;
312
313 vec4 someFunc();
314
315 vec4 someFunc1() {
316 return someFunc();
317 }
318
319 vec4 someFunc() {
320 return someFunc1();
321 }
322
323 void main() {
324 gl_FragColor = u_color * someFunc();
325 }
326 );
327
328 static const char* shaderWithRecursion2 = SHADER(
329 precision mediump float;
330 uniform vec4 u_color;
331 vec4 someFunc() {
332 if (u_color.x > 0.5) {
333 return someFunc();
334 } else {
335 return vec4(1);
336 }
337 }
338
339 void main() {
340 gl_FragColor = someFunc();
341 }
342 );
343
344 static const char* shaderWithRecursion3 = SHADER(
345 precision mediump float;
346 uniform vec4 u_color;
347 vec4 someFunc() {
348 if (u_color.x > 0.5) {
349 return vec4(1);
350 } else {
351 return someFunc();
352 }
353 }
354
355 void main() {
356 gl_FragColor = someFunc();
357 }
358 );
359
360 static const char* shaderWithRecursion4 = SHADER(
361 precision mediump float;
362 uniform vec4 u_color;
363 vec4 someFunc() {
364 return (u_color.x > 0.5) ? vec4(1) : someFunc();
365 }
366
367 void main() {
368 gl_FragColor = someFunc();
369 }
370 );
371
372 static const char* shaderWithRecursion5 = SHADER(
373 precision mediump float;
374 uniform vec4 u_color;
375 vec4 someFunc() {
376 return (u_color.x > 0.5) ? someFunc() : vec4(1);
377 }
378
379 void main() {
380 gl_FragColor = someFunc();
381 }
382 );
383
384 static const char* shaderWithRecursion6 = SHADER(
385 precision mediump float;
386 uniform vec4 u_color;
387 vec4 someFunc() {
388 return someFunc();
389 }
390
391 void main() {
392 gl_FragColor = u_color;
393 }
394 );
395
396 static const char* shaderWithNoRecursion = SHADER(
397 precision mediump float;
398 uniform vec4 u_color;
399
400 vec3 rgb(int r, int g, int b) {
401 return vec3(float(r) / 255.0, float(g) / 255.0, float(b) / 255.0);
402 }
403
404 // these external calls used to incorrectly trigger
405 // recursion detection.
406 vec3 hairColor0 = rgb(151, 200, 234);
407 vec3 faceColor2 = rgb(183, 148, 133);
408
409 void main() {
410 gl_FragColor = u_color + vec4(hairColor0 + faceColor2, 0);
411 }
412 );
413
414 static const char* shaderWithRecursion7 = SHADER(
415 precision mediump float;
416 uniform vec4 u_color;
417
418 vec4 function2() {
419 return u_color;
420 }
421
422 vec4 function1() {
423 vec4 a = function2();
424 vec4 b = function1();
425 return a + b;
426 }
427
428 void main() {
429 gl_FragColor = function1();
430 }
431 );
432
433 static const char* shaderWithRecursion8 = SHADER(
434 precision mediump float;
435 uniform vec4 u_color;
436
437 vec4 function1();
438
439 vec4 function3() {
440 return function1();
441 }
442
443 vec4 function2() {
444 return function3();
445 }
446
447 vec4 function1() {
448 return function2();
449 }
450
451 void main() {
452 gl_FragColor = function1();
453 }
454 );
455
456 // Check simple recursions fails.
457 EXPECT_TRUE(CheckShaderCompilation(
458 vertexCompiler, shaderWithRecursion0,
459 compileOptions, kHasRecursion));
460 // Check simple recursions fails.
461 EXPECT_TRUE(CheckShaderCompilation(
462 vertexCompiler, shaderWithRecursion1,
463 compileOptions, kHasRecursion));
464 // Check if recursions fails.
465 EXPECT_TRUE(CheckShaderCompilation(
466 vertexCompiler, shaderWithRecursion2,
467 compileOptions, kHasRecursion));
468 // Check if recursions fails.
469 EXPECT_TRUE(CheckShaderCompilation(
470 vertexCompiler, shaderWithRecursion3,
471 compileOptions, kHasRecursion));
472 // Check ternary recursions fails.
473 EXPECT_TRUE(CheckShaderCompilation(
474 vertexCompiler, shaderWithRecursion4,
475 compileOptions, kHasRecursion));
476 // Check ternary recursions fails.
477 EXPECT_TRUE(CheckShaderCompilation(
478 vertexCompiler, shaderWithRecursion5,
479 compileOptions, kHasRecursion));
480 // Check unused recursions passes.
481 EXPECT_TRUE(CheckShaderCompilation(
482 vertexCompiler, shaderWithRecursion6,
483 compileOptions, NULL));
484 EXPECT_TRUE(CheckShaderCompilation(
485 vertexCompiler, shaderWithRecursion7,
486 compileOptions, kHasRecursion));
487 EXPECT_TRUE(CheckShaderCompilation(
488 vertexCompiler, shaderWithRecursion8,
489 compileOptions, kHasRecursion));
490 // Check unused recursions fails if limiting call stack
491 // since we check all paths.
492 EXPECT_TRUE(CheckShaderCompilation(
493 vertexCompiler, shaderWithRecursion6,
494 compileOptions | SH_LIMIT_CALL_STACK_DEPTH, kHasRecursion));
495
496 // Check unused recursions passes.
497 EXPECT_TRUE(CheckShaderCompilation(
498 vertexCompiler, shaderWithNoRecursion,
499 compileOptions, NULL));
500 // Check unused recursions passes if limiting call stack.
501 EXPECT_TRUE(CheckShaderCompilation(
502 vertexCompiler, shaderWithNoRecursion,
503 compileOptions | SH_LIMIT_CALL_STACK_DEPTH, NULL));
504}
505