diff options
Diffstat (limited to 'gfx/angle/src/tests/compiler_tests/ExpressionLimit_test.cpp')
-rwxr-xr-x | gfx/angle/src/tests/compiler_tests/ExpressionLimit_test.cpp | 557 |
1 files changed, 557 insertions, 0 deletions
diff --git a/gfx/angle/src/tests/compiler_tests/ExpressionLimit_test.cpp b/gfx/angle/src/tests/compiler_tests/ExpressionLimit_test.cpp new file mode 100755 index 000000000..8f7d7ef4f --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/ExpressionLimit_test.cpp @@ -0,0 +1,557 @@ +// +// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +#include <sstream> +#include <string> +#include <vector> +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" + +#define SHADER(Src) #Src + +class ExpressionLimitTest : public testing::Test { +protected: + static const int kMaxExpressionComplexity = 16; + static const int kMaxCallStackDepth = 16; + static const int kMaxFunctionParameters = 16; + static const char* kExpressionTooComplex; + static const char* kCallStackTooDeep; + static const char* kHasRecursion; + static const char *kTooManyParameters; + + virtual void SetUp() + { + memset(&resources, 0, sizeof(resources)); + + GenerateResources(&resources); + } + + // Set up the per compile resources + static void GenerateResources(ShBuiltInResources *res) + { + sh::InitBuiltInResources(res); + + res->MaxVertexAttribs = 8; + res->MaxVertexUniformVectors = 128; + res->MaxVaryingVectors = 8; + res->MaxVertexTextureImageUnits = 0; + res->MaxCombinedTextureImageUnits = 8; + res->MaxTextureImageUnits = 8; + res->MaxFragmentUniformVectors = 16; + res->MaxDrawBuffers = 1; + + res->OES_standard_derivatives = 0; + res->OES_EGL_image_external = 0; + + res->MaxExpressionComplexity = kMaxExpressionComplexity; + res->MaxCallStackDepth = kMaxCallStackDepth; + res->MaxFunctionParameters = kMaxFunctionParameters; + } + + void GenerateLongExpression(int length, std::stringstream* ss) + { + for (int ii = 0; ii < length; ++ii) { + *ss << "+ vec4(" << ii << ")"; + } + } + + std::string GenerateShaderWithLongExpression(int length) + { + static const char* shaderStart = SHADER( + precision mediump float; + uniform vec4 u_color; + void main() + { + gl_FragColor = u_color + ); + + std::stringstream ss; + ss << shaderStart; + GenerateLongExpression(length, &ss); + ss << "; }"; + + return ss.str(); + } + + std::string GenerateShaderWithUnusedLongExpression(int length) + { + static const char* shaderStart = SHADER( + precision mediump float; + uniform vec4 u_color; + void main() + { + gl_FragColor = u_color; + } + vec4 someFunction() { + return u_color + ); + + std::stringstream ss; + + ss << shaderStart; + GenerateLongExpression(length, &ss); + ss << "; }"; + + return ss.str(); + } + + void GenerateDeepFunctionStack(int length, std::stringstream* ss) + { + static const char* shaderStart = SHADER( + precision mediump float; + uniform vec4 u_color; + vec4 function0() { + return u_color; + } + ); + + *ss << shaderStart; + for (int ii = 0; ii < length; ++ii) { + *ss << "vec4 function" << (ii + 1) << "() {\n" + << " return function" << ii << "();\n" + << "}\n"; + } + } + + std::string GenerateShaderWithDeepFunctionStack(int length) + { + std::stringstream ss; + + GenerateDeepFunctionStack(length, &ss); + + ss << "void main() {\n" + << " gl_FragColor = function" << length << "();\n" + << "}"; + + return ss.str(); + } + + std::string GenerateShaderWithUnusedDeepFunctionStack(int length) + { + std::stringstream ss; + + GenerateDeepFunctionStack(length, &ss); + + ss << "void main() {\n" + << " gl_FragColor = vec4(0,0,0,0);\n" + << "}"; + + + return ss.str(); + } + + std::string GenerateShaderWithFunctionParameters(int parameters) + { + std::stringstream ss; + + ss << "precision mediump float;\n" + << "\n" + << "float foo("; + for (int i = 0; i < parameters; ++i) + { + ss << "float f" << i; + if (i + 1 < parameters) + { + ss << ", "; + } + } + ss << ")\n" + << "{\n" + << " return f0;\n" + << "}\n" + << "\n" + << "void main()\n" + << "{\n" + << " gl_FragColor = vec4(0,0,0,0);\n" + << "}"; + + return ss.str(); + } + + // Compiles a shader and if there's an error checks for a specific + // substring in the error log. This way we know the error is specific + // to the issue we are testing. + bool CheckShaderCompilation(ShHandle compiler, + const char *source, + ShCompileOptions compileOptions, + const char *expected_error) + { + bool success = sh::Compile(compiler, &source, 1, compileOptions) != 0; + if (success) + { + success = !expected_error; + } + else + { + std::string log = sh::GetInfoLog(compiler); + if (expected_error) + success = log.find(expected_error) != std::string::npos; + + EXPECT_TRUE(success) << log << "\n----shader----\n" << source; + } + return success; + } + + ShBuiltInResources resources; +}; + +const char* ExpressionLimitTest::kExpressionTooComplex = + "Expression too complex"; +const char* ExpressionLimitTest::kCallStackTooDeep = + "Call stack too deep"; +const char* ExpressionLimitTest::kHasRecursion = + "Function recursion detected"; +const char* ExpressionLimitTest::kTooManyParameters = + "Function has too many parameters"; + +TEST_F(ExpressionLimitTest, ExpressionComplexity) +{ + ShShaderSpec spec = SH_WEBGL_SPEC; + ShShaderOutput output = SH_ESSL_OUTPUT; + ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources); + ShCompileOptions compileOptions = SH_LIMIT_EXPRESSION_COMPLEXITY; + + // Test expression under the limit passes. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, + GenerateShaderWithLongExpression( + kMaxExpressionComplexity - 10).c_str(), + compileOptions, NULL)); + // Test expression over the limit fails. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, + GenerateShaderWithLongExpression( + kMaxExpressionComplexity + 10).c_str(), + compileOptions, kExpressionTooComplex)); + // Test expression over the limit without a limit does not fail. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, + GenerateShaderWithLongExpression( + kMaxExpressionComplexity + 10).c_str(), + compileOptions & ~SH_LIMIT_EXPRESSION_COMPLEXITY, NULL)); + sh::Destruct(vertexCompiler); +} + +TEST_F(ExpressionLimitTest, UnusedExpressionComplexity) +{ + ShShaderSpec spec = SH_WEBGL_SPEC; + ShShaderOutput output = SH_ESSL_OUTPUT; + ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources); + ShCompileOptions compileOptions = SH_LIMIT_EXPRESSION_COMPLEXITY; + + // Test expression under the limit passes. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, + GenerateShaderWithUnusedLongExpression( + kMaxExpressionComplexity - 10).c_str(), + compileOptions, NULL)); + // Test expression over the limit fails. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, + GenerateShaderWithUnusedLongExpression( + kMaxExpressionComplexity + 10).c_str(), + compileOptions, kExpressionTooComplex)); + // Test expression over the limit without a limit does not fail. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, + GenerateShaderWithUnusedLongExpression( + kMaxExpressionComplexity + 10).c_str(), + compileOptions & ~SH_LIMIT_EXPRESSION_COMPLEXITY, NULL)); + sh::Destruct(vertexCompiler); +} + +TEST_F(ExpressionLimitTest, CallStackDepth) +{ + ShShaderSpec spec = SH_WEBGL_SPEC; + ShShaderOutput output = SH_ESSL_OUTPUT; + ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources); + ShCompileOptions compileOptions = SH_LIMIT_CALL_STACK_DEPTH; + + // Test call stack under the limit passes. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, + GenerateShaderWithDeepFunctionStack( + kMaxCallStackDepth - 10).c_str(), + compileOptions, NULL)); + // Test call stack over the limit fails. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, + GenerateShaderWithDeepFunctionStack( + kMaxCallStackDepth + 10).c_str(), + compileOptions, kCallStackTooDeep)); + // Test call stack over the limit without limit does not fail. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, + GenerateShaderWithDeepFunctionStack( + kMaxCallStackDepth + 10).c_str(), + compileOptions & ~SH_LIMIT_CALL_STACK_DEPTH, NULL)); + sh::Destruct(vertexCompiler); +} + +TEST_F(ExpressionLimitTest, UnusedCallStackDepth) +{ + ShShaderSpec spec = SH_WEBGL_SPEC; + ShShaderOutput output = SH_ESSL_OUTPUT; + ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources); + ShCompileOptions compileOptions = SH_LIMIT_CALL_STACK_DEPTH; + + // Test call stack under the limit passes. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, + GenerateShaderWithUnusedDeepFunctionStack( + kMaxCallStackDepth - 10).c_str(), + compileOptions, NULL)); + // Test call stack over the limit fails. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, + GenerateShaderWithUnusedDeepFunctionStack( + kMaxCallStackDepth + 10).c_str(), + compileOptions, kCallStackTooDeep)); + // Test call stack over the limit without limit does not fail. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, + GenerateShaderWithUnusedDeepFunctionStack( + kMaxCallStackDepth + 10).c_str(), + compileOptions & ~SH_LIMIT_CALL_STACK_DEPTH, NULL)); + sh::Destruct(vertexCompiler); +} + +TEST_F(ExpressionLimitTest, Recursion) +{ + ShShaderSpec spec = SH_WEBGL_SPEC; + ShShaderOutput output = SH_ESSL_OUTPUT; + ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources); + ShCompileOptions compileOptions = 0; + + static const char* shaderWithRecursion0 = SHADER( + precision mediump float; + uniform vec4 u_color; + vec4 someFunc() { + return someFunc(); + } + + void main() { + gl_FragColor = u_color * someFunc(); + } + ); + + static const char* shaderWithRecursion1 = SHADER( + precision mediump float; + uniform vec4 u_color; + + vec4 someFunc(); + + vec4 someFunc1() { + return someFunc(); + } + + vec4 someFunc() { + return someFunc1(); + } + + void main() { + gl_FragColor = u_color * someFunc(); + } + ); + + static const char* shaderWithRecursion2 = SHADER( + precision mediump float; + uniform vec4 u_color; + vec4 someFunc() { + if (u_color.x > 0.5) { + return someFunc(); + } else { + return vec4(1); + } + } + + void main() { + gl_FragColor = someFunc(); + } + ); + + static const char* shaderWithRecursion3 = SHADER( + precision mediump float; + uniform vec4 u_color; + vec4 someFunc() { + if (u_color.x > 0.5) { + return vec4(1); + } else { + return someFunc(); + } + } + + void main() { + gl_FragColor = someFunc(); + } + ); + + static const char* shaderWithRecursion4 = SHADER( + precision mediump float; + uniform vec4 u_color; + vec4 someFunc() { + return (u_color.x > 0.5) ? vec4(1) : someFunc(); + } + + void main() { + gl_FragColor = someFunc(); + } + ); + + static const char* shaderWithRecursion5 = SHADER( + precision mediump float; + uniform vec4 u_color; + vec4 someFunc() { + return (u_color.x > 0.5) ? someFunc() : vec4(1); + } + + void main() { + gl_FragColor = someFunc(); + } + ); + + static const char* shaderWithRecursion6 = SHADER( + precision mediump float; + uniform vec4 u_color; + vec4 someFunc() { + return someFunc(); + } + + void main() { + gl_FragColor = u_color; + } + ); + + static const char* shaderWithNoRecursion = SHADER( + precision mediump float; + uniform vec4 u_color; + + vec3 rgb(int r, int g, int b) { + return vec3(float(r) / 255.0, float(g) / 255.0, float(b) / 255.0); + } + + void main() { + vec3 hairColor0 = rgb(151, 200, 234); + vec3 faceColor2 = rgb(183, 148, 133); + gl_FragColor = u_color + vec4(hairColor0 + faceColor2, 0); + } + ); + + static const char* shaderWithRecursion7 = SHADER( + precision mediump float; + uniform vec4 u_color; + + vec4 function2() { + return u_color; + } + + vec4 function1() { + vec4 a = function2(); + vec4 b = function1(); + return a + b; + } + + void main() { + gl_FragColor = function1(); + } + ); + + static const char* shaderWithRecursion8 = SHADER( + precision mediump float; + uniform vec4 u_color; + + vec4 function1(); + + vec4 function3() { + return function1(); + } + + vec4 function2() { + return function3(); + } + + vec4 function1() { + return function2(); + } + + void main() { + gl_FragColor = function1(); + } + ); + + // Check simple recursions fails. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, shaderWithRecursion0, + compileOptions, kHasRecursion)); + // Check simple recursions fails. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, shaderWithRecursion1, + compileOptions, kHasRecursion)); + // Check if recursions fails. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, shaderWithRecursion2, + compileOptions, kHasRecursion)); + // Check if recursions fails. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, shaderWithRecursion3, + compileOptions, kHasRecursion)); + // Check ternary recursions fails. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, shaderWithRecursion4, + compileOptions, kHasRecursion)); + // Check ternary recursions fails. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, shaderWithRecursion5, + compileOptions, kHasRecursion)); + + // Check some more forms of recursion + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, shaderWithRecursion6, + compileOptions, kHasRecursion)); + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, shaderWithRecursion7, + compileOptions, kHasRecursion)); + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, shaderWithRecursion8, + compileOptions, kHasRecursion)); + // Check unused recursions fails if limiting call stack + // since we check all paths. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, shaderWithRecursion6, + compileOptions | SH_LIMIT_CALL_STACK_DEPTH, kHasRecursion)); + + // Check unused recursions passes. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, shaderWithNoRecursion, + compileOptions, NULL)); + // Check unused recursions passes if limiting call stack. + EXPECT_TRUE(CheckShaderCompilation( + vertexCompiler, shaderWithNoRecursion, + compileOptions | SH_LIMIT_CALL_STACK_DEPTH, NULL)); + sh::Destruct(vertexCompiler); +} + +TEST_F(ExpressionLimitTest, FunctionParameterCount) +{ + ShShaderSpec spec = SH_WEBGL_SPEC; + ShShaderOutput output = SH_ESSL_OUTPUT; + ShHandle compiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources); + ShCompileOptions compileOptions = SH_LIMIT_EXPRESSION_COMPLEXITY; + + // Test parameters under the limit succeeds. + EXPECT_TRUE(CheckShaderCompilation( + compiler, GenerateShaderWithFunctionParameters(kMaxFunctionParameters).c_str(), + compileOptions, nullptr)); + // Test parameters over the limit fails. + EXPECT_TRUE(CheckShaderCompilation( + compiler, GenerateShaderWithFunctionParameters(kMaxFunctionParameters + 1).c_str(), + compileOptions, kTooManyParameters)); + // Test parameters over the limit without limit does not fail. + EXPECT_TRUE(CheckShaderCompilation( + compiler, GenerateShaderWithFunctionParameters(kMaxFunctionParameters + 1).c_str(), + compileOptions & ~SH_LIMIT_EXPRESSION_COMPLEXITY, nullptr)); + sh::Destruct(compiler); +} |