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