Home | History | Annotate | Download | only in compiler_tests
      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 "GLSLANG/ShaderLang.h"
     10 #include "gtest/gtest.h"
     11 
     12 #define SHADER(Src) #Src
     13 
     14 class ExpressionLimitTest : public testing::Test {
     15 protected:
     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 
     26         GenerateResources(&resources);
     27     }
     28 
     29     // 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) {
    149         bool success = ShCompile(compiler, &source, 1, compileOptions) != 0;
    150         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 
    170 const char* ExpressionLimitTest::kExpressionTooComplex =
    171     "Expression too complex";
    172 const char* ExpressionLimitTest::kCallStackTooDeep =
    173     "call stack too deep";
    174 const char* ExpressionLimitTest::kHasRecursion =
    175     "Function recursion detected";
    176 
    177 TEST_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 
    205 TEST_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 
    233 TEST_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 
    261 TEST_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 
    289 TEST_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 
    506