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