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