| // |
| // Copyright 2018 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. |
| // |
| // CompilerPerfTest: |
| // Performance test for the shader translator. The test initializes the compiler once and then |
| // compiles the same shader repeatedly. There are different variations of the tests using |
| // different shaders. |
| // |
| |
| #include "ANGLEPerfTest.h" |
| |
| #include "GLSLANG/ShaderLang.h" |
| #include "compiler/translator/Compiler.h" |
| #include "compiler/translator/InitializeGlobals.h" |
| #include "compiler/translator/PoolAlloc.h" |
| |
| namespace |
| { |
| |
| const char *kSimpleESSL100FragSource = R"( |
| precision mediump float; |
| void main() |
| { |
| gl_FragColor = vec4(0, 1, 0, 1); |
| } |
| )"; |
| |
| const char *kSimpleESSL100Id = "SimpleESSL100"; |
| |
| const char *kSimpleESSL300FragSource = R"(#version 300 es |
| precision highp float; |
| out vec4 outColor; |
| void main() |
| { |
| outColor = vec4(0, 1, 0, 1); |
| } |
| )"; |
| |
| const char *kSimpleESSL300Id = "SimpleESSL300"; |
| |
| const char *kRealWorldESSL100FragSource = R"(precision highp float; |
| precision highp sampler2D; |
| precision highp int; |
| varying vec2 vPixelCoords; // in pixels |
| uniform int uCircleCount; |
| uniform sampler2D uCircleParameters; |
| uniform sampler2D uBrushTex; |
| void main(void) |
| { |
| float destAlpha = 0.0; |
| for (int i = 0; i < 32; ++i) |
| { |
| vec4 parameterColor = texture2D(uCircleParameters,vec2(0.25, (float(i) + 0.5) / 32.0)); |
| vec2 center = parameterColor.xy; |
| float circleRadius = parameterColor.z; |
| float circleFlowAlpha = parameterColor.w; |
| vec4 parameterColor2 = texture2D(uCircleParameters,vec2(0.75, (float(i) + 0.5) / 32.0)); |
| float circleRotation = parameterColor2.x; |
| vec2 centerDiff = vPixelCoords - center; |
| float radius = max(circleRadius, 0.5); |
| float flowAlpha = (circleRadius < 0.5) ? circleFlowAlpha * circleRadius * circleRadius * 4.0: circleFlowAlpha; |
| float antialiasMult = clamp((radius + 1.0 - length(centerDiff)) * 0.5, 0.0, 1.0); |
| mat2 texRotation = mat2(cos(circleRotation), -sin(circleRotation), sin(circleRotation), cos(circleRotation)); |
| vec2 texCoords = texRotation * centerDiff / radius * 0.5 + 0.5; |
| float texValue = texture2D(uBrushTex, texCoords).r; |
| float circleAlpha = flowAlpha * antialiasMult * texValue; |
| if (i < uCircleCount) |
| { |
| destAlpha = clamp(circleAlpha + (1.0 - circleAlpha) * destAlpha, 0.0, 1.0); |
| } |
| } |
| gl_FragColor = vec4(0.0, 0.0, 0.0, destAlpha); |
| })"; |
| |
| const char *kRealWorldESSL100Id = "RealWorldESSL100"; |
| |
| // This shader is intended to trigger many AST transformations, particularly on the HLSL backend. |
| const char *kTrickyESSL300FragSource = R"(#version 300 es |
| precision highp float; |
| precision highp sampler2D; |
| precision highp isampler2D; |
| precision highp int; |
| |
| float globalF; |
| |
| uniform ivec4 uivec; |
| uniform int ui; |
| |
| struct SS |
| { |
| int iField; |
| float fField; |
| vec2 f2Field; |
| sampler2D sField; |
| isampler2D isField; |
| }; |
| uniform SS us; |
| |
| out vec4 my_FragColor; |
| |
| float[3] sideEffectArray() |
| { |
| globalF += 1.0; |
| return float[3](globalF, globalF * 2.0, globalF * 3.0); |
| } |
| |
| // This struct is unused and can be pruned. |
| struct SUnused |
| { |
| vec2 fField; |
| }; |
| |
| void main() |
| { |
| struct S2 |
| { |
| float fField; |
| } s2; |
| vec4 foo = vec4(ui); |
| mat4 fooM = mat4(foo.x); |
| |
| // Some unused variables that can be pruned. |
| float fUnused, fUnused2; |
| ivec4 iUnused, iUnused2; |
| |
| globalF = us.fField; |
| s2.fField = us.fField; |
| |
| float[3] fa = sideEffectArray(); |
| |
| globalF -= us.fField; |
| if (fa == sideEffectArray()) |
| { |
| globalF += us.fField * sin(2.0); |
| } |
| |
| // Switch with fall-through. |
| switch (ui) |
| { |
| case 0: |
| // Sequence operator and matrix and vector dynamic indexing. |
| (globalF += 1.0, fooM[ui][ui] += fooM[ui - 1][uivec[ui] + 1]); |
| case 1: |
| // Built-in emulation. |
| foo[3] = tanh(foo[1]); |
| default: |
| // Sequence operator and length of an array expression with side effects. |
| foo[2] += (globalF -= 1.0, float((sideEffectArray()).length() * 2)); |
| } |
| int i = 0; |
| do |
| { |
| s2.fField = us.fField * us.f2Field.x; |
| // Sequence operator and short-circuiting operator with side effects on the right hand side. |
| } while ((++i, i < int(us.fField) && ++i <= ui || ++i < ui * 2 - 3)); |
| // Samplers in structures and integer texture sampling. |
| foo += texture(us.sField, us.f2Field) + intBitsToFloat(texture(us.isField, us.f2Field + 4.0)); |
| my_FragColor = foo * s2.fField * globalF + fooM[ui]; |
| })"; |
| |
| const char *kTrickyESSL300Id = "TrickyESSL300"; |
| |
| constexpr int kNumIterationsPerStep = 4; |
| |
| struct CompilerParameters |
| { |
| CompilerParameters() { output = SH_HLSL_4_1_OUTPUT; } |
| |
| CompilerParameters(ShShaderOutput output) : output(output) {} |
| |
| const char *str() const |
| { |
| switch (output) |
| { |
| case SH_HLSL_4_1_OUTPUT: |
| return "HLSL_4_1"; |
| case SH_GLSL_450_CORE_OUTPUT: |
| return "GLSL_4_50"; |
| case SH_ESSL_OUTPUT: |
| return "ESSL"; |
| default: |
| UNREACHABLE(); |
| return "unk"; |
| } |
| } |
| |
| ShShaderOutput output; |
| }; |
| |
| bool IsPlatformAvailable(const CompilerParameters ¶m) |
| { |
| switch (param.output) |
| { |
| case SH_HLSL_4_1_OUTPUT: |
| case SH_HLSL_4_0_FL9_3_OUTPUT: |
| case SH_HLSL_3_0_OUTPUT: |
| { |
| angle::PoolAllocator allocator; |
| InitializePoolIndex(); |
| allocator.push(); |
| SetGlobalPoolAllocator(&allocator); |
| ShHandle translator = |
| sh::ConstructCompiler(GL_FRAGMENT_SHADER, SH_WEBGL2_SPEC, param.output); |
| bool success = translator != nullptr; |
| SetGlobalPoolAllocator(nullptr); |
| allocator.pop(); |
| FreePoolIndex(); |
| if (!success) |
| { |
| return false; |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| struct CompilerPerfParameters final : public CompilerParameters |
| { |
| CompilerPerfParameters(ShShaderOutput output, |
| const char *shaderSource, |
| const char *shaderSourceId) |
| : CompilerParameters(output), shaderSource(shaderSource) |
| { |
| testId = shaderSourceId; |
| testId += "_"; |
| testId += CompilerParameters::str(); |
| } |
| |
| const char *shaderSource; |
| std::string testId; |
| }; |
| |
| std::ostream &operator<<(std::ostream &stream, const CompilerPerfParameters &p) |
| { |
| stream << p.testId; |
| return stream; |
| } |
| |
| class CompilerPerfTest : public ANGLEPerfTest, |
| public ::testing::WithParamInterface<CompilerPerfParameters> |
| { |
| public: |
| CompilerPerfTest(); |
| |
| void step() override; |
| |
| void SetUp() override; |
| void TearDown() override; |
| |
| protected: |
| void setTestShader(const char *str) { mTestShader = str; } |
| |
| private: |
| const char *mTestShader; |
| |
| ShBuiltInResources mResources; |
| angle::PoolAllocator mAllocator; |
| sh::TCompiler *mTranslator; |
| }; |
| |
| CompilerPerfTest::CompilerPerfTest() |
| : ANGLEPerfTest("CompilerPerf", GetParam().testId, kNumIterationsPerStep) |
| {} |
| |
| void CompilerPerfTest::SetUp() |
| { |
| ANGLEPerfTest::SetUp(); |
| |
| InitializePoolIndex(); |
| mAllocator.push(); |
| SetGlobalPoolAllocator(&mAllocator); |
| |
| const auto ¶ms = GetParam(); |
| |
| mTranslator = sh::ConstructCompiler(GL_FRAGMENT_SHADER, SH_WEBGL2_SPEC, params.output); |
| sh::InitBuiltInResources(&mResources); |
| mResources.FragmentPrecisionHigh = true; |
| if (!mTranslator->Init(mResources)) |
| { |
| SafeDelete(mTranslator); |
| } |
| |
| setTestShader(params.shaderSource); |
| } |
| |
| void CompilerPerfTest::TearDown() |
| { |
| SafeDelete(mTranslator); |
| |
| SetGlobalPoolAllocator(nullptr); |
| mAllocator.pop(); |
| |
| FreePoolIndex(); |
| |
| ANGLEPerfTest::TearDown(); |
| } |
| |
| void CompilerPerfTest::step() |
| { |
| const char *shaderStrings[] = {mTestShader}; |
| |
| ShCompileOptions compileOptions = SH_OBJECT_CODE | SH_VARIABLES | |
| SH_INITIALIZE_UNINITIALIZED_LOCALS | SH_INIT_OUTPUT_VARIABLES; |
| |
| #if !defined(NDEBUG) |
| // Make sure that compilation succeeds and print the info log if it doesn't in debug mode. |
| if (!mTranslator->compile(shaderStrings, 1, compileOptions)) |
| { |
| std::cout << "Compiling perf test shader failed with log:\n" |
| << mTranslator->getInfoSink().info.c_str(); |
| } |
| #endif |
| |
| for (unsigned int iteration = 0; iteration < kNumIterationsPerStep; ++iteration) |
| { |
| mTranslator->compile(shaderStrings, 1, compileOptions); |
| } |
| } |
| |
| TEST_P(CompilerPerfTest, Run) |
| { |
| run(); |
| } |
| |
| ANGLE_INSTANTIATE_TEST( |
| CompilerPerfTest, |
| CompilerPerfParameters(SH_HLSL_4_1_OUTPUT, kSimpleESSL100FragSource, kSimpleESSL100Id), |
| CompilerPerfParameters(SH_HLSL_4_1_OUTPUT, kSimpleESSL300FragSource, kSimpleESSL300Id), |
| CompilerPerfParameters(SH_HLSL_4_1_OUTPUT, kRealWorldESSL100FragSource, kRealWorldESSL100Id), |
| CompilerPerfParameters(SH_HLSL_4_1_OUTPUT, kTrickyESSL300FragSource, kTrickyESSL300Id), |
| CompilerPerfParameters(SH_GLSL_450_CORE_OUTPUT, kSimpleESSL100FragSource, kSimpleESSL100Id), |
| CompilerPerfParameters(SH_GLSL_450_CORE_OUTPUT, kSimpleESSL300FragSource, kSimpleESSL300Id), |
| CompilerPerfParameters(SH_GLSL_450_CORE_OUTPUT, |
| kRealWorldESSL100FragSource, |
| kRealWorldESSL100Id), |
| CompilerPerfParameters(SH_GLSL_450_CORE_OUTPUT, kTrickyESSL300FragSource, kTrickyESSL300Id), |
| CompilerPerfParameters(SH_ESSL_OUTPUT, kSimpleESSL100FragSource, kSimpleESSL100Id), |
| CompilerPerfParameters(SH_ESSL_OUTPUT, kSimpleESSL300FragSource, kSimpleESSL300Id), |
| CompilerPerfParameters(SH_ESSL_OUTPUT, kRealWorldESSL100FragSource, kRealWorldESSL100Id), |
| CompilerPerfParameters(SH_ESSL_OUTPUT, kTrickyESSL300FragSource, kTrickyESSL300Id)); |
| |
| } // anonymous namespace |