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