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