From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- gfx/angle/src/tests/compiler_tests/API_test.cpp | 26 + .../tests/compiler_tests/CollectVariables_test.cpp | 742 +++++ .../tests/compiler_tests/ConstantFolding_test.cpp | 1064 +++++++ .../compiler_tests/DebugShaderPrecision_test.cpp | 1047 +++++++ .../EXT_blend_func_extended_test.cpp | 285 ++ .../EmulateGLFragColorBroadcast_test.cpp | 81 + .../tests/compiler_tests/ExpressionLimit_test.cpp | 557 ++++ .../src/tests/compiler_tests/FragDepth_test.cpp | 119 + .../GLSLCompatibilityOutput_test.cpp | 35 + .../src/tests/compiler_tests/IntermNode_test.cpp | 232 ++ .../tests/compiler_tests/MalformedShader_test.cpp | 3158 ++++++++++++++++++++ .../tests/compiler_tests/NV_draw_buffers_test.cpp | 41 + .../src/tests/compiler_tests/Pack_Unpack_test.cpp | 122 + .../compiler_tests/PruneEmptyDeclarations_test.cpp | 60 + .../compiler_tests/PruneUnusedFunctions_test.cpp | 93 + .../QualificationOrderESSL31_test.cpp | 184 ++ .../compiler_tests/QualificationOrder_test.cpp | 571 ++++ .../RecordConstantPrecision_test.cpp | 95 + .../src/tests/compiler_tests/RemovePow_test.cpp | 158 + .../src/tests/compiler_tests/ShCompile_test.cpp | 83 + .../tests/compiler_tests/ShaderExtension_test.cpp | 218 ++ .../src/tests/compiler_tests/ShaderImage_test.cpp | 259 ++ .../tests/compiler_tests/ShaderVariable_test.cpp | 421 +++ .../src/tests/compiler_tests/TypeTracking_test.cpp | 456 +++ .../tests/compiler_tests/UnrollFlatten_test.cpp | 209 ++ .../tests/compiler_tests/VariablePacker_test.cpp | 237 ++ .../tests/compiler_tests/WorkGroupSize_test.cpp | 99 + 27 files changed, 10652 insertions(+) create mode 100755 gfx/angle/src/tests/compiler_tests/API_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/CollectVariables_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/ConstantFolding_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/DebugShaderPrecision_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/EXT_blend_func_extended_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/EmulateGLFragColorBroadcast_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/ExpressionLimit_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/FragDepth_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/GLSLCompatibilityOutput_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/IntermNode_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/MalformedShader_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/NV_draw_buffers_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/Pack_Unpack_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/PruneEmptyDeclarations_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/PruneUnusedFunctions_test.cpp create mode 100644 gfx/angle/src/tests/compiler_tests/QualificationOrderESSL31_test.cpp create mode 100644 gfx/angle/src/tests/compiler_tests/QualificationOrder_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/RecordConstantPrecision_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/RemovePow_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/ShCompile_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/ShaderExtension_test.cpp create mode 100644 gfx/angle/src/tests/compiler_tests/ShaderImage_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/ShaderVariable_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/TypeTracking_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/UnrollFlatten_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/VariablePacker_test.cpp create mode 100755 gfx/angle/src/tests/compiler_tests/WorkGroupSize_test.cpp (limited to 'gfx/angle/src/tests/compiler_tests') diff --git a/gfx/angle/src/tests/compiler_tests/API_test.cpp b/gfx/angle/src/tests/compiler_tests/API_test.cpp new file mode 100755 index 000000000..090e31ebc --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/API_test.cpp @@ -0,0 +1,26 @@ +// +// Copyright (c) 2014 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. +// +// API_test.cpp: +// Some tests for the compiler API. +// + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" + +TEST(APITest, CompareShBuiltInResources) +{ + ShBuiltInResources a_resources; + memset(&a_resources, 88, sizeof(a_resources)); + sh::InitBuiltInResources(&a_resources); + + ShBuiltInResources b_resources; + memset(&b_resources, 77, sizeof(b_resources)); + sh::InitBuiltInResources(&b_resources); + + EXPECT_TRUE(memcmp(&a_resources, &b_resources, sizeof(a_resources)) == 0); +} + diff --git a/gfx/angle/src/tests/compiler_tests/CollectVariables_test.cpp b/gfx/angle/src/tests/compiler_tests/CollectVariables_test.cpp new file mode 100755 index 000000000..a93411668 --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/CollectVariables_test.cpp @@ -0,0 +1,742 @@ +// +// Copyright (c) 2014 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. +// +// CollectVariables_test.cpp: +// Some tests for shader inspection +// + +#include + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" +#include "compiler/translator/TranslatorGLSL.h" + +using namespace sh; + +#define EXPECT_GLENUM_EQ(expected, actual) \ + EXPECT_EQ(static_cast<::GLenum>(expected), static_cast<::GLenum>(actual)) + +class CollectVariablesTest : public testing::Test +{ + public: + CollectVariablesTest(::GLenum shaderType) : mShaderType(shaderType) {} + + protected: + void SetUp() override + { + ShBuiltInResources resources; + InitBuiltInResources(&resources); + resources.MaxDrawBuffers = 8; + + initTranslator(resources); + } + + void initTranslator(const ShBuiltInResources &resources) + { + mTranslator.reset( + new TranslatorGLSL(mShaderType, SH_GLES3_SPEC, SH_GLSL_COMPATIBILITY_OUTPUT)); + ASSERT_TRUE(mTranslator->Init(resources)); + } + + // For use in the gl_DepthRange tests. + void validateDepthRangeShader(const std::string &shaderString) + { + const char *shaderStrings[] = { shaderString.c_str() }; + ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES)); + + const std::vector &uniforms = mTranslator->getUniforms(); + ASSERT_EQ(1u, uniforms.size()); + + const Uniform &uniform = uniforms[0]; + EXPECT_EQ("gl_DepthRange", uniform.name); + ASSERT_TRUE(uniform.isStruct()); + ASSERT_EQ(3u, uniform.fields.size()); + + bool foundNear = false; + bool foundFar = false; + bool foundDiff = false; + + for (const auto &field : uniform.fields) + { + if (field.name == "near") + { + EXPECT_FALSE(foundNear); + foundNear = true; + } + else if (field.name == "far") + { + EXPECT_FALSE(foundFar); + foundFar = true; + } + else + { + ASSERT_EQ("diff", field.name); + EXPECT_FALSE(foundDiff); + foundDiff = true; + } + + EXPECT_EQ(0u, field.arraySize); + EXPECT_FALSE(field.isStruct()); + EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision); + EXPECT_TRUE(field.staticUse); + EXPECT_GLENUM_EQ(GL_FLOAT, field.type); + } + + EXPECT_TRUE(foundNear && foundFar && foundDiff); + } + + // For use in tests for output varibles. + void validateOutputVariableForShader(const std::string &shaderString, + unsigned int varIndex, + const char *varName, + const OutputVariable **outResult) + { + const char *shaderStrings[] = {shaderString.c_str()}; + ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES)) + << mTranslator->getInfoSink().info.str(); + + const auto &outputVariables = mTranslator->getOutputVariables(); + ASSERT_LT(varIndex, outputVariables.size()); + const OutputVariable &outputVariable = outputVariables[varIndex]; + EXPECT_EQ(-1, outputVariable.location); + EXPECT_TRUE(outputVariable.staticUse); + EXPECT_EQ(varName, outputVariable.name); + *outResult = &outputVariable; + } + + void compile(const std::string &shaderString) + { + const char *shaderStrings[] = {shaderString.c_str()}; + ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES)); + } + + ::GLenum mShaderType; + std::unique_ptr mTranslator; +}; + +class CollectVertexVariablesTest : public CollectVariablesTest +{ + public: + CollectVertexVariablesTest() : CollectVariablesTest(GL_VERTEX_SHADER) {} +}; + +class CollectFragmentVariablesTest : public CollectVariablesTest +{ + public: + CollectFragmentVariablesTest() : CollectVariablesTest(GL_FRAGMENT_SHADER) {} +}; + +TEST_F(CollectFragmentVariablesTest, SimpleOutputVar) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 out_fragColor;\n" + "void main() {\n" + " out_fragColor = vec4(1.0);\n" + "}\n"; + + compile(shaderString); + + const auto &outputVariables = mTranslator->getOutputVariables(); + ASSERT_EQ(1u, outputVariables.size()); + + const OutputVariable &outputVariable = outputVariables[0]; + + EXPECT_EQ(0u, outputVariable.arraySize); + EXPECT_EQ(-1, outputVariable.location); + EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable.precision); + EXPECT_TRUE(outputVariable.staticUse); + EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable.type); + EXPECT_EQ("out_fragColor", outputVariable.name); +} + +TEST_F(CollectFragmentVariablesTest, LocationOutputVar) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "layout(location=5) out vec4 out_fragColor;\n" + "void main() {\n" + " out_fragColor = vec4(1.0);\n" + "}\n"; + + compile(shaderString); + + const auto &outputVariables = mTranslator->getOutputVariables(); + ASSERT_EQ(1u, outputVariables.size()); + + const OutputVariable &outputVariable = outputVariables[0]; + + EXPECT_EQ(0u, outputVariable.arraySize); + EXPECT_EQ(5, outputVariable.location); + EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable.precision); + EXPECT_TRUE(outputVariable.staticUse); + EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable.type); + EXPECT_EQ("out_fragColor", outputVariable.name); +} + +TEST_F(CollectVertexVariablesTest, LocationAttribute) +{ + const std::string &shaderString = + "#version 300 es\n" + "layout(location=5) in vec4 in_Position;\n" + "void main() {\n" + " gl_Position = in_Position;\n" + "}\n"; + + compile(shaderString); + + const std::vector &attributes = mTranslator->getAttributes(); + ASSERT_EQ(1u, attributes.size()); + + const Attribute &attribute = attributes[0]; + + EXPECT_EQ(0u, attribute.arraySize); + EXPECT_EQ(5, attribute.location); + EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, attribute.precision); + EXPECT_TRUE(attribute.staticUse); + EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, attribute.type); + EXPECT_EQ("in_Position", attribute.name); +} + +TEST_F(CollectVertexVariablesTest, SimpleInterfaceBlock) +{ + const std::string &shaderString = + "#version 300 es\n" + "uniform b {\n" + " float f;\n" + "};" + "void main() {\n" + " gl_Position = vec4(f, 0.0, 0.0, 1.0);\n" + "}\n"; + + compile(shaderString); + + const std::vector &interfaceBlocks = mTranslator->getInterfaceBlocks(); + ASSERT_EQ(1u, interfaceBlocks.size()); + + const InterfaceBlock &interfaceBlock = interfaceBlocks[0]; + + EXPECT_EQ(0u, interfaceBlock.arraySize); + EXPECT_FALSE(interfaceBlock.isRowMajorLayout); + EXPECT_EQ(BLOCKLAYOUT_SHARED, interfaceBlock.layout); + EXPECT_EQ("b", interfaceBlock.name); + EXPECT_TRUE(interfaceBlock.staticUse); + + ASSERT_EQ(1u, interfaceBlock.fields.size()); + + const InterfaceBlockField &field = interfaceBlock.fields[0]; + + EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision); + EXPECT_TRUE(field.staticUse); + EXPECT_GLENUM_EQ(GL_FLOAT, field.type); + EXPECT_EQ("f", field.name); + EXPECT_FALSE(field.isRowMajorLayout); + EXPECT_TRUE(field.fields.empty()); +} + +TEST_F(CollectVertexVariablesTest, SimpleInstancedInterfaceBlock) +{ + const std::string &shaderString = + "#version 300 es\n" + "uniform b {\n" + " float f;\n" + "} blockInstance;" + "void main() {\n" + " gl_Position = vec4(blockInstance.f, 0.0, 0.0, 1.0);\n" + "}\n"; + + compile(shaderString); + + const std::vector &interfaceBlocks = mTranslator->getInterfaceBlocks(); + ASSERT_EQ(1u, interfaceBlocks.size()); + + const InterfaceBlock &interfaceBlock = interfaceBlocks[0]; + + EXPECT_EQ(0u, interfaceBlock.arraySize); + EXPECT_FALSE(interfaceBlock.isRowMajorLayout); + EXPECT_EQ(BLOCKLAYOUT_SHARED, interfaceBlock.layout); + EXPECT_EQ("b", interfaceBlock.name); + EXPECT_EQ("blockInstance", interfaceBlock.instanceName); + EXPECT_TRUE(interfaceBlock.staticUse); + + ASSERT_EQ(1u, interfaceBlock.fields.size()); + + const InterfaceBlockField &field = interfaceBlock.fields[0]; + + EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision); + EXPECT_TRUE(field.staticUse); + EXPECT_GLENUM_EQ(GL_FLOAT, field.type); + EXPECT_EQ("f", field.name); + EXPECT_FALSE(field.isRowMajorLayout); + EXPECT_TRUE(field.fields.empty()); +} + +TEST_F(CollectVertexVariablesTest, StructInterfaceBlock) +{ + const std::string &shaderString = + "#version 300 es\n" + "struct st { float f; };" + "uniform b {\n" + " st s;\n" + "};" + "void main() {\n" + " gl_Position = vec4(s.f, 0.0, 0.0, 1.0);\n" + "}\n"; + + compile(shaderString); + + const std::vector &interfaceBlocks = mTranslator->getInterfaceBlocks(); + ASSERT_EQ(1u, interfaceBlocks.size()); + + const InterfaceBlock &interfaceBlock = interfaceBlocks[0]; + + EXPECT_EQ(0u, interfaceBlock.arraySize); + EXPECT_FALSE(interfaceBlock.isRowMajorLayout); + EXPECT_EQ(BLOCKLAYOUT_SHARED, interfaceBlock.layout); + EXPECT_EQ("b", interfaceBlock.name); + EXPECT_TRUE(interfaceBlock.staticUse); + + ASSERT_EQ(1u, interfaceBlock.fields.size()); + + const InterfaceBlockField &field = interfaceBlock.fields[0]; + + EXPECT_TRUE(field.isStruct()); + EXPECT_TRUE(field.staticUse); + EXPECT_EQ("s", field.name); + EXPECT_FALSE(field.isRowMajorLayout); + + const ShaderVariable &member = field.fields[0]; + + // NOTE: we don't currently mark struct members as statically used or not + EXPECT_FALSE(member.isStruct()); + EXPECT_EQ("f", member.name); + EXPECT_GLENUM_EQ(GL_FLOAT, member.type); + EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, member.precision); +} + +TEST_F(CollectVertexVariablesTest, StructInstancedInterfaceBlock) +{ + const std::string &shaderString = + "#version 300 es\n" + "struct st { float f; };" + "uniform b {\n" + " st s;\n" + "} instanceName;" + "void main() {\n" + " gl_Position = vec4(instanceName.s.f, 0.0, 0.0, 1.0);\n" + "}\n"; + + compile(shaderString); + + const std::vector &interfaceBlocks = mTranslator->getInterfaceBlocks(); + ASSERT_EQ(1u, interfaceBlocks.size()); + + const InterfaceBlock &interfaceBlock = interfaceBlocks[0]; + + EXPECT_EQ(0u, interfaceBlock.arraySize); + EXPECT_FALSE(interfaceBlock.isRowMajorLayout); + EXPECT_EQ(BLOCKLAYOUT_SHARED, interfaceBlock.layout); + EXPECT_EQ("b", interfaceBlock.name); + EXPECT_EQ("instanceName", interfaceBlock.instanceName); + EXPECT_TRUE(interfaceBlock.staticUse); + + ASSERT_EQ(1u, interfaceBlock.fields.size()); + + const InterfaceBlockField &field = interfaceBlock.fields[0]; + + EXPECT_TRUE(field.isStruct()); + EXPECT_TRUE(field.staticUse); + EXPECT_EQ("s", field.name); + EXPECT_FALSE(field.isRowMajorLayout); + + const ShaderVariable &member = field.fields[0]; + + // NOTE: we don't currently mark struct members as statically used or not + EXPECT_FALSE(member.isStruct()); + EXPECT_EQ("f", member.name); + EXPECT_GLENUM_EQ(GL_FLOAT, member.type); + EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, member.precision); +} + +TEST_F(CollectVertexVariablesTest, NestedStructRowMajorInterfaceBlock) +{ + const std::string &shaderString = + "#version 300 es\n" + "struct st { mat2 m; };" + "layout(row_major) uniform b {\n" + " st s;\n" + "};" + "void main() {\n" + " gl_Position = vec4(s.m);\n" + "}\n"; + + compile(shaderString); + + const std::vector &interfaceBlocks = mTranslator->getInterfaceBlocks(); + ASSERT_EQ(1u, interfaceBlocks.size()); + + const InterfaceBlock &interfaceBlock = interfaceBlocks[0]; + + EXPECT_EQ(0u, interfaceBlock.arraySize); + EXPECT_TRUE(interfaceBlock.isRowMajorLayout); + EXPECT_EQ(BLOCKLAYOUT_SHARED, interfaceBlock.layout); + EXPECT_EQ("b", interfaceBlock.name); + EXPECT_TRUE(interfaceBlock.staticUse); + + ASSERT_EQ(1u, interfaceBlock.fields.size()); + + const InterfaceBlockField &field = interfaceBlock.fields[0]; + + EXPECT_TRUE(field.isStruct()); + EXPECT_TRUE(field.staticUse); + EXPECT_EQ("s", field.name); + EXPECT_TRUE(field.isRowMajorLayout); + + const ShaderVariable &member = field.fields[0]; + + // NOTE: we don't currently mark struct members as statically used or not + EXPECT_FALSE(member.isStruct()); + EXPECT_EQ("m", member.name); + EXPECT_GLENUM_EQ(GL_FLOAT_MAT2, member.type); + EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, member.precision); +} + +TEST_F(CollectVertexVariablesTest, VaryingInterpolation) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "centroid out float vary;\n" + "void main() {\n" + " gl_Position = vec4(1.0);\n" + " vary = 1.0;\n" + "}\n"; + + compile(shaderString); + + const std::vector &varyings = mTranslator->getVaryings(); + ASSERT_EQ(2u, varyings.size()); + + const Varying *varying = &varyings[0]; + + if (varying->name == "gl_Position") + { + varying = &varyings[1]; + } + + EXPECT_EQ(0u, varying->arraySize); + EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, varying->precision); + EXPECT_TRUE(varying->staticUse); + EXPECT_GLENUM_EQ(GL_FLOAT, varying->type); + EXPECT_EQ("vary", varying->name); + EXPECT_EQ(INTERPOLATION_CENTROID, varying->interpolation); +} + +// Test for builtin uniform "gl_DepthRange" (Vertex shader) +TEST_F(CollectVertexVariablesTest, DepthRange) +{ + const std::string &shaderString = + "attribute vec4 position;\n" + "void main() {\n" + " gl_Position = position + vec4(gl_DepthRange.near, gl_DepthRange.far, gl_DepthRange.diff, 1.0);\n" + "}\n"; + + validateDepthRangeShader(shaderString); +} + +// Test for builtin uniform "gl_DepthRange" (Fragment shader) +TEST_F(CollectFragmentVariablesTest, DepthRange) +{ + const std::string &shaderString = + "precision mediump float;\n" + "void main() {\n" + " gl_FragColor = vec4(gl_DepthRange.near, gl_DepthRange.far, gl_DepthRange.diff, 1.0);\n" + "}\n"; + + validateDepthRangeShader(shaderString); +} + +// Test that gl_FragColor built-in usage in ESSL1 fragment shader is reflected in the output +// variables list. +TEST_F(CollectFragmentVariablesTest, OutputVarESSL1FragColor) +{ + const std::string &fragColorShader = + "precision mediump float;\n" + "void main() {\n" + " gl_FragColor = vec4(1.0);\n" + "}\n"; + + const OutputVariable *outputVariable = nullptr; + validateOutputVariableForShader(fragColorShader, 0u, "gl_FragColor", &outputVariable); + ASSERT_NE(outputVariable, nullptr); + EXPECT_EQ(0u, outputVariable->arraySize); + EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable->type); + EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable->precision); +} + +// Test that gl_FragData built-in usage in ESSL1 fragment shader is reflected in the output +// variables list. +TEST_F(CollectFragmentVariablesTest, OutputVarESSL1FragData) +{ + const std::string &fragDataShader = + "#extension GL_EXT_draw_buffers : require\n" + "precision mediump float;\n" + "void main() {\n" + " gl_FragData[0] = vec4(1.0);\n" + " gl_FragData[1] = vec4(0.5);\n" + "}\n"; + + ShBuiltInResources resources = mTranslator->getResources(); + resources.EXT_draw_buffers = 1; + const unsigned int kMaxDrawBuffers = 3u; + resources.MaxDrawBuffers = kMaxDrawBuffers; + initTranslator(resources); + + const OutputVariable *outputVariable = nullptr; + validateOutputVariableForShader(fragDataShader, 0u, "gl_FragData", &outputVariable); + ASSERT_NE(outputVariable, nullptr); + EXPECT_EQ(kMaxDrawBuffers, outputVariable->arraySize); + EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable->type); + EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable->precision); +} + +// Test that gl_FragDataEXT built-in usage in ESSL1 fragment shader is reflected in the output +// variables list. Also test that the precision is mediump. +TEST_F(CollectFragmentVariablesTest, OutputVarESSL1FragDepthMediump) +{ + const std::string &fragDepthShader = + "#extension GL_EXT_frag_depth : require\n" + "precision mediump float;\n" + "void main() {\n" + " gl_FragDepthEXT = 0.7;" + "}\n"; + + ShBuiltInResources resources = mTranslator->getResources(); + resources.EXT_frag_depth = 1; + initTranslator(resources); + + const OutputVariable *outputVariable = nullptr; + validateOutputVariableForShader(fragDepthShader, 0u, "gl_FragDepthEXT", &outputVariable); + ASSERT_NE(outputVariable, nullptr); + EXPECT_EQ(0u, outputVariable->arraySize); + EXPECT_GLENUM_EQ(GL_FLOAT, outputVariable->type); + EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable->precision); +} + +// Test that gl_FragDataEXT built-in usage in ESSL1 fragment shader is reflected in the output +// variables list. Also test that the precision is highp if user requests it. +TEST_F(CollectFragmentVariablesTest, OutputVarESSL1FragDepthHighp) +{ + const std::string &fragDepthHighShader = + "#extension GL_EXT_frag_depth : require\n" + "void main() {\n" + " gl_FragDepthEXT = 0.7;" + "}\n"; + + ShBuiltInResources resources = mTranslator->getResources(); + resources.EXT_frag_depth = 1; + resources.FragmentPrecisionHigh = 1; + initTranslator(resources); + + const OutputVariable *outputVariable = nullptr; + validateOutputVariableForShader(fragDepthHighShader, 0u, "gl_FragDepthEXT", &outputVariable); + ASSERT_NE(outputVariable, nullptr); + EXPECT_EQ(0u, outputVariable->arraySize); + EXPECT_GLENUM_EQ(GL_FLOAT, outputVariable->type); + EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, outputVariable->precision); +} + +// Test that gl_FragData built-in usage in ESSL3 fragment shader is reflected in the output +// variables list. Also test that the precision is highp. +TEST_F(CollectFragmentVariablesTest, OutputVarESSL3FragDepthHighp) +{ + const std::string &fragDepthHighShader = + "#version 300 es\n" + "precision mediump float;\n" + "void main() {\n" + " gl_FragDepth = 0.7;" + "}\n"; + + ShBuiltInResources resources = mTranslator->getResources(); + resources.EXT_frag_depth = 1; + initTranslator(resources); + + const OutputVariable *outputVariable = nullptr; + validateOutputVariableForShader(fragDepthHighShader, 0u, "gl_FragDepth", &outputVariable); + ASSERT_NE(outputVariable, nullptr); + EXPECT_EQ(0u, outputVariable->arraySize); + EXPECT_GLENUM_EQ(GL_FLOAT, outputVariable->type); + EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, outputVariable->precision); +} + +// Test that gl_SecondaryFragColorEXT built-in usage in ESSL1 fragment shader is reflected in the +// output variables list. +TEST_F(CollectFragmentVariablesTest, OutputVarESSL1EXTBlendFuncExtendedSecondaryFragColor) +{ + const char *secondaryFragColorShader = + "#extension GL_EXT_blend_func_extended : require\n" + "precision mediump float;\n" + "void main() {\n" + " gl_FragColor = vec4(1.0);\n" + " gl_SecondaryFragColorEXT = vec4(1.0);\n" + "}\n"; + + const unsigned int kMaxDrawBuffers = 3u; + ShBuiltInResources resources = mTranslator->getResources(); + resources.EXT_blend_func_extended = 1; + resources.EXT_draw_buffers = 1; + resources.MaxDrawBuffers = kMaxDrawBuffers; + resources.MaxDualSourceDrawBuffers = resources.MaxDrawBuffers; + initTranslator(resources); + + const OutputVariable *outputVariable = nullptr; + validateOutputVariableForShader(secondaryFragColorShader, 0u, "gl_FragColor", &outputVariable); + ASSERT_NE(outputVariable, nullptr); + EXPECT_EQ(0u, outputVariable->arraySize); + EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable->type); + EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable->precision); + + outputVariable = nullptr; + validateOutputVariableForShader(secondaryFragColorShader, 1u, "gl_SecondaryFragColorEXT", + &outputVariable); + ASSERT_NE(outputVariable, nullptr); + EXPECT_EQ(0u, outputVariable->arraySize); + EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable->type); + EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable->precision); +} + +// Test that gl_SecondaryFragDataEXT built-in usage in ESSL1 fragment shader is reflected in the +// output variables list. +TEST_F(CollectFragmentVariablesTest, OutputVarESSL1EXTBlendFuncExtendedSecondaryFragData) +{ + const char *secondaryFragDataShader = + "#extension GL_EXT_blend_func_extended : require\n" + "#extension GL_EXT_draw_buffers : require\n" + "precision mediump float;\n" + "void main() {\n" + " gl_FragData[0] = vec4(1.0);\n" + " gl_FragData[1] = vec4(0.5);\n" + " gl_SecondaryFragDataEXT[0] = vec4(1.0);\n" + " gl_SecondaryFragDataEXT[1] = vec4(0.8);\n" + "}\n"; + const unsigned int kMaxDrawBuffers = 3u; + ShBuiltInResources resources = mTranslator->getResources(); + resources.EXT_blend_func_extended = 1; + resources.EXT_draw_buffers = 1; + resources.MaxDrawBuffers = kMaxDrawBuffers; + resources.MaxDualSourceDrawBuffers = resources.MaxDrawBuffers; + initTranslator(resources); + + const OutputVariable *outputVariable = nullptr; + validateOutputVariableForShader(secondaryFragDataShader, 0u, "gl_FragData", &outputVariable); + ASSERT_NE(outputVariable, nullptr); + EXPECT_EQ(kMaxDrawBuffers, outputVariable->arraySize); + EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable->type); + EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable->precision); + + outputVariable = nullptr; + validateOutputVariableForShader(secondaryFragDataShader, 1u, "gl_SecondaryFragDataEXT", + &outputVariable); + ASSERT_NE(outputVariable, nullptr); + EXPECT_EQ(kMaxDrawBuffers, outputVariable->arraySize); + EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable->type); + EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable->precision); +} + +static khronos_uint64_t SimpleTestHash(const char *str, size_t len) +{ + return static_cast(len); +} + +class CollectHashedVertexVariablesTest : public CollectVertexVariablesTest +{ + protected: + void SetUp() override + { + // Initialize the translate with a hash function + ShBuiltInResources resources; + sh::InitBuiltInResources(&resources); + resources.HashFunction = SimpleTestHash; + initTranslator(resources); + } +}; + +TEST_F(CollectHashedVertexVariablesTest, InstancedInterfaceBlock) +{ + const std::string &shaderString = + "#version 300 es\n" + "uniform blockName {\n" + " float field;\n" + "} blockInstance;" + "void main() {\n" + " gl_Position = vec4(blockInstance.field, 0.0, 0.0, 1.0);\n" + "}\n"; + + compile(shaderString); + + const std::vector &interfaceBlocks = mTranslator->getInterfaceBlocks(); + ASSERT_EQ(1u, interfaceBlocks.size()); + + const InterfaceBlock &interfaceBlock = interfaceBlocks[0]; + + EXPECT_EQ(0u, interfaceBlock.arraySize); + EXPECT_FALSE(interfaceBlock.isRowMajorLayout); + EXPECT_EQ(BLOCKLAYOUT_SHARED, interfaceBlock.layout); + EXPECT_EQ("blockName", interfaceBlock.name); + EXPECT_EQ("blockInstance", interfaceBlock.instanceName); + EXPECT_EQ("webgl_9", interfaceBlock.mappedName); + EXPECT_TRUE(interfaceBlock.staticUse); + + ASSERT_EQ(1u, interfaceBlock.fields.size()); + + const InterfaceBlockField &field = interfaceBlock.fields[0]; + + EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision); + EXPECT_TRUE(field.staticUse); + EXPECT_GLENUM_EQ(GL_FLOAT, field.type); + EXPECT_EQ("field", field.name); + EXPECT_EQ("webgl_5", field.mappedName); + EXPECT_FALSE(field.isRowMajorLayout); + EXPECT_TRUE(field.fields.empty()); +} + +TEST_F(CollectHashedVertexVariablesTest, StructUniform) +{ + const std::string &shaderString = + "#version 300 es\n" + "struct sType {\n" + " float field;\n" + "};" + "uniform sType u;" + "void main() {\n" + " gl_Position = vec4(u.field, 0.0, 0.0, 1.0);\n" + "}\n"; + + compile(shaderString); + + const auto &uniforms = mTranslator->getUniforms(); + ASSERT_EQ(1u, uniforms.size()); + + const Uniform &uniform = uniforms[0]; + + EXPECT_EQ(0u, uniform.arraySize); + EXPECT_EQ("u", uniform.name); + EXPECT_EQ("webgl_1", uniform.mappedName); + EXPECT_TRUE(uniform.staticUse); + + ASSERT_EQ(1u, uniform.fields.size()); + + const ShaderVariable &field = uniform.fields[0]; + + EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision); + // EXPECT_TRUE(field.staticUse); // we don't yet support struct static use + EXPECT_GLENUM_EQ(GL_FLOAT, field.type); + EXPECT_EQ("field", field.name); + EXPECT_EQ("webgl_5", field.mappedName); + EXPECT_TRUE(field.fields.empty()); +} diff --git a/gfx/angle/src/tests/compiler_tests/ConstantFolding_test.cpp b/gfx/angle/src/tests/compiler_tests/ConstantFolding_test.cpp new file mode 100755 index 000000000..4c5baffe7 --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/ConstantFolding_test.cpp @@ -0,0 +1,1064 @@ +// +// Copyright (c) 2015 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. +// +// ConstantFolding_test.cpp: +// Tests for constant folding +// + +#include + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" +#include "compiler/translator/PoolAlloc.h" +#include "compiler/translator/TranslatorESSL.h" + +using namespace sh; + +template +class ConstantFinder : public TIntermTraverser +{ + public: + ConstantFinder(const std::vector &constantVector) + : TIntermTraverser(true, false, false), + mConstantVector(constantVector), + mFaultTolerance(T()), + mFound(false) + {} + + ConstantFinder(const std::vector &constantVector, const T &faultTolerance) + : TIntermTraverser(true, false, false), + mConstantVector(constantVector), + mFaultTolerance(faultTolerance), + mFound(false) + {} + + ConstantFinder(const T &value) + : TIntermTraverser(true, false, false), + mFaultTolerance(T()), + mFound(false) + { + mConstantVector.push_back(value); + } + + void visitConstantUnion(TIntermConstantUnion *node) + { + if (node->getType().getObjectSize() == mConstantVector.size()) + { + bool found = true; + for (size_t i = 0; i < mConstantVector.size(); i++) + { + if (!isEqual(node->getUnionArrayPointer()[i], mConstantVector[i])) + { + found = false; + break; + } + } + if (found) + { + mFound = found; + } + } + } + + bool found() const { return mFound; } + + private: + bool isEqual(const TConstantUnion &node, const float &value) const + { + return mFaultTolerance >= fabsf(node.getFConst() - value); + } + + bool isEqual(const TConstantUnion &node, const int &value) const + { + ASSERT(mFaultTolerance < std::numeric_limits::max()); + // abs() returns 0 at least on some platforms when the minimum int value is passed in (it + // doesn't have a positive counterpart). + return mFaultTolerance >= abs(node.getIConst() - value) && + (node.getIConst() - value) != std::numeric_limits::min(); + } + + bool isEqual(const TConstantUnion &node, const unsigned int &value) const + { + ASSERT(mFaultTolerance < static_cast(std::numeric_limits::max())); + return static_cast(mFaultTolerance) >= + abs(static_cast(node.getUConst() - value)) && + static_cast(node.getUConst() - value) != std::numeric_limits::min(); + } + + bool isEqual(const TConstantUnion &node, const bool &value) const + { + return node.getBConst() == value; + } + + std::vector mConstantVector; + T mFaultTolerance; + bool mFound; +}; + +class ConstantFoldingTest : public testing::Test +{ + public: + ConstantFoldingTest() {} + + protected: + virtual void SetUp() + { + allocator.push(); + SetGlobalPoolAllocator(&allocator); + ShBuiltInResources resources; + InitBuiltInResources(&resources); + + mTranslatorESSL = new TranslatorESSL(GL_FRAGMENT_SHADER, SH_GLES3_SPEC); + ASSERT_TRUE(mTranslatorESSL->Init(resources)); + } + + virtual void TearDown() + { + delete mTranslatorESSL; + SetGlobalPoolAllocator(NULL); + allocator.pop(); + } + + void compile(const std::string& shaderString) + { + const char *shaderStrings[] = { shaderString.c_str() }; + + mASTRoot = mTranslatorESSL->compileTreeForTesting(shaderStrings, 1, SH_OBJECT_CODE); + if (!mASTRoot) + { + TInfoSink &infoSink = mTranslatorESSL->getInfoSink(); + FAIL() << "Shader compilation into ESSL failed " << infoSink.info.c_str(); + } + } + + template + bool constantFoundInAST(T constant) + { + ConstantFinder finder(constant); + mASTRoot->traverse(&finder); + return finder.found(); + } + + template + bool constantVectorFoundInAST(const std::vector &constantVector) + { + ConstantFinder finder(constantVector); + mASTRoot->traverse(&finder); + return finder.found(); + } + + template + bool constantColumnMajorMatrixFoundInAST(const std::vector &constantMatrix) + { + return constantVectorFoundInAST(constantMatrix); + } + + template + bool constantVectorNearFoundInAST(const std::vector &constantVector, const T &faultTolerance) + { + ConstantFinder finder(constantVector, faultTolerance); + mASTRoot->traverse(&finder); + return finder.found(); + } + + private: + TranslatorESSL *mTranslatorESSL; + TIntermNode *mASTRoot; + + TPoolAllocator allocator; +}; + +TEST_F(ConstantFoldingTest, FoldIntegerAdd) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out int my_Int;\n" + "void main() {\n" + " const int i = 1124 + 5;\n" + " my_Int = i;\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(constantFoundInAST(1124)); + ASSERT_FALSE(constantFoundInAST(5)); + ASSERT_TRUE(constantFoundInAST(1129)); +} + +TEST_F(ConstantFoldingTest, FoldIntegerSub) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out int my_Int;\n" + "void main() {\n" + " const int i = 1124 - 5;\n" + " my_Int = i;\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(constantFoundInAST(1124)); + ASSERT_FALSE(constantFoundInAST(5)); + ASSERT_TRUE(constantFoundInAST(1119)); +} + +TEST_F(ConstantFoldingTest, FoldIntegerMul) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out int my_Int;\n" + "void main() {\n" + " const int i = 1124 * 5;\n" + " my_Int = i;\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(constantFoundInAST(1124)); + ASSERT_FALSE(constantFoundInAST(5)); + ASSERT_TRUE(constantFoundInAST(5620)); +} + +TEST_F(ConstantFoldingTest, FoldIntegerDiv) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out int my_Int;\n" + "void main() {\n" + " const int i = 1124 / 5;\n" + " my_Int = i;\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(constantFoundInAST(1124)); + ASSERT_FALSE(constantFoundInAST(5)); + // Rounding mode of division is undefined in the spec but ANGLE can be expected to round down. + ASSERT_TRUE(constantFoundInAST(224)); +} + +TEST_F(ConstantFoldingTest, FoldIntegerModulus) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out int my_Int;\n" + "void main() {\n" + " const int i = 1124 % 5;\n" + " my_Int = i;\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(constantFoundInAST(1124)); + ASSERT_FALSE(constantFoundInAST(5)); + ASSERT_TRUE(constantFoundInAST(4)); +} + +TEST_F(ConstantFoldingTest, FoldVectorCrossProduct) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec3 my_Vec3;" + "void main() {\n" + " const vec3 v3 = cross(vec3(1.0f, 1.0f, 1.0f), vec3(1.0f, -1.0f, 1.0f));\n" + " my_Vec3 = v3;\n" + "}\n"; + compile(shaderString); + std::vector input1(3, 1.0f); + ASSERT_FALSE(constantVectorFoundInAST(input1)); + std::vector input2; + input2.push_back(1.0f); + input2.push_back(-1.0f); + input2.push_back(1.0f); + ASSERT_FALSE(constantVectorFoundInAST(input2)); + std::vector result; + result.push_back(2.0f); + result.push_back(0.0f); + result.push_back(-2.0f); + ASSERT_TRUE(constantVectorFoundInAST(result)); +} + +// FoldMxNMatrixInverse tests check if the matrix 'inverse' operation +// on MxN matrix is constant folded when argument is constant expression and also +// checks the correctness of the result returned by the constant folding operation. +// All the matrices including matrices in the shader code are in column-major order. +TEST_F(ConstantFoldingTest, Fold2x2MatrixInverse) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "in float i;\n" + "out vec2 my_Vec;\n" + "void main() {\n" + " const mat2 m2 = inverse(mat2(2.0f, 3.0f,\n" + " 5.0f, 7.0f));\n" + " mat2 m = m2 * mat2(i);\n" + " my_Vec = m[0];\n" + "}\n"; + compile(shaderString); + float inputElements[] = + { + 2.0f, 3.0f, + 5.0f, 7.0f + }; + std::vector input(inputElements, inputElements + 4); + ASSERT_FALSE(constantColumnMajorMatrixFoundInAST(input)); + float outputElements[] = + { + -7.0f, 3.0f, + 5.0f, -2.0f + }; + std::vector result(outputElements, outputElements + 4); + ASSERT_TRUE(constantColumnMajorMatrixFoundInAST(result)); +} + +// Check if the matrix 'inverse' operation on 3x3 matrix is constant folded. +TEST_F(ConstantFoldingTest, Fold3x3MatrixInverse) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "in float i;\n" + "out vec3 my_Vec;\n" + "void main() {\n" + " const mat3 m3 = inverse(mat3(11.0f, 13.0f, 19.0f,\n" + " 23.0f, 29.0f, 31.0f,\n" + " 37.0f, 41.0f, 43.0f));\n" + " mat3 m = m3 * mat3(i);\n" + " my_Vec = m3[0];\n" + "}\n"; + compile(shaderString); + float inputElements[] = + { + 11.0f, 13.0f, 19.0f, + 23.0f, 29.0f, 31.0f, + 37.0f, 41.0f, 43.0f + }; + std::vector input(inputElements, inputElements + 9); + ASSERT_FALSE(constantVectorFoundInAST(input)); + float outputElements[] = + { + 3.0f / 85.0f, -11.0f / 34.0f, 37.0f / 170.0f, + -79.0f / 340.0f, 23.0f / 68.0f, -12.0f / 85.0f, + 13.0f / 68.0f, -3.0f / 68.0f, -1.0f / 34.0f + }; + std::vector result(outputElements, outputElements + 9); + const float floatFaultTolerance = 0.000001f; + ASSERT_TRUE(constantVectorNearFoundInAST(result, floatFaultTolerance)); +} + +// Check if the matrix 'inverse' operation on 4x4 matrix is constant folded. +TEST_F(ConstantFoldingTest, Fold4x4MatrixInverse) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "in float i;\n" + "out vec4 my_Vec;\n" + "void main() {\n" + " const mat4 m4 = inverse(mat4(29.0f, 31.0f, 37.0f, 41.0f,\n" + " 43.0f, 47.0f, 53.0f, 59.0f,\n" + " 61.0f, 67.0f, 71.0f, 73.0f,\n" + " 79.0f, 83.0f, 89.0f, 97.0f));\n" + " mat4 m = m4 * mat4(i);\n" + " my_Vec = m[0];\n" + "}\n"; + compile(shaderString); + float inputElements[] = + { + 29.0f, 31.0f, 37.0f, 41.0f, + 43.0f, 47.0f, 53.0f, 59.0f, + 61.0f, 67.0f, 71.0f, 73.0f, + 79.0f, 83.0f, 89.0f, 97.0f + }; + std::vector input(inputElements, inputElements + 16); + ASSERT_FALSE(constantVectorFoundInAST(input)); + float outputElements[] = + { + 43.0f / 126.0f, -11.0f / 21.0f, -2.0f / 21.0f, 31.0f / 126.0f, + -5.0f / 7.0f, 9.0f / 14.0f, 1.0f / 14.0f, -1.0f / 7.0f, + 85.0f / 126.0f, -11.0f / 21.0f, 43.0f / 210.0f, -38.0f / 315.0f, + -2.0f / 7.0f, 5.0f / 14.0f, -6.0f / 35.0f, 3.0f / 70.0f + }; + std::vector result(outputElements, outputElements + 16); + const float floatFaultTolerance = 0.00001f; + ASSERT_TRUE(constantVectorNearFoundInAST(result, floatFaultTolerance)); +} + +// FoldMxNMatrixDeterminant tests check if the matrix 'determinant' operation +// on MxN matrix is constant folded when argument is constant expression and also +// checks the correctness of the result returned by the constant folding operation. +// All the matrices including matrices in the shader code are in column-major order. +TEST_F(ConstantFoldingTest, Fold2x2MatrixDeterminant) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out float my_Float;" + "void main() {\n" + " const float f = determinant(mat2(2.0f, 3.0f,\n" + " 5.0f, 7.0f));\n" + " my_Float = f;\n" + "}\n"; + compile(shaderString); + float inputElements[] = + { + 2.0f, 3.0f, + 5.0f, 7.0f + }; + std::vector input(inputElements, inputElements + 4); + ASSERT_FALSE(constantColumnMajorMatrixFoundInAST(input)); + ASSERT_TRUE(constantFoundInAST(-1.0f)); +} + +// Check if the matrix 'determinant' operation on 3x3 matrix is constant folded. +TEST_F(ConstantFoldingTest, Fold3x3MatrixDeterminant) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out float my_Float;" + "void main() {\n" + " const float f = determinant(mat3(11.0f, 13.0f, 19.0f,\n" + " 23.0f, 29.0f, 31.0f,\n" + " 37.0f, 41.0f, 43.0f));\n" + " my_Float = f;\n" + "}\n"; + compile(shaderString); + float inputElements[] = + { + 11.0f, 13.0f, 19.0f, + 23.0f, 29.0f, 31.0f, + 37.0f, 41.0f, 43.0f + }; + std::vector input(inputElements, inputElements + 9); + ASSERT_FALSE(constantColumnMajorMatrixFoundInAST(input)); + ASSERT_TRUE(constantFoundInAST(-680.0f)); +} + +// Check if the matrix 'determinant' operation on 4x4 matrix is constant folded. +TEST_F(ConstantFoldingTest, Fold4x4MatrixDeterminant) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out float my_Float;" + "void main() {\n" + " const float f = determinant(mat4(29.0f, 31.0f, 37.0f, 41.0f,\n" + " 43.0f, 47.0f, 53.0f, 59.0f,\n" + " 61.0f, 67.0f, 71.0f, 73.0f,\n" + " 79.0f, 83.0f, 89.0f, 97.0f));\n" + " my_Float = f;\n" + "}\n"; + compile(shaderString); + float inputElements[] = + { + 29.0f, 31.0f, 37.0f, 41.0f, + 43.0f, 47.0f, 53.0f, 59.0f, + 61.0f, 67.0f, 71.0f, 73.0f, + 79.0f, 83.0f, 89.0f, 97.0f + }; + std::vector input(inputElements, inputElements + 16); + ASSERT_FALSE(constantColumnMajorMatrixFoundInAST(input)); + ASSERT_TRUE(constantFoundInAST(-2520.0f)); +} + +// Check if the matrix 'transpose' operation on 3x3 matrix is constant folded. +// All the matrices including matrices in the shader code are in column-major order. +TEST_F(ConstantFoldingTest, Fold3x3MatrixTranspose) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "in float i;\n" + "out vec3 my_Vec;\n" + "void main() {\n" + " const mat3 m3 = transpose(mat3(11.0f, 13.0f, 19.0f,\n" + " 23.0f, 29.0f, 31.0f,\n" + " 37.0f, 41.0f, 43.0f));\n" + " mat3 m = m3 * mat3(i);\n" + " my_Vec = m[0];\n" + "}\n"; + compile(shaderString); + float inputElements[] = + { + 11.0f, 13.0f, 19.0f, + 23.0f, 29.0f, 31.0f, + 37.0f, 41.0f, 43.0f + }; + std::vector input(inputElements, inputElements + 9); + ASSERT_FALSE(constantColumnMajorMatrixFoundInAST(input)); + float outputElements[] = + { + 11.0f, 23.0f, 37.0f, + 13.0f, 29.0f, 41.0f, + 19.0f, 31.0f, 43.0f + }; + std::vector result(outputElements, outputElements + 9); + ASSERT_TRUE(constantColumnMajorMatrixFoundInAST(result)); +} + +// Test that 0xFFFFFFFF wraps to -1 when parsed as integer. +// This is featured in the examples of ESSL3 section 4.1.3. ESSL3 section 12.42 +// means that any 32-bit unsigned integer value is a valid literal. +TEST_F(ConstantFoldingTest, ParseWrappedHexIntLiteral) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "precision highp int;\n" + "uniform int inInt;\n" + "out vec4 my_Vec;\n" + "void main() {\n" + " const int i = 0xFFFFFFFF;\n" + " my_Vec = vec4(i * inInt);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(-1)); +} + +// Test that 3000000000 wraps to -1294967296 when parsed as integer. +// This is featured in the examples of GLSL 4.5, and ESSL behavior should match +// desktop GLSL when it comes to integer parsing. +TEST_F(ConstantFoldingTest, ParseWrappedDecimalIntLiteral) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "precision highp int;\n" + "uniform int inInt;\n" + "out vec4 my_Vec;\n" + "void main() {\n" + " const int i = 3000000000;\n" + " my_Vec = vec4(i * inInt);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(-1294967296)); +} + +// Test that 0xFFFFFFFFu is parsed correctly as an unsigned integer literal. +// This is featured in the examples of ESSL3 section 4.1.3. ESSL3 section 12.42 +// means that any 32-bit unsigned integer value is a valid literal. +TEST_F(ConstantFoldingTest, ParseMaxUintLiteral) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "precision highp int;\n" + "uniform uint inInt;\n" + "out vec4 my_Vec;\n" + "void main() {\n" + " const uint i = 0xFFFFFFFFu;\n" + " my_Vec = vec4(i * inInt);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(0xFFFFFFFFu)); +} + +// Test that unary minus applied to unsigned int is constant folded correctly. +// This is featured in the examples of ESSL3 section 4.1.3. ESSL3 section 12.42 +// means that any 32-bit unsigned integer value is a valid literal. +TEST_F(ConstantFoldingTest, FoldUnaryMinusOnUintLiteral) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "precision highp int;\n" + "uniform uint inInt;\n" + "out vec4 my_Vec;\n" + "void main() {\n" + " const uint i = -1u;\n" + " my_Vec = vec4(i * inInt);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(0xFFFFFFFFu)); +} + +// Test that constant mat2 initialization with a mat2 parameter works correctly. +TEST_F(ConstantFoldingTest, FoldMat2ConstructorTakingMat2) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float mult;\n" + "void main() {\n" + " const mat2 cm = mat2(mat2(0.0, 1.0, 2.0, 3.0));\n" + " mat2 m = cm * mult;\n" + " gl_FragColor = vec4(m[0], m[1]);\n" + "}\n"; + compile(shaderString); + float outputElements[] = + { + 0.0f, 1.0f, + 2.0f, 3.0f + }; + std::vector result(outputElements, outputElements + 4); + ASSERT_TRUE(constantColumnMajorMatrixFoundInAST(result)); +} + +// Test that constant mat2 initialization with an int parameter works correctly. +TEST_F(ConstantFoldingTest, FoldMat2ConstructorTakingScalar) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float mult;\n" + "void main() {\n" + " const mat2 cm = mat2(3);\n" + " mat2 m = cm * mult;\n" + " gl_FragColor = vec4(m[0], m[1]);\n" + "}\n"; + compile(shaderString); + float outputElements[] = + { + 3.0f, 0.0f, + 0.0f, 3.0f + }; + std::vector result(outputElements, outputElements + 4); + ASSERT_TRUE(constantColumnMajorMatrixFoundInAST(result)); +} + +// Test that constant mat2 initialization with a mix of parameters works correctly. +TEST_F(ConstantFoldingTest, FoldMat2ConstructorTakingMix) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float mult;\n" + "void main() {\n" + " const mat2 cm = mat2(-1, vec2(0.0, 1.0), vec4(2.0));\n" + " mat2 m = cm * mult;\n" + " gl_FragColor = vec4(m[0], m[1]);\n" + "}\n"; + compile(shaderString); + float outputElements[] = + { + -1.0, 0.0f, + 1.0f, 2.0f + }; + std::vector result(outputElements, outputElements + 4); + ASSERT_TRUE(constantColumnMajorMatrixFoundInAST(result)); +} + +// Test that constant mat2 initialization with a mat3 parameter works correctly. +TEST_F(ConstantFoldingTest, FoldMat2ConstructorTakingMat3) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float mult;\n" + "void main() {\n" + " const mat2 cm = mat2(mat3(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0));\n" + " mat2 m = cm * mult;\n" + " gl_FragColor = vec4(m[0], m[1]);\n" + "}\n"; + compile(shaderString); + float outputElements[] = + { + 0.0f, 1.0f, + 3.0f, 4.0f + }; + std::vector result(outputElements, outputElements + 4); + ASSERT_TRUE(constantColumnMajorMatrixFoundInAST(result)); +} + +// Test that constant mat4x3 initialization with a mat3x2 parameter works correctly. +TEST_F(ConstantFoldingTest, FoldMat4x3ConstructorTakingMat3x2) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "uniform float mult;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " const mat4x3 cm = mat4x3(mat3x2(1.0, 2.0,\n" + " 3.0, 4.0,\n" + " 5.0, 6.0));\n" + " mat4x3 m = cm * mult;\n" + " my_FragColor = vec4(m[0], m[1][0]);\n" + "}\n"; + compile(shaderString); + float outputElements[] = + { + 1.0f, 2.0f, 0.0f, + 3.0f, 4.0f, 0.0f, + 5.0f, 6.0f, 1.0f, + 0.0f, 0.0f, 0.0f + }; + std::vector result(outputElements, outputElements + 12); + ASSERT_TRUE(constantColumnMajorMatrixFoundInAST(result)); +} + + +// Test that constant mat2 initialization with a vec4 parameter works correctly. +TEST_F(ConstantFoldingTest, FoldMat2ConstructorTakingVec4) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float mult;\n" + "void main() {\n" + " const mat2 cm = mat2(vec4(0.0, 1.0, 2.0, 3.0));\n" + " mat2 m = cm * mult;\n" + " gl_FragColor = vec4(m[0], m[1]);\n" + "}\n"; + compile(shaderString); + float outputElements[] = + { + 0.0f, 1.0f, + 2.0f, 3.0f + }; + std::vector result(outputElements, outputElements + 4); + ASSERT_TRUE(constantColumnMajorMatrixFoundInAST(result)); +} + +// Test that equality comparison of two different structs with a nested struct inside returns false. +TEST_F(ConstantFoldingTest, FoldNestedDifferentStructEqualityComparison) +{ + const std::string &shaderString = + "precision mediump float;\n" + "struct nested {\n" + " float f\n;" + "};\n" + "struct S {\n" + " nested a;\n" + " float f;\n" + "};\n" + "uniform vec4 mult;\n" + "void main()\n" + "{\n" + " const S s1 = S(nested(0.0), 2.0);\n" + " const S s2 = S(nested(0.0), 3.0);\n" + " gl_FragColor = (s1 == s2 ? 1.0 : 0.5) * mult;\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(0.5f)); +} + +// Test that equality comparison of two identical structs with a nested struct inside returns true. +TEST_F(ConstantFoldingTest, FoldNestedIdenticalStructEqualityComparison) +{ + const std::string &shaderString = + "precision mediump float;\n" + "struct nested {\n" + " float f\n;" + "};\n" + "struct S {\n" + " nested a;\n" + " float f;\n" + " int i;\n" + "};\n" + "uniform vec4 mult;\n" + "void main()\n" + "{\n" + " const S s1 = S(nested(0.0), 2.0, 3);\n" + " const S s2 = S(nested(0.0), 2.0, 3);\n" + " gl_FragColor = (s1 == s2 ? 1.0 : 0.5) * mult;\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(1.0f)); +} + +// Test that right elements are chosen from non-square matrix +TEST_F(ConstantFoldingTest, FoldNonSquareMatrixIndexing) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " my_FragColor = mat3x4(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)[1];\n" + "}\n"; + compile(shaderString); + float outputElements[] = {4.0f, 5.0f, 6.0f, 7.0f}; + std::vector result(outputElements, outputElements + 4); + ASSERT_TRUE(constantVectorFoundInAST(result)); +} + +// Test that folding outer product of vectors with non-matching lengths works. +TEST_F(ConstantFoldingTest, FoldNonSquareOuterProduct) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " mat3x2 prod = outerProduct(vec2(2.0, 3.0), vec3(5.0, 7.0, 11.0));\n" + " my_FragColor = vec4(prod[0].x);\n" + "}\n"; + compile(shaderString); + // clang-format off + float outputElements[] = + { + 10.0f, 15.0f, + 14.0f, 21.0f, + 22.0f, 33.0f + }; + // clang-format on + std::vector result(outputElements, outputElements + 6); + ASSERT_TRUE(constantColumnMajorMatrixFoundInAST(result)); +} + +// Test that folding bit shift left with non-matching signedness works. +TEST_F(ConstantFoldingTest, FoldBitShiftLeftDifferentSignedness) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " uint u = 0xffffffffu << 31;\n" + " my_FragColor = vec4(u);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(0x80000000u)); +} + +// Test that folding bit shift right with non-matching signedness works. +TEST_F(ConstantFoldingTest, FoldBitShiftRightDifferentSignedness) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " uint u = 0xffffffffu >> 30;\n" + " my_FragColor = vec4(u);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(0x3u)); +} + +// Test that folding signed bit shift right extends the sign bit. +// ESSL 3.00.6 section 5.9 Expressions. +TEST_F(ConstantFoldingTest, FoldBitShiftRightExtendSignBit) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " const int i = 0x8fffe000 >> 6;\n" + " uint u = uint(i);" + " my_FragColor = vec4(u);\n" + "}\n"; + compile(shaderString); + // The bits of the operand are 0x8fffe000 = 1000 1111 1111 1111 1110 0000 0000 0000 + // After shifting, they become 1111 1110 0011 1111 1111 1111 1000 0000 = 0xfe3fff80 + ASSERT_TRUE(constantFoundInAST(0xfe3fff80u)); +} + +// Signed bit shift left should interpret its operand as a bit pattern. As a consequence a number +// may turn from positive to negative when shifted left. +// ESSL 3.00.6 section 5.9 Expressions. +TEST_F(ConstantFoldingTest, FoldBitShiftLeftInterpretedAsBitPattern) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " const int i = 0x1fffffff << 3;\n" + " uint u = uint(i);" + " my_FragColor = vec4(u);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(0xfffffff8u)); +} + +// Test that dividing the minimum signed integer by -1 works. +// ESSL 3.00.6 section 4.1.3 Integers: +// "However, for the case where the minimum representable value is divided by -1, it is allowed to +// return either the minimum representable value or the maximum representable value." +TEST_F(ConstantFoldingTest, FoldDivideMinimumIntegerByMinusOne) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " int i = 0x80000000 / (-1);\n" + " my_FragColor = vec4(i);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(0x7fffffff) || constantFoundInAST(-0x7fffffff - 1)); +} + +// Test that folding an unsigned integer addition that overflows works. +// ESSL 3.00.6 section 4.1.3 Integers: +// "For all precisions, operations resulting in overflow or underflow will not cause any exception, +// nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result where +// n is the size in bits of the integer." +TEST_F(ConstantFoldingTest, FoldUnsignedIntegerAddOverflow) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " uint u = 0xffffffffu + 43u;\n" + " my_FragColor = vec4(u);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(42u)); +} + +// Test that folding a signed integer addition that overflows works. +// ESSL 3.00.6 section 4.1.3 Integers: +// "For all precisions, operations resulting in overflow or underflow will not cause any exception, +// nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result where +// n is the size in bits of the integer." +TEST_F(ConstantFoldingTest, FoldSignedIntegerAddOverflow) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " int i = 0x7fffffff + 4;\n" + " my_FragColor = vec4(i);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(-0x7ffffffd)); +} + +// Test that folding an unsigned integer subtraction that overflows works. +// ESSL 3.00.6 section 4.1.3 Integers: +// "For all precisions, operations resulting in overflow or underflow will not cause any exception, +// nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result where +// n is the size in bits of the integer." +TEST_F(ConstantFoldingTest, FoldUnsignedIntegerDiffOverflow) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " uint u = 0u - 5u;\n" + " my_FragColor = vec4(u);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(0xfffffffbu)); +} + +// Test that folding a signed integer subtraction that overflows works. +// ESSL 3.00.6 section 4.1.3 Integers: +// "For all precisions, operations resulting in overflow or underflow will not cause any exception, +// nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result where +// n is the size in bits of the integer." +TEST_F(ConstantFoldingTest, FoldSignedIntegerDiffOverflow) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " int i = -0x7fffffff - 7;\n" + " my_FragColor = vec4(i);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(0x7ffffffa)); +} + +// Test that folding an unsigned integer multiplication that overflows works. +// ESSL 3.00.6 section 4.1.3 Integers: +// "For all precisions, operations resulting in overflow or underflow will not cause any exception, +// nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result where +// n is the size in bits of the integer." +TEST_F(ConstantFoldingTest, FoldUnsignedIntegerMultiplyOverflow) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " uint u = 0xffffffffu * 10u;\n" + " my_FragColor = vec4(u);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(0xfffffff6u)); +} + +// Test that folding a signed integer multiplication that overflows works. +// ESSL 3.00.6 section 4.1.3 Integers: +// "For all precisions, operations resulting in overflow or underflow will not cause any exception, +// nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result where +// n is the size in bits of the integer." +TEST_F(ConstantFoldingTest, FoldSignedIntegerMultiplyOverflow) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " int i = 0x7fffffff * 42;\n" + " my_FragColor = vec4(i);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(-42)); +} + +// Test that folding of negating the minimum representable integer works. Note that in the test +// "0x80000000" is a negative literal, and the minus sign before it is the negation operator. +// ESSL 3.00.6 section 4.1.3 Integers: +// "For all precisions, operations resulting in overflow or underflow will not cause any exception, +// nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result where +// n is the size in bits of the integer." +TEST_F(ConstantFoldingTest, FoldMinimumSignedIntegerNegation) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " int i = -0x80000000;\n" + " my_FragColor = vec4(i);\n" + "}\n"; + compile(shaderString); + // Negating the minimum signed integer overflows the positive range, so it wraps back to itself. + ASSERT_TRUE(constantFoundInAST(-0x7fffffff - 1)); +} + +// Test that folding of shifting the minimum representable integer works. +TEST_F(ConstantFoldingTest, FoldMinimumSignedIntegerRightShift) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " int i = (0x80000000 >> 1);\n" + " int j = (0x80000000 >> 7);\n" + " my_FragColor = vec4(i, j, i, j);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(-0x40000000)); + ASSERT_TRUE(constantFoundInAST(-0x01000000)); +} + +// Test that folding of shifting by 0 works. +TEST_F(ConstantFoldingTest, FoldShiftByZero) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " int i = (3 >> 0);\n" + " int j = (73 << 0);\n" + " my_FragColor = vec4(i, j, i, j);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(3)); + ASSERT_TRUE(constantFoundInAST(73)); +} diff --git a/gfx/angle/src/tests/compiler_tests/DebugShaderPrecision_test.cpp b/gfx/angle/src/tests/compiler_tests/DebugShaderPrecision_test.cpp new file mode 100755 index 000000000..d1bee424a --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/DebugShaderPrecision_test.cpp @@ -0,0 +1,1047 @@ +// +// Copyright (c) 2014 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. +// +// DebugShaderPrecision_test.cpp: +// Tests for writing the code for shader precision emulation. +// + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" +#include "tests/test_utils/compiler_test.h" + +using namespace sh; + +class DebugShaderPrecisionTest : public MatchOutputCodeTest +{ + public: + DebugShaderPrecisionTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, 0, SH_ESSL_OUTPUT) + { + addOutputType(SH_GLSL_COMPATIBILITY_OUTPUT); +#if defined(ANGLE_ENABLE_HLSL) + addOutputType(SH_HLSL_4_1_OUTPUT); +#endif + getResources()->WEBGL_debug_shader_precision = 1; + } + + protected: + bool foundInAllGLSLCode(const char *str) + { + return foundInCode(SH_GLSL_COMPATIBILITY_OUTPUT, str) && foundInCode(SH_ESSL_OUTPUT, str); + } + + bool foundInHLSLCode(const char *stringToFind) const + { +#if defined(ANGLE_ENABLE_HLSL) + return foundInCode(SH_HLSL_4_1_OUTPUT, stringToFind); +#else + return true; +#endif + } +}; + +class NoDebugShaderPrecisionTest : public MatchOutputCodeTest +{ + public: + NoDebugShaderPrecisionTest() + : MatchOutputCodeTest(GL_FRAGMENT_SHADER, 0, SH_GLSL_COMPATIBILITY_OUTPUT) + { + } +}; + +TEST_F(DebugShaderPrecisionTest, RoundingFunctionsDefined) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float u;\n" + "void main() {\n" + " gl_FragColor = vec4(u);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInESSLCode("highp float angle_frm(in highp float")); + ASSERT_TRUE(foundInESSLCode("highp vec2 angle_frm(in highp vec2")); + ASSERT_TRUE(foundInESSLCode("highp vec3 angle_frm(in highp vec3")); + ASSERT_TRUE(foundInESSLCode("highp vec4 angle_frm(in highp vec4")); + ASSERT_TRUE(foundInESSLCode("highp mat2 angle_frm(in highp mat2")); + ASSERT_TRUE(foundInESSLCode("highp mat3 angle_frm(in highp mat3")); + ASSERT_TRUE(foundInESSLCode("highp mat4 angle_frm(in highp mat4")); + + ASSERT_TRUE(foundInESSLCode("highp float angle_frl(in highp float")); + ASSERT_TRUE(foundInESSLCode("highp vec2 angle_frl(in highp vec2")); + ASSERT_TRUE(foundInESSLCode("highp vec3 angle_frl(in highp vec3")); + ASSERT_TRUE(foundInESSLCode("highp vec4 angle_frl(in highp vec4")); + ASSERT_TRUE(foundInESSLCode("highp mat2 angle_frl(in highp mat2")); + ASSERT_TRUE(foundInESSLCode("highp mat3 angle_frl(in highp mat3")); + ASSERT_TRUE(foundInESSLCode("highp mat4 angle_frl(in highp mat4")); + + ASSERT_TRUE(foundInGLSLCode("float angle_frm(in float")); + ASSERT_TRUE(foundInGLSLCode("vec2 angle_frm(in vec2")); + ASSERT_TRUE(foundInGLSLCode("vec3 angle_frm(in vec3")); + ASSERT_TRUE(foundInGLSLCode("vec4 angle_frm(in vec4")); + ASSERT_TRUE(foundInGLSLCode("mat2 angle_frm(in mat2")); + ASSERT_TRUE(foundInGLSLCode("mat3 angle_frm(in mat3")); + ASSERT_TRUE(foundInGLSLCode("mat4 angle_frm(in mat4")); + + ASSERT_TRUE(foundInGLSLCode("float angle_frl(in float")); + ASSERT_TRUE(foundInGLSLCode("vec2 angle_frl(in vec2")); + ASSERT_TRUE(foundInGLSLCode("vec3 angle_frl(in vec3")); + ASSERT_TRUE(foundInGLSLCode("vec4 angle_frl(in vec4")); + ASSERT_TRUE(foundInGLSLCode("mat2 angle_frl(in mat2")); + ASSERT_TRUE(foundInGLSLCode("mat3 angle_frl(in mat3")); + ASSERT_TRUE(foundInGLSLCode("mat4 angle_frl(in mat4")); + + ASSERT_TRUE(foundInHLSLCode("float1 angle_frm(float1")); + ASSERT_TRUE(foundInHLSLCode("float2 angle_frm(float2")); + ASSERT_TRUE(foundInHLSLCode("float3 angle_frm(float3")); + ASSERT_TRUE(foundInHLSLCode("float4 angle_frm(float4")); + ASSERT_TRUE(foundInHLSLCode("float2x2 angle_frm(float2x2")); + ASSERT_TRUE(foundInHLSLCode("float3x3 angle_frm(float3x3")); + ASSERT_TRUE(foundInHLSLCode("float4x4 angle_frm(float4x4")); + + ASSERT_TRUE(foundInHLSLCode("float1 angle_frl(float1")); + ASSERT_TRUE(foundInHLSLCode("float2 angle_frl(float2")); + ASSERT_TRUE(foundInHLSLCode("float3 angle_frl(float3")); + ASSERT_TRUE(foundInHLSLCode("float4 angle_frl(float4")); + ASSERT_TRUE(foundInHLSLCode("float2x2 angle_frl(float2x2")); + ASSERT_TRUE(foundInHLSLCode("float3x3 angle_frl(float3x3")); + ASSERT_TRUE(foundInHLSLCode("float4x4 angle_frl(float4x4")); + + // Check that ESSL 3.00 rounding functions for non-square matrices are not defined. + ASSERT_TRUE(notFoundInCode("mat2x")); + ASSERT_TRUE(notFoundInCode("mat3x")); + ASSERT_TRUE(notFoundInCode("mat4x")); +} + +// Test that all ESSL 3.00 shaders get rounding function definitions for non-square matrices. +TEST_F(DebugShaderPrecisionTest, NonSquareMatrixRoundingFunctionsDefinedES3) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "uniform float u;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " my_FragColor = vec4(u);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInESSLCode("highp mat2x3 angle_frm(in highp mat2x3")); + ASSERT_TRUE(foundInESSLCode("highp mat2x4 angle_frm(in highp mat2x4")); + ASSERT_TRUE(foundInESSLCode("highp mat3x2 angle_frm(in highp mat3x2")); + ASSERT_TRUE(foundInESSLCode("highp mat3x4 angle_frm(in highp mat3x4")); + ASSERT_TRUE(foundInESSLCode("highp mat4x2 angle_frm(in highp mat4x2")); + ASSERT_TRUE(foundInESSLCode("highp mat4x3 angle_frm(in highp mat4x3")); + + ASSERT_TRUE(foundInESSLCode("highp mat2x3 angle_frl(in highp mat2x3")); + ASSERT_TRUE(foundInESSLCode("highp mat2x4 angle_frl(in highp mat2x4")); + ASSERT_TRUE(foundInESSLCode("highp mat3x2 angle_frl(in highp mat3x2")); + ASSERT_TRUE(foundInESSLCode("highp mat3x4 angle_frl(in highp mat3x4")); + ASSERT_TRUE(foundInESSLCode("highp mat4x2 angle_frl(in highp mat4x2")); + ASSERT_TRUE(foundInESSLCode("highp mat4x3 angle_frl(in highp mat4x3")); + + ASSERT_TRUE(foundInGLSLCode("mat2x3 angle_frm(in mat2x3")); + ASSERT_TRUE(foundInGLSLCode("mat2x4 angle_frm(in mat2x4")); + ASSERT_TRUE(foundInGLSLCode("mat3x2 angle_frm(in mat3x2")); + ASSERT_TRUE(foundInGLSLCode("mat3x4 angle_frm(in mat3x4")); + ASSERT_TRUE(foundInGLSLCode("mat4x2 angle_frm(in mat4x2")); + ASSERT_TRUE(foundInGLSLCode("mat4x3 angle_frm(in mat4x3")); + + ASSERT_TRUE(foundInGLSLCode("mat2x3 angle_frl(in mat2x3")); + ASSERT_TRUE(foundInGLSLCode("mat2x4 angle_frl(in mat2x4")); + ASSERT_TRUE(foundInGLSLCode("mat3x2 angle_frl(in mat3x2")); + ASSERT_TRUE(foundInGLSLCode("mat3x4 angle_frl(in mat3x4")); + ASSERT_TRUE(foundInGLSLCode("mat4x2 angle_frl(in mat4x2")); + ASSERT_TRUE(foundInGLSLCode("mat4x3 angle_frl(in mat4x3")); + + ASSERT_TRUE(foundInHLSLCode("float2x3 angle_frm(float2x3")); + ASSERT_TRUE(foundInHLSLCode("float2x4 angle_frm(float2x4")); + ASSERT_TRUE(foundInHLSLCode("float3x2 angle_frm(float3x2")); + ASSERT_TRUE(foundInHLSLCode("float3x4 angle_frm(float3x4")); + ASSERT_TRUE(foundInHLSLCode("float4x2 angle_frm(float4x2")); + ASSERT_TRUE(foundInHLSLCode("float4x3 angle_frm(float4x3")); + + ASSERT_TRUE(foundInHLSLCode("float2x3 angle_frl(float2x3")); + ASSERT_TRUE(foundInHLSLCode("float2x4 angle_frl(float2x4")); + ASSERT_TRUE(foundInHLSLCode("float3x2 angle_frl(float3x2")); + ASSERT_TRUE(foundInHLSLCode("float3x4 angle_frl(float3x4")); + ASSERT_TRUE(foundInHLSLCode("float4x2 angle_frl(float4x2")); + ASSERT_TRUE(foundInHLSLCode("float4x3 angle_frl(float4x3")); +} + +TEST_F(DebugShaderPrecisionTest, PragmaDisablesEmulation) +{ + const std::string &shaderString = + "#pragma webgl_debug_shader_precision(off)\n" + "precision mediump float;\n" + "uniform float u;\n" + "void main() {\n" + " gl_FragColor = vec4(u);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(notFoundInCode("angle_frm")); + const std::string &shaderStringPragmaOn = + "#pragma webgl_debug_shader_precision(on)\n" + "precision mediump float;\n" + "uniform float u;\n" + "void main() {\n" + " gl_FragColor = vec4(u);\n" + "}\n"; + compile(shaderStringPragmaOn); + ASSERT_TRUE(foundInCode("angle_frm")); +} + +// Emulation can't be toggled on for only a part of a shader. +// Only the last pragma in the shader has an effect. +TEST_F(DebugShaderPrecisionTest, MultiplePragmas) +{ + const std::string &shaderString = + "#pragma webgl_debug_shader_precision(off)\n" + "precision mediump float;\n" + "uniform float u;\n" + "void main() {\n" + " gl_FragColor = vec4(u);\n" + "}\n" + "#pragma webgl_debug_shader_precision(on)\n"; + compile(shaderString); + ASSERT_TRUE(foundInCode("angle_frm")); +} + +TEST_F(NoDebugShaderPrecisionTest, HelpersWrittenOnlyWithExtension) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float u;\n" + "void main() {\n" + " gl_FragColor = vec4(u);\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundInCode("angle_frm")); +} + +TEST_F(NoDebugShaderPrecisionTest, PragmaHasEffectsOnlyWithExtension) +{ + const std::string &shaderString = + "#pragma webgl_debug_shader_precision(on)\n" + "precision mediump float;\n" + "uniform float u;\n" + "void main() {\n" + " gl_FragColor = vec4(u);\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundInCode("angle_frm")); +} + +TEST_F(DebugShaderPrecisionTest, DeclarationsAndConstants) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform vec4 f;\n" + "uniform float uu, uu2;\n" + "varying float vv, vv2;\n" + "float gg = 0.0, gg2;\n" + "void main() {\n" + " float aa = 0.0, aa2;\n" + " gl_FragColor = f;\n" + "}\n"; + compile(shaderString); + // Declarations or constants should not have rounding inserted around them + ASSERT_TRUE(notFoundInCode("angle_frm(0")); + ASSERT_TRUE(notFoundInCode("angle_frm(uu")); + ASSERT_TRUE(notFoundInCode("angle_frm(vv")); + ASSERT_TRUE(notFoundInCode("angle_frm(gg")); + ASSERT_TRUE(notFoundInCode("angle_frm(aa")); +} + +// Test that expressions that are part of initialization have rounding. +TEST_F(DebugShaderPrecisionTest, InitializerRounding) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float u;\n" + "void main() {\n" + " float a = u;\n" + " gl_FragColor = vec4(a);\n" + "}\n"; + compile(shaderString); + // An expression that's part of initialization should have rounding + ASSERT_TRUE(foundInAllGLSLCode("angle_frm(u)")); + ASSERT_TRUE(foundInHLSLCode("angle_frm(_u)")); +} + +// Test that compound additions have rounding in the GLSL translations. +TEST_F(DebugShaderPrecisionTest, CompoundAddFunction) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform vec4 u;\n" + "uniform vec4 u2;\n" + "void main() {\n" + " vec4 v = u;\n" + " v += u2;\n" + " gl_FragColor = v;\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInESSLCode( + "highp vec4 angle_compound_add_frm(inout highp vec4 x, in highp vec4 y) {\n" + " x = angle_frm(angle_frm(x) + y);" + )); + ASSERT_TRUE(foundInGLSLCode( + "vec4 angle_compound_add_frm(inout vec4 x, in vec4 y) {\n" + " x = angle_frm(angle_frm(x) + y);" + )); + ASSERT_TRUE( + foundInHLSLCode("float4 angle_compound_add_frm(inout float4 x, in float4 y) {\n" + " x = angle_frm(angle_frm(x) + y);")); + ASSERT_TRUE(foundInAllGLSLCode("angle_compound_add_frm(v, angle_frm(u2));")); + ASSERT_TRUE(foundInHLSLCode("angle_compound_add_frm(_v, angle_frm(_u2));")); + ASSERT_TRUE(notFoundInCode("+=")); +} + +TEST_F(DebugShaderPrecisionTest, CompoundSubFunction) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform vec4 u;\n" + "uniform vec4 u2;\n" + "void main() {\n" + " vec4 v = u;\n" + " v -= u2;\n" + " gl_FragColor = v;\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInESSLCode( + "highp vec4 angle_compound_sub_frm(inout highp vec4 x, in highp vec4 y) {\n" + " x = angle_frm(angle_frm(x) - y);" + )); + ASSERT_TRUE(foundInGLSLCode( + "vec4 angle_compound_sub_frm(inout vec4 x, in vec4 y) {\n" + " x = angle_frm(angle_frm(x) - y);" + )); + ASSERT_TRUE( + foundInHLSLCode("float4 angle_compound_sub_frm(inout float4 x, in float4 y) {\n" + " x = angle_frm(angle_frm(x) - y);")); + ASSERT_TRUE(foundInAllGLSLCode("angle_compound_sub_frm(v, angle_frm(u2));")); + ASSERT_TRUE(foundInHLSLCode("angle_compound_sub_frm(_v, angle_frm(_u2));")); + ASSERT_TRUE(notFoundInCode("-=")); +} + +TEST_F(DebugShaderPrecisionTest, CompoundDivFunction) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform vec4 u;\n" + "uniform vec4 u2;\n" + "void main() {\n" + " vec4 v = u;\n" + " v /= u2;\n" + " gl_FragColor = v;\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInESSLCode( + "highp vec4 angle_compound_div_frm(inout highp vec4 x, in highp vec4 y) {\n" + " x = angle_frm(angle_frm(x) / y);" + )); + ASSERT_TRUE(foundInGLSLCode( + "vec4 angle_compound_div_frm(inout vec4 x, in vec4 y) {\n" + " x = angle_frm(angle_frm(x) / y);" + )); + ASSERT_TRUE( + foundInHLSLCode("float4 angle_compound_div_frm(inout float4 x, in float4 y) {\n" + " x = angle_frm(angle_frm(x) / y);")); + ASSERT_TRUE(foundInAllGLSLCode("angle_compound_div_frm(v, angle_frm(u2));")); + ASSERT_TRUE(foundInHLSLCode("angle_compound_div_frm(_v, angle_frm(_u2));")); + ASSERT_TRUE(notFoundInCode("/=")); +} + +TEST_F(DebugShaderPrecisionTest, CompoundMulFunction) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform vec4 u;\n" + "uniform vec4 u2;\n" + "void main() {\n" + " vec4 v = u;\n" + " v *= u2;\n" + " gl_FragColor = v;\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInESSLCode( + "highp vec4 angle_compound_mul_frm(inout highp vec4 x, in highp vec4 y) {\n" + " x = angle_frm(angle_frm(x) * y);" + )); + ASSERT_TRUE(foundInGLSLCode( + "vec4 angle_compound_mul_frm(inout vec4 x, in vec4 y) {\n" + " x = angle_frm(angle_frm(x) * y);" + )); + ASSERT_TRUE( + foundInHLSLCode("float4 angle_compound_mul_frm(inout float4 x, in float4 y) {\n" + " x = angle_frm(angle_frm(x) * y);")); + ASSERT_TRUE(foundInAllGLSLCode("angle_compound_mul_frm(v, angle_frm(u2));")); + ASSERT_TRUE(foundInHLSLCode("angle_compound_mul_frm(_v, angle_frm(_u2));")); + ASSERT_TRUE(notFoundInCode("*=")); +} + +TEST_F(DebugShaderPrecisionTest, CompoundAddVectorPlusScalarFunction) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform vec4 u;\n" + "uniform float u2;\n" + "void main() {\n" + " vec4 v = u;\n" + " v += u2;\n" + " gl_FragColor = v;\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInESSLCode( + "highp vec4 angle_compound_add_frm(inout highp vec4 x, in highp float y) {\n" + " x = angle_frm(angle_frm(x) + y);" + )); + ASSERT_TRUE(foundInGLSLCode( + "vec4 angle_compound_add_frm(inout vec4 x, in float y) {\n" + " x = angle_frm(angle_frm(x) + y);" + )); + ASSERT_TRUE( + foundInHLSLCode("float4 angle_compound_add_frm(inout float4 x, in float y) {\n" + " x = angle_frm(angle_frm(x) + y);")); + ASSERT_TRUE(foundInAllGLSLCode("angle_compound_add_frm(v, angle_frm(u2));")); + ASSERT_TRUE(foundInHLSLCode("angle_compound_add_frm(_v, angle_frm(_u2));")); + ASSERT_TRUE(notFoundInCode("+=")); +} + +TEST_F(DebugShaderPrecisionTest, CompoundMatrixTimesMatrixFunction) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform mat4 u;\n" + "uniform mat4 u2;\n" + "void main() {\n" + " mat4 m = u;\n" + " m *= u2;\n" + " gl_FragColor = m[0];\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInESSLCode( + "highp mat4 angle_compound_mul_frm(inout highp mat4 x, in highp mat4 y) {\n" + " x = angle_frm(angle_frm(x) * y);" + )); + ASSERT_TRUE(foundInGLSLCode( + "mat4 angle_compound_mul_frm(inout mat4 x, in mat4 y) {\n" + " x = angle_frm(angle_frm(x) * y);" + )); + ASSERT_TRUE( + foundInHLSLCode("float4x4 angle_compound_mul_frm(inout float4x4 x, in float4x4 y) {\n" + " x = angle_frm(angle_frm(x) * y);")); + ASSERT_TRUE(foundInAllGLSLCode("angle_compound_mul_frm(m, angle_frm(u2));")); + ASSERT_TRUE(foundInHLSLCode("angle_compound_mul_frm(_m, angle_frm(_u2));")); + ASSERT_TRUE(notFoundInCode("*=")); +} + +// Test that compound multiplying a non-square matrix with another matrix gets translated into an +// angle_compound_mul function call. +TEST_F(DebugShaderPrecisionTest, CompoundNonSquareMatrixTimesMatrixFunction) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "uniform mat2x4 u;\n" + "uniform mat2 u2;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " mat2x4 m = u;\n" + " m *= u2;\n" + " my_FragColor = m[0];\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInESSLCode( + "highp mat2x4 angle_compound_mul_frm(inout highp mat2x4 x, in highp mat2 y) {\n" + " x = angle_frm(angle_frm(x) * y);")); + ASSERT_TRUE( + foundInGLSLCode("mat2x4 angle_compound_mul_frm(inout mat2x4 x, in mat2 y) {\n" + " x = angle_frm(angle_frm(x) * y);")); + ASSERT_TRUE( + foundInHLSLCode("float2x4 angle_compound_mul_frm(inout float2x4 x, in float2x2 y) {\n" + " x = angle_frm(angle_frm(x) * y);")); + ASSERT_TRUE(foundInAllGLSLCode("angle_compound_mul_frm(m, angle_frm(u2));")); + ASSERT_TRUE(foundInHLSLCode("angle_compound_mul_frm(_m, angle_frm(_u2));")); + ASSERT_TRUE(notFoundInCode("*=")); +} + +TEST_F(DebugShaderPrecisionTest, CompoundMatrixTimesScalarFunction) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform mat4 u;\n" + "uniform float u2;\n" + "void main() {\n" + " mat4 m = u;\n" + " m *= u2;\n" + " gl_FragColor = m[0];\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInESSLCode( + "highp mat4 angle_compound_mul_frm(inout highp mat4 x, in highp float y) {\n" + " x = angle_frm(angle_frm(x) * y);" + )); + ASSERT_TRUE(foundInGLSLCode( + "mat4 angle_compound_mul_frm(inout mat4 x, in float y) {\n" + " x = angle_frm(angle_frm(x) * y);" + )); + ASSERT_TRUE( + foundInHLSLCode("float4x4 angle_compound_mul_frm(inout float4x4 x, in float y) {\n" + " x = angle_frm(angle_frm(x) * y);")); + ASSERT_TRUE(foundInAllGLSLCode("angle_compound_mul_frm(m, angle_frm(u2));")); + ASSERT_TRUE(foundInHLSLCode("angle_compound_mul_frm(_m, angle_frm(_u2));")); + ASSERT_TRUE(notFoundInCode("*=")); +} + +TEST_F(DebugShaderPrecisionTest, CompoundVectorTimesMatrixFunction) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform vec4 u;\n" + "uniform mat4 u2;\n" + "void main() {\n" + " vec4 v = u;\n" + " v *= u2;\n" + " gl_FragColor = v;\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInESSLCode( + "highp vec4 angle_compound_mul_frm(inout highp vec4 x, in highp mat4 y) {\n" + " x = angle_frm(angle_frm(x) * y);" + )); + ASSERT_TRUE(foundInGLSLCode("vec4 angle_compound_mul_frm(inout vec4 x, in mat4 y) {\n" + " x = angle_frm(angle_frm(x) * y);" + )); + ASSERT_TRUE( + foundInHLSLCode("float4 angle_compound_mul_frm(inout float4 x, in float4x4 y) {\n" + " x = angle_frm(angle_frm(x) * y);")); + ASSERT_TRUE(foundInAllGLSLCode("angle_compound_mul_frm(v, angle_frm(u2));")); + ASSERT_TRUE(foundInHLSLCode("angle_compound_mul_frm(_v, angle_frm(_u2));")); + ASSERT_TRUE(notFoundInCode("*=")); +} + +TEST_F(DebugShaderPrecisionTest, CompoundVectorTimesScalarFunction) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform vec4 u;\n" + "uniform float u2;\n" + "void main() {\n" + " vec4 v = u;\n" + " v *= u2;\n" + " gl_FragColor = v;\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInESSLCode( + "highp vec4 angle_compound_mul_frm(inout highp vec4 x, in highp float y) {\n" + " x = angle_frm(angle_frm(x) * y);" + )); + ASSERT_TRUE(foundInGLSLCode( + "vec4 angle_compound_mul_frm(inout vec4 x, in float y) {\n" + " x = angle_frm(angle_frm(x) * y);" + )); + ASSERT_TRUE( + foundInHLSLCode("float4 angle_compound_mul_frm(inout float4 x, in float y) {\n" + " x = angle_frm(angle_frm(x) * y);")); + ASSERT_TRUE(foundInAllGLSLCode("angle_compound_mul_frm(v, angle_frm(u2));")); + ASSERT_TRUE(foundInHLSLCode("angle_compound_mul_frm(_v, angle_frm(_u2));")); + ASSERT_TRUE(notFoundInCode("*=")); +} + +TEST_F(DebugShaderPrecisionTest, BinaryMathRounding) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform vec4 u1;\n" + "uniform vec4 u2;\n" + "uniform vec4 u3;\n" + "uniform vec4 u4;\n" + "uniform vec4 u5;\n" + "void main() {\n" + " vec4 v1 = u1 + u2;\n" + " vec4 v2 = u2 - u3;\n" + " vec4 v3 = u3 * u4;\n" + " vec4 v4 = u4 / u5;\n" + " vec4 v5;\n" + " vec4 v6 = (v5 = u5);\n" + " gl_FragColor = v1 + v2 + v3 + v4;\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInAllGLSLCode("v1 = angle_frm((angle_frm(u1) + angle_frm(u2)))")); + ASSERT_TRUE(foundInAllGLSLCode("v2 = angle_frm((angle_frm(u2) - angle_frm(u3)))")); + ASSERT_TRUE(foundInAllGLSLCode("v3 = angle_frm((angle_frm(u3) * angle_frm(u4)))")); + ASSERT_TRUE(foundInAllGLSLCode("v4 = angle_frm((angle_frm(u4) / angle_frm(u5)))")); + ASSERT_TRUE(foundInAllGLSLCode("v6 = angle_frm((v5 = angle_frm(u5)))")); + + ASSERT_TRUE(foundInHLSLCode("v1 = angle_frm((angle_frm(_u1) + angle_frm(_u2)))")); + ASSERT_TRUE(foundInHLSLCode("v2 = angle_frm((angle_frm(_u2) - angle_frm(_u3)))")); + ASSERT_TRUE(foundInHLSLCode("v3 = angle_frm((angle_frm(_u3) * angle_frm(_u4)))")); + ASSERT_TRUE(foundInHLSLCode("v4 = angle_frm((angle_frm(_u4) / angle_frm(_u5)))")); + ASSERT_TRUE(foundInHLSLCode("v6 = angle_frm((_v5 = angle_frm(_u5)))")); +} + +TEST_F(DebugShaderPrecisionTest, BuiltInMathFunctionRounding) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform vec4 u1;\n" + "uniform vec4 u2;\n" + "uniform vec4 u3;\n" + "uniform float uf;\n" + "uniform float uf2;\n" + "uniform vec3 uf31;\n" + "uniform vec3 uf32;\n" + "uniform mat4 um1;\n" + "uniform mat4 um2;\n" + "void main() {\n" + " vec4 v1 = radians(u1);\n" + " vec4 v2 = degrees(u1);\n" + " vec4 v3 = sin(u1);\n" + " vec4 v4 = cos(u1);\n" + " vec4 v5 = tan(u1);\n" + " vec4 v6 = asin(u1);\n" + " vec4 v7 = acos(u1);\n" + " vec4 v8 = atan(u1);\n" + " vec4 v9 = atan(u1, u2);\n" + " vec4 v10 = pow(u1, u2);\n" + " vec4 v11 = exp(u1);\n" + " vec4 v12 = log(u1);\n" + " vec4 v13 = exp2(u1);\n" + " vec4 v14 = log2(u1);\n" + " vec4 v15 = sqrt(u1);\n" + " vec4 v16 = inversesqrt(u1);\n" + " vec4 v17 = abs(u1);\n" + " vec4 v18 = sign(u1);\n" + " vec4 v19 = floor(u1);\n" + " vec4 v20 = ceil(u1);\n" + " vec4 v21 = fract(u1);\n" + " vec4 v22 = mod(u1, uf);\n" + " vec4 v23 = mod(u1, u2);\n" + " vec4 v24 = min(u1, uf);\n" + " vec4 v25 = min(u1, u2);\n" + " vec4 v26 = max(u1, uf);\n" + " vec4 v27 = max(u1, u2);\n" + " vec4 v28 = clamp(u1, u2, u3);\n" + " vec4 v29 = clamp(u1, uf, uf2);\n" + " vec4 v30 = mix(u1, u2, u3);\n" + " vec4 v31 = mix(u1, u2, uf);\n" + " vec4 v32 = step(u1, u2);\n" + " vec4 v33 = step(uf, u1);\n" + " vec4 v34 = smoothstep(u1, u2, u3);\n" + " vec4 v35 = smoothstep(uf, uf2, u1);\n" + " vec4 v36 = normalize(u1);\n" + " vec4 v37 = faceforward(u1, u2, u3);\n" + " vec4 v38 = reflect(u1, u2);\n" + " vec4 v39 = refract(u1, u2, uf);\n" + + " float f1 = length(u1);\n" + " float f2 = distance(u1, u2);\n" + " float f3 = dot(u1, u2);\n" + " vec3 vf31 = cross(uf31, uf32);\n" + " mat4 m1 = matrixCompMult(um1, um2);\n" + + " gl_FragColor = v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 +" + "v11 + v12 + v13 + v14 + v15 + v16 + v17 + v18 + v19 + v20 +" + "v21 + v22 + v23 + v24 + v25 + v26 + v27 + v28 + v29 + v30 +" + "v31 + v32 + v33 + v34 + v35 + v36 + v37 + v38 + v39 +" + "vec4(f1, f2, f3, 0.0) + vec4(vf31, 0.0) + m1[0];\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInAllGLSLCode("v1 = angle_frm(radians(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("v2 = angle_frm(degrees(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("v3 = angle_frm(sin(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("v4 = angle_frm(cos(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("v5 = angle_frm(tan(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("v6 = angle_frm(asin(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("v7 = angle_frm(acos(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("v8 = angle_frm(atan(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("v9 = angle_frm(atan(angle_frm(u1), angle_frm(u2)))")); + ASSERT_TRUE(foundInAllGLSLCode("v10 = angle_frm(pow(angle_frm(u1), angle_frm(u2)))")); + ASSERT_TRUE(foundInAllGLSLCode("v11 = angle_frm(exp(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("v12 = angle_frm(log(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("v13 = angle_frm(exp2(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("v14 = angle_frm(log2(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("v15 = angle_frm(sqrt(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("v16 = angle_frm(inversesqrt(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("v17 = angle_frm(abs(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("v18 = angle_frm(sign(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("v19 = angle_frm(floor(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("v20 = angle_frm(ceil(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("v21 = angle_frm(fract(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("v22 = angle_frm(mod(angle_frm(u1), angle_frm(uf)))")); + ASSERT_TRUE(foundInAllGLSLCode("v23 = angle_frm(mod(angle_frm(u1), angle_frm(u2)))")); + ASSERT_TRUE(foundInAllGLSLCode("v24 = angle_frm(min(angle_frm(u1), angle_frm(uf)))")); + ASSERT_TRUE(foundInAllGLSLCode("v25 = angle_frm(min(angle_frm(u1), angle_frm(u2)))")); + ASSERT_TRUE(foundInAllGLSLCode("v26 = angle_frm(max(angle_frm(u1), angle_frm(uf)))")); + ASSERT_TRUE(foundInAllGLSLCode("v27 = angle_frm(max(angle_frm(u1), angle_frm(u2)))")); + ASSERT_TRUE( + foundInAllGLSLCode("v28 = angle_frm(clamp(angle_frm(u1), angle_frm(u2), angle_frm(u3)))")); + ASSERT_TRUE( + foundInAllGLSLCode("v29 = angle_frm(clamp(angle_frm(u1), angle_frm(uf), angle_frm(uf2)))")); + ASSERT_TRUE( + foundInAllGLSLCode("v30 = angle_frm(mix(angle_frm(u1), angle_frm(u2), angle_frm(u3)))")); + ASSERT_TRUE( + foundInAllGLSLCode("v31 = angle_frm(mix(angle_frm(u1), angle_frm(u2), angle_frm(uf)))")); + ASSERT_TRUE(foundInAllGLSLCode("v32 = angle_frm(step(angle_frm(u1), angle_frm(u2)))")); + ASSERT_TRUE(foundInAllGLSLCode("v33 = angle_frm(step(angle_frm(uf), angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode( + "v34 = angle_frm(smoothstep(angle_frm(u1), angle_frm(u2), angle_frm(u3)))")); + ASSERT_TRUE(foundInAllGLSLCode( + "v35 = angle_frm(smoothstep(angle_frm(uf), angle_frm(uf2), angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("v36 = angle_frm(normalize(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode( + "v37 = angle_frm(faceforward(angle_frm(u1), angle_frm(u2), angle_frm(u3)))")); + ASSERT_TRUE(foundInAllGLSLCode("v38 = angle_frm(reflect(angle_frm(u1), angle_frm(u2)))")); + ASSERT_TRUE(foundInAllGLSLCode( + "v39 = angle_frm(refract(angle_frm(u1), angle_frm(u2), angle_frm(uf)))")); + + ASSERT_TRUE(foundInAllGLSLCode("f1 = angle_frm(length(angle_frm(u1)))")); + ASSERT_TRUE(foundInAllGLSLCode("f2 = angle_frm(distance(angle_frm(u1), angle_frm(u2)))")); + ASSERT_TRUE(foundInAllGLSLCode("f3 = angle_frm(dot(angle_frm(u1), angle_frm(u2)))")); + ASSERT_TRUE(foundInAllGLSLCode("vf31 = angle_frm(cross(angle_frm(uf31), angle_frm(uf32)))")); + ASSERT_TRUE( + foundInAllGLSLCode("m1 = angle_frm(matrixCompMult(angle_frm(um1), angle_frm(um2)))")); + + ASSERT_TRUE(foundInHLSLCode("v1 = angle_frm(radians(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("v2 = angle_frm(degrees(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("v3 = angle_frm(sin(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("v4 = angle_frm(cos(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("v5 = angle_frm(tan(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("v6 = angle_frm(asin(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("v7 = angle_frm(acos(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("v8 = angle_frm(atan(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("v9 = angle_frm(webgl_atan_emu(angle_frm(_u1), angle_frm(_u2)))")); + ASSERT_TRUE(foundInHLSLCode("v10 = angle_frm(pow(angle_frm(_u1), angle_frm(_u2)))")); + ASSERT_TRUE(foundInHLSLCode("v11 = angle_frm(exp(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("v12 = angle_frm(log(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("v13 = angle_frm(exp2(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("v14 = angle_frm(log2(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("v15 = angle_frm(sqrt(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("v16 = angle_frm(rsqrt(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("v17 = angle_frm(abs(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("v18 = angle_frm(sign(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("v19 = angle_frm(floor(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("v20 = angle_frm(ceil(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("v21 = angle_frm(frac(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("v22 = angle_frm(webgl_mod_emu(angle_frm(_u1), angle_frm(_uf)))")); + ASSERT_TRUE(foundInHLSLCode("v23 = angle_frm(webgl_mod_emu(angle_frm(_u1), angle_frm(_u2)))")); + ASSERT_TRUE(foundInHLSLCode("v24 = angle_frm(min(angle_frm(_u1), angle_frm(_uf)))")); + ASSERT_TRUE(foundInHLSLCode("v25 = angle_frm(min(angle_frm(_u1), angle_frm(_u2)))")); + ASSERT_TRUE(foundInHLSLCode("v26 = angle_frm(max(angle_frm(_u1), angle_frm(_uf)))")); + ASSERT_TRUE(foundInHLSLCode("v27 = angle_frm(max(angle_frm(_u1), angle_frm(_u2)))")); + ASSERT_TRUE( + foundInHLSLCode("v28 = angle_frm(clamp(angle_frm(_u1), angle_frm(_u2), angle_frm(_u3)))")); + ASSERT_TRUE( + foundInHLSLCode("v29 = angle_frm(clamp(angle_frm(_u1), angle_frm(_uf), angle_frm(_uf2)))")); + ASSERT_TRUE( + foundInHLSLCode("v30 = angle_frm(lerp(angle_frm(_u1), angle_frm(_u2), angle_frm(_u3)))")); + ASSERT_TRUE( + foundInHLSLCode("v31 = angle_frm(lerp(angle_frm(_u1), angle_frm(_u2), angle_frm(_uf)))")); + ASSERT_TRUE(foundInHLSLCode("v32 = angle_frm(step(angle_frm(_u1), angle_frm(_u2)))")); + ASSERT_TRUE(foundInHLSLCode("v33 = angle_frm(step(angle_frm(_uf), angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode( + "v34 = angle_frm(smoothstep(angle_frm(_u1), angle_frm(_u2), angle_frm(_u3)))")); + ASSERT_TRUE(foundInHLSLCode( + "v35 = angle_frm(smoothstep(angle_frm(_uf), angle_frm(_uf2), angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("v36 = angle_frm(normalize(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode( + "v37 = angle_frm(webgl_faceforward_emu(angle_frm(_u1), angle_frm(_u2), angle_frm(_u3)))")); + ASSERT_TRUE(foundInHLSLCode("v38 = angle_frm(reflect(angle_frm(_u1), angle_frm(_u2)))")); + ASSERT_TRUE(foundInHLSLCode( + "v39 = angle_frm(refract(angle_frm(_u1), angle_frm(_u2), angle_frm(_uf)))")); + + ASSERT_TRUE(foundInHLSLCode("f1 = angle_frm(length(angle_frm(_u1)))")); + ASSERT_TRUE(foundInHLSLCode("f2 = angle_frm(distance(angle_frm(_u1), angle_frm(_u2)))")); + ASSERT_TRUE(foundInHLSLCode("f3 = angle_frm(dot(angle_frm(_u1), angle_frm(_u2)))")); + ASSERT_TRUE(foundInHLSLCode("vf31 = angle_frm(cross(angle_frm(_uf31), angle_frm(_uf32)))")); + ASSERT_TRUE(foundInHLSLCode("m1 = angle_frm((angle_frm(_um1) * angle_frm(_um2)))")); +} + +TEST_F(DebugShaderPrecisionTest, BuiltInRelationalFunctionRounding) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform vec4 u1;\n" + "uniform vec4 u2;\n" + "void main() {\n" + " bvec4 bv1 = lessThan(u1, u2);\n" + " bvec4 bv2 = lessThanEqual(u1, u2);\n" + " bvec4 bv3 = greaterThan(u1, u2);\n" + " bvec4 bv4 = greaterThanEqual(u1, u2);\n" + " bvec4 bv5 = equal(u1, u2);\n" + " bvec4 bv6 = notEqual(u1, u2);\n" + " gl_FragColor = vec4(bv1) + vec4(bv2) + vec4(bv3) + vec4(bv4) + vec4(bv5) + vec4(bv6);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInAllGLSLCode("bv1 = lessThan(angle_frm(u1), angle_frm(u2))")); + ASSERT_TRUE(foundInAllGLSLCode("bv2 = lessThanEqual(angle_frm(u1), angle_frm(u2))")); + ASSERT_TRUE(foundInAllGLSLCode("bv3 = greaterThan(angle_frm(u1), angle_frm(u2))")); + ASSERT_TRUE(foundInAllGLSLCode("bv4 = greaterThanEqual(angle_frm(u1), angle_frm(u2))")); + ASSERT_TRUE(foundInAllGLSLCode("bv5 = equal(angle_frm(u1), angle_frm(u2))")); + ASSERT_TRUE(foundInAllGLSLCode("bv6 = notEqual(angle_frm(u1), angle_frm(u2))")); + + ASSERT_TRUE(foundInHLSLCode("bv1 = (angle_frm(_u1) < angle_frm(_u2))")); + ASSERT_TRUE(foundInHLSLCode("bv2 = (angle_frm(_u1) <= angle_frm(_u2))")); + ASSERT_TRUE(foundInHLSLCode("bv3 = (angle_frm(_u1) > angle_frm(_u2))")); + ASSERT_TRUE(foundInHLSLCode("bv4 = (angle_frm(_u1) >= angle_frm(_u2))")); + ASSERT_TRUE(foundInHLSLCode("bv5 = (angle_frm(_u1) == angle_frm(_u2))")); + ASSERT_TRUE(foundInHLSLCode("bv6 = (angle_frm(_u1) != angle_frm(_u2))")); +} + +TEST_F(DebugShaderPrecisionTest, ConstructorRounding) +{ + const std::string &shaderString = + "precision mediump float;\n" + "precision mediump int;\n" + "uniform float u1;\n" + "uniform float u2;\n" + "uniform float u3;\n" + "uniform float u4;\n" + "uniform ivec4 uiv;\n" + "void main() {\n" + " vec4 v1 = vec4(u1, u2, u3, u4);\n" + " vec4 v2 = vec4(uiv);\n" + " gl_FragColor = v1 + v2;\n" + "}\n"; + compile(shaderString); + // Note: this is suboptimal for the case taking four floats, but optimizing would be tricky. + ASSERT_TRUE(foundInAllGLSLCode( + "v1 = angle_frm(vec4(angle_frm(u1), angle_frm(u2), angle_frm(u3), angle_frm(u4)))")); + ASSERT_TRUE(foundInAllGLSLCode("v2 = angle_frm(vec4(uiv))")); + + ASSERT_TRUE(foundInHLSLCode( + "v1 = angle_frm(vec4(angle_frm(_u1), angle_frm(_u2), angle_frm(_u3), angle_frm(_u4)))")); + ASSERT_TRUE(foundInHLSLCode("v2 = angle_frm(vec4(_uiv))")); +} + +TEST_F(DebugShaderPrecisionTest, StructConstructorNoRounding) +{ + const std::string &shaderString = + "precision mediump float;\n" + "struct S { mediump vec4 a; };\n" + "uniform vec4 u;\n" + "void main() {\n" + " S s = S(u);\n" + " gl_FragColor = s.a;\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInAllGLSLCode("s = S(angle_frm(u))")); + ASSERT_TRUE(foundInHLSLCode("s = _S_ctor(angle_frm(_u))")); + ASSERT_TRUE(notFoundInCode("angle_frm(S")); +} + +TEST_F(DebugShaderPrecisionTest, SwizzleRounding) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform vec4 u;\n" + "void main() {\n" + " vec4 v = u.xyxy;" + " gl_FragColor = v;\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInAllGLSLCode("v = angle_frm(u).xyxy")); + ASSERT_TRUE(foundInHLSLCode("v = angle_frm(_u).xyxy")); +} + +TEST_F(DebugShaderPrecisionTest, BuiltInTexFunctionRounding) +{ + const std::string &shaderString = + "precision mediump float;\n" + "precision lowp sampler2D;\n" + "uniform vec2 u;\n" + "uniform sampler2D s;\n" + "void main() {\n" + " lowp vec4 v = texture2D(s, u);\n" + " gl_FragColor = v;\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInAllGLSLCode("v = angle_frl(texture2D(s, angle_frm(u)))")); + ASSERT_TRUE(foundInHLSLCode("v = angle_frl(gl_texture2D(_s, angle_frm(_u)))")); +} + +TEST_F(DebugShaderPrecisionTest, FunctionCallParameterQualifiersFromDefinition) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform vec4 u1;\n" + "uniform vec4 u2;\n" + "uniform vec4 u3;\n" + "uniform vec4 u4;\n" + "uniform vec4 u5;\n" + "vec4 add(in vec4 x, in vec4 y) {\n" + " return x + y;\n" + "}\n" + "void compound_add(inout vec4 x, in vec4 y) {\n" + " x = x + y;\n" + "}\n" + "void add_to_last(in vec4 x, in vec4 y, out vec4 z) {\n" + " z = x + y;\n" + "}\n" + "void main() {\n" + " vec4 v = add(u1, u2);\n" + " compound_add(v, u3);\n" + " vec4 v2;\n" + " add_to_last(u4, u5, v2);\n" + " gl_FragColor = v + v2;\n" + "}\n"; + compile(shaderString); + // Note that this is not optimal code, there are redundant frm calls. + // However, getting the implementation working when other operations + // are nested within function calls would be tricky if to get right + // otherwise. + // Test in parameters + ASSERT_TRUE(foundInAllGLSLCode("v = add(angle_frm(u1), angle_frm(u2))")); + ASSERT_TRUE(foundInHLSLCode("v = _add_float4_float4(angle_frm(_u1), angle_frm(_u2))")); + // Test inout parameter + ASSERT_TRUE(foundInAllGLSLCode("compound_add(v, angle_frm(u3))")); + ASSERT_TRUE(foundInHLSLCode("compound_add_float4_float4(_v, angle_frm(_u3))")); + // Test out parameter + ASSERT_TRUE(foundInAllGLSLCode("add_to_last(angle_frm(u4), angle_frm(u5), v2)")); + ASSERT_TRUE( + foundInHLSLCode("add_to_last_float4_float4_float4(angle_frm(_u4), angle_frm(_u5), _v2)")); +} + +TEST_F(DebugShaderPrecisionTest, FunctionCallParameterQualifiersFromPrototype) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform vec4 u1;\n" + "uniform vec4 u2;\n" + "uniform vec4 u3;\n" + "uniform vec4 u4;\n" + "uniform vec4 u5;\n" + "vec4 add(in vec4 x, in vec4 y);\n" + "void compound_add(inout vec4 x, in vec4 y);\n" + "void add_to_last(in vec4 x, in vec4 y, out vec4 z);\n" + "void main() {\n" + " vec4 v = add(u1, u2);\n" + " compound_add(v, u3);\n" + " vec4 v2;\n" + " add_to_last(u4, u5, v2);\n" + " gl_FragColor = v + v2;\n" + "}\n" + "vec4 add(in vec4 x, in vec4 y) {\n" + " return x + y;\n" + "}\n" + "void compound_add(inout vec4 x, in vec4 y) {\n" + " x = x + y;\n" + "}\n" + "void add_to_last(in vec4 x, in vec4 y, out vec4 z) {\n" + " z = x + y;\n" + "}\n"; + compile(shaderString); + // Test in parameters + ASSERT_TRUE(foundInAllGLSLCode("v = add(angle_frm(u1), angle_frm(u2))")); + ASSERT_TRUE(foundInHLSLCode("v = _add_float4_float4(angle_frm(_u1), angle_frm(_u2))")); + // Test inout parameter + ASSERT_TRUE(foundInAllGLSLCode("compound_add(v, angle_frm(u3))")); + ASSERT_TRUE(foundInHLSLCode("compound_add_float4_float4(_v, angle_frm(_u3))")); + // Test out parameter + ASSERT_TRUE(foundInAllGLSLCode("add_to_last(angle_frm(u4), angle_frm(u5), v2)")); + ASSERT_TRUE( + foundInHLSLCode("add_to_last_float4_float4_float4(angle_frm(_u4), angle_frm(_u5), _v2)")); +} + +TEST_F(DebugShaderPrecisionTest, NestedFunctionCalls) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform vec4 u1;\n" + "uniform vec4 u2;\n" + "uniform vec4 u3;\n" + "vec4 add(in vec4 x, in vec4 y) {\n" + " return x + y;\n" + "}\n" + "vec4 compound_add(inout vec4 x, in vec4 y) {\n" + " x = x + y;\n" + " return x;\n" + "}\n" + "void main() {\n" + " vec4 v = u1;\n" + " vec4 v2 = add(compound_add(v, u2), fract(u3));\n" + " gl_FragColor = v + v2;\n" + "}\n"; + compile(shaderString); + // Test nested calls + ASSERT_TRUE(foundInAllGLSLCode( + "v2 = add(compound_add(v, angle_frm(u2)), angle_frm(fract(angle_frm(u3))))")); + ASSERT_TRUE( + foundInHLSLCode("v2 = _add_float4_float4(_compound_add_float4_float4(_v, angle_frm(_u2)), " + "angle_frm(frac(angle_frm(_u3))))")); +} + +// Test that code inside an index of a function out parameter gets processed. +TEST_F(DebugShaderPrecisionTest, OpInIndexOfFunctionOutParameter) +{ + const std::string &shaderString = + "precision mediump float;\n" + "void foo(out vec4 f) { f.x = 0.0; }\n" + "uniform float u2;\n" + "void main() {\n" + " vec4 v[2];\n" + " foo(v[int(exp2(u2))]);\n" + " gl_FragColor = v[0];\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInAllGLSLCode("angle_frm(exp2(angle_frm(u2)))")); + ASSERT_TRUE(foundInHLSLCode("angle_frm(exp2(angle_frm(_u2)))")); +} + +// Test that code inside an index of an l-value gets processed. +TEST_F(DebugShaderPrecisionTest, OpInIndexOfLValue) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform vec4 u1;\n" + "uniform float u2;\n" + "void main() {\n" + " vec4 v[2];\n" + " v[int(exp2(u2))] = u1;\n" + " gl_FragColor = v[0];\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInAllGLSLCode("angle_frm(exp2(angle_frm(u2)))")); + ASSERT_TRUE(foundInHLSLCode("angle_frm(exp2(angle_frm(_u2)))")); +} + +// Test that the out parameter of modf doesn't get rounded +TEST_F(DebugShaderPrecisionTest, ModfOutParameter) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "uniform float u;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " float o;\n" + " float f = modf(u, o);\n" + " my_FragColor = vec4(f, o, 0, 1);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInAllGLSLCode("modf(angle_frm(u), o)")); + ASSERT_TRUE(foundInHLSLCode("modf(angle_frm(_u), _o)")); +} + +#if defined(ANGLE_ENABLE_HLSL) +// Tests precision emulation with HLSL 3.0 output -- should error gracefully. +TEST(DebugShaderPrecisionNegativeTest, HLSL3Unsupported) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float u;\n" + "void main() {\n" + " gl_FragColor = vec4(u);\n" + "}\n"; + std::string infoLog; + std::string translatedCode; + ShBuiltInResources resources; + sh::InitBuiltInResources(&resources); + resources.WEBGL_debug_shader_precision = 1; + ASSERT_FALSE(compileTestShader(GL_FRAGMENT_SHADER, SH_GLES3_SPEC, SH_HLSL_3_0_OUTPUT, + shaderString, &resources, 0, &translatedCode, &infoLog)); +} +#endif // defined(ANGLE_ENABLE_HLSL) diff --git a/gfx/angle/src/tests/compiler_tests/EXT_blend_func_extended_test.cpp b/gfx/angle/src/tests/compiler_tests/EXT_blend_func_extended_test.cpp new file mode 100755 index 000000000..3ac0ff6d8 --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/EXT_blend_func_extended_test.cpp @@ -0,0 +1,285 @@ +// +// Copyright (c) 2015 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. +// +// EXT_blend_func_extended.cpp: +// Test for EXT_blend_func_extended_test +// + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" + +using testing::Combine; +using testing::Values; +using testing::make_tuple; + +namespace +{ +const char ESSLVersion100[] = "#version 100\n"; +const char ESSLVersion300[] = "#version 300 es\n"; +const char ESSLVersion310[] = "#version 310 es\n"; +const char EXTBFEPragma[] = "#extension GL_EXT_blend_func_extended : require\n"; + +const char ESSL100_SimpleShader1[] = + "precision mediump float;\n" + "void main() { \n" + " gl_FragColor = vec4(1.0);\n" + " gl_SecondaryFragColorEXT = vec4(gl_MaxDualSourceDrawBuffersEXT / 10);\n" + "}\n"; + +// Shader that tests only the access to gl_MaxDualSourceDrawBuffersEXT. +const char ESSL100_MaxDualSourceAccessShader[] = + "precision mediump float;\n" + "void main() { gl_FragColor = vec4(gl_MaxDualSourceDrawBuffersEXT / 10); }\n"; + +// Shader that writes to SecondaryFragData. +const char ESSL100_FragDataShader[] = + "#extension GL_EXT_draw_buffers : require\n" + "precision mediump float;\n" + "void main() {\n" + " gl_FragData[gl_MaxDrawBuffers - 1] = vec4(1.0);\n" + " gl_SecondaryFragDataEXT[gl_MaxDualSourceDrawBuffersEXT - 1] = vec4(0.1);\n" + "}\n"; + +// Shader that writes to SecondaryFragColor and SecondaryFragData does not compile. +const char ESSL100_ColorAndDataWriteFailureShader1[] = + "precision mediump float;\n" + "void main() {\n" + " gl_SecondaryFragColorEXT = vec4(1.0);\n" + " gl_SecondaryFragDataEXT[gl_MaxDualSourceDrawBuffersEXT] = vec4(0.1);\n" + "}\n"; + +// Shader that writes to FragColor and SecondaryFragData does not compile. +const char ESSL100_ColorAndDataWriteFailureShader2[] = + "precision mediump float;\n" + "void main() {\n" + " gl_FragColor = vec4(1.0);\n" + " gl_SecondaryFragDataEXT[gl_MaxDualSourceDrawBuffersEXT] = vec4(0.1);\n" + "}\n"; + +// Shader that writes to FragData and SecondaryFragColor. +const char ESSL100_ColorAndDataWriteFailureShader3[] = + "#extension GL_EXT_draw_buffers : require\n" + "precision mediump float;\n" + "void main() {\n" + " gl_SecondaryFragColorEXT = vec4(1.0);\n" + " gl_FragData[gl_MaxDrawBuffers] = vec4(0.1);\n" + "}\n"; + +// In GLSL version 300 es, the gl_MaxDualSourceDrawBuffersEXT is available. +const char ESSL300_MaxDualSourceAccessShader[] = + "precision mediump float;\n" + "layout(location = 0) out mediump vec4 fragColor;" + "void main() {\n" + " fragColor = vec4(gl_MaxDualSourceDrawBuffersEXT / 10);\n" + "}\n"; + +// In GLSL version 300 es, the only way to write a correct shader is to require the extension and +// then leave the locations unspecified. The caller will then bind the variables with the extension +// binding functions. +const char ESSL300_LocationAndUnspecifiedOutputShader[] = + "precision mediump float;\n" + "layout(location = 0) out mediump vec4 fragColor;" + "out mediump vec4 secondaryFragColor;" + "void main() {\n" + " fragColor = vec4(1.0);\n" + " secondaryFragColor = vec4(1.0);\n" + "}\n"; + +const char ESSL300_TwoUnspecifiedLocationOutputsShader[] = + "precision mediump float;\n" + "out mediump vec4 fragColor;" + "out mediump vec4 secondaryFragColor;" + "void main() {\n" + " fragColor = vec4(1.0);\n" + " secondaryFragColor = vec4(1.0);\n" + "}\n"; + +// Shader that is correct in GLSL ES 3.10 fails when used in version 300 es. +const char ESSL310_LocationIndexShader[] = + "precision mediump float;\n" + "layout(location = 0) out mediump vec4 fragColor;" + "layout(location = 0, index = 1) out mediump vec4 secondaryFragColor;" + "void main() {\n" + " fragColor = vec4(1);\n" + " secondaryFragColor = vec4(1);\n" + "}\n"; + +// Shader that specifies index layout qualifier but not location fails to compile. Currently fails +// to compile due to version 310 es not being supported. +const char ESSL310_LocationIndexFailureShader[] = + "precision mediump float;\n" + "layout(location = 0) out mediump vec4 fragColor;" + "layout(index = 1) out mediump vec4 secondaryFragColor;" + "void main() {\n" + " fragColor = vec4(1.0);\n" + " secondaryFragColor = vec4(1.0);\n" + "}\n"; + +class EXTBlendFuncExtendedTest + : public testing::TestWithParam> +{ + protected: + virtual void SetUp() + { + sh::InitBuiltInResources(&mResources); + // EXT_draw_buffers is used in some of the shaders for test purposes. + mResources.EXT_draw_buffers = 1; + mResources.NV_draw_buffers = 2; + + mCompiler = NULL; + } + + virtual void TearDown() { DestroyCompiler(); } + void DestroyCompiler() + { + if (mCompiler) + { + sh::Destruct(mCompiler); + mCompiler = NULL; + } + } + + void InitializeCompiler() + { + DestroyCompiler(); + mCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, testing::get<0>(GetParam()), + SH_GLSL_COMPATIBILITY_OUTPUT, &mResources); + ASSERT_TRUE(mCompiler != NULL) << "Compiler could not be constructed."; + } + + testing::AssertionResult TestShaderCompile(const char *pragma) + { + return TestShaderCompile(testing::get<1>(GetParam()), // Version. + pragma, + testing::get<2>(GetParam()) // Shader. + ); + } + + testing::AssertionResult TestShaderCompile(const char *version, + const char *pragma, + const char *shader) + { + const char *shaderStrings[] = {version, pragma, shader}; + bool success = sh::Compile(mCompiler, shaderStrings, 3, 0); + if (success) + { + return ::testing::AssertionSuccess() << "Compilation success"; + } + return ::testing::AssertionFailure() << sh::GetInfoLog(mCompiler); + } + + protected: + ShBuiltInResources mResources; + ShHandle mCompiler; +}; + +// Extension flag is required to compile properly. Expect failure when it is +// not present. +TEST_P(EXTBlendFuncExtendedTest, CompileFailsWithoutExtension) +{ + mResources.EXT_blend_func_extended = 0; + InitializeCompiler(); + EXPECT_FALSE(TestShaderCompile(EXTBFEPragma)); +} + +// Extension directive is required to compile properly. Expect failure when +// it is not present. +TEST_P(EXTBlendFuncExtendedTest, CompileFailsWithExtensionWithoutPragma) +{ + mResources.EXT_blend_func_extended = 1; + mResources.MaxDualSourceDrawBuffers = 1; + InitializeCompiler(); + EXPECT_FALSE(TestShaderCompile("")); +} + +// With extension flag and extension directive, compiling succeeds. +// Also test that the extension directive state is reset correctly. +TEST_P(EXTBlendFuncExtendedTest, CompileSucceedsWithExtensionAndPragma) +{ + mResources.EXT_blend_func_extended = 1; + mResources.MaxDualSourceDrawBuffers = 1; + InitializeCompiler(); + EXPECT_TRUE(TestShaderCompile(EXTBFEPragma)); + // Test reset functionality. + EXPECT_FALSE(TestShaderCompile("")); + EXPECT_TRUE(TestShaderCompile(EXTBFEPragma)); +} + +// The SL #version 100 shaders that are correct work similarly +// in both GL2 and GL3, with and without the version string. +INSTANTIATE_TEST_CASE_P(CorrectESSL100Shaders, + EXTBlendFuncExtendedTest, + Combine(Values(SH_GLES2_SPEC, SH_GLES3_SPEC), + Values("", ESSLVersion100), + Values(ESSL100_SimpleShader1, + ESSL100_MaxDualSourceAccessShader, + ESSL100_FragDataShader))); + +INSTANTIATE_TEST_CASE_P(CorrectESSL300Shaders, + EXTBlendFuncExtendedTest, + Combine(Values(SH_GLES3_SPEC), + Values(ESSLVersion300), + Values(ESSL300_MaxDualSourceAccessShader, + ESSL300_LocationAndUnspecifiedOutputShader, + ESSL300_TwoUnspecifiedLocationOutputsShader))); + +class EXTBlendFuncExtendedCompileFailureTest : public EXTBlendFuncExtendedTest +{ +}; + +TEST_P(EXTBlendFuncExtendedCompileFailureTest, CompileFails) +{ + // Expect compile failure due to shader error, with shader having correct pragma. + mResources.EXT_blend_func_extended = 1; + mResources.MaxDualSourceDrawBuffers = 1; + InitializeCompiler(); + EXPECT_FALSE(TestShaderCompile(EXTBFEPragma)); +} + +// Incorrect #version 100 shaders fail. +INSTANTIATE_TEST_CASE_P(IncorrectESSL100Shaders, + EXTBlendFuncExtendedCompileFailureTest, + Combine(Values(SH_GLES2_SPEC), + Values(ESSLVersion100), + Values(ESSL100_ColorAndDataWriteFailureShader1, + ESSL100_ColorAndDataWriteFailureShader2, + ESSL100_ColorAndDataWriteFailureShader3))); + +// Correct #version 300 es shaders fail in GLES2 context, regardless of version string. +INSTANTIATE_TEST_CASE_P(CorrectESSL300Shaders, + EXTBlendFuncExtendedCompileFailureTest, + Combine(Values(SH_GLES2_SPEC), + Values("", ESSLVersion100, ESSLVersion300), + Values(ESSL300_LocationAndUnspecifiedOutputShader, + ESSL300_TwoUnspecifiedLocationOutputsShader))); + +// Correct #version 100 shaders fail when used with #version 300 es. +INSTANTIATE_TEST_CASE_P(CorrectESSL100Shaders, + EXTBlendFuncExtendedCompileFailureTest, + Combine(Values(SH_GLES3_SPEC), + Values(ESSLVersion300), + Values(ESSL100_SimpleShader1, ESSL100_FragDataShader))); + +// Incorrect #version 310 es always fails. +INSTANTIATE_TEST_CASE_P(IncorrectESSL310Shaders, + EXTBlendFuncExtendedCompileFailureTest, + Combine(Values(SH_GLES3_SPEC), + Values(ESSLVersion300, ESSLVersion310), + Values(ESSL310_LocationIndexFailureShader))); + +// Correct #version 310 es fails in #version 300 es. +INSTANTIATE_TEST_CASE_P( + CorrectESSL310ShadersInESSL300, + EXTBlendFuncExtendedCompileFailureTest, + Values(make_tuple(SH_GLES3_SPEC, &ESSLVersion300[0], &ESSL310_LocationIndexShader[0]))); + +// Correct #version 310 es fails in #version 310 es, due to 3.1 not being supported. +INSTANTIATE_TEST_CASE_P( + CorrectESSL310Shaders, + EXTBlendFuncExtendedCompileFailureTest, + Values(make_tuple(SH_GLES3_SPEC, &ESSLVersion310[0], &ESSL310_LocationIndexShader[0]))); + +} // namespace diff --git a/gfx/angle/src/tests/compiler_tests/EmulateGLFragColorBroadcast_test.cpp b/gfx/angle/src/tests/compiler_tests/EmulateGLFragColorBroadcast_test.cpp new file mode 100755 index 000000000..07cf3fd68 --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/EmulateGLFragColorBroadcast_test.cpp @@ -0,0 +1,81 @@ +// +// Copyright (c) 2016 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. +// +// EmulateGLFragColorBroadcast_test.cpp: +// Tests for gl_FragColor broadcast behavior emulation. +// + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" +#include "tests/test_utils/compiler_test.h" + +using namespace sh; + +namespace +{ + +const int kMaxDrawBuffers = 2; + +class EmulateGLFragColorBroadcastTest : public MatchOutputCodeTest +{ + public: + EmulateGLFragColorBroadcastTest() + : MatchOutputCodeTest(GL_FRAGMENT_SHADER, + 0, // compile options + SH_GLSL_COMPATIBILITY_OUTPUT) + { + getResources()->MaxDrawBuffers = kMaxDrawBuffers; + getResources()->EXT_draw_buffers = 1; + } +}; + +// Verifies that without explicitly enabling GL_EXT_draw_buffers extension +// in the shader, no broadcast emulation. +TEST_F(EmulateGLFragColorBroadcastTest, FragColorNoBroadcast) +{ + const std::string shaderString = + "void main()\n" + "{\n" + " gl_FragColor = vec4(1, 0, 0, 0);\n" + "}\n"; + compile(shaderString); + EXPECT_TRUE(foundInCode("gl_FragColor")); + EXPECT_FALSE(foundInCode("gl_FragData[0]")); + EXPECT_FALSE(foundInCode("gl_FragData[1]")); +} + +// Verifies that with explicitly enabling GL_EXT_draw_buffers extension +// in the shader, broadcast is emualted by replacing gl_FragColor with gl_FragData. +TEST_F(EmulateGLFragColorBroadcastTest, FragColorBroadcast) +{ + const std::string shaderString = + "#extension GL_EXT_draw_buffers : require\n" + "void main()\n" + "{\n" + " gl_FragColor = vec4(1, 0, 0, 0);\n" + "}\n"; + compile(shaderString); + EXPECT_FALSE(foundInCode("gl_FragColor")); + EXPECT_TRUE(foundInCode("gl_FragData[0]")); + EXPECT_TRUE(foundInCode("gl_FragData[1]")); +} + +// Verifies that with explicitly enabling GL_EXT_draw_buffers extension +// in the shader with an empty main(), anothing happens. +TEST_F(EmulateGLFragColorBroadcastTest, EmptyMain) +{ + const std::string shaderString = + "#extension GL_EXT_draw_buffers : require\n" + "void main()\n" + "{\n" + "}\n"; + compile(shaderString); + EXPECT_FALSE(foundInCode("gl_FragColor")); + EXPECT_FALSE(foundInCode("gl_FragData[0]")); + EXPECT_FALSE(foundInCode("gl_FragData[1]")); +} + +} // namespace anonymous 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 +#include +#include +#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); +} diff --git a/gfx/angle/src/tests/compiler_tests/FragDepth_test.cpp b/gfx/angle/src/tests/compiler_tests/FragDepth_test.cpp new file mode 100755 index 000000000..1d3e358ca --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/FragDepth_test.cpp @@ -0,0 +1,119 @@ +// +// Copyright (c) 2015 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. +// +// FragDepth_test.cpp: +// Test for GLES SL 3.0 gl_FragDepth variable implementation. +// + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" + +namespace +{ +const char ESSLVersion100[] = "#version 100\n"; +const char ESSLVersion300[] = "#version 300 es\n"; +const char EXTFDPragma[] = "#extension GL_EXT_frag_depth : require\n"; +} // namespace + +class FragDepthTest : public testing::TestWithParam +{ + protected: + void SetUp() override + { + sh::InitBuiltInResources(&mResources); + mCompiler = nullptr; + mResources.EXT_frag_depth = GetParam(); + } + + void TearDown() override { DestroyCompiler(); } + void DestroyCompiler() + { + if (mCompiler) + { + sh::Destruct(mCompiler); + mCompiler = nullptr; + } + } + + void InitializeCompiler() + { + DestroyCompiler(); + mCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, SH_GLES3_SPEC, + SH_GLSL_COMPATIBILITY_OUTPUT, &mResources); + ASSERT_TRUE(mCompiler != nullptr) << "Compiler could not be constructed."; + } + + testing::AssertionResult TestShaderCompile(const char *version, + const char *pragma, + const char *shader) + { + const char *shaderStrings[] = {version, pragma, shader}; + bool success = sh::Compile(mCompiler, shaderStrings, 3, 0); + if (success) + { + return ::testing::AssertionSuccess() << "Compilation success"; + } + return ::testing::AssertionFailure() << sh::GetInfoLog(mCompiler); + } + + protected: + ShBuiltInResources mResources; + ShHandle mCompiler; +}; + +// The GLES SL 3.0 built-in variable gl_FragDepth fails to compile with GLES SL 1.0. +TEST_P(FragDepthTest, CompileFailsESSL100) +{ + static const char shaderString[] = + "precision mediump float;\n" + "void main() { \n" + " gl_FragDepth = 1.0;\n" + "}\n"; + + InitializeCompiler(); + EXPECT_FALSE(TestShaderCompile(ESSLVersion100, "", shaderString)); + EXPECT_FALSE(TestShaderCompile("", "", shaderString)); + EXPECT_FALSE(TestShaderCompile("", EXTFDPragma, shaderString)); +} + +// The GLES SL 3.0 built-in variable gl_FragDepth compiles with GLES SL 3.0. +TEST_P(FragDepthTest, CompileSucceedsESSL300) +{ + static const char shaderString[] = + "precision mediump float;\n" + "void main() { \n" + " gl_FragDepth = 1.0;\n" + "}\n"; + InitializeCompiler(); + EXPECT_TRUE(TestShaderCompile(ESSLVersion300, "", shaderString)); +} + +// Using #extension GL_EXT_frag_depth in GLSL ES 3.0 shader fails to compile. +TEST_P(FragDepthTest, ExtensionFDFailsESSL300) +{ + static const char shaderString[] = + "precision mediump float;\n" + "out vec4 fragColor;\n" + "void main() { \n" + " fragColor = vec4(1.0);\n" + "}\n"; + InitializeCompiler(); + if (mResources.EXT_frag_depth == 1) + { + // TODO(kkinnunen, geofflang): this should fail. Extensions need to have similar level + // system to SymbolTable. The biggest task is to implement version-aware preprocessor, so + // that the extension defines can be defined depending on the version that the preprocessor + // saw or did not see. + EXPECT_TRUE(TestShaderCompile(ESSLVersion300, EXTFDPragma, shaderString)); + } + else + { + EXPECT_FALSE(TestShaderCompile(ESSLVersion300, EXTFDPragma, shaderString)); + } +} + +// The tests should pass regardless whether the EXT_frag_depth is on or not. +INSTANTIATE_TEST_CASE_P(FragDepthTests, FragDepthTest, testing::Values(false, true)); diff --git a/gfx/angle/src/tests/compiler_tests/GLSLCompatibilityOutput_test.cpp b/gfx/angle/src/tests/compiler_tests/GLSLCompatibilityOutput_test.cpp new file mode 100755 index 000000000..8adec774a --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/GLSLCompatibilityOutput_test.cpp @@ -0,0 +1,35 @@ +// +// Copyright (c) 2016 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. +// +// GLSLCompatibilityOutputTest.cpp +// Test compiler output for glsl compatibility mode +// + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" +#include "tests/test_utils/compiler_test.h" + +using namespace sh; + +class GLSLCompatibilityOutputTest : public MatchOutputCodeTest +{ + public: + GLSLCompatibilityOutputTest() + : MatchOutputCodeTest(GL_VERTEX_SHADER, SH_VARIABLES, SH_GLSL_COMPATIBILITY_OUTPUT) + { + } +}; + +// Verify gl_Position is written when compiling in compatibility mode +TEST_F(GLSLCompatibilityOutputTest, GLPositionWrittenTest) +{ + const std::string &shaderString = + "precision mediump float;\n" + "void main() {\n" + "}"; + compile(shaderString); + EXPECT_TRUE(foundInCode("gl_Position")); +} \ No newline at end of file diff --git a/gfx/angle/src/tests/compiler_tests/IntermNode_test.cpp b/gfx/angle/src/tests/compiler_tests/IntermNode_test.cpp new file mode 100755 index 000000000..1a14ad86c --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/IntermNode_test.cpp @@ -0,0 +1,232 @@ +// +// Copyright (c) 2015 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. +// +// IntermNode_test.cpp: +// Unit tests for the AST node classes. +// + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "compiler/translator/InfoSink.h" +#include "compiler/translator/IntermNode.h" +#include "compiler/translator/PoolAlloc.h" + +using namespace sh; + +class IntermNodeTest : public testing::Test +{ + public: + IntermNodeTest() : mUniqueIndex(0) {} + + protected: + void SetUp() override + { + allocator.push(); + SetGlobalPoolAllocator(&allocator); + } + + void TearDown() override + { + SetGlobalPoolAllocator(nullptr); + allocator.pop(); + } + + TIntermSymbol *createTestSymbol(const TType &type) + { + TInfoSinkBase symbolNameOut; + symbolNameOut << "test" << mUniqueIndex; + TString symbolName = symbolNameOut.c_str(); + ++mUniqueIndex; + + TIntermSymbol *node = new TIntermSymbol(0, symbolName, type); + node->setLine(createUniqueSourceLoc()); + node->setInternal(true); + node->getTypePointer()->setQualifier(EvqTemporary); + return node; + } + + TIntermSymbol *createTestSymbol() + { + TType type(EbtFloat, EbpHigh); + return createTestSymbol(type); + } + + void checkTypeEqualWithQualifiers(const TType &original, const TType ©) + { + ASSERT_EQ(original, copy); + ASSERT_EQ(original.getPrecision(), copy.getPrecision()); + ASSERT_EQ(original.getQualifier(), copy.getQualifier()); + } + + void checkSymbolCopy(TIntermNode *aOriginal, TIntermNode *aCopy) + { + ASSERT_NE(aOriginal, aCopy); + TIntermSymbol *copy = aCopy->getAsSymbolNode(); + TIntermSymbol *original = aOriginal->getAsSymbolNode(); + ASSERT_NE(nullptr, copy); + ASSERT_NE(nullptr, original); + ASSERT_NE(original, copy); + ASSERT_EQ(original->getId(), copy->getId()); + ASSERT_EQ(original->getName().getString(), copy->getName().getString()); + ASSERT_EQ(original->getName().isInternal(), copy->getName().isInternal()); + checkTypeEqualWithQualifiers(original->getType(), copy->getType()); + ASSERT_EQ(original->getLine().first_file, copy->getLine().first_file); + ASSERT_EQ(original->getLine().first_line, copy->getLine().first_line); + ASSERT_EQ(original->getLine().last_file, copy->getLine().last_file); + ASSERT_EQ(original->getLine().last_line, copy->getLine().last_line); + } + + TSourceLoc createUniqueSourceLoc() + { + TSourceLoc loc; + loc.first_file = mUniqueIndex; + loc.first_line = mUniqueIndex + 1; + loc.last_file = mUniqueIndex + 2; + loc.last_line = mUniqueIndex + 3; + ++mUniqueIndex; + return loc; + } + + static TSourceLoc getTestSourceLoc() + { + TSourceLoc loc; + loc.first_file = 1; + loc.first_line = 2; + loc.last_file = 3; + loc.last_line = 4; + return loc; + } + + static void checkTestSourceLoc(const TSourceLoc &loc) + { + ASSERT_EQ(1, loc.first_file); + ASSERT_EQ(2, loc.first_line); + ASSERT_EQ(3, loc.last_file); + ASSERT_EQ(4, loc.last_line); + } + + private: + TPoolAllocator allocator; + int mUniqueIndex; +}; + +// Check that the deep copy of a symbol node is an actual copy with the same attributes as the +// original. +TEST_F(IntermNodeTest, DeepCopySymbolNode) +{ + TType type(EbtInt, EbpHigh); + TIntermSymbol *original = new TIntermSymbol(0, TString("name"), type); + original->setLine(getTestSourceLoc()); + original->setInternal(true); + TIntermTyped *copy = original->deepCopy(); + checkSymbolCopy(original, copy); + checkTestSourceLoc(copy->getLine()); +} + +// Check that the deep copy of a constant union node is an actual copy with the same attributes as +// the original. +TEST_F(IntermNodeTest, DeepCopyConstantUnionNode) +{ + TType type(EbtInt, EbpHigh); + TConstantUnion *constValue = new TConstantUnion[1]; + constValue[0].setIConst(101); + TIntermConstantUnion *original = new TIntermConstantUnion(constValue, type); + original->setLine(getTestSourceLoc()); + TIntermTyped *copyTyped = original->deepCopy(); + TIntermConstantUnion *copy = copyTyped->getAsConstantUnion(); + ASSERT_NE(nullptr, copy); + ASSERT_NE(original, copy); + checkTestSourceLoc(copy->getLine()); + checkTypeEqualWithQualifiers(original->getType(), copy->getType()); + ASSERT_EQ(101, copy->getIConst(0)); +} + +// Check that the deep copy of a binary node is an actual copy with the same attributes as the +// original. Child nodes also need to be copies with the same attributes as the original children. +TEST_F(IntermNodeTest, DeepCopyBinaryNode) +{ + TType type(EbtFloat, EbpHigh); + + TIntermBinary *original = new TIntermBinary(EOpAdd, createTestSymbol(), createTestSymbol()); + original->setLine(getTestSourceLoc()); + TIntermTyped *copyTyped = original->deepCopy(); + TIntermBinary *copy = copyTyped->getAsBinaryNode(); + ASSERT_NE(nullptr, copy); + ASSERT_NE(original, copy); + checkTestSourceLoc(copy->getLine()); + checkTypeEqualWithQualifiers(original->getType(), copy->getType()); + + checkSymbolCopy(original->getLeft(), copy->getLeft()); + checkSymbolCopy(original->getRight(), copy->getRight()); +} + +// Check that the deep copy of a unary node is an actual copy with the same attributes as the +// original. The child node also needs to be a copy with the same attributes as the original child. +TEST_F(IntermNodeTest, DeepCopyUnaryNode) +{ + TType type(EbtFloat, EbpHigh); + + TIntermUnary *original = new TIntermUnary(EOpPreIncrement, createTestSymbol()); + original->setLine(getTestSourceLoc()); + TIntermTyped *copyTyped = original->deepCopy(); + TIntermUnary *copy = copyTyped->getAsUnaryNode(); + ASSERT_NE(nullptr, copy); + ASSERT_NE(original, copy); + checkTestSourceLoc(copy->getLine()); + checkTypeEqualWithQualifiers(original->getType(), copy->getType()); + + checkSymbolCopy(original->getOperand(), copy->getOperand()); +} + +// Check that the deep copy of an aggregate node is an actual copy with the same attributes as the +// original. Child nodes also need to be copies with the same attributes as the original children. +TEST_F(IntermNodeTest, DeepCopyAggregateNode) +{ + TType type(EbtFloat, EbpHigh); + + TIntermAggregate *original = new TIntermAggregate(EOpMix); + original->setLine(getTestSourceLoc()); + TIntermSequence *originalSeq = original->getSequence(); + originalSeq->push_back(createTestSymbol()); + originalSeq->push_back(createTestSymbol()); + originalSeq->push_back(createTestSymbol()); + TIntermTyped *copyTyped = original->deepCopy(); + TIntermAggregate *copy = copyTyped->getAsAggregate(); + ASSERT_NE(nullptr, copy); + ASSERT_NE(original, copy); + checkTestSourceLoc(copy->getLine()); + checkTypeEqualWithQualifiers(original->getType(), copy->getType()); + + ASSERT_EQ(original->getSequence()->size(), copy->getSequence()->size()); + TIntermSequence::size_type i = 0; + for (auto *copyChild : *copy->getSequence()) + { + TIntermNode *originalChild = originalSeq->at(i); + checkSymbolCopy(originalChild, copyChild); + ++i; + } +} + +// Check that the deep copy of a ternary node is an actual copy with the same attributes as the +// original. Child nodes also need to be copies with the same attributes as the original children. +TEST_F(IntermNodeTest, DeepCopyTernaryNode) +{ + TType type(EbtFloat, EbpHigh); + + TIntermTernary *original = new TIntermTernary(createTestSymbol(TType(EbtBool, EbpUndefined)), + createTestSymbol(), createTestSymbol()); + original->setLine(getTestSourceLoc()); + TIntermTyped *copyTyped = original->deepCopy(); + TIntermTernary *copy = copyTyped->getAsTernaryNode(); + ASSERT_NE(nullptr, copy); + ASSERT_NE(original, copy); + checkTestSourceLoc(copy->getLine()); + checkTypeEqualWithQualifiers(original->getType(), copy->getType()); + + checkSymbolCopy(original->getCondition(), copy->getCondition()); + checkSymbolCopy(original->getTrueExpression(), copy->getTrueExpression()); + checkSymbolCopy(original->getFalseExpression(), copy->getFalseExpression()); +} + diff --git a/gfx/angle/src/tests/compiler_tests/MalformedShader_test.cpp b/gfx/angle/src/tests/compiler_tests/MalformedShader_test.cpp new file mode 100755 index 000000000..e84fc7014 --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/MalformedShader_test.cpp @@ -0,0 +1,3158 @@ +// +// Copyright (c) 2015 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. +// +// MalformedShader_test.cpp: +// Tests that malformed shaders fail compilation. +// + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" +#include "compiler/translator/TranslatorESSL.h" + +using namespace sh; + +class MalformedShaderTest : public testing::Test +{ + public: + MalformedShaderTest() : mExtraCompileOptions(0) {} + + protected: + virtual void SetUp() + { + ShBuiltInResources resources; + sh::InitBuiltInResources(&resources); + + mTranslator = new TranslatorESSL(GL_FRAGMENT_SHADER, SH_GLES3_SPEC); + ASSERT_TRUE(mTranslator->Init(resources)); + } + + virtual void TearDown() + { + delete mTranslator; + } + + // Return true when compilation succeeds + bool compile(const std::string& shaderString) + { + const char *shaderStrings[] = { shaderString.c_str() }; + bool compilationSuccess = + mTranslator->compile(shaderStrings, 1, SH_INTERMEDIATE_TREE | mExtraCompileOptions); + TInfoSink &infoSink = mTranslator->getInfoSink(); + mInfoLog = infoSink.info.c_str(); + return compilationSuccess; + } + + bool hasWarning() const + { + return mInfoLog.find("WARNING: ") != std::string::npos; + } + + protected: + std::string mInfoLog; + TranslatorESSL *mTranslator; + ShCompileOptions mExtraCompileOptions; +}; + +class MalformedVertexShaderTest : public MalformedShaderTest +{ + public: + MalformedVertexShaderTest() {} + + protected: + void SetUp() override + { + ShBuiltInResources resources; + sh::InitBuiltInResources(&resources); + + mTranslator = new TranslatorESSL(GL_VERTEX_SHADER, SH_GLES3_SPEC); + ASSERT_TRUE(mTranslator->Init(resources)); + } +}; + +class MalformedWebGL2ShaderTest : public MalformedShaderTest +{ + public: + MalformedWebGL2ShaderTest() {} + + protected: + void SetUp() override + { + ShBuiltInResources resources; + sh::InitBuiltInResources(&resources); + + mTranslator = new TranslatorESSL(GL_FRAGMENT_SHADER, SH_WEBGL2_SPEC); + ASSERT_TRUE(mTranslator->Init(resources)); + } +}; + +class MalformedWebGL1ShaderTest : public MalformedShaderTest +{ + public: + MalformedWebGL1ShaderTest() {} + + protected: + void SetUp() override + { + ShBuiltInResources resources; + sh::InitBuiltInResources(&resources); + + mTranslator = new TranslatorESSL(GL_FRAGMENT_SHADER, SH_WEBGL_SPEC); + ASSERT_TRUE(mTranslator->Init(resources)); + } +}; + +class MalformedVertexShaderGLES31Test : public MalformedShaderTest +{ + public: + MalformedVertexShaderGLES31Test() {} + + private: + void SetUp() override + { + ShBuiltInResources resources; + sh::InitBuiltInResources(&resources); + mTranslator = new TranslatorESSL(GL_VERTEX_SHADER, SH_GLES3_1_SPEC); + ASSERT_TRUE(mTranslator->Init(resources)); + } +}; + +class MalformedFragmentShaderGLES31Test : public MalformedShaderTest +{ + public: + MalformedFragmentShaderGLES31Test() {} + + private: + void SetUp() override + { + ShBuiltInResources resources; + sh::InitBuiltInResources(&resources); + mTranslator = new TranslatorESSL(GL_FRAGMENT_SHADER, SH_GLES3_1_SPEC); + ASSERT_TRUE(mTranslator->Init(resources)); + } +}; + +class MalformedComputeShaderTest : public MalformedShaderTest +{ + public: + MalformedComputeShaderTest() {} + + private: + void SetUp() override + { + ShBuiltInResources resources; + sh::InitBuiltInResources(&resources); + mTranslator = new TranslatorESSL(GL_COMPUTE_SHADER, SH_GLES3_1_SPEC); + ASSERT_TRUE(mTranslator->Init(resources)); + } +}; + +class UnrollForLoopsTest : public MalformedShaderTest +{ + public: + UnrollForLoopsTest() { mExtraCompileOptions = SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX; } +}; + +// This is a test for a bug that used to exist in ANGLE: +// Calling a function with all parameters missing should not succeed. +TEST_F(MalformedShaderTest, FunctionParameterMismatch) +{ + const std::string &shaderString = + "precision mediump float;\n" + "float fun(float a) {\n" + " return a * 2.0;\n" + "}\n" + "void main() {\n" + " float ff = fun();\n" + " gl_FragColor = vec4(ff);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Functions can't be redeclared as variables in the same scope (ESSL 1.00 section 4.2.7) +TEST_F(MalformedShaderTest, RedeclaringFunctionAsVariable) +{ + const std::string &shaderString = + "precision mediump float;\n" + "float fun(float a) {\n" + " return a * 2.0;\n" + "}\n" + "float fun;\n" + "void main() {\n" + " gl_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Functions can't be redeclared as structs in the same scope (ESSL 1.00 section 4.2.7) +TEST_F(MalformedShaderTest, RedeclaringFunctionAsStruct) +{ + const std::string &shaderString = + "precision mediump float;\n" + "float fun(float a) {\n" + " return a * 2.0;\n" + "}\n" + "struct fun { float a; };\n" + "void main() {\n" + " gl_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Functions can't be redeclared with different qualifiers (ESSL 1.00 section 6.1.0) +TEST_F(MalformedShaderTest, RedeclaringFunctionWithDifferentQualifiers) +{ + const std::string &shaderString = + "precision mediump float;\n" + "float fun(out float a);\n" + "float fun(float a) {\n" + " return a * 2.0;\n" + "}\n" + "void main() {\n" + " gl_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Assignment and equality are undefined for structures containing arrays (ESSL 1.00 section 5.7) +TEST_F(MalformedShaderTest, CompareStructsContainingArrays) +{ + const std::string &shaderString = + "precision mediump float;\n" + "struct s { float a[3]; };\n" + "void main() {\n" + " s a;\n" + " s b;\n" + " bool c = (a == b);\n" + " gl_FragColor = vec4(c ? 1.0 : 0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Assignment and equality are undefined for structures containing arrays (ESSL 1.00 section 5.7) +TEST_F(MalformedShaderTest, AssignStructsContainingArrays) +{ + const std::string &shaderString = + "precision mediump float;\n" + "struct s { float a[3]; };\n" + "void main() {\n" + " s a;\n" + " s b;\n" + " b.a[0] = 0.0;\n" + " a = b;\n" + " gl_FragColor = vec4(a.a[0]);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Assignment and equality are undefined for structures containing samplers (ESSL 1.00 sections 5.7 and 5.9) +TEST_F(MalformedShaderTest, CompareStructsContainingSamplers) +{ + const std::string &shaderString = + "precision mediump float;\n" + "struct s { sampler2D foo; };\n" + "uniform s a;\n" + "uniform s b;\n" + "void main() {\n" + " bool c = (a == b);\n" + " gl_FragColor = vec4(c ? 1.0 : 0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Samplers are not allowed as l-values (ESSL 3.00 section 4.1.7), our interpretation is that this +// extends to structs containing samplers. ESSL 1.00 spec is clearer about this. +TEST_F(MalformedShaderTest, AssignStructsContainingSamplers) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "struct s { sampler2D foo; };\n" + "uniform s a;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " s b;\n" + " b = a;\n" + " my_FragColor = vec4(1.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// This is a regression test for a particular bug that was in ANGLE. +// It also verifies that ESSL3 functionality doesn't leak to ESSL1. +TEST_F(MalformedShaderTest, ArrayWithNoSizeInInitializerList) +{ + const std::string &shaderString = + "precision mediump float;\n" + "void main() {\n" + " float a[2], b[];\n" + " gl_FragColor = vec4(1.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Const variables need an initializer. +TEST_F(MalformedShaderTest, ConstVarNotInitialized) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " const float a;\n" + " my_FragColor = vec4(1.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Const variables need an initializer. In ESSL1 const structs containing +// arrays are not allowed at all since it's impossible to initialize them. +// Even though this test is for ESSL3 the only thing that's critical for +// ESSL1 is the non-initialization check that's used for both language versions. +// Whether ESSL1 compilation generates the most helpful error messages is a +// secondary concern. +TEST_F(MalformedShaderTest, ConstStructNotInitialized) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "struct S {\n" + " float a[3];\n" + "};\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " const S b;\n" + " my_FragColor = vec4(1.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Const variables need an initializer. In ESSL1 const arrays are not allowed +// at all since it's impossible to initialize them. +// Even though this test is for ESSL3 the only thing that's critical for +// ESSL1 is the non-initialization check that's used for both language versions. +// Whether ESSL1 compilation generates the most helpful error messages is a +// secondary concern. +TEST_F(MalformedShaderTest, ConstArrayNotInitialized) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " const float a[3];\n" + " my_FragColor = vec4(1.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Block layout qualifiers can't be used on non-block uniforms (ESSL 3.00 section 4.3.8.3) +TEST_F(MalformedShaderTest, BlockLayoutQualifierOnRegularUniform) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "layout(packed) uniform mat2 x;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " my_FragColor = vec4(1.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Block layout qualifiers can't be used on non-block uniforms (ESSL 3.00 section 4.3.8.3) +TEST_F(MalformedShaderTest, BlockLayoutQualifierOnUniformWithEmptyDecl) +{ + // Yes, the comma in the declaration below is not a typo. + // Empty declarations are allowed in GLSL. + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "layout(packed) uniform mat2, x;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " my_FragColor = vec4(1.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Arrays of arrays are not allowed (ESSL 3.00 section 4.1.9) +TEST_F(MalformedShaderTest, ArraysOfArrays1) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " float[5] a[3];\n" + " my_FragColor = vec4(1.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Arrays of arrays are not allowed (ESSL 3.00 section 4.1.9) +TEST_F(MalformedShaderTest, ArraysOfArrays2) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " float[2] a, b[3];\n" + " my_FragColor = vec4(1.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Implicitly sized arrays need to be initialized (ESSL 3.00 section 4.1.9) +TEST_F(MalformedShaderTest, UninitializedImplicitArraySize) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " float[] a;\n" + " my_FragColor = vec4(1.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// An operator can only form a constant expression if all the operands are constant expressions +// - even operands of ternary operator that are never evaluated. (ESSL 3.00 section 4.3.3) +TEST_F(MalformedShaderTest, TernaryOperatorNotConstantExpression) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "uniform bool u;\n" + "void main() {\n" + " const bool a = true ? true : u;\n" + " my_FragColor = vec4(1.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Ternary operator can't operate on arrays (ESSL 3.00 section 5.7) +TEST_F(MalformedShaderTest, TernaryOperatorOnArrays) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " float[1] a = float[1](0.0);\n" + " float[1] b = float[1](1.0);\n" + " float[1] c = true ? a : b;\n" + " my_FragColor = vec4(1.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Ternary operator can't operate on structs (ESSL 3.00 section 5.7) +TEST_F(MalformedShaderTest, TernaryOperatorOnStructs) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "struct S { float foo; };\n" + "void main() {\n" + " S a = S(0.0);\n" + " S b = S(1.0);\n" + " S c = true ? a : b;\n" + " my_FragColor = vec4(1.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Array length() returns a constant signed integral expression (ESSL 3.00 section 4.1.9) +// Assigning it to unsigned should result in an error. +TEST_F(MalformedShaderTest, AssignArrayLengthToUnsigned) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " int[1] arr;\n" + " uint l = arr.length();\n" + " my_FragColor = vec4(float(l));\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Global variable initializers need to be constant expressions (ESSL 1.00 section 4.3) +// Initializing with a varying should be an error. +TEST_F(MalformedShaderTest, AssignVaryingToGlobal) +{ + const std::string &shaderString = + "precision mediump float;\n" + "varying float a;\n" + "float b = a * 2.0;\n" + "void main() {\n" + " gl_FragColor = vec4(b);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Global variable initializers need to be constant expressions (ESSL 3.00 section 4.3) +// Initializing with an uniform should be an error. +TEST_F(MalformedShaderTest, AssignUniformToGlobalESSL3) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "uniform float a;\n" + "float b = a * 2.0;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " my_FragColor = vec4(b);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Global variable initializers need to be constant expressions (ESSL 1.00 section 4.3) +// Initializing with an uniform should generate a warning +// (we don't generate an error on ESSL 1.00 because of legacy compatibility) +TEST_F(MalformedShaderTest, AssignUniformToGlobalESSL1) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float a;\n" + "float b = a * 2.0;\n" + "void main() {\n" + " gl_FragColor = vec4(b);\n" + "}\n"; + if (compile(shaderString)) + { + if (!hasWarning()) + { + FAIL() << "Shader compilation succeeded without warnings, expecting warning " << mInfoLog; + } + } + else + { + FAIL() << "Shader compilation failed, expecting success with warning " << mInfoLog; + } +} + +// Global variable initializers need to be constant expressions (ESSL 1.00 section 4.3) +// Initializing with an user-defined function call should be an error. +TEST_F(MalformedShaderTest, AssignFunctionCallToGlobal) +{ + const std::string &shaderString = + "precision mediump float;\n" + "float foo() { return 1.0; }\n" + "float b = foo();\n" + "void main() {\n" + " gl_FragColor = vec4(b);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Global variable initializers need to be constant expressions (ESSL 1.00 section 4.3) +// Initializing with an assignment to another global should be an error. +TEST_F(MalformedShaderTest, AssignAssignmentToGlobal) +{ + const std::string &shaderString = + "precision mediump float;\n" + "float c = 1.0;\n" + "float b = (c = 0.0);\n" + "void main() {\n" + " gl_FragColor = vec4(b);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Global variable initializers need to be constant expressions (ESSL 1.00 section 4.3) +// Initializing with incrementing another global should be an error. +TEST_F(MalformedShaderTest, AssignIncrementToGlobal) +{ + const std::string &shaderString = + "precision mediump float;\n" + "float c = 1.0;\n" + "float b = (c++);\n" + "void main() {\n" + " gl_FragColor = vec4(b);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Global variable initializers need to be constant expressions (ESSL 1.00 section 4.3) +// Initializing with a texture lookup function call should be an error. +TEST_F(MalformedShaderTest, AssignTexture2DToGlobal) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform mediump sampler2D s;\n" + "float b = texture2D(s, vec2(0.5, 0.5)).x;\n" + "void main() {\n" + " gl_FragColor = vec4(b);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Global variable initializers need to be constant expressions (ESSL 3.00 section 4.3) +// Initializing with a non-constant global should be an error. +TEST_F(MalformedShaderTest, AssignNonConstGlobalToGlobal) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "float a = 1.0;\n" + "float b = a * 2.0;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " my_FragColor = vec4(b);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Global variable initializers need to be constant expressions (ESSL 3.00 section 4.3) +// Initializing with a constant global should be fine. +TEST_F(MalformedShaderTest, AssignConstGlobalToGlobal) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "const float a = 1.0;\n" + "float b = a * 2.0;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " my_FragColor = vec4(b);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Statically assigning to both gl_FragData and gl_FragColor is forbidden (ESSL 1.00 section 7.2) +TEST_F(MalformedShaderTest, WriteBothFragDataAndFragColor) +{ + const std::string &shaderString = + "precision mediump float;\n" + "void foo() {\n" + " gl_FragData[0].a++;\n" + "}\n" + "void main() {\n" + " gl_FragColor.x += 0.0;\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Version directive must be on the first line (ESSL 3.00 section 3.3) +TEST_F(MalformedShaderTest, VersionOnSecondLine) +{ + const std::string &shaderString = + "\n" + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " my_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Layout qualifier can only appear in global scope (ESSL 3.00 section 4.3.8) +TEST_F(MalformedShaderTest, LayoutQualifierInCondition) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "uniform vec4 u;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " int i = 0;\n" + " for (int j = 0; layout(location = 0) bool b = false; ++j) {\n" + " ++i;\n" + " }\n" + " my_FragColor = u;\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Layout qualifier can only appear where specified (ESSL 3.00 section 4.3.8) +TEST_F(MalformedShaderTest, LayoutQualifierInFunctionReturnType) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "uniform vec4 u;\n" + "out vec4 my_FragColor;\n" + "layout(location = 0) vec4 foo() {\n" + " return u;\n" + "}\n" + "void main() {\n" + " my_FragColor = foo();\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// If there is more than one output, the location must be specified for all outputs. +// (ESSL 3.00.04 section 4.3.8.2) +TEST_F(MalformedShaderTest, TwoOutputsNoLayoutQualifiers) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "uniform vec4 u;\n" + "out vec4 my_FragColor;\n" + "out vec4 my_SecondaryFragColor;\n" + "void main() {\n" + " my_FragColor = vec4(1.0);\n" + " my_SecondaryFragColor = vec4(0.5);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// (ESSL 3.00.04 section 4.3.8.2) +TEST_F(MalformedShaderTest, TwoOutputsFirstLayoutQualifier) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "uniform vec4 u;\n" + "layout(location = 0) out vec4 my_FragColor;\n" + "out vec4 my_SecondaryFragColor;\n" + "void main() {\n" + " my_FragColor = vec4(1.0);\n" + " my_SecondaryFragColor = vec4(0.5);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// (ESSL 3.00.04 section 4.3.8.2) +TEST_F(MalformedShaderTest, TwoOutputsSecondLayoutQualifier) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "uniform vec4 u;\n" + "out vec4 my_FragColor;\n" + "layout(location = 0) out vec4 my_SecondaryFragColor;\n" + "void main() {\n" + " my_FragColor = vec4(1.0);\n" + " my_SecondaryFragColor = vec4(0.5);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Uniforms can be arrays (ESSL 3.00 section 4.3.5) +TEST_F(MalformedShaderTest, UniformArray) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "uniform vec4[2] u;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " my_FragColor = u[0];\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Fragment shader input variables cannot be arrays of structs (ESSL 3.00 section 4.3.4) +TEST_F(MalformedShaderTest, FragmentInputArrayOfStructs) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "struct S {\n" + " vec4 foo;\n" + "};\n" + "in S i[2];\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " my_FragColor = i[0].foo;\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Vertex shader inputs can't be arrays (ESSL 3.00 section 4.3.4) +// This test is testing the case where the array brackets are after the variable name, so +// the arrayness isn't known when the type and qualifiers are initially parsed. +TEST_F(MalformedVertexShaderTest, VertexShaderInputArray) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "in vec4 i[2];\n" + "void main() {\n" + " gl_Position = i[0];\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Vertex shader inputs can't be arrays (ESSL 3.00 section 4.3.4) +// This test is testing the case where the array brackets are after the type. +TEST_F(MalformedVertexShaderTest, VertexShaderInputArrayType) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "in vec4[2] i;\n" + "void main() {\n" + " gl_Position = i[0];\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Fragment shader inputs can't contain booleans (ESSL 3.00 section 4.3.4) +TEST_F(MalformedShaderTest, FragmentShaderInputStructWithBool) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "struct S {\n" + " bool foo;\n" + "};\n" + "in S s;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " my_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Fragment shader inputs without a flat qualifier can't contain integers (ESSL 3.00 section 4.3.4) +TEST_F(MalformedShaderTest, FragmentShaderInputStructWithInt) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "struct S {\n" + " int foo;\n" + "};\n" + "in S s;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " my_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Selecting a field of a vector that's the result of dynamic indexing a constant array should work. +TEST_F(MalformedShaderTest, ShaderSelectingFieldOfVectorIndexedFromArray) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "uniform int i;\n" + "void main() {\n" + " float f = vec2[1](vec2(0.0, 0.1))[i].x;\n" + " my_FragColor = vec4(f);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Passing an array into a function and then passing a value from that array into another function +// should work. This is a regression test for a bug where the mangled name of a TType was not +// properly updated when determining the type resulting from array indexing. +TEST_F(MalformedShaderTest, ArrayValueFromFunctionParameterAsParameter) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float u;\n" + "float foo(float f) {\n" + " return f * 2.0;\n" + "}\n" + "float bar(float[2] f) {\n" + " return foo(f[0]);\n" + "}\n" + "void main()\n" + "{\n" + " float arr[2];\n" + " arr[0] = u;\n" + " gl_FragColor = vec4(bar(arr));\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Test that out-of-range integer literal generates an error in ESSL 3.00. +TEST_F(MalformedShaderTest, OutOfRangeIntegerLiteral) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "precision highp int;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " my_FragColor = vec4(0x100000000);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that vector field selection from a value taken from an array constructor is accepted as a +// constant expression. +TEST_F(MalformedShaderTest, FieldSelectionFromVectorArrayConstructorIsConst) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " const float f = vec2[1](vec2(0.0, 1.0))[0].x;\n" + " my_FragColor = vec4(f);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Test that structure field selection from a value taken from an array constructor is accepted as a +// constant expression. +TEST_F(MalformedShaderTest, FieldSelectionFromStructArrayConstructorIsConst) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "struct S { float member; };\n" + "void main()\n" + "{\n" + " const float f = S[1](S(0.0))[0].member;\n" + " my_FragColor = vec4(f);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Test that a reference to a const array is accepted as a constant expression. +TEST_F(MalformedShaderTest, ArraySymbolIsConst) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " const float[2] arr = float[2](0.0, 1.0);\n" + " const float f = arr[0];\n" + " my_FragColor = vec4(f);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Test that using an array constructor in a parameter to a built-in function is accepted as a +// constant expression. +TEST_F(MalformedShaderTest, BuiltInFunctionAppliedToArrayConstructorIsConst) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " const float f = sin(float[2](0.0, 1.0)[0]);\n" + " my_FragColor = vec4(f);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Test that using an array constructor in a parameter to a built-in function is accepted as a +// constant expression. +TEST_F(MalformedShaderTest, BuiltInFunctionWithMultipleParametersAppliedToArrayConstructorIsConst) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " const float f = pow(1.0, float[2](0.0, 1.0)[0]);\n" + " my_FragColor = vec4(f);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Test that using an array constructor in a parameter to a constructor is accepted as a constant +// expression. +TEST_F(MalformedShaderTest, ConstructorWithMultipleParametersAppliedToArrayConstructorIsConst) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " const vec2 f = vec2(1.0, float[2](0.0, 1.0)[0]);\n" + " my_FragColor = vec4(f.x);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Test that using an array constructor in an operand of the ternary selection operator is accepted +// as a constant expression. +TEST_F(MalformedShaderTest, TernaryOperatorAppliedToArrayConstructorIsConst) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " const float f = true ? float[2](0.0, 1.0)[0] : 1.0;\n" + " my_FragColor = vec4(f);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Test that a ternary operator with one unevaluated non-constant operand is not a constant +// expression. +TEST_F(MalformedShaderTest, TernaryOperatorNonConstantOperand) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float u;\n" + "void main()\n" + "{\n" + " const float f = true ? 1.0 : u;\n" + " gl_FragColor = vec4(f);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that a sampler can't be used in constructor argument list +TEST_F(MalformedShaderTest, SamplerInConstructorArguments) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform sampler2D s;\n" + "void main()\n" + "{\n" + " vec2 v = vec2(0.0, s);\n" + " gl_FragColor = vec4(v, 0.0, 0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that void can't be used in constructor argument list +TEST_F(MalformedShaderTest, VoidInConstructorArguments) +{ + const std::string &shaderString = + "precision mediump float;\n" + "void foo() {}\n" + "void main()\n" + "{\n" + " vec2 v = vec2(0.0, foo());\n" + " gl_FragColor = vec4(v, 0.0, 0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that a shader passing a struct into a constructor of array of structs with 1 element works. +TEST_F(MalformedShaderTest, SingleStructArrayConstructor) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "uniform float u;\n" + "struct S { float member; };\n" + "void main()\n" + "{\n" + " S[1] sarr = S[1](S(u));\n" + " my_FragColor = vec4(sarr[0].member);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Test that a shader with empty constructor parameter list is not accepted. +TEST_F(MalformedShaderTest, EmptyArrayConstructor) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "uniform float u;\n" + "const float[] f = f[]();\n" + "void main()\n" + "{\n" + " my_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that indexing fragment outputs with a non-constant expression is forbidden, even if ANGLE +// is able to constant fold the index expression. ESSL 3.00 section 4.3.6. +TEST_F(MalformedShaderTest, DynamicallyIndexedFragmentOutput) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "uniform int a;\n" + "out vec4[2] my_FragData;\n" + "void main()\n" + "{\n" + " my_FragData[true ? 0 : a] = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that indexing an interface block array with a non-constant expression is forbidden, even if +// ANGLE is able to constant fold the index expression. ESSL 3.00 section 4.3.7. +TEST_F(MalformedShaderTest, DynamicallyIndexedInterfaceBlock) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "uniform int a;\n" + "uniform B\n" + "{\n" + " vec4 f;\n" + "}\n" + "blocks[2];\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " my_FragColor = blocks[true ? 0 : a].f;\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that a shader that uses a struct definition in place of a struct constructor does not +// compile. See GLSL ES 1.00 section 5.4.3. +TEST_F(MalformedShaderTest, StructConstructorWithStructDefinition) +{ + const std::string &shaderString = + "precision mediump float;\n" + "void main()\n" + "{\n" + " struct s { float f; } (0.0);\n" + " gl_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that indexing gl_FragData with a non-constant expression is forbidden in WebGL 2.0, even +// when ANGLE is able to constant fold the index. +// WebGL 2.0 spec section 'GLSL ES 1.00 Fragment Shader Output' +TEST_F(MalformedWebGL2ShaderTest, IndexFragDataWithNonConstant) +{ + const std::string &shaderString = + "precision mediump float;\n" + "void main()\n" + "{\n" + " for (int i = 0; i < 2; ++i) {\n" + " gl_FragData[true ? 0 : i] = vec4(0.0);\n" + " }\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that a non-constant texture offset is not accepted for textureOffset. +// ESSL 3.00 section 8.8 +TEST_F(MalformedShaderTest, TextureOffsetNonConst) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "uniform vec3 u_texCoord;\n" + "uniform mediump sampler3D u_sampler;\n" + "uniform int x;\n" + "void main()\n" + "{\n" + " my_FragColor = textureOffset(u_sampler, u_texCoord, ivec3(x, 3, -8));\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that a non-constant texture offset is not accepted for textureProjOffset with bias. +// ESSL 3.00 section 8.8 +TEST_F(MalformedShaderTest, TextureProjOffsetNonConst) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "uniform vec4 u_texCoord;\n" + "uniform mediump sampler3D u_sampler;\n" + "uniform int x;\n" + "void main()\n" + "{\n" + " my_FragColor = textureProjOffset(u_sampler, u_texCoord, ivec3(x, 3, -8), 0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that an out-of-range texture offset is not accepted. +// GLES 3.0.4 section 3.8.10 specifies that out-of-range offset has undefined behavior. +TEST_F(MalformedShaderTest, TextureLodOffsetOutOfRange) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "uniform vec3 u_texCoord;\n" + "uniform mediump sampler3D u_sampler;\n" + "void main()\n" + "{\n" + " my_FragColor = textureLodOffset(u_sampler, u_texCoord, 0.0, ivec3(0, 0, 8));\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that default precision qualifier for uint is not accepted. +// ESSL 3.00.4 section 4.5.4: Only allowed for float, int and sampler types. +TEST_F(MalformedShaderTest, DefaultPrecisionUint) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "precision mediump uint;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " my_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that sampler3D needs to be precision qualified. +// ESSL 3.00.4 section 4.5.4: New ESSL 3.00 sampler types don't have predefined precision. +TEST_F(MalformedShaderTest, NoPrecisionSampler3D) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "uniform sampler3D s;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " my_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that using a non-constant expression in a for loop initializer is forbidden in WebGL 1.0, +// even when ANGLE is able to constant fold the initializer. +// ESSL 1.00 Appendix A. +TEST_F(MalformedWebGL1ShaderTest, NonConstantLoopIndex) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform int u;\n" + "void main()\n" + "{\n" + " for (int i = (true ? 1 : u); i < 5; ++i) {\n" + " gl_FragColor = vec4(0.0);\n" + " }\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Regression test for an old crash bug in ANGLE. +// ForLoopUnroll used to crash when it encountered a while loop. +TEST_F(UnrollForLoopsTest, WhileLoop) +{ + const std::string &shaderString = + "precision mediump float;\n" + "void main()\n" + "{\n" + " while (true) {\n" + " gl_FragColor = vec4(0.0);\n" + " break;\n" + " }\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Regression test for an old crash bug in ANGLE. +// ForLoopUnroll used to crash when it encountered a loop that didn't fit the ESSL 1.00 +// Appendix A limitations. +TEST_F(UnrollForLoopsTest, UnlimitedForLoop) +{ + const std::string &shaderString = + "precision mediump float;\n" + "void main()\n" + "{\n" + " for (;true;) {\n" + " gl_FragColor = vec4(0.0);\n" + " break;\n" + " }\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Check that indices that are not integers are rejected. +// The check should be done even if ESSL 1.00 Appendix A limitations are not applied. +TEST_F(MalformedShaderTest, NonIntegerIndex) +{ + const std::string &shaderString = + "precision mediump float;\n" + "void main()\n" + "{\n" + " float f[3];\n" + " const float i = 2.0;\n" + " gl_FragColor = vec4(f[i]);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// ESSL1 shaders with a duplicate function prototype should be rejected. +// ESSL 1.00.17 section 4.2.7. +TEST_F(MalformedShaderTest, DuplicatePrototypeESSL1) +{ + const std::string &shaderString = + "precision mediump float;\n" + "void foo();\n" + "void foo();\n" + "void foo() {}\n" + "void main()\n" + "{\n" + " gl_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// ESSL3 shaders with a duplicate function prototype should be allowed. +// ESSL 3.00.4 section 4.2.3. +TEST_F(MalformedShaderTest, DuplicatePrototypeESSL3) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void foo();\n" + "void foo();\n" + "void foo() {}\n" + "void main()\n" + "{\n" + " my_FragColor = vec4(0.0);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Shaders with a local function prototype should be rejected. +// ESSL 3.00.4 section 4.2.4. +TEST_F(MalformedShaderTest, LocalFunctionPrototype) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " void foo();\n" + " my_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// ESSL 3.00 fragment shaders can not use #pragma STDGL invariant(all). +// ESSL 3.00.4 section 4.6.1. Does not apply to other versions of ESSL. +TEST_F(MalformedShaderTest, ESSL300FragmentInvariantAll) +{ + const std::string &shaderString = + "#version 300 es\n" + "#pragma STDGL invariant(all)\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " my_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Built-in functions can be overloaded in ESSL 1.00. +TEST_F(MalformedShaderTest, ESSL100BuiltInFunctionOverload) +{ + const std::string &shaderString = + "precision mediump float;\n" + "int sin(int x)\n" + "{\n" + " return int(sin(float(x)));\n" + "}\n" + "void main()\n" + "{\n" + " gl_FragColor = vec4(sin(1));" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Built-in functions can not be overloaded in ESSL 3.00. +TEST_F(MalformedShaderTest, ESSL300BuiltInFunctionOverload) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "int sin(int x)\n" + "{\n" + " return int(sin(float(x)));\n" + "}\n" + "void main()\n" + "{\n" + " my_FragColor = vec4(sin(1));" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Multiplying a 4x2 matrix with a 4x2 matrix should not work. +TEST_F(MalformedShaderTest, CompoundMultiplyMatrixIdenticalNonSquareDimensions) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " mat4x2 foo;\n" + " foo *= mat4x2(4.0);\n" + " my_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Multiplying a matrix with 2 columns and 4 rows with a 2x2 matrix should work. +TEST_F(MalformedShaderTest, CompoundMultiplyMatrixValidNonSquareDimensions) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " mat2x4 foo;\n" + " foo *= mat2x2(4.0);\n" + " my_FragColor = vec4(0.0);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Covers a bug where we would set the incorrect result size on an out-of-bounds vector swizzle. +TEST_F(MalformedShaderTest, OutOfBoundsVectorSwizzle) +{ + const std::string &shaderString = + "void main() {\n" + " vec2(0).qq;\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Covers a bug where strange preprocessor defines could trigger asserts. +TEST_F(MalformedShaderTest, DefineWithSemicolon) +{ + const std::string &shaderString = + "#define Def; highp\n" + "uniform Def vec2 a;\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Covers a bug in our parsing of malformed shift preprocessor expressions. +TEST_F(MalformedShaderTest, LineDirectiveUndefinedShift) +{ + const std::string &shaderString = "#line x << y"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Covers a bug in our parsing of malformed shift preprocessor expressions. +TEST_F(MalformedShaderTest, LineDirectiveNegativeShift) +{ + const std::string &shaderString = "#line x << -1"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// gl_MaxImageUnits is only available in ES 3.1 shaders. +TEST_F(MalformedShaderTest, MaxImageUnitsInES3Shader) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 myOutput;" + "void main() {\n" + " float ff = float(gl_MaxImageUnits);\n" + " myOutput = vec4(ff);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// struct += struct is an invalid operation. +TEST_F(MalformedShaderTest, StructCompoundAssignStruct) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 myOutput;\n" + "struct S { float foo; };\n" + "void main() {\n" + " S a, b;\n" + " a += b;\n" + " myOutput = vec4(0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// struct == different struct is an invalid operation. +TEST_F(MalformedShaderTest, StructEqDifferentStruct) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 myOutput;\n" + "struct S { float foo; };\n" + "struct S2 { float foobar; };\n" + "void main() {\n" + " S a;\n" + " S2 b;\n" + " a == b;\n" + " myOutput = vec4(0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Compute shaders are not supported in versions lower than 310. +TEST_F(MalformedComputeShaderTest, Version100) +{ + const std::string &shaderString = + "void main()\n" + "layout(local_size_x=1) in;\n" + "{\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Compute shaders are not supported in versions lower than 310. +TEST_F(MalformedComputeShaderTest, Version300) +{ + const std::string &shaderString = + "#version 300 es\n" + "void main()\n" + "layout(local_size_x=1) in;\n" + "{\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Compute shaders should have work group size specified. However, it is not a compile time error +// to not have the size specified, but rather a link time one. +// GLSL ES 3.10 Revision 4, 4.4.1.1 Compute Shader Inputs +TEST_F(MalformedComputeShaderTest, NoWorkGroupSizeSpecified) +{ + const std::string &shaderString = + "#version 310 es\n" + "void main()\n" + "{\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Work group size is less than 1. It should be at least 1. +// GLSL ES 3.10 Revision 4, 7.1.3 Compute Shader Special Variables +// The spec is not clear whether having a local size qualifier equal zero +// is correct. +// TODO (mradev): Ask people from Khronos to clarify the spec. +TEST_F(MalformedComputeShaderTest, WorkGroupSizeTooSmallXdimension) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 0) in;\n" + "void main()\n" + "{\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Work group size is correct for the x and y dimensions, but not for the z dimension. +// GLSL ES 3.10 Revision 4, 7.1.3 Compute Shader Special Variables +TEST_F(MalformedComputeShaderTest, WorkGroupSizeTooSmallZDimension) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 4, local_size_y = 6, local_size_z = 0) in;\n" + "void main()\n" + "{\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Work group size is bigger than the minimum in the x dimension. +// GLSL ES 3.10 Revision 4, 4.4.1.1 Compute Shader Inputs +TEST_F(MalformedComputeShaderTest, WorkGroupSizeTooBigXDimension) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 9989899) in;\n" + "void main()\n" + "{\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Work group size is bigger than the minimum in the y dimension. +// GLSL ES 3.10 Revision 4, 4.4.1.1 Compute Shader Inputs +TEST_F(MalformedComputeShaderTest, WorkGroupSizeTooBigYDimension) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 5, local_size_y = 9989899) in;\n" + "void main()\n" + "{\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Work group size is definitely bigger than the minimum in the z dimension. +// GLSL ES 3.10 Revision 4, 4.4.1.1 Compute Shader Inputs +TEST_F(MalformedComputeShaderTest, WorkGroupSizeTooBigZDimension) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 5, local_size_y = 5, local_size_z = 9989899) in;\n" + "void main()\n" + "{\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Work group size specified through macro expansion. +TEST_F(MalformedComputeShaderTest, WorkGroupSizeMacro) +{ + const std::string &shaderString = + "#version 310 es\n" + "#define MYDEF(x) x" + "layout(local_size_x = MYDEF(127)) in;\n" + "void main()\n" + "{\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Work group size specified as an unsigned integer. +TEST_F(MalformedComputeShaderTest, WorkGroupSizeUnsignedInteger) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 123u) in;\n" + "void main()\n" + "{\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Work group size specified in hexadecimal. +TEST_F(MalformedComputeShaderTest, WorkGroupSizeHexadecimal) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 0x3A) in;\n" + "void main()\n" + "{\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// local_size_x is -1 in hexadecimal format. +// -1 is used as unspecified value in the TLayoutQualifier structure. +TEST_F(MalformedComputeShaderTest, WorkGroupSizeMinusOneHexadecimal) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 0xFFFFFFFF) in;\n" + "void main()\n" + "{\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Work group size specified in octal. +TEST_F(MalformedComputeShaderTest, WorkGroupSizeOctal) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 013) in;\n" + "void main()\n" + "{\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Work group size is negative. It is specified in hexadecimal. +TEST_F(MalformedComputeShaderTest, WorkGroupSizeNegativeHexadecimal) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 0xFFFFFFEC) in;\n" + "void main()\n" + "{\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Multiple work group layout qualifiers with differing values. +// GLSL ES 3.10 Revision 4, 4.4.1.1 Compute Shader Inputs +TEST_F(MalformedComputeShaderTest, DifferingLayoutQualifiers) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 5, local_size_x = 6) in;\n" + "void main()\n" + "{\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Multiple work group input variables with differing local size values. +// GLSL ES 3.10 Revision 4, 4.4.1.1 Compute Shader Inputs +TEST_F(MalformedComputeShaderTest, MultipleInputVariablesDifferingLocalSize) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 5, local_size_y = 6) in;\n" + "layout(local_size_x = 5, local_size_y = 7) in;\n" + "void main()\n" + "{\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Multiple work group input variables with differing local size values. +// GLSL ES 3.10 Revision 4, 4.4.1.1 Compute Shader Inputs +TEST_F(MalformedComputeShaderTest, MultipleInputVariablesDifferingLocalSize2) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 5) in;\n" + "layout(local_size_x = 5, local_size_y = 7) in;\n" + "void main()\n" + "{\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Multiple work group input variables with the same local size values. It should compile. +// GLSL ES 3.10 Revision 4, 4.4.1.1 Compute Shader Inputs +TEST_F(MalformedComputeShaderTest, MultipleInputVariablesSameLocalSize) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 5, local_size_y = 6) in;\n" + "layout(local_size_x = 5, local_size_y = 6) in;\n" + "void main()\n" + "{\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Multiple work group input variables with the same local size values. It should compile. +// Since the default value is 1, it should compile. +// GLSL ES 3.10 Revision 4, 4.4.1.1 Compute Shader Inputs +TEST_F(MalformedComputeShaderTest, MultipleInputVariablesSameLocalSize2) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 5) in;\n" + "layout(local_size_x = 5, local_size_y = 1) in;\n" + "void main()\n" + "{\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Multiple work group input variables with the same local size values. It should compile. +// Since the default value is 1, it should compile. +// GLSL ES 3.10 Revision 4, 4.4.1.1 Compute Shader Inputs +TEST_F(MalformedComputeShaderTest, MultipleInputVariablesSameLocalSize3) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 5, local_size_y = 1) in;\n" + "layout(local_size_x = 5) in;\n" + "void main()\n" + "{\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Specifying row_major qualifier in a work group size layout. +// GLSL ES 3.10 Revision 4, 4.4.1.1 Compute Shader Inputs +TEST_F(MalformedComputeShaderTest, RowMajorInComputeInputLayout) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 5, row_major) in;\n" + "void main()\n" + "{\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// local size layout can be used only with compute input variables +// GLSL ES 3.10 Revision 4, 4.4.1.1 Compute Shader Inputs +TEST_F(MalformedComputeShaderTest, UniformComputeInputLayout) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 5) uniform;\n" + "void main()\n" + "{\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// local size layout can be used only with compute input variables +// GLSL ES 3.10 Revision 4, 4.4.1.1 Compute Shader Inputs +TEST_F(MalformedComputeShaderTest, UniformBufferComputeInputLayout) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 5) uniform SomeBuffer { vec4 something; };\n" + "void main()\n" + "{\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// local size layout can be used only with compute input variables +// GLSL ES 3.10 Revision 4, 4.4.1.1 Compute Shader Inputs +TEST_F(MalformedComputeShaderTest, StructComputeInputLayout) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 5) struct SomeBuffer { vec4 something; };\n" + "void main()\n" + "{\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// local size layout can be used only with compute input variables +// GLSL ES 3.10 Revision 4, 4.4.1.1 Compute Shader Inputs +TEST_F(MalformedComputeShaderTest, StructBodyComputeInputLayout) +{ + const std::string &shaderString = + "#version 310 es\n" + "struct S {\n" + " layout(local_size_x = 12) vec4 foo;\n" + "};\n" + "void main()" + "{" + "}"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// local size layout can be used only with compute input variables +// GLSL ES 3.10 Revision 4, 4.4.1.1 Compute Shader Inputs +TEST_F(MalformedComputeShaderTest, TypeComputeInputLayout) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 5) vec4;\n" + "void main()\n" + "{\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Invalid use of the out storage qualifier in a compute shader. +// GLSL ES 3.10 Revision 4, 4.4.1.1 Compute Shader Inputs +TEST_F(MalformedComputeShaderTest, InvalidOutStorageQualifier) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 15) in;\n" + "out vec4 myOutput;\n" + "void main() {\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Invalid use of the out storage qualifier in a compute shader. +// GLSL ES 3.10 Revision 4, 4.4.1.1 Compute Shader Inputs +TEST_F(MalformedComputeShaderTest, InvalidOutStorageQualifier2) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 15) in;\n" + "out myOutput;\n" + "void main() {\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Invalid use of the in storage qualifier. Can be only used to describe the local block size. +// GLSL ES 3.10 Revision 4, 4.4.1.1 Compute Shader Inputs +TEST_F(MalformedComputeShaderTest, InvalidInStorageQualifier) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 15) in;\n" + "in vec4 myInput;\n" + "void main() {\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Invalid use of the in storage qualifier. Can be only used to describe the local block size. +// The test checks a different part of the GLSL grammar than what InvalidInStorageQualifier checks. +// GLSL ES 3.10 Revision 4, 4.4.1.1 Compute Shader Inputs +TEST_F(MalformedComputeShaderTest, InvalidInStorageQualifier2) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 15) in;\n" + "in myInput;\n" + "void main() {\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// The local_size layout qualifier is only available in compute shaders. +TEST_F(MalformedVertexShaderGLES31Test, InvalidUseOfLocalSizeX) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "layout(local_size_x = 15) in vec4 myInput;\n" + "out vec4 myOutput;\n" + "void main() {\n" + " myOutput = myInput;\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// The local_size layout qualifier is only available in compute shaders. +TEST_F(MalformedFragmentShaderGLES31Test, InvalidUseOfLocalSizeX) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "layout(local_size_x = 15) in vec4 myInput;\n" + "out vec4 myOutput;\n" + "void main() {\n" + " myOutput = myInput;\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// It is a compile time error to use the gl_WorkGroupSize constant if +// the local size has not been declared yet. +// GLSL ES 3.10 Revision 4, 7.1.3 Compute Shader Special Variables +TEST_F(MalformedComputeShaderTest, InvalidUsageOfWorkGroupSize) +{ + const std::string &shaderString = + "#version 310 es\n" + "void main()\n" + "{\n" + " uvec3 WorkGroupSize = gl_WorkGroupSize;\n" + "}\n" + "layout(local_size_x = 12) in;\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// The test covers the compute shader built-in variables and constants. +TEST_F(MalformedComputeShaderTest, CorrectUsageOfComputeBuiltins) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 12) in;\n" + "void main()\n" + "{\n" + " uvec3 NumWorkGroups = gl_NumWorkGroups;\n" + " uvec3 WorkGroupSize = gl_WorkGroupSize;\n" + " uvec3 WorkGroupID = gl_WorkGroupID;\n" + " uvec3 GlobalInvocationID = gl_GlobalInvocationID;\n" + " uvec3 LocalInvocationID = gl_LocalInvocationID;\n" + " uint LocalInvocationIndex = gl_LocalInvocationIndex;\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// It is illegal to write to a special variable. +TEST_F(MalformedComputeShaderTest, SpecialVariableNumWorkGroups) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 12) in;\n" + "void main()\n" + "{\n" + " gl_NumWorkGroups = uvec3(1); \n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// It is illegal to write to a special variable. +TEST_F(MalformedComputeShaderTest, SpecialVariableWorkGroupID) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 12) in;\n" + "void main()\n" + "{\n" + " gl_WorkGroupID = uvec3(1); \n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// It is illegal to write to a special variable. +TEST_F(MalformedComputeShaderTest, SpecialVariableLocalInvocationID) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 12) in;\n" + "void main()\n" + "{\n" + " gl_LocalInvocationID = uvec3(1); \n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// It is illegal to write to a special variable. +TEST_F(MalformedComputeShaderTest, SpecialVariableGlobalInvocationID) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 12) in;\n" + "void main()\n" + "{\n" + " gl_GlobalInvocationID = uvec3(1); \n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// It is illegal to write to a special variable. +TEST_F(MalformedComputeShaderTest, SpecialVariableLocalInvocationIndex) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 12) in;\n" + "void main()\n" + "{\n" + " gl_LocalInvocationIndex = 1; \n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// It is illegal to write to a special variable. +TEST_F(MalformedComputeShaderTest, SpecialVariableWorkGroupSize) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 12) in;\n" + "void main()\n" + "{\n" + " gl_WorkGroupSize = uvec3(1); \n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// It is illegal to apply an unary operator to a sampler. +TEST_F(MalformedShaderTest, SamplerUnaryOperator) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform sampler2D s;\n" + "void main()\n" + "{\n" + " -s;\n" + " gl_FragColor = vec4(0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Invariant cannot be used with a work group size declaration. +TEST_F(MalformedComputeShaderTest, InvariantBlockSize) +{ + const std::string &shaderString = + "#version 310 es\n" + "invariant layout(local_size_x = 15) in;\n" + "void main() {\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Invariant cannot be used with a non-output variable in ESSL3. +TEST_F(MalformedShaderTest, InvariantNonOuput) +{ + const std::string &shaderString = + "#version 300 es\n" + "invariant int value;\n" + "void main() {\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Invariant declaration should follow the following format "invariant ". +// Test having an incorrect qualifier in the invariant declaration. +TEST_F(MalformedShaderTest, InvariantDeclarationWithStorageQualifier) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 foo;\n" + "invariant centroid foo;\n" + "void main() {\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Invariant declaration should follow the following format "invariant ". +// Test having an incorrect precision qualifier in the invariant declaration. +TEST_F(MalformedShaderTest, InvariantDeclarationWithPrecisionQualifier) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 foo;\n" + "invariant highp foo;\n" + "void main() {\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Invariant declaration should follow the following format "invariant ". +// Test having an incorrect layout qualifier in the invariant declaration. +TEST_F(MalformedShaderTest, InvariantDeclarationWithLayoutQualifier) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 foo;\n" + "invariant layout(location=0) foo;\n" + "void main() {\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Variable declaration with both invariant and layout qualifiers is not valid in the formal grammar +// provided in the ESSL 3.00 spec. ESSL 3.10 starts allowing this combination, but ESSL 3.00 should +// still disallow it. +TEST_F(MalformedShaderTest, VariableDeclarationWithInvariantAndLayoutQualifierESSL300) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "invariant layout(location = 0) out vec4 my_FragColor;\n" + "void main() {\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Bit shift with a rhs value > 31 has an undefined result in the GLSL spec. We disallow it. +// ESSL 3.00.6 section 5.9. +TEST_F(MalformedShaderTest, ShiftBy32) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "void main() {\n" + " uint u = 1u << 32u;\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that deferring global variable init works with an empty main(). +TEST_F(MalformedShaderTest, DeferGlobalVariableInitWithEmptyMain) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float u;\n" + "float foo = u;\n" + "void main() {}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Test that pruning empty declarations from loop init expression works. +TEST_F(MalformedShaderTest, EmptyDeclarationAsLoopInit) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " int i = 0;\n" + " for (int; i < 3; i++)\n" + " {\n" + " my_FragColor = vec4(i);\n" + " }\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} +// r32f, r32i, r32ui do not require either the writeonly or readonly memory qualifiers. +// GLSL ES 3.10, Revision 4, 4.9 Memory Access Qualifiers +TEST_F(MalformedFragmentShaderGLES31Test, ImageR32FNoMemoryQualifier) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "precision mediump image2D;\n" + "in vec4 myInput;\n" + "layout(r32f) uniform image2D myImage;\n" + "void main() {\n" + "}\n"; + + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Images which do not have r32f, r32i or r32ui as internal format, must have readonly or writeonly +// specified. +// GLSL ES 3.10, Revision 4, 4.9 Memory Access Qualifiers +TEST_F(MalformedFragmentShaderGLES31Test, ImageR32FWithCorrectMemoryQualifier) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "precision mediump image2D;\n" + "in vec4 myInput;\n" + "layout(rgba32f) uniform image2D myImage;\n" + "void main() {\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// It is a compile-time error to call imageStore when the image is qualified as readonly. +// GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers +TEST_F(MalformedFragmentShaderGLES31Test, StoreInReadOnlyImage) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "precision mediump image2D;\n" + "in vec4 myInput;\n" + "layout(r32f) uniform readonly image2D myImage;\n" + "void main() {\n" + " imageStore(myImage, ivec2(0), vec4(1.0));\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// It is a compile-time error to call imageLoad when the image is qualified as writeonly. +// GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers +TEST_F(MalformedFragmentShaderGLES31Test, LoadFromWriteOnlyImage) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "precision mediump image2D;\n" + "in vec4 myInput;\n" + "layout(r32f) uniform writeonly image2D myImage;\n" + "void main() {\n" + " imageLoad(myImage, ivec2(0));\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// A valid declaration and usage of an image3D. +TEST_F(MalformedFragmentShaderGLES31Test, ValidImage3D) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "precision mediump image3D;\n" + "in vec4 myInput;\n" + "layout(rgba32f) uniform readonly image3D myImage;\n" + "void main() {\n" + " imageLoad(myImage, ivec3(0));\n" + "}\n"; + + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// A valid declaration and usage of an imageCube. +TEST_F(MalformedFragmentShaderGLES31Test, ValidImageCube) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "precision mediump imageCube;\n" + "in vec4 myInput;\n" + "layout(rgba32f) uniform readonly imageCube myImage;\n" + "void main() {\n" + " imageLoad(myImage, ivec3(0));\n" + "}\n"; + + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// A valid declaration and usage of an image2DArray. +TEST_F(MalformedFragmentShaderGLES31Test, ValidImage2DArray) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "precision mediump image2DArray;\n" + "in vec4 myInput;\n" + "layout(rgba32f) uniform readonly image2DArray myImage;\n" + "void main() {\n" + " imageLoad(myImage, ivec3(0));\n" + "}\n"; + + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Images cannot be l-values. +// GLSL ES 3.10 Revision 4, 4.1.7 Opaque Types +TEST_F(MalformedFragmentShaderGLES31Test, ImageLValueFunctionDefinitionInOut) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "precision mediump image2D;\n" + "void myFunc(inout image2D someImage) {}\n" + "void main() {\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Cannot assign to images. +// GLSL ES 3.10 Revision 4, 4.1.7 Opaque Types +TEST_F(MalformedFragmentShaderGLES31Test, ImageAssignment) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "precision mediump image2D;\n" + "layout(rgba32f) uniform readonly image2D myImage;\n" + "layout(rgba32f) uniform readonly image2D myImage2;\n" + "void main() {\n" + " myImage = myImage2;\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Passing an image qualifier to a function should not be able to discard the readonly qualifier. +// GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers +TEST_F(MalformedFragmentShaderGLES31Test, ReadOnlyQualifierMissingInFunctionArgument) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "precision mediump image2D;\n" + "layout(rgba32f) uniform readonly image2D myImage;\n" + "void myFunc(in image2D someImage) {}\n" + "void main() {\n" + " myFunc(myImage);\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Passing an image qualifier to a function should not be able to discard the writeonly qualifier. +// GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers +TEST_F(MalformedFragmentShaderGLES31Test, WriteOnlyQualifierMissingInFunctionArgument) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "precision mediump image2D;\n" + "layout(rgba32f) uniform writeonly image2D myImage;\n" + "void myFunc(in image2D someImage) {}\n" + "void main() {\n" + " myFunc(myImage);\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Passing an image parameter as an argument to another function should not be able to discard the +// writeonly qualifier. +// GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers +TEST_F(MalformedFragmentShaderGLES31Test, DiscardWriteonlyInFunctionBody) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "precision mediump image2D;\n" + "layout(rgba32f) uniform writeonly image2D myImage;\n" + "void myFunc1(in image2D someImage) {}\n" + "void myFunc2(in writeonly image2D someImage) { myFunc1(someImage); }\n" + "void main() {\n" + " myFunc2(myImage);\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// The memory qualifiers for the image declaration and function argument match and the test should +// pass. +TEST_F(MalformedFragmentShaderGLES31Test, CorrectImageMemoryQualifierSpecified) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "precision mediump image2D;\n" + "layout(r32f) uniform image2D myImage;\n" + "void myFunc(in image2D someImage) {}\n" + "void main() {\n" + " myFunc(myImage);\n" + "}\n"; + + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// The test adds additional qualifiers to the argument in the function header. +// This is correct since no memory qualifiers are discarded upon the function call. +// GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers +TEST_F(MalformedFragmentShaderGLES31Test, CorrectImageMemoryQualifierSpecified2) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "precision mediump image2D;\n" + "layout(r32f) uniform image2D myImage;\n" + "void myFunc(in readonly writeonly image2D someImage) {}\n" + "void main() {\n" + " myFunc(myImage);\n" + "}\n"; + + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Images are not allowed in structs. +// GLSL ES 3.10 Revision 4, 4.1.8 Structures +TEST_F(MalformedFragmentShaderGLES31Test, ImageInStruct) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "precision mediump image2D;\n" + "struct myStruct { layout(r32f) image2D myImage; };\n" + "void main() {\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Images are not allowed in interface blocks. +// GLSL ES 3.10 Revision 4, 4.3.9 Interface Blocks +TEST_F(MalformedFragmentShaderGLES31Test, ImageInInterfaceBlock) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "precision mediump image2D;\n" + "uniform myBlock { layout(r32f) image2D myImage; };\n" + "void main() {\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Readonly used with an interface block. +TEST_F(MalformedFragmentShaderGLES31Test, ReadonlyWithInterfaceBlock) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "uniform readonly myBlock { float something; };\n" + "void main() {\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Readonly used with an invariant. +TEST_F(MalformedFragmentShaderGLES31Test, ReadonlyWithInvariant) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "out vec4 something;\n" + "invariant readonly something;\n" + "void main() {\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Readonly used with a member of a structure. +TEST_F(MalformedFragmentShaderGLES31Test, ReadonlyWithStructMember) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "out vec4 something;\n" + "struct MyStruct { readonly float myMember; };\n" + "void main() {\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// It should not be possible to use an internal format layout qualifier with an interface block. +TEST_F(MalformedFragmentShaderGLES31Test, ImageInternalFormatWithInterfaceBlock) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "out vec4 something;\n" + "layout(rgba32f) uniform MyStruct { float myMember; };\n" + "void main() {\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// It should not be possible to use an internal format layout qualifier with a uniform without a +// type. +TEST_F(MalformedFragmentShaderGLES31Test, ImageInternalFormatInGlobalLayoutQualifier) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "out vec4 something;\n" + "layout(rgba32f) uniform;\n" + "void main() {\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// ESSL 1.00 section 4.1.7. +// Samplers are not allowed as operands for most operations. Test this for ternary operator. +TEST_F(MalformedShaderTest, SamplerAsTernaryOperand) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform bool u;\n" + "uniform sampler2D s1;\n" + "uniform sampler2D s2;\n" + "void main() {\n" + " gl_FragColor = texture2D(u ? s1 : s2, vec2(0, 0));\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// ESSL 1.00.17 section 4.5.2. +// ESSL 3.00.6 section 4.5.3. +// Precision must be specified for floats. Test this with a declaration with no qualifiers. +TEST_F(MalformedShaderTest, FloatDeclarationNoQualifiersNoPrecision) +{ + const std::string &shaderString = + "vec4 foo = vec4(0.0);\n" + "void main()\n" + "{\n" + " gl_FragColor = foo;\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Check compiler doesn't crash on incorrect unsized array declarations. +TEST_F(MalformedShaderTest, IncorrectUnsizedArray) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "float foo[] = 0.0;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " foo[0] = 1.0;\n" + " my_FragColor = vec4(foo[0]);\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Check compiler doesn't crash when a bvec is on the right hand side of a logical operator. +// ESSL 3.00.6 section 5.9. +TEST_F(MalformedShaderTest, LogicalOpRHSIsBVec) +{ + const std::string &shaderString = + "#version 300 es\n" + "void main()\n" + "{\n" + " bool b;\n" + " bvec3 b3;\n" + " b && b3;\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Check compiler doesn't crash when there's an unsized array constructor with no parameters. +// ESSL 3.00.6 section 4.1.9: Array size must be greater than zero. +TEST_F(MalformedShaderTest, UnsizedArrayConstructorNoParameters) +{ + const std::string &shaderString = + "#version 300 es\n" + "void main()\n" + "{\n" + " int[]();\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Passing an image parameter as an argument to another function should not be able to discard the +// coherent qualifier. +TEST_F(MalformedFragmentShaderGLES31Test, CoherentQualifierMissingInFunctionArgument) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "precision mediump image2D;\n" + "layout(r32f) uniform coherent image2D myImage;\n" + "void myFunc(in image2D someImage) {}\n" + "void main() {\n" + " myFunc(myImage);\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Passing an image parameter as an argument to another function should not be able to discard the +// volatile qualifier. +TEST_F(MalformedFragmentShaderGLES31Test, VolatileQualifierMissingInFunctionArgument) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "precision mediump image2D;\n" + "layout(r32f) uniform volatile image2D myImage;\n" + "void myFunc(in image2D someImage) {}\n" + "void main() {\n" + " myFunc(myImage);\n" + "}\n"; + + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// The restrict qualifier can be discarded from a function argument. +// GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers +TEST_F(MalformedFragmentShaderGLES31Test, RestrictQualifierDiscardedInFunctionArgument) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "precision mediump image2D;\n" + "layout(r32f) uniform restrict image2D myImage;\n" + "void myFunc(in image2D someImage) {}\n" + "void main() {\n" + " myFunc(myImage);\n" + "}\n"; + + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Function image arguments can be overqualified. +// GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers +TEST_F(MalformedFragmentShaderGLES31Test, OverqualifyingImageParameter) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision mediump float;\n" + "precision mediump image2D;\n" + "layout(r32f) uniform image2D myImage;\n" + "void myFunc(in coherent volatile image2D someImage) {}\n" + "void main() {\n" + " myFunc(myImage);\n" + "}\n"; + + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} \ No newline at end of file diff --git a/gfx/angle/src/tests/compiler_tests/NV_draw_buffers_test.cpp b/gfx/angle/src/tests/compiler_tests/NV_draw_buffers_test.cpp new file mode 100755 index 000000000..13a44fd59 --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/NV_draw_buffers_test.cpp @@ -0,0 +1,41 @@ +// +// Copyright (c) 2014 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. +// +// NV_draw_buffers_test.cpp: +// Test for NV_draw_buffers setting +// + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" +#include "tests/test_utils/compiler_test.h" + +using namespace sh; + +class NVDrawBuffersTest : public MatchOutputCodeTest +{ + public: + NVDrawBuffersTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, 0, SH_ESSL_OUTPUT) + { + ShBuiltInResources *resources = getResources(); + resources->MaxDrawBuffers = 8; + resources->EXT_draw_buffers = 1; + resources->NV_draw_buffers = 1; + } +}; + +TEST_F(NVDrawBuffersTest, NVDrawBuffers) +{ + const std::string &shaderString = + "#extension GL_EXT_draw_buffers : require\n" + "precision mediump float;\n" + "void main() {\n" + " gl_FragData[0] = vec4(1.0);\n" + " gl_FragData[1] = vec4(0.0);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInCode("GL_NV_draw_buffers")); + ASSERT_FALSE(foundInCode("GL_EXT_draw_buffers")); +} diff --git a/gfx/angle/src/tests/compiler_tests/Pack_Unpack_test.cpp b/gfx/angle/src/tests/compiler_tests/Pack_Unpack_test.cpp new file mode 100755 index 000000000..a6333e78e --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/Pack_Unpack_test.cpp @@ -0,0 +1,122 @@ +// +// Copyright (c) 2015 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. +// +// Pack_Unpack_test.cpp: +// Tests for the emulating pack_unpack functions for GLSL. +// + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" +#include "tests/test_utils/compiler_test.h" + +using namespace sh; + +namespace +{ + +class PackUnpackTest : public MatchOutputCodeTest +{ + public: + PackUnpackTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, 0, SH_GLSL_400_CORE_OUTPUT) {} +}; + +// Check if PackSnorm2x16 Emulation for GLSL < 4.2 compile correctly. +TEST_F(PackUnpackTest, PackSnorm2x16Emulation) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "layout(location = 0) out mediump vec4 fragColor;" + "void main() {\n" + " vec2 v;\n" + " uint u = packSnorm2x16(v);\n" + " fragColor = vec4(0.0);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInCode("uint webgl_packSnorm2x16_emu(vec2 v)")); +} + +// Check if UnpackSnorm2x16 Emulation for GLSL < 4.2 compile correctly. +TEST_F(PackUnpackTest, UnpackSnorm2x16Emulation) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "layout(location = 0) out mediump vec4 fragColor;" + "void main() {\n" + " uint u;\n" + " vec2 v=unpackSnorm2x16(u);\n" + " fragColor = vec4(0.0);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInCode("vec2 webgl_unpackSnorm2x16_emu(uint u)")); +} + +// Check if PackUnorm2x16 Emulation for GLSL < 4.1 compiles correctly. +TEST_F(PackUnpackTest, PackUnorm2x16Emulation) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "layout(location = 0) out mediump vec4 fragColor;" + "void main() {\n" + " vec2 v;\n" + " uint u = packUnorm2x16(v);\n" + " fragColor = vec4(0.0);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInCode("uint webgl_packUnorm2x16_emu(vec2 v)")); +} + +// Check if UnpackSnorm2x16 Emulation for GLSL < 4.1 compiles correctly. +TEST_F(PackUnpackTest, UnpackUnorm2x16Emulation) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "layout(location = 0) out mediump vec4 fragColor;" + "void main() {\n" + " uint u;\n" + " vec2 v=unpackUnorm2x16(u);\n" + " fragColor = vec4(0.0);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInCode("vec2 webgl_unpackUnorm2x16_emu(uint u)")); +} + +// Check if PackHalf2x16 Emulation for GLSL < 4.2 compiles correctly. +TEST_F(PackUnpackTest, PackHalf2x16Emulation) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "layout(location = 0) out mediump vec4 fragColor;" + "void main() {\n" + " vec2 v;\n" + " uint u=packHalf2x16(v);\n" + " fragColor = vec4(0.0);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInCode("uint webgl_packHalf2x16_emu(vec2 v)")); +} + +// Check if UnpackHalf2x16 Emulation for GLSL < 4.2 compiles correctly. +TEST_F(PackUnpackTest, UnpackHalf2x16Emulation) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "layout(location = 0) out mediump vec4 fragColor;" + "void main() {\n" + " uint u;\n" + " vec2 v=unpackHalf2x16(u);\n" + " fragColor = vec4(0.0);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInCode("vec2 webgl_unpackHalf2x16_emu(uint u)")); +} + +} // namespace diff --git a/gfx/angle/src/tests/compiler_tests/PruneEmptyDeclarations_test.cpp b/gfx/angle/src/tests/compiler_tests/PruneEmptyDeclarations_test.cpp new file mode 100755 index 000000000..6b59e0cd5 --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/PruneEmptyDeclarations_test.cpp @@ -0,0 +1,60 @@ +// +// Copyright (c) 2016 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. +// +// PruneEmptyDeclarations_test.cpp: +// Tests for pruning empty declarations. +// + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" +#include "tests/test_utils/compiler_test.h" + +using namespace sh; + +namespace +{ + +class PruneEmptyDeclarationsTest : public MatchOutputCodeTest +{ + public: + PruneEmptyDeclarationsTest() + : MatchOutputCodeTest(GL_VERTEX_SHADER, 0, SH_GLSL_COMPATIBILITY_OUTPUT) + { + } +}; + +TEST_F(PruneEmptyDeclarationsTest, EmptyDeclarationStartsDeclaratorList) +{ + const std::string shaderString = + "precision mediump float;\n" + "uniform float u;\n" + "void main()\n" + "{\n" + " float, f;\n" + " gl_Position = vec4(u * f);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInCode("float f")); + ASSERT_TRUE(notFoundInCode("float, f")); +} + +TEST_F(PruneEmptyDeclarationsTest, EmptyStructDeclarationWithQualifiers) +{ + const std::string shaderString = + "precision mediump float;\n" + "const struct S { float f; };\n" + "uniform S s;" + "void main()\n" + "{\n" + " gl_Position = vec4(s.f);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInCode("struct S")); + ASSERT_TRUE(foundInCode("uniform S")); + ASSERT_TRUE(notFoundInCode("const struct S")); +} + +} // namespace diff --git a/gfx/angle/src/tests/compiler_tests/PruneUnusedFunctions_test.cpp b/gfx/angle/src/tests/compiler_tests/PruneUnusedFunctions_test.cpp new file mode 100755 index 000000000..fe710d8e6 --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/PruneUnusedFunctions_test.cpp @@ -0,0 +1,93 @@ +// +// Copyright (c) 2015 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. +// +// PruneUnusedFunctions_test.cpp: +// Test for the pruning of unused function with the SH_PRUNE_UNUSED_FUNCTIONS compile flag +// + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" +#include "tests/test_utils/compiler_test.h" + +using namespace sh; + +namespace +{ + +class PruneUnusedFunctionsTest : public MatchOutputCodeTest +{ + public: + PruneUnusedFunctionsTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, 0, SH_ESSL_OUTPUT) {} + + protected: + void compile(const std::string &shaderString, bool prune) + { + int compilationFlags = SH_VARIABLES | (prune ? 0 : SH_DONT_PRUNE_UNUSED_FUNCTIONS); + MatchOutputCodeTest::compile(shaderString, compilationFlags); + } +}; + +// Check that unused function and prototypes are removed iff the options is set +TEST_F(PruneUnusedFunctionsTest, UnusedFunctionAndProto) +{ + const std::string &shaderString = + "precision mediump float;\n" + "float unused(float a);\n" + "void main() {\n" + " gl_FragColor = vec4(1.0);\n" + "}\n" + "float unused(float a) {\n" + " return a;\n" + "}\n"; + compile(shaderString, true); + EXPECT_TRUE(notFoundInCode("unused(")); + EXPECT_TRUE(foundInCode("main(", 1)); + + compile(shaderString, false); + EXPECT_TRUE(foundInCode("unused(", 2)); + EXPECT_TRUE(foundInCode("main(", 1)); +} + +// Check that unimplemented prototypes are removed iff the options is set +TEST_F(PruneUnusedFunctionsTest, UnimplementedPrototype) +{ + const std::string &shaderString = + "precision mediump float;\n" + "float unused(float a);\n" + "void main() {\n" + " gl_FragColor = vec4(1.0);\n" + "}\n"; + compile(shaderString, true); + EXPECT_TRUE(notFoundInCode("unused(")); + EXPECT_TRUE(foundInCode("main(", 1)); + + compile(shaderString, false); + EXPECT_TRUE(foundInCode("unused(", 1)); + EXPECT_TRUE(foundInCode("main(", 1)); +} + +// Check that used functions are not prunued (duh) +TEST_F(PruneUnusedFunctionsTest, UsedFunction) +{ + const std::string &shaderString = + "precision mediump float;\n" + "float used(float a);\n" + "void main() {\n" + " gl_FragColor = vec4(used(1.0));\n" + "}\n" + "float used(float a) {\n" + " return a;\n" + "}\n"; + compile(shaderString, true); + EXPECT_TRUE(foundInCode("used(", 3)); + EXPECT_TRUE(foundInCode("main(", 1)); + + compile(shaderString, false); + EXPECT_TRUE(foundInCode("used(", 3)); + EXPECT_TRUE(foundInCode("main(", 1)); +} + +} diff --git a/gfx/angle/src/tests/compiler_tests/QualificationOrderESSL31_test.cpp b/gfx/angle/src/tests/compiler_tests/QualificationOrderESSL31_test.cpp new file mode 100644 index 000000000..dd3aa9a65 --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/QualificationOrderESSL31_test.cpp @@ -0,0 +1,184 @@ +// +// Copyright (c) 2016 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. +// +// QualificationOrderESSL31_test.cpp: +// OpenGL ES 3.1 removes the strict order of qualifiers imposed by the grammar. +// This file contains tests for invalid order and usage of qualifiers in GLSL ES 3.10. + +#include "gtest/gtest.h" + +#include "angle_gl.h" +#include "compiler/translator/TranslatorESSL.h" +#include "GLSLANG/ShaderLang.h" +#include "tests/test_utils/compiler_test.h" + +using namespace sh; + +class QualificationVertexShaderTestESSL31 : public testing::Test +{ + public: + QualificationVertexShaderTestESSL31() {} + protected: + virtual void SetUp() + { + ShBuiltInResources resources; + InitBuiltInResources(&resources); + + mTranslator = new TranslatorESSL(GL_VERTEX_SHADER, SH_GLES3_1_SPEC); + ASSERT_TRUE(mTranslator->Init(resources)); + } + + virtual void TearDown() { delete mTranslator; } + + // Return true when compilation succeeds + bool compile(const std::string &shaderString) + { + const char *shaderStrings[] = {shaderString.c_str()}; + mASTRoot = mTranslator->compileTreeForTesting(shaderStrings, 1, + SH_INTERMEDIATE_TREE | SH_VARIABLES); + TInfoSink &infoSink = mTranslator->getInfoSink(); + mInfoLog = infoSink.info.c_str(); + return mASTRoot != nullptr; + } + + const TIntermSymbol *findSymbolInAST(const TString &symbolName, TBasicType basicType) + { + return FindSymbolNode(mASTRoot, symbolName, basicType); + } + + protected: + TranslatorESSL *mTranslator; + TIntermNode *mASTRoot; + std::string mInfoLog; +}; + +// GLSL ES 3.10 has relaxed checks on qualifier order. Any order is correct. +TEST_F(QualificationVertexShaderTestESSL31, CentroidOut) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision lowp float;\n" + "out centroid float something;\n" + "void main(){\n" + " something = 1.0;\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success" << mInfoLog; + } + else + { + const TIntermSymbol *node = findSymbolInAST("something", EbtFloat); + ASSERT_NE(nullptr, node); + + const TType &type = node->getType(); + EXPECT_EQ(EvqCentroidOut, type.getQualifier()); + } +} + +// GLSL ES 3.10 has relaxed checks on qualifier order. Any order is correct. +TEST_F(QualificationVertexShaderTestESSL31, AllQualifiersMixed) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision lowp float;\n" + "highp out invariant centroid flat vec4 something;\n" + "void main(){\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success" << mInfoLog; + } + else + { + const TIntermSymbol *node = findSymbolInAST("something", EbtFloat); + ASSERT_NE(nullptr, node); + + const TType &type = node->getType(); + EXPECT_TRUE(type.isInvariant()); + EXPECT_EQ(EvqFlatOut, type.getQualifier()); + EXPECT_EQ(EbpHigh, type.getPrecision()); + } +} + +// GLSL ES 3.10 allows multiple layout qualifiers to be specified. +TEST_F(QualificationVertexShaderTestESSL31, MultipleLayouts) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision lowp float;\n" + "in layout(location=1) layout(location=2) vec4 something;\n" + "void main(){\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success" << mInfoLog; + } + else + { + const TIntermSymbol *node = findSymbolInAST("something", EbtFloat); + ASSERT_NE(nullptr, node); + + const TType &type = node->getType(); + EXPECT_EQ(EvqVertexIn, type.getQualifier()); + EXPECT_EQ(2, type.getLayoutQualifier().location); + } +} + +// The test checks layout qualifier overriding when multiple layouts are specified. +TEST_F(QualificationVertexShaderTestESSL31, MultipleLayoutsInterfaceBlock) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision lowp float;\n" + "out float someValue;\n" + "layout(shared) layout(std140) layout(column_major) uniform MyInterface\n" + "{ vec4 something; } MyInterfaceName;\n" + "void main(){\n" + " someValue = MyInterfaceName.something.r;\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success" << mInfoLog; + } + else + { + const TIntermSymbol *node = findSymbolInAST("MyInterfaceName", EbtInterfaceBlock); + ASSERT_NE(nullptr, node); + + const TType &type = node->getType(); + TLayoutQualifier layoutQualifier = type.getLayoutQualifier(); + EXPECT_EQ(EbsStd140, layoutQualifier.blockStorage); + EXPECT_EQ(EmpColumnMajor, layoutQualifier.matrixPacking); + } +} + +// The test checks layout qualifier overriding when multiple layouts are specified. +TEST_F(QualificationVertexShaderTestESSL31, MultipleLayoutsInterfaceBlock2) +{ + const std::string &shaderString = + "#version 310 es\n" + "precision lowp float;\n" + "out float someValue;\n" + "layout(row_major) layout(std140) layout(shared) uniform MyInterface\n" + "{ vec4 something; } MyInterfaceName;\n" + "void main(){\n" + " someValue = MyInterfaceName.something.r;\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success" << mInfoLog; + } + else + { + const TIntermSymbol *node = findSymbolInAST("MyInterfaceName", EbtInterfaceBlock); + ASSERT_NE(nullptr, node); + + const TType &type = node->getType(); + TLayoutQualifier layoutQualifier = type.getLayoutQualifier(); + EXPECT_EQ(EbsShared, layoutQualifier.blockStorage); + EXPECT_EQ(EmpRowMajor, layoutQualifier.matrixPacking); + } +} diff --git a/gfx/angle/src/tests/compiler_tests/QualificationOrder_test.cpp b/gfx/angle/src/tests/compiler_tests/QualificationOrder_test.cpp new file mode 100644 index 000000000..33ecbf77d --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/QualificationOrder_test.cpp @@ -0,0 +1,571 @@ +// +// Copyright (c) 2016 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. +// +// QualificationOrder_test.cpp: +// OpenGL ES 3.1 removes the strict order of qualifiers imposed by the grammar. +// This file contains tests for invalid order and usage of qualifiers. + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" +#include "compiler/translator/TranslatorESSL.h" + +using namespace sh; + +class QualificationOrderShaderTest : public testing::Test +{ + public: + QualificationOrderShaderTest() {} + + protected: + virtual void SetUp() {} + + virtual void TearDown() {} + + // Return true when compilation succeeds + bool compile(const std::string &shaderString, ::GLenum shaderType, ShShaderSpec spec) + { + ShBuiltInResources resources; + InitBuiltInResources(&resources); + resources.MaxDrawBuffers = (spec == SH_GLES2_SPEC) ? 1 : 8; + + TranslatorESSL *translator = new TranslatorESSL(shaderType, spec); + EXPECT_TRUE(translator->Init(resources)); + + const char *shaderStrings[] = {shaderString.c_str()}; + bool compilationSuccess = translator->compile(shaderStrings, 1, SH_INTERMEDIATE_TREE); + TInfoSink &infoSink = translator->getInfoSink(); + mInfoLog = infoSink.info.c_str(); + + delete translator; + + return compilationSuccess; + } + + protected: + std::string mInfoLog; +}; + +// Repeating centroid qualifier is invalid. +TEST_F(QualificationOrderShaderTest, RepeatingCentroid) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "flat centroid centroid in float myValue;\n" + "void main() {\n" + "}\n"; + + if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_1_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Repeating uniform storage qualifiers is invalid. +TEST_F(QualificationOrderShaderTest, RepeatingUniforms) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "uniform uniform float myValue;\n" + "void main() {\n" + "}\n"; + + if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_1_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Repeating varying storage qualifiers is invalid. +TEST_F(QualificationOrderShaderTest, RepeatingVaryings) +{ + const std::string &shaderString = + "precision mediump float;\n" + "varying varying vec4 myColor;\n" + "void main() {\n" + "}\n"; + + if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_1_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Layout qualifier should be before the storage qualifiers. +TEST_F(QualificationOrderShaderTest, WrongOrderQualifiers) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out layout(location=1) vec4 myColor;\n" + "void main() {\n" + "}\n"; + + if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_1_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Centroid out is the correct order. Out centroid is incorrect. +TEST_F(QualificationOrderShaderTest, WrongOrderCentroidOut) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "in vec4 uv;\n" + "out centroid vec4 position;\n" + "void main() {\n" + "position = uv;\n" + "gl_Position = uv;\n" + "}\n"; + + if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Centroid in is the correct order. In centroid is incorrect. +TEST_F(QualificationOrderShaderTest, WrongOrderCentroidIn) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "in centroid vec4 colorIN;\n" + "out vec4 colorOUT;\n" + "void main() {\n" + "colorOUT = colorIN;\n" + "}\n"; + + if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Type cannot be before the storage qualifier. +TEST_F(QualificationOrderShaderTest, WrongOrderTypeStorage) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "centroid in vec4 colorIN;\n" + "vec4 out colorOUT;\n" + "void main() {\n" + "colorOUT = colorIN;\n" + "}\n"; + + if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// A variable cannot have two conflicting storage qualifiers. +TEST_F(QualificationOrderShaderTest, RepeatingDifferentStorageQualifiers) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "centroid in vec4 colorIN;\n" + "uniform out vec4 colorOUT;\n" + "void main() {\n" + "colorOUT = colorIN;\n" + "}\n"; + + if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// A variable cannot have two different layout qualifiers. +TEST_F(QualificationOrderShaderTest, RepeatingLayoutQualifiers) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "in vec4 colorIN;\n" + "layout(location=0) layout(location=0) out vec4 colorOUT;\n" + "void main() {\n" + "colorOUT = colorIN;\n" + "}\n"; + + if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// A variable cannot have repeating invariant qualifiers. +TEST_F(QualificationOrderShaderTest, RepeatingInvariantQualifiers) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "in vec4 colorIN;\n" + "invariant invariant out vec4 colorOUT;\n" + "void main() {\n" + "colorOUT = colorIN;\n" + "}\n"; + + if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// A variable cannot have repeating storage qualifiers. +TEST_F(QualificationOrderShaderTest, RepeatingAttributes) +{ + const std::string &shaderString = + "precision mediump float;\n" + "attribute attribute vec4 positionIN;\n" + "void main() {\n" + "gl_Position = positionIN;\n" + "}\n"; + + if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Wrong order for invariant varying. It should be 'invariant varying', not 'varying invariant'. +TEST_F(QualificationOrderShaderTest, VaryingInvariantWrongOrder) +{ + const std::string &shaderString = + "precision mediump float;\n" + "attribute vec4 positionIN;\n" + "varying invariant vec4 dataOUT;\n" + "void main() {\n" + "gl_Position = positionIN;\n" + "dataOUT = 0.5 * dataOUT + vec4(0.5);\n" + "}\n"; + + if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// A variable cannot have repeating storage qualifiers. +TEST_F(QualificationOrderShaderTest, AttributeVaryingMix) +{ + const std::string &shaderString1 = + "precision mediump float;\n" + "attribute varying vec4 positionIN;\n" + "void main() {\n" + "gl_Position = positionIN;\n" + "}\n"; + + const std::string &shaderString2 = + "precision mediump float;\n" + "varying attribute vec4 positionIN;\n" + "void main() {\n" + "gl_Position = positionIN;\n" + "}\n"; + + if (compile(shaderString1, GL_VERTEX_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } + + if (compile(shaderString2, GL_VERTEX_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// A variable cannot have repeating interpolation qualifiers. +TEST_F(QualificationOrderShaderTest, RepeatingInterpolationQualifiers) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "in vec4 positionIN;\n" + "flat flat out vec4 dataOUT;\n" + "void main() {\n" + "gl_Position = positionIN;\n" + "dataOUT = 0.5 * dataOUT + vec4(0.5);\n" + "}\n"; + + if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Wrong order for the interpolation and storage qualifier. The correct order is interpolation +// qualifier and then storage qualifier. +TEST_F(QualificationOrderShaderTest, WrongOrderInterpolationStorageQualifiers) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "in vec4 positionIN;\n" + "out flat vec4 dataOUT;\n" + "void main() {\n" + "gl_Position = positionIN;\n" + "dataOUT = 0.5 * dataOUT + vec4(0.5);\n" + "}\n"; + + if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// The correct order is invariant, interpolation, storage. +TEST_F(QualificationOrderShaderTest, WrongOrderInvariantInterpolationStorageQualifiers) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "in vec4 positionIN;\n" + "flat invariant out vec4 dataOUT;\n" + "void main() {\n" + "gl_Position = positionIN;\n" + "dataOUT = 0.5 * dataOUT + vec4(0.5);\n" + "}\n"; + + if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// The invariant qualifer has to be before the storage qualifiers. +TEST_F(QualificationOrderShaderTest, WrongOrderInvariantNotFirst) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "in vec4 positionIN;\n" + "centroid out invariant vec4 dataOUT;\n" + "void main() {\n" + "gl_Position = positionIN;\n" + "dataOUT = 0.5 * dataOUT + vec4(0.5);\n" + "}\n"; + + if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// The precision qualifier is after the storage qualifiers. +TEST_F(QualificationOrderShaderTest, WrongOrderPrecision) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "in vec4 positionIN;\n" + "highp centroid out vec4 dataOUT;\n" + "void main() {\n" + "gl_Position = positionIN;\n" + "dataOUT = 0.5 * dataOUT + vec4(0.5);\n" + "}\n"; + + if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// A variable cannot have multiple declarations of the 'in' storage qualifier. +TEST_F(QualificationOrderShaderTest, RepeatingInQualifier) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "in in vec4 positionIN;\n" + "void main() {\n" + "gl_Position = positionIN;\n" + "}\n"; + + if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// A variable cannot have multiple declarations of the 'attribute' storage qualifier. +TEST_F(QualificationOrderShaderTest, RepeatingAttributeQualifier) +{ + const std::string &shaderString = + "precision mediump float;\n" + "attribute attribute vec4 positionIN;\n" + "void main() {\n" + "gl_Position = positionIN;\n" + "}\n"; + + if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Vertex input cannot be qualified with invariant. +TEST_F(QualificationOrderShaderTest, InvariantVertexInput) +{ + const std::string &shaderString = + "precision mediump float;\n" + "invariant attribute vec4 positionIN;\n" + "void main() {\n" + "gl_Position = positionIN;\n" + "}\n"; + + if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Cannot have a function parameter with the invariant qualifier. +TEST_F(QualificationOrderShaderTest, InvalidFunctionParametersInvariant) +{ + const std::string &shaderString = + "precision lowp float;\n" + "varying float value;\n" + "float foo0 (invariant in float x) {\n" + " return 2.0*x;\n" + "}\n" + "void main()\n" + "{\n" + " gl_FragColor = vec4(foo0(value));\n" + "}\n"; + + if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure" << mInfoLog; + } +} + +// Cannot have a function parameter with the attribute qualifier. +TEST_F(QualificationOrderShaderTest, InvalidFunctionParametersAttribute) +{ + const std::string &shaderString = + "precision lowp float;\n" + "varying float value;\n" + "float foo0 (attribute float x) {\n" + " return 2.0*x;\n" + "}\n" + "void main()\n" + "{\n" + " gl_FragColor = vec4(foo0(value));\n" + "}\n"; + + if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure" << mInfoLog; + } +} + +// Cannot have a function parameter with the varying qualifier. +TEST_F(QualificationOrderShaderTest, InvalidFunctionParametersVarying) +{ + const std::string &shaderString = + "precision lowp float;\n" + "varying float value;\n" + "float foo0 (varying float x) {\n" + " return 2.0*x;\n" + "}\n" + "void main()\n" + "{\n" + " gl_FragColor = vec4(foo0(value));\n" + "}\n"; + + if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure" << mInfoLog; + } +} + +// Cannot have a function parameter with the layout qualifier +TEST_F(QualificationOrderShaderTest, InvalidFunctionParametersLayout) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision lowp float;\n" + "in float value;\n" + "float foo0 (layout(location = 3) in float x) {\n" + " return 2.0*x;\n" + "}\n" + "out vec4 colorOUT;\n" + "void main()\n" + "{\n" + " colorOUT = vec4(foo0(value));\n" + "}\n"; + + if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure" << mInfoLog; + } +} + +// Cannot have a function parameter with the centroid qualifier +TEST_F(QualificationOrderShaderTest, InvalidFunctionParametersCentroidIn) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision lowp float;\n" + "in float value;\n" + "float foo0 (centroid in float x) {\n" + " return 2.0*x;\n" + "}\n" + "out vec4 colorOUT;\n" + "void main()\n" + "{\n" + " colorOUT = vec4(foo0(value));\n" + "}\n"; + + if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure" << mInfoLog; + } +} + +// Cannot have a function parameter with the flat qualifier +TEST_F(QualificationOrderShaderTest, InvalidFunctionParametersFlatIn) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision lowp float;\n" + "in float value;\n" + "float foo0 (flat in float x) {\n" + " return 2.0*x;\n" + "}\n" + "out vec4 colorOUT;\n" + "void main()\n" + "{\n" + " colorOUT = vec4(foo0(value));\n" + "}\n"; + + if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure" << mInfoLog; + } +} + +// Output layout location qualifier can't appear more than once within a declaration. +// GLSL ES 3.00.6 section 4.3.8.2 Output Layout Qualifiers. +TEST_F(QualificationOrderShaderTest, TwoOutputLocations) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "layout(location=1, location=2) out vec4 myColor;\n" + "void main() {\n" + "}\n"; + + if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} diff --git a/gfx/angle/src/tests/compiler_tests/RecordConstantPrecision_test.cpp b/gfx/angle/src/tests/compiler_tests/RecordConstantPrecision_test.cpp new file mode 100755 index 000000000..2cd2bfdf4 --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/RecordConstantPrecision_test.cpp @@ -0,0 +1,95 @@ +// +// Copyright (c) 2015 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. +// +// RecordConstantPrecision_test.cpp: +// Test for recording constant variable precision when it affects consuming expression. +// + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" +#include "tests/test_utils/compiler_test.h" + +using namespace sh; + +class RecordConstantPrecisionTest : public MatchOutputCodeTest +{ + public: + RecordConstantPrecisionTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, 0, SH_ESSL_OUTPUT) {} +}; + +// The constant cannot be folded if its precision is higher than the other operands, since it +// increases the precision of the consuming expression. +TEST_F(RecordConstantPrecisionTest, HigherPrecisionConstantAsParameter) +{ + const std::string &shaderString = + "uniform mediump float u;" + "void main()\n" + "{\n" + " const highp float a = 4096.5;\n" + " mediump float b = fract(a + u);\n" + " gl_FragColor = vec4(b);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInCode("const highp float webgl_angle_s")); + ASSERT_FALSE(foundInCode("fract(4096.5")); + ASSERT_FALSE(foundInCode("fract((4096.5")); +} + +// The constant can be folded if its precision is equal to the other operands, as it does not +// increase the precision of the consuming expression. +TEST_F(RecordConstantPrecisionTest, EqualPrecisionConstantAsParameter) +{ + const std::string &shaderString = + "uniform mediump float u;" + "void main()\n" + "{\n" + " const mediump float a = 4096.5;\n" + " mediump float b = fract(a + u);\n" + " gl_FragColor = vec4(b);\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundInCode("const mediump float webgl_angle_s")); + ASSERT_TRUE(foundInCode("fract((4096.5")); +} + +// The constant cannot be folded if its precision is higher than the other operands, since it +// increases the precision of the consuming expression. This applies also when the constant is +// part of a constant expression that can be folded. +TEST_F(RecordConstantPrecisionTest, FoldedBinaryConstantPrecisionIsHigher) +{ + const std::string &shaderString = + "uniform mediump float u;" + "void main()\n" + "{\n" + " const highp float a = 4095.5;\n" + " mediump float b = fract((a + 1.0) + u);\n" + " gl_FragColor = vec4(b);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInCode("const highp float webgl_angle_s")); + ASSERT_FALSE(foundInCode("fract(4096.5")); + ASSERT_FALSE(foundInCode("fract((4096.5")); +} + + +// The constant cannot be folded if its precision is higher than the other operands, since it +// increases the precision of the consuming expression. This applies also when the constant is +// part of a constant expression that can be folded. +TEST_F(RecordConstantPrecisionTest, FoldedUnaryConstantPrecisionIsHigher) +{ + const std::string &shaderString = + "uniform mediump float u;" + "void main()\n" + "{\n" + " const highp float a = 0.5;\n" + " mediump float b = sin(fract(a) + u);\n" + " gl_FragColor = vec4(b);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(foundInCode("const highp float webgl_angle_s")); + ASSERT_FALSE(foundInCode("sin(0.5")); + ASSERT_FALSE(foundInCode("sin((0.5")); +} diff --git a/gfx/angle/src/tests/compiler_tests/RemovePow_test.cpp b/gfx/angle/src/tests/compiler_tests/RemovePow_test.cpp new file mode 100755 index 000000000..8367ac745 --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/RemovePow_test.cpp @@ -0,0 +1,158 @@ +// +// Copyright (c) 2015 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. +// +// RemovePow_test.cpp: +// Tests for removing pow() function calls from the AST. +// + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" +#include "compiler/translator/NodeSearch.h" +#include "compiler/translator/TranslatorGLSL.h" + +using namespace sh; + +class RemovePowTest : public testing::Test +{ + public: + RemovePowTest() {} + + protected: + void SetUp() override + { + allocator.push(); + SetGlobalPoolAllocator(&allocator); + ShBuiltInResources resources; + sh::InitBuiltInResources(&resources); + mTranslatorGLSL = + new sh::TranslatorGLSL(GL_FRAGMENT_SHADER, SH_GLES2_SPEC, SH_GLSL_COMPATIBILITY_OUTPUT); + ASSERT_TRUE(mTranslatorGLSL->Init(resources)); + } + + void TearDown() override + { + SafeDelete(mTranslatorGLSL); + SetGlobalPoolAllocator(nullptr); + allocator.pop(); + } + + void compile(const std::string& shaderString) + { + const char *shaderStrings[] = { shaderString.c_str() }; + mASTRoot = mTranslatorGLSL->compileTreeForTesting(shaderStrings, 1, + SH_OBJECT_CODE | SH_REMOVE_POW_WITH_CONSTANT_EXPONENT); + if (!mASTRoot) + { + TInfoSink &infoSink = mTranslatorGLSL->getInfoSink(); + FAIL() << "Shader compilation into ESSL failed " << infoSink.info.c_str(); + } + } + + template + bool foundInAST() + { + return T::search(mASTRoot); + } + + private: + sh::TranslatorGLSL *mTranslatorGLSL; + TIntermNode *mASTRoot; + + TPoolAllocator allocator; +}; + +// Check if there's a pow() node anywhere in the tree. +class FindPow : public sh::NodeSearchTraverser +{ + public: + bool visitBinary(Visit visit, TIntermBinary *node) override + { + if (node->getOp() == EOpPow) + { + mFound = true; + } + return !mFound; + } +}; + +// Check if the tree starting at node corresponds to exp2(y * log2(x)) +// If the tree matches, set base to the node corresponding to x. +bool IsPowWorkaround(TIntermNode *node, TIntermNode **base) +{ + TIntermUnary *exp = node->getAsUnaryNode(); + if (exp != nullptr && exp->getOp() == EOpExp2) + { + TIntermBinary *mul = exp->getOperand()->getAsBinaryNode(); + if (mul != nullptr && mul->isMultiplication()) + { + TIntermUnary *log = mul->getRight()->getAsUnaryNode(); + if (mul->getLeft()->getAsConstantUnion() && log != nullptr) + { + if (log->getOp() == EOpLog2) + { + if (base) + *base = log->getOperand(); + return true; + } + } + } + } + return false; +} + +// Check if there's a node with the correct workaround to pow anywhere in the tree. +class FindPowWorkaround : public sh::NodeSearchTraverser +{ + public: + bool visitUnary(Visit visit, TIntermUnary *node) override + { + mFound = IsPowWorkaround(node, nullptr); + return !mFound; + } +}; + +// Check if there's a node with the correct workaround to pow with another workaround to pow +// nested within it anywhere in the tree. +class FindNestedPowWorkaround : public sh::NodeSearchTraverser +{ + public: + bool visitUnary(Visit visit, TIntermUnary *node) override + { + TIntermNode *base = nullptr; + bool oneFound = IsPowWorkaround(node, &base); + if (oneFound && base) + mFound = IsPowWorkaround(base, nullptr); + return !mFound; + } +}; + +TEST_F(RemovePowTest, PowWithConstantExponent) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float u;\n" + "void main() {\n" + " gl_FragColor = pow(vec4(u), vec4(0.5));\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundInAST()); + ASSERT_TRUE(foundInAST()); + ASSERT_FALSE(foundInAST()); +} + +TEST_F(RemovePowTest, NestedPowWithConstantExponent) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float u;\n" + "void main() {\n" + " gl_FragColor = pow(pow(vec4(u), vec4(2.0)), vec4(0.5));\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundInAST()); + ASSERT_TRUE(foundInAST()); +} + diff --git a/gfx/angle/src/tests/compiler_tests/ShCompile_test.cpp b/gfx/angle/src/tests/compiler_tests/ShCompile_test.cpp new file mode 100755 index 000000000..21e4a3cbe --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/ShCompile_test.cpp @@ -0,0 +1,83 @@ +// +// Copyright (c) 2015 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. +// +// ShCompile_test.cpp +// Test the sh::Compile interface with different parameters. +// + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" + +class ShCompileTest : public testing::Test +{ + public: + ShCompileTest() {} + + protected: + void SetUp() override + { + sh::InitBuiltInResources(&mResources); + mCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, SH_WEBGL_SPEC, + SH_GLSL_COMPATIBILITY_OUTPUT, &mResources); + ASSERT_TRUE(mCompiler != nullptr) << "Compiler could not be constructed."; + } + + void TearDown() override + { + if (mCompiler) + { + sh::Destruct(mCompiler); + mCompiler = nullptr; + } + } + + void testCompile(const char **shaderStrings, int stringCount, bool expectation) + { + bool success = sh::Compile(mCompiler, shaderStrings, stringCount, 0); + const std::string &compileLog = sh::GetInfoLog(mCompiler); + EXPECT_EQ(expectation, success) << compileLog; + } + + private: + ShBuiltInResources mResources; + ShHandle mCompiler; +}; + +// Test calling sh::Compile with more than one shader source string. +TEST_F(ShCompileTest, MultipleShaderStrings) +{ + const std::string &shaderString1 = + "precision mediump float;\n" + "void main() {\n"; + const std::string &shaderString2 = + " gl_FragColor = vec4(0.0);\n" + "}"; + + const char *shaderStrings[] = {shaderString1.c_str(), shaderString2.c_str()}; + + testCompile(shaderStrings, 2, true); +} + +// Test calling sh::Compile with a tokens split into different shader source strings. +TEST_F(ShCompileTest, TokensSplitInShaderStrings) +{ + const std::string &shaderString1 = + "precision mediump float;\n" + "void ma"; + const std::string &shaderString2 = + "in() {\n" + "#i"; + const std::string &shaderString3 = + "f 1\n" + " gl_FragColor = vec4(0.0);\n" + "#endif\n" + "}"; + + const char *shaderStrings[] = {shaderString1.c_str(), shaderString2.c_str(), + shaderString3.c_str()}; + + testCompile(shaderStrings, 3, true); +} diff --git a/gfx/angle/src/tests/compiler_tests/ShaderExtension_test.cpp b/gfx/angle/src/tests/compiler_tests/ShaderExtension_test.cpp new file mode 100755 index 000000000..eb2d9b9bd --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/ShaderExtension_test.cpp @@ -0,0 +1,218 @@ +// +// Copyright (c) 2015 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. +// +// Extension_test.cpp +// Test that shaders need various extensions to be compiled. +// + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" + +static const char *FragDepthExtShdr[] = +{ + // First string of the shader. The required pragma declaration. + "#extension GL_EXT_frag_depth : enable\n", + + // Second string of the shader. The rest of the shader body. + "void main() { gl_FragDepthEXT = 1.0; }\n" +}; + +static const char *StandDerivExtShdr[] = +{ + // First string of the shader. The required pragma declaration. + "#extension GL_OES_standard_derivatives : enable\n", + + // Second string of the shader. The rest of the shader body. + "precision mediump float;\n" + "varying vec2 texCoord;\n" + "void main() { gl_FragColor = vec4(dFdx(texCoord.x), dFdy(texCoord.y), fwidth(texCoord.x), 1.0); }\n" +}; + +static const char *TextureLODShdr[] = +{ + // First string of the shader. The required pragma declaration. + "#extension GL_EXT_shader_texture_lod : enable\n", + + // Second string of the shader. The rest of the shader body. + "precision mediump float;\n" + "varying vec2 texCoord0v;\n" + "uniform float lod;\n" + "uniform sampler2D tex;\n" + "void main() { vec4 color = texture2DLodEXT(tex, texCoord0v, lod); }\n" +}; + +class ShaderExtensionTest : public testing::Test +{ + public: + ShaderExtensionTest() {} + + protected: + virtual void SetUp() + { + sh::InitBuiltInResources(&mResources); + mCompiler = NULL; + } + + virtual void TearDown() + { + DestroyCompiler(); + } + + void DestroyCompiler() + { + if (mCompiler) + { + sh::Destruct(mCompiler); + mCompiler = NULL; + } + } + + void InitializeCompiler() + { + DestroyCompiler(); + mCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, SH_WEBGL_SPEC, + SH_GLSL_COMPATIBILITY_OUTPUT, &mResources); + ASSERT_TRUE(mCompiler != NULL) << "Compiler could not be constructed."; + } + + void TestShaderExtension(const char **shaderStrings, int stringCount, bool expectation) + { + bool success = sh::Compile(mCompiler, shaderStrings, stringCount, 0); + const std::string &compileLog = sh::GetInfoLog(mCompiler); + EXPECT_EQ(expectation, success) << compileLog; + } + + protected: + ShBuiltInResources mResources; + ShHandle mCompiler; +}; + +TEST_F(ShaderExtensionTest, FragDepthExtRequiresExt) +{ + // Extension is required to compile properly. + mResources.EXT_frag_depth = 0; + InitializeCompiler(); + TestShaderExtension(FragDepthExtShdr, 2, false); +} + +TEST_F(ShaderExtensionTest, FragDepthExtRequiresPragma) +{ + // Pragma is required to compile properly. + mResources.EXT_frag_depth = 1; + InitializeCompiler(); + TestShaderExtension(&FragDepthExtShdr[1], 1, false); +} + +TEST_F(ShaderExtensionTest, FragDepthExtCompiles) +{ + // Test that it compiles properly with extension enabled. + mResources.EXT_frag_depth = 1; + InitializeCompiler(); + TestShaderExtension(FragDepthExtShdr, 2, true); +} + +TEST_F(ShaderExtensionTest, FragDepthExtResetsInternalStates) +{ + // Test that extension internal states are reset properly between compiles. + mResources.EXT_frag_depth = 1; + InitializeCompiler(); + + TestShaderExtension(FragDepthExtShdr, 2, true); + TestShaderExtension(&FragDepthExtShdr[1], 1, false); + TestShaderExtension(FragDepthExtShdr, 2, true); +} + +TEST_F(ShaderExtensionTest, StandDerivExtRequiresExt) +{ + // Extension is required to compile properly. + mResources.OES_standard_derivatives = 0; + InitializeCompiler(); + TestShaderExtension(StandDerivExtShdr, 2, false); +} + +TEST_F(ShaderExtensionTest, StandDerivExtRequiresPragma) +{ + // Pragma is required to compile properly. + mResources.OES_standard_derivatives = 1; + InitializeCompiler(); + TestShaderExtension(&StandDerivExtShdr[1], 1, false); +} + +TEST_F(ShaderExtensionTest, StandDerivExtCompiles) +{ + // Test that it compiles properly with extension enabled. + mResources.OES_standard_derivatives = 1; + InitializeCompiler(); + TestShaderExtension(StandDerivExtShdr, 2, true); +} + +TEST_F(ShaderExtensionTest, StandDerivExtResetsInternalStates) +{ + // Test that extension internal states are reset properly between compiles. + mResources.OES_standard_derivatives = 1; + InitializeCompiler(); + + TestShaderExtension(StandDerivExtShdr, 2, true); + TestShaderExtension(&StandDerivExtShdr[1], 1, false); + TestShaderExtension(StandDerivExtShdr, 2, true); + TestShaderExtension(&StandDerivExtShdr[1], 1, false); +} + +TEST_F(ShaderExtensionTest, TextureLODExtRequiresExt) +{ + // Extension is required to compile properly. + mResources.EXT_shader_texture_lod = 0; + InitializeCompiler(); + TestShaderExtension(TextureLODShdr, 2, false); +} + +TEST_F(ShaderExtensionTest, TextureLODExtRequiresPragma) +{ + // Pragma is required to compile properly. + mResources.EXT_shader_texture_lod = 1; + InitializeCompiler(); + TestShaderExtension(&TextureLODShdr[1], 1, false); +} + +TEST_F(ShaderExtensionTest, TextureLODExtCompiles) +{ + // Test that it compiles properly with extension enabled. + mResources.EXT_shader_texture_lod = 1; + InitializeCompiler(); + TestShaderExtension(TextureLODShdr, 2, true); +} + +TEST_F(ShaderExtensionTest, TextureLODExtResetsInternalStates) +{ + // Test that extension internal states are reset properly between compiles. + mResources.EXT_shader_texture_lod = 1; + InitializeCompiler(); + + TestShaderExtension(&TextureLODShdr[1], 1, false); + TestShaderExtension(TextureLODShdr, 2, true); + TestShaderExtension(&TextureLODShdr[1], 1, false); + TestShaderExtension(TextureLODShdr, 2, true); +} + +// Test a bug where we could modify the value of a builtin variable. +TEST_F(ShaderExtensionTest, BuiltinRewritingBug) +{ + mResources.MaxDrawBuffers = 4; + mResources.EXT_draw_buffers = 1; + InitializeCompiler(); + + const std::string &shaderString = + "#extension GL_EXT_draw_buffers : require\n" + "precision mediump float;\n" + "void main() {\n" + " gl_FragData[gl_MaxDrawBuffers] = vec4(0.0);\n" + "}"; + + const char *shaderStrings[] = { shaderString.c_str() }; + + TestShaderExtension(shaderStrings, 1, false); + TestShaderExtension(shaderStrings, 1, false); +} diff --git a/gfx/angle/src/tests/compiler_tests/ShaderImage_test.cpp b/gfx/angle/src/tests/compiler_tests/ShaderImage_test.cpp new file mode 100644 index 000000000..b069c6547 --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/ShaderImage_test.cpp @@ -0,0 +1,259 @@ +// +// Copyright (c) 2016 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. +// +// ShaderImage_test.cpp: +// Tests for images +// + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" +#include "compiler/translator/TranslatorESSL.h" +#include "tests/test_utils/compiler_test.h" + +using namespace sh; + +namespace +{ + +// Checks that the imageStore call with mangled name imageStoreMangledName exists in the AST. +// Further each argument is checked whether it matches the expected properties given the compiled +// shader. +void CheckImageStoreCall(TIntermNode *astRoot, + const TString &imageStoreMangledName, + TBasicType imageType, + int storeLocationNominalSize, + TBasicType storeValueType, + int storeValueNominalSize) +{ + const TIntermAggregate *imageStoreFunctionCall = + FindFunctionCallNode(astRoot, imageStoreMangledName); + ASSERT_NE(nullptr, imageStoreFunctionCall); + + const TIntermSequence *storeArguments = imageStoreFunctionCall->getSequence(); + ASSERT_EQ(3u, storeArguments->size()); + + const TIntermTyped *storeArgument1Typed = (*storeArguments)[0]->getAsTyped(); + ASSERT_EQ(imageType, storeArgument1Typed->getBasicType()); + + const TIntermTyped *storeArgument2Typed = (*storeArguments)[1]->getAsTyped(); + ASSERT_EQ(EbtInt, storeArgument2Typed->getBasicType()); + ASSERT_EQ(storeLocationNominalSize, storeArgument2Typed->getNominalSize()); + + const TIntermTyped *storeArgument3Typed = (*storeArguments)[2]->getAsTyped(); + ASSERT_EQ(storeValueType, storeArgument3Typed->getBasicType()); + ASSERT_EQ(storeValueNominalSize, storeArgument3Typed->getNominalSize()); +} + +// Checks that the imageLoad call with mangled name imageLoadMangledName exists in the AST. +// Further each argument is checked whether it matches the expected properties given the compiled +// shader. +void CheckImageLoadCall(TIntermNode *astRoot, + const TString &imageLoadMangledName, + TBasicType imageType, + int loadLocationNominalSize) +{ + const TIntermAggregate *imageLoadFunctionCall = + FindFunctionCallNode(astRoot, imageLoadMangledName); + ASSERT_NE(nullptr, imageLoadFunctionCall); + + const TIntermSequence *loadArguments = imageLoadFunctionCall->getSequence(); + ASSERT_EQ(2u, loadArguments->size()); + + const TIntermTyped *loadArgument1Typed = (*loadArguments)[0]->getAsTyped(); + ASSERT_EQ(imageType, loadArgument1Typed->getBasicType()); + + const TIntermTyped *loadArgument2Typed = (*loadArguments)[1]->getAsTyped(); + ASSERT_EQ(EbtInt, loadArgument2Typed->getBasicType()); + ASSERT_EQ(loadLocationNominalSize, loadArgument2Typed->getNominalSize()); +} + +// Checks whether the image is properly exported as a uniform by the compiler. +void CheckExportedImageUniform(const std::vector &uniforms, + size_t uniformIndex, + ::GLenum imageTypeGL, + const TString &imageName) +{ + ASSERT_EQ(1u, uniforms.size()); + + const auto &imageUniform = uniforms[uniformIndex]; + ASSERT_EQ(imageTypeGL, imageUniform.type); + ASSERT_STREQ(imageUniform.name.c_str(), imageName.c_str()); +} + +// Checks whether the image is saved in the AST as a node with the correct properties given the +// shader. +void CheckImageDeclaration(TIntermNode *astRoot, + const TString &imageName, + TBasicType imageType, + TLayoutImageInternalFormat internalFormat, + bool readonly, + bool writeonly, + bool coherent, + bool restrictQualifier, + bool volatileQualifier) +{ + const TIntermSymbol *myImageNode = FindSymbolNode(astRoot, imageName, imageType); + ASSERT_NE(nullptr, myImageNode); + + const TType &myImageType = myImageNode->getType(); + TLayoutQualifier myImageLayoutQualifier = myImageType.getLayoutQualifier(); + ASSERT_EQ(internalFormat, myImageLayoutQualifier.imageInternalFormat); + TMemoryQualifier myImageMemoryQualifier = myImageType.getMemoryQualifier(); + ASSERT_EQ(readonly, myImageMemoryQualifier.readonly); + ASSERT_EQ(writeonly, myImageMemoryQualifier.writeonly); + ASSERT_EQ(coherent, myImageMemoryQualifier.coherent); + ASSERT_EQ(restrictQualifier, myImageMemoryQualifier.restrictQualifier); + ASSERT_EQ(volatileQualifier, myImageMemoryQualifier.volatileQualifier); +} + +} // namespace + +class ShaderImageTest : public testing::Test +{ + public: + ShaderImageTest() {} + + protected: + virtual void SetUp() + { + ShBuiltInResources resources; + sh::InitBuiltInResources(&resources); + + mTranslator = new sh::TranslatorESSL(GL_COMPUTE_SHADER, SH_GLES3_1_SPEC); + ASSERT_TRUE(mTranslator->Init(resources)); + } + + virtual void TearDown() { delete mTranslator; } + + // Return true when compilation succeeds + bool compile(const std::string &shaderString) + { + const char *shaderStrings[] = {shaderString.c_str()}; + mASTRoot = mTranslator->compileTreeForTesting(shaderStrings, 1, + SH_INTERMEDIATE_TREE | SH_VARIABLES); + TInfoSink &infoSink = mTranslator->getInfoSink(); + mInfoLog = infoSink.info.c_str(); + return mASTRoot != nullptr; + } + + protected: + std::string mTranslatedCode; + std::string mInfoLog; + sh::TranslatorESSL *mTranslator; + TIntermNode *mASTRoot; +}; + +// Test that an image2D is properly parsed and exported as a uniform. +TEST_F(ShaderImageTest, Image2DDeclaration) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 4) in;\n" + "layout(rgba32f) uniform highp readonly image2D myImage;\n" + "void main() {\n" + " ivec2 sz = imageSize(myImage);\n" + "}"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed" << mInfoLog; + } + + CheckExportedImageUniform(mTranslator->getUniforms(), 0, GL_IMAGE_2D, "myImage"); + CheckImageDeclaration(mASTRoot, "myImage", EbtImage2D, EiifRGBA32F, true, false, false, false, + false); +} + +// Test that an image3D is properly parsed and exported as a uniform. +TEST_F(ShaderImageTest, Image3DDeclaration) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 4) in;\n" + "layout(rgba32ui) uniform highp writeonly readonly uimage3D myImage;\n" + "void main() {\n" + " ivec3 sz = imageSize(myImage);\n" + "}"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed" << mInfoLog; + } + + CheckExportedImageUniform(mTranslator->getUniforms(), 0, GL_UNSIGNED_INT_IMAGE_3D, "myImage"); + CheckImageDeclaration(mASTRoot, "myImage", EbtUImage3D, EiifRGBA32UI, true, true, false, false, + false); +} + +// Check that imageLoad calls get correctly parsed. +TEST_F(ShaderImageTest, ImageLoad) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 4) in;\n" + "layout(rgba32f) uniform highp readonly image2D my2DImageInput;\n" + "layout(rgba32i) uniform highp readonly iimage3D my3DImageInput;\n" + "void main() {\n" + " vec4 result = imageLoad(my2DImageInput, ivec2(gl_LocalInvocationID.xy));\n" + " ivec4 result2 = imageLoad(my3DImageInput, ivec3(gl_LocalInvocationID.xyz));\n" + "}"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed" << mInfoLog; + } + + // imageLoad call with image2D passed + CheckImageLoadCall(mASTRoot, "imageLoad(im21;vi2;", EbtImage2D, 2); + + // imageLoad call with image3D passed + CheckImageLoadCall(mASTRoot, "imageLoad(iim31;vi3;", EbtIImage3D, 3); +} + +// Check that imageStore calls get correctly parsed. +TEST_F(ShaderImageTest, ImageStore) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 4) in;\n" + "layout(rgba32f) uniform highp writeonly image2D my2DImageOutput;\n" + "layout(rgba32ui) uniform highp writeonly uimage2DArray my2DImageArrayOutput;\n" + "void main() {\n" + " imageStore(my2DImageOutput, ivec2(gl_LocalInvocationID.xy), vec4(0.0));\n" + " imageStore(my2DImageArrayOutput, ivec3(gl_LocalInvocationID.xyz), uvec4(0));\n" + "}"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed" << mInfoLog; + } + + // imageStore call with image2D + CheckImageStoreCall(mASTRoot, "imageStore(im21;vi2;vf4;", EbtImage2D, 2, EbtFloat, 4); + + // imageStore call with image2DArray + CheckImageStoreCall(mASTRoot, "imageStore(uim2a1;vi3;vu4;", EbtUImage2DArray, 3, EbtUInt, 4); +} + +// Check that memory qualifiers are correctly parsed. +TEST_F(ShaderImageTest, ImageMemoryQualifiers) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x = 4) in;" + "layout(rgba32f) uniform highp coherent readonly image2D image1;\n" + "layout(rgba32f) uniform highp volatile writeonly image2D image2;\n" + "layout(rgba32f) uniform highp volatile restrict readonly writeonly image2D image3;\n" + "void main() {\n" + "}"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed" << mInfoLog; + } + + CheckImageDeclaration(mASTRoot, "image1", EbtImage2D, EiifRGBA32F, true, false, true, false, + false); + CheckImageDeclaration(mASTRoot, "image2", EbtImage2D, EiifRGBA32F, false, true, true, false, + true); + CheckImageDeclaration(mASTRoot, "image3", EbtImage2D, EiifRGBA32F, true, true, true, true, + true); +} diff --git a/gfx/angle/src/tests/compiler_tests/ShaderVariable_test.cpp b/gfx/angle/src/tests/compiler_tests/ShaderVariable_test.cpp new file mode 100755 index 000000000..53dd93fe4 --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/ShaderVariable_test.cpp @@ -0,0 +1,421 @@ +// +// Copyright (c) 2014 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. +// +// CollectVariables_test.cpp: +// Some tests for shader inspection +// + +#include "angle_gl.h" +#include "compiler/translator/Compiler.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" + +namespace sh +{ + +TEST(ShaderVariableTest, FindInfoByMappedName) +{ + // struct A { + // float x[2]; + // vec3 y; + // }; + // struct B { + // A a[3]; + // }; + // B uni[2]; + ShaderVariable uni; + uni.arraySize = 2; + uni.name = "uni"; + uni.mappedName = "m_uni"; + uni.structName = "B"; + { + ShaderVariable a; + a.arraySize = 3; + a.name = "a"; + a.mappedName = "m_a"; + a.structName = "A"; + { + ShaderVariable x(GL_FLOAT, 2); + x.name = "x"; + x.mappedName = "m_x"; + a.fields.push_back(x); + + ShaderVariable y(GL_FLOAT_VEC3, 0); + y.name = "y"; + y.mappedName = "m_y"; + a.fields.push_back(y); + } + uni.fields.push_back(a); + } + + const ShaderVariable *leafVar = nullptr; + std::string originalFullName; + + std::string mappedFullName = "wrongName"; + EXPECT_FALSE(uni.findInfoByMappedName( + mappedFullName, &leafVar, &originalFullName)); + + mappedFullName = "m_uni"; + EXPECT_TRUE(uni.findInfoByMappedName( + mappedFullName, &leafVar, &originalFullName)); + EXPECT_EQ(&uni, leafVar); + EXPECT_STREQ("uni", originalFullName.c_str()); + + mappedFullName = "m_uni[0].m_a[1].wrongName"; + EXPECT_FALSE(uni.findInfoByMappedName( + mappedFullName, &leafVar, &originalFullName)); + + mappedFullName = "m_uni[0].m_a[1].m_x"; + EXPECT_TRUE(uni.findInfoByMappedName( + mappedFullName, &leafVar, &originalFullName)); + EXPECT_EQ(&(uni.fields[0].fields[0]), leafVar); + EXPECT_STREQ("uni[0].a[1].x", originalFullName.c_str()); + + mappedFullName = "m_uni[0].m_a[1].m_x[0]"; + EXPECT_TRUE(uni.findInfoByMappedName( + mappedFullName, &leafVar, &originalFullName)); + EXPECT_EQ(&(uni.fields[0].fields[0]), leafVar); + EXPECT_STREQ("uni[0].a[1].x[0]", originalFullName.c_str()); + + mappedFullName = "m_uni[0].m_a[1].m_y"; + EXPECT_TRUE(uni.findInfoByMappedName( + mappedFullName, &leafVar, &originalFullName)); + EXPECT_EQ(&(uni.fields[0].fields[1]), leafVar); + EXPECT_STREQ("uni[0].a[1].y", originalFullName.c_str()); +} + +TEST(ShaderVariableTest, IsSameUniformWithDifferentFieldOrder) +{ + // struct A { + // float x; + // float y; + // }; + // uniform A uni; + Uniform vx_a; + vx_a.arraySize = 0; + vx_a.name = "uni"; + vx_a.mappedName = "m_uni"; + vx_a.structName = "A"; + { + ShaderVariable x(GL_FLOAT, 0); + x.name = "x"; + x.mappedName = "m_x"; + vx_a.fields.push_back(x); + + ShaderVariable y(GL_FLOAT, 0); + y.name = "y"; + y.mappedName = "m_y"; + vx_a.fields.push_back(y); + } + + // struct A { + // float y; + // float x; + // }; + // uniform A uni; + Uniform fx_a; + fx_a.arraySize = 0; + fx_a.name = "uni"; + fx_a.mappedName = "m_uni"; + fx_a.structName = "A"; + { + ShaderVariable y(GL_FLOAT, 0); + y.name = "y"; + y.mappedName = "m_y"; + fx_a.fields.push_back(y); + + ShaderVariable x(GL_FLOAT, 0); + x.name = "x"; + x.mappedName = "m_x"; + fx_a.fields.push_back(x); + } + + EXPECT_FALSE(vx_a.isSameUniformAtLinkTime(fx_a)); +} + +TEST(ShaderVariableTest, IsSameUniformWithDifferentStructNames) +{ + // struct A { + // float x; + // float y; + // }; + // uniform A uni; + Uniform vx_a; + vx_a.arraySize = 0; + vx_a.name = "uni"; + vx_a.mappedName = "m_uni"; + vx_a.structName = "A"; + { + ShaderVariable x(GL_FLOAT, 0); + x.name = "x"; + x.mappedName = "m_x"; + vx_a.fields.push_back(x); + + ShaderVariable y(GL_FLOAT, 0); + y.name = "y"; + y.mappedName = "m_y"; + vx_a.fields.push_back(y); + } + + // struct B { + // float x; + // float y; + // }; + // uniform B uni; + Uniform fx_a; + fx_a.arraySize = 0; + fx_a.name = "uni"; + fx_a.mappedName = "m_uni"; + { + ShaderVariable x(GL_FLOAT, 0); + x.name = "x"; + x.mappedName = "m_x"; + fx_a.fields.push_back(x); + + ShaderVariable y(GL_FLOAT, 0); + y.name = "y"; + y.mappedName = "m_y"; + fx_a.fields.push_back(y); + } + + fx_a.structName = "B"; + EXPECT_FALSE(vx_a.isSameUniformAtLinkTime(fx_a)); + + fx_a.structName = "A"; + EXPECT_TRUE(vx_a.isSameUniformAtLinkTime(fx_a)); + + fx_a.structName = ""; + EXPECT_FALSE(vx_a.isSameUniformAtLinkTime(fx_a)); +} + +TEST(ShaderVariableTest, IsSameVaryingWithDifferentInvariance) +{ + // invariant varying float vary; + Varying vx; + vx.type = GL_FLOAT; + vx.arraySize = 0; + vx.precision = GL_MEDIUM_FLOAT; + vx.name = "vary"; + vx.mappedName = "m_vary"; + vx.staticUse = true; + vx.isInvariant = true; + + // varying float vary; + Varying fx; + fx.type = GL_FLOAT; + fx.arraySize = 0; + fx.precision = GL_MEDIUM_FLOAT; + fx.name = "vary"; + fx.mappedName = "m_vary"; + fx.staticUse = true; + fx.isInvariant = false; + + // Default to ESSL1 behavior: invariance must match + EXPECT_FALSE(vx.isSameVaryingAtLinkTime(fx)); + EXPECT_FALSE(vx.isSameVaryingAtLinkTime(fx, 100)); + // ESSL3 behavior: invariance doesn't need to match + EXPECT_TRUE(vx.isSameVaryingAtLinkTime(fx, 300)); + + // invariant varying float vary; + fx.isInvariant = true; + EXPECT_TRUE(vx.isSameVaryingAtLinkTime(fx)); + EXPECT_TRUE(vx.isSameVaryingAtLinkTime(fx, 100)); + EXPECT_TRUE(vx.isSameVaryingAtLinkTime(fx, 300)); +} + +// Test that using invariant varyings doesn't trigger a double delete. +TEST(ShaderVariableTest, InvariantDoubleDeleteBug) +{ + ShBuiltInResources resources; + sh::InitBuiltInResources(&resources); + + ShHandle compiler = sh::ConstructCompiler(GL_VERTEX_SHADER, SH_GLES2_SPEC, + SH_GLSL_COMPATIBILITY_OUTPUT, &resources); + EXPECT_NE(static_cast(0), compiler); + + const char *program[] = + { + "attribute vec4 position;\n" + "varying float v;\n" + "invariant v;\n" + "void main() {\n" + " v = 1.0;\n" + " gl_Position = position;\n" + "}" + }; + + EXPECT_TRUE(sh::Compile(compiler, program, 1, SH_OBJECT_CODE)); + EXPECT_TRUE(sh::Compile(compiler, program, 1, SH_OBJECT_CODE)); + sh::Destruct(compiler); +} + +TEST(ShaderVariableTest, IllegalInvariantVarying) +{ + ShBuiltInResources resources; + sh::InitBuiltInResources(&resources); + + ShHandle compiler = sh::ConstructCompiler(GL_VERTEX_SHADER, SH_GLES2_SPEC, + SH_GLSL_COMPATIBILITY_OUTPUT, &resources); + EXPECT_NE(static_cast(0), compiler); + + const char *program1[] = + { + "void foo() {\n" + " vec4 v;\n" + "}\n" + "varying vec4 v_varying;\n" + "invariant v_varying;\n" + "void main() {\n" + " foo();\n" + " gl_Position = v_varying;\n" + "}" + }; + const char *program2[] = + { + "varying vec4 v_varying;\n" + "void foo() {\n" + " invariant v_varying;\n" + "}\n" + "void main() {\n" + " foo();\n" + " gl_Position = v_varying;\n" + "}" + }; + + EXPECT_TRUE(sh::Compile(compiler, program1, 1, SH_VARIABLES)); + EXPECT_FALSE(sh::Compile(compiler, program2, 1, SH_VARIABLES)); +} + +TEST(ShaderVariableTest, InvariantLeakAcrossShaders) +{ + ShBuiltInResources resources; + sh::InitBuiltInResources(&resources); + + ShHandle compiler = sh::ConstructCompiler(GL_VERTEX_SHADER, SH_GLES2_SPEC, + SH_GLSL_COMPATIBILITY_OUTPUT, &resources); + EXPECT_NE(static_cast(0), compiler); + + const char *program1[] = + { + "varying vec4 v_varying;\n" + "invariant v_varying;\n" + "void main() {\n" + " gl_Position = v_varying;\n" + "}" + }; + const char *program2[] = + { + "varying vec4 v_varying;\n" + "void main() {\n" + " gl_Position = v_varying;\n" + "}" + }; + + EXPECT_TRUE(sh::Compile(compiler, program1, 1, SH_VARIABLES)); + const std::vector *varyings = sh::GetVaryings(compiler); + for (const sh::Varying &varying : *varyings) + { + if (varying.name == "v_varying") + EXPECT_TRUE(varying.isInvariant); + } + EXPECT_TRUE(sh::Compile(compiler, program2, 1, SH_VARIABLES)); + varyings = sh::GetVaryings(compiler); + for (const sh::Varying &varying : *varyings) + { + if (varying.name == "v_varying") + EXPECT_FALSE(varying.isInvariant); + } +} + +TEST(ShaderVariableTest, GlobalInvariantLeakAcrossShaders) +{ + ShBuiltInResources resources; + sh::InitBuiltInResources(&resources); + + ShHandle compiler = sh::ConstructCompiler(GL_VERTEX_SHADER, SH_GLES2_SPEC, + SH_GLSL_COMPATIBILITY_OUTPUT, &resources); + EXPECT_NE(static_cast(0), compiler); + + const char *program1[] = + { + "#pragma STDGL invariant(all)\n" + "varying vec4 v_varying;\n" + "void main() {\n" + " gl_Position = v_varying;\n" + "}" + }; + const char *program2[] = + { + "varying vec4 v_varying;\n" + "void main() {\n" + " gl_Position = v_varying;\n" + "}" + }; + + EXPECT_TRUE(sh::Compile(compiler, program1, 1, SH_VARIABLES)); + const std::vector *varyings = sh::GetVaryings(compiler); + for (const sh::Varying &varying : *varyings) + { + if (varying.name == "v_varying") + EXPECT_TRUE(varying.isInvariant); + } + EXPECT_TRUE(sh::Compile(compiler, program2, 1, SH_VARIABLES)); + varyings = sh::GetVaryings(compiler); + for (const sh::Varying &varying : *varyings) + { + if (varying.name == "v_varying") + EXPECT_FALSE(varying.isInvariant); + } +} + +TEST(ShaderVariableTest, BuiltinInvariantVarying) +{ + + ShBuiltInResources resources; + sh::InitBuiltInResources(&resources); + + ShHandle compiler = sh::ConstructCompiler(GL_VERTEX_SHADER, SH_GLES2_SPEC, + SH_GLSL_COMPATIBILITY_OUTPUT, &resources); + EXPECT_NE(static_cast(0), compiler); + + const char *program1[] = + { + "invariant gl_Position;\n" + "void main() {\n" + " gl_Position = vec4(0, 0, 0, 0);\n" + "}" + }; + const char *program2[] = + { + "void main() {\n" + " gl_Position = vec4(0, 0, 0, 0);\n" + "}" + }; + const char *program3[] = + { + "void main() {\n" + " invariant gl_Position;\n" + " gl_Position = vec4(0, 0, 0, 0);\n" + "}" + }; + + EXPECT_TRUE(sh::Compile(compiler, program1, 1, SH_VARIABLES)); + const std::vector *varyings = sh::GetVaryings(compiler); + for (const sh::Varying &varying : *varyings) + { + if (varying.name == "gl_Position") + EXPECT_TRUE(varying.isInvariant); + } + EXPECT_TRUE(sh::Compile(compiler, program2, 1, SH_VARIABLES)); + varyings = sh::GetVaryings(compiler); + for (const sh::Varying &varying : *varyings) + { + if (varying.name == "gl_Position") + EXPECT_FALSE(varying.isInvariant); + } + EXPECT_FALSE(sh::Compile(compiler, program3, 1, SH_VARIABLES)); +} + +} // namespace sh diff --git a/gfx/angle/src/tests/compiler_tests/TypeTracking_test.cpp b/gfx/angle/src/tests/compiler_tests/TypeTracking_test.cpp new file mode 100755 index 000000000..ac8ee70f6 --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/TypeTracking_test.cpp @@ -0,0 +1,456 @@ +// +// Copyright (c) 2014 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. +// +// TypeTracking_test.cpp: +// Test for tracking types resulting from math operations, including their +// precision. +// + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" +#include "compiler/translator/TranslatorESSL.h" + +using namespace sh; + +class TypeTrackingTest : public testing::Test +{ + public: + TypeTrackingTest() {} + + protected: + void SetUp() override + { + ShBuiltInResources resources; + InitBuiltInResources(&resources); + resources.FragmentPrecisionHigh = 1; + + mTranslator = new TranslatorESSL(GL_FRAGMENT_SHADER, SH_GLES3_SPEC); + ASSERT_TRUE(mTranslator->Init(resources)); + } + + void TearDown() override { delete mTranslator; } + + void compile(const std::string& shaderString) + { + const char *shaderStrings[] = { shaderString.c_str() }; + bool compilationSuccess = mTranslator->compile(shaderStrings, 1, SH_INTERMEDIATE_TREE); + TInfoSink &infoSink = mTranslator->getInfoSink(); + mInfoLog = RemoveSymbolIdsFromInfoLog(infoSink.info.c_str()); + if (!compilationSuccess) + FAIL() << "Shader compilation failed " << mInfoLog; + } + + bool foundErrorInIntermediateTree() const + { + return foundInIntermediateTree("ERROR:"); + } + + bool foundInIntermediateTree(const char* stringToFind) const + { + return mInfoLog.find(stringToFind) != std::string::npos; + } + + private: + // Remove symbol ids from info log - the tests don't care about them. + static std::string RemoveSymbolIdsFromInfoLog(const char *infoLog) + { + std::string filteredLog(infoLog); + size_t idPrefixPos = 0u; + do + { + idPrefixPos = filteredLog.find(" (symbol id"); + if (idPrefixPos != std::string::npos) + { + size_t idSuffixPos = filteredLog.find(")", idPrefixPos); + filteredLog.erase(idPrefixPos, idSuffixPos - idPrefixPos + 1u); + } + } while (idPrefixPos != std::string::npos); + return filteredLog; + } + + TranslatorESSL *mTranslator; + std::string mInfoLog; +}; + +TEST_F(TypeTrackingTest, FunctionPrototypeMangling) +{ + const std::string &shaderString = + "precision mediump float;\n" + "float fun(float a);\n" + "uniform float f;\n" + "void main() {\n" + " float ff = fun(f);\n" + " gl_FragColor = vec4(ff);\n" + "}\n" + "float fun(float a) {\n" + " return a * 2.0;\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundErrorInIntermediateTree()); + ASSERT_TRUE(foundInIntermediateTree("Function Prototype: fun(f1;")); + ASSERT_TRUE(foundInIntermediateTree("Function Definition: fun(f1;")); +} + +TEST_F(TypeTrackingTest, BuiltInFunctionResultPrecision) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float f;\n" + "void main() {\n" + " float ff = sin(f);\n" + " gl_FragColor = vec4(ff);\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundErrorInIntermediateTree()); + ASSERT_TRUE(foundInIntermediateTree("sine (mediump float)")); +} + +TEST_F(TypeTrackingTest, BinaryMathResultPrecision) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float f;\n" + "void main() {\n" + " float ff = f * 0.5;\n" + " gl_FragColor = vec4(ff);\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundErrorInIntermediateTree()); + ASSERT_TRUE(foundInIntermediateTree("multiply (mediump float)")); +} + +TEST_F(TypeTrackingTest, BuiltInVecFunctionResultTypeAndPrecision) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform vec2 a;\n" + "void main() {\n" + " float b = length(a);\n" + " float c = dot(a, vec2(0.5));\n" + " float d = distance(vec2(0.5), a);\n" + " gl_FragColor = vec4(b, c, d, 1.0);\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundErrorInIntermediateTree()); + ASSERT_TRUE(foundInIntermediateTree("length (mediump float)")); + ASSERT_TRUE(foundInIntermediateTree("dot-product (mediump float)")); + ASSERT_TRUE(foundInIntermediateTree("distance (mediump float)")); +} + +TEST_F(TypeTrackingTest, BuiltInMatFunctionResultTypeAndPrecision) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "uniform mat4 m;\n" + "void main() {\n" + " mat3x2 tmp32 = mat3x2(m);\n" + " mat2x3 tmp23 = mat2x3(m);\n" + " mat4x2 tmp42 = mat4x2(m);\n" + " mat2x4 tmp24 = mat2x4(m);\n" + " mat4x3 tmp43 = mat4x3(m);\n" + " mat3x4 tmp34 = mat3x4(m);\n" + " my_FragColor = vec4(tmp32[2][1] * tmp23[1][2], tmp42[3][1] * tmp24[1][3], tmp43[3][2] * tmp34[2][3], 1.0);\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundErrorInIntermediateTree()); + ASSERT_TRUE(foundInIntermediateTree("Construct mat2x3 (mediump 2X3 matrix of float)")); + ASSERT_TRUE(foundInIntermediateTree("Construct mat3x2 (mediump 3X2 matrix of float)")); + ASSERT_TRUE(foundInIntermediateTree("Construct mat2x4 (mediump 2X4 matrix of float)")); + ASSERT_TRUE(foundInIntermediateTree("Construct mat4x2 (mediump 4X2 matrix of float)")); + ASSERT_TRUE(foundInIntermediateTree("Construct mat3x4 (mediump 3X4 matrix of float)")); + ASSERT_TRUE(foundInIntermediateTree("Construct mat4x3 (mediump 4X3 matrix of float)")); +} + +TEST_F(TypeTrackingTest, BuiltInFunctionChoosesHigherPrecision) +{ + const std::string &shaderString = + "precision lowp float;\n" + "uniform mediump vec2 a;\n" + "uniform lowp vec2 b;\n" + "void main() {\n" + " float c = dot(a, b);\n" + " float d = distance(b, a);\n" + " gl_FragColor = vec4(c, d, 0.0, 1.0);\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundErrorInIntermediateTree()); + ASSERT_TRUE(foundInIntermediateTree("dot-product (mediump float)")); + ASSERT_TRUE(foundInIntermediateTree("distance (mediump float)")); +} + +TEST_F(TypeTrackingTest, BuiltInBoolFunctionResultType) +{ + const std::string &shaderString = + "uniform bvec4 bees;\n" + "void main() {\n" + " bool b = any(bees);\n" + " bool c = all(bees);\n" + " bvec4 d = not(bees);\n" + " gl_FragColor = vec4(b ? 1.0 : 0.0, c ? 1.0 : 0.0, d.x ? 1.0 : 0.0, 1.0);\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundErrorInIntermediateTree()); + ASSERT_TRUE(foundInIntermediateTree("any (bool)")); + ASSERT_TRUE(foundInIntermediateTree("all (bool)")); + ASSERT_TRUE(foundInIntermediateTree("Negate conditional (4-component vector of bool)")); +} + +TEST_F(TypeTrackingTest, BuiltInVecToBoolFunctionResultType) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform vec2 apples;\n" + "uniform vec2 oranges;\n" + "uniform ivec2 foo;\n" + "uniform ivec2 bar;\n" + "void main() {\n" + " bvec2 a = lessThan(apples, oranges);\n" + " bvec2 b = greaterThan(foo, bar);\n" + " gl_FragColor = vec4(any(a) ? 1.0 : 0.0, any(b) ? 1.0 : 0.0, 0.0, 1.0);\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundErrorInIntermediateTree()); + ASSERT_TRUE(foundInIntermediateTree("Less Than (2-component vector of bool)")); + ASSERT_TRUE(foundInIntermediateTree("Greater Than (2-component vector of bool)")); +} + +TEST_F(TypeTrackingTest, Texture2DResultTypeAndPrecision) +{ + // ESSL spec section 4.5.3: sampler2D and samplerCube are lowp by default + // ESSL spec section 8: For the texture functions, the precision of the return type matches the precision of the sampler type. + const std::string &shaderString = + "precision mediump float;\n" + "uniform sampler2D s;\n" + "uniform vec2 a;\n" + "void main() {\n" + " vec4 c = texture2D(s, a);\n" + " gl_FragColor = c;\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundErrorInIntermediateTree()); + ASSERT_TRUE(foundInIntermediateTree("texture2D(s21;vf2; (lowp 4-component vector of float)")); +} + +TEST_F(TypeTrackingTest, TextureCubeResultTypeAndPrecision) +{ + // ESSL spec section 4.5.3: sampler2D and samplerCube are lowp by default + // ESSL spec section 8: For the texture functions, the precision of the return type matches the precision of the sampler type. + const std::string &shaderString = + "precision mediump float;\n" + "uniform samplerCube sc;\n" + "uniform vec3 a;\n" + "void main() {\n" + " vec4 c = textureCube(sc, a);\n" + " gl_FragColor = c;\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundErrorInIntermediateTree()); + ASSERT_TRUE(foundInIntermediateTree("textureCube(sC1;vf3; (lowp 4-component vector of float)")); +} + +TEST_F(TypeTrackingTest, TextureSizeResultTypeAndPrecision) +{ + // ESSL 3.0 spec section 8: textureSize has predefined precision highp + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "uniform sampler2D s;\n" + "void main() {\n" + " ivec2 size = textureSize(s, 0);\n" + " if (size.x > 100) {\n" + " my_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n" + " } else {\n" + " my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" + " }\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundErrorInIntermediateTree()); + ASSERT_TRUE(foundInIntermediateTree("textureSize(s21;i1; (highp 2-component vector of int)")); +} + +TEST_F(TypeTrackingTest, BuiltInConstructorResultTypeAndPrecision) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float u1;\n" + "uniform float u2;\n" + "uniform float u3;\n" + "uniform float u4;\n" + "void main() {\n" + " vec4 a = vec4(u1, u2, u3, u4);\n" + " gl_FragColor = a;\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundErrorInIntermediateTree()); + ASSERT_TRUE(foundInIntermediateTree("Construct vec4 (mediump 4-component vector of float)")); +} + +TEST_F(TypeTrackingTest, StructConstructorResultNoPrecision) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform vec4 u1;\n" + "uniform vec4 u2;\n" + "struct S { highp vec4 a; highp vec4 b; };\n" + "void main() {\n" + " S s = S(u1, u2);\n" + " gl_FragColor = s.a;\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundErrorInIntermediateTree()); + ASSERT_TRUE(foundInIntermediateTree("Construct structure (structure)")); +} + +TEST_F(TypeTrackingTest, PackResultTypeAndPrecision) +{ + // ESSL 3.0 spec section 8.4: pack functions have predefined precision highp + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "precision mediump int;\n" + "uniform vec2 uv;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " uint u0 = packSnorm2x16(uv);\n" + " uint u1 = packUnorm2x16(uv);\n" + " uint u2 = packHalf2x16(uv);\n" + " if (u0 + u1 + u2 > 100u) {\n" + " my_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n" + " } else {\n" + " my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" + " }\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundErrorInIntermediateTree()); + ASSERT_TRUE(foundInIntermediateTree("pack Snorm 2x16 (highp uint)")); + ASSERT_TRUE(foundInIntermediateTree("pack Unorm 2x16 (highp uint)")); + ASSERT_TRUE(foundInIntermediateTree("pack half 2x16 (highp uint)")); +} + +TEST_F(TypeTrackingTest, UnpackNormResultTypeAndPrecision) +{ + // ESSL 3.0 spec section 8.4: unpack(S/U)norm2x16 has predefined precision highp + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "precision mediump int;\n" + "uniform uint uu;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " vec2 v0 = unpackSnorm2x16(uu);\n" + " vec2 v1 = unpackUnorm2x16(uu);\n" + " if (v0.x * v1.x > 1.0) {\n" + " my_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n" + " } else {\n" + " my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" + " }\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundErrorInIntermediateTree()); + ASSERT_TRUE(foundInIntermediateTree("unpack Snorm 2x16 (highp 2-component vector of float)")); + ASSERT_TRUE(foundInIntermediateTree("unpack Unorm 2x16 (highp 2-component vector of float)")); +} + +TEST_F(TypeTrackingTest, UnpackHalfResultTypeAndPrecision) +{ + // ESSL 3.0 spec section 8.4: unpackHalf2x16 has predefined precision mediump + const std::string &shaderString = + "#version 300 es\n" + "precision highp float;\n" + "precision highp int;\n" + "uniform uint uu;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " vec2 v = unpackHalf2x16(uu);\n" + " if (v.x > 1.0) {\n" + " my_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n" + " } else {\n" + " my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" + " }\n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundErrorInIntermediateTree()); + ASSERT_TRUE(foundInIntermediateTree("unpack half 2x16 (mediump 2-component vector of float)")); +} + +TEST_F(TypeTrackingTest, BuiltInAbsSignFunctionFloatResultTypeAndPrecision) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float fval1;\n" + "void main() {\n" + " float fval2 = abs(fval1);\n" + " float fval3 = sign(fval1);\n" + " gl_FragColor = vec4(fval1, 0.0, 0.0, 1.0); \n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundErrorInIntermediateTree()); + ASSERT_TRUE(foundInIntermediateTree("Absolute value (mediump float)")); + ASSERT_TRUE(foundInIntermediateTree("Sign (mediump float)")); +} + +TEST_F(TypeTrackingTest, BuiltInAbsSignFunctionIntResultTypeAndPrecision) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "precision mediump int;\n" + "uniform int ival1;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " int ival2 = abs(ival1);\n" + " int ival3 = sign(ival1);\n" + " my_FragColor = vec4(0.0, 0.0, 0.0, 1.0); \n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundErrorInIntermediateTree()); + ASSERT_TRUE(foundInIntermediateTree("Absolute value (mediump int)")); + ASSERT_TRUE(foundInIntermediateTree("Sign (mediump int)")); +} + +TEST_F(TypeTrackingTest, BuiltInFloatBitsToIntResultTypeAndPrecision) +{ + // ESSL 3.00 section 8.3: floatBitsTo(U)int returns a highp value. + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "precision mediump int;\n" + "uniform float f;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " int i = floatBitsToInt(f);\n" + " uint u = floatBitsToUint(f);\n" + " my_FragColor = vec4(0.0, 0.0, 0.0, 1.0); \n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundErrorInIntermediateTree()); + ASSERT_TRUE(foundInIntermediateTree("float bits to int (highp int)")); + ASSERT_TRUE(foundInIntermediateTree("float bits to uint (highp uint)")); +} + +TEST_F(TypeTrackingTest, BuiltInIntBitsToFloatResultTypeAndPrecision) +{ + // ESSL 3.00 section 8.3: (u)intBitsToFloat returns a highp value. + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "precision mediump int;\n" + "uniform int i;\n" + "uniform uint u;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " float f1 = intBitsToFloat(i);\n" + " float f2 = uintBitsToFloat(u);\n" + " my_FragColor = vec4(f1, f2, 0.0, 1.0); \n" + "}\n"; + compile(shaderString); + ASSERT_FALSE(foundErrorInIntermediateTree()); + ASSERT_TRUE(foundInIntermediateTree("int bits to float (highp float)")); + ASSERT_TRUE(foundInIntermediateTree("uint bits to float (highp float)")); +} diff --git a/gfx/angle/src/tests/compiler_tests/UnrollFlatten_test.cpp b/gfx/angle/src/tests/compiler_tests/UnrollFlatten_test.cpp new file mode 100755 index 000000000..78f388a44 --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/UnrollFlatten_test.cpp @@ -0,0 +1,209 @@ +// +// Copyright (c) 2015 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. +// +// UnrollFlatten_test.cpp: +// Test for the outputting of [[unroll]] and [[flatten]] for the D3D compiler. +// This test can only be enabled when HLSL support is enabled. +// + +#include "angle_gl.h" +#include "common/angleutils.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" +#include "tests/test_utils/compiler_test.h" + +using namespace sh; + +namespace +{ + +class UnrollFlattenTest : public testing::Test +{ + public: + UnrollFlattenTest() {} + + protected: + void compile(const std::string &shaderString) + { + std::string infoLog; + bool compilationSuccess = + compileTestShader(GL_FRAGMENT_SHADER, SH_GLES2_SPEC, SH_HLSL_4_1_OUTPUT, shaderString, + SH_VARIABLES, &mTranslatedSource, &infoLog); + if (!compilationSuccess) + { + FAIL() << "Shader compilation failed " << infoLog; + } + // Ignore the beginning of the shader to avoid the definitions of LOOP and FLATTEN + mCurrentPosition = static_cast(mTranslatedSource.find("GL_USES_FRAG_COLOR")); + } + + void expect(const char *patterns[], size_t count) + { + const char *badPatterns[] = { UNROLL, FLATTEN }; + for (size_t i = 0; i < count; i++) + { + const char *pattern = patterns[i]; + auto position = mTranslatedSource.find(pattern, mCurrentPosition); + if (position == std::string::npos) + { + FAIL() << "Couldn't find '" << pattern << "' after expectations '" + << mExpectationList << "' in translated source:\n" << mTranslatedSource; + } + + for (size_t j = 0; j < ArraySize(badPatterns); j++) + { + const char *badPattern = badPatterns[j]; + if (pattern != badPattern && + mTranslatedSource.find(badPattern, mCurrentPosition) < position) + { + FAIL() << "Found '" << badPattern << "' before '" << pattern << "' after expectations '" + << mExpectationList << "' in translated source:\n" << mTranslatedSource; + } + } + mExpectationList += " - " + std::string(pattern); + mCurrentPosition = static_cast(position) + 1; + } + } + + static const char *UNROLL; + static const char *FLATTEN; + + private: + std::string mTranslatedSource; + + int mCurrentPosition; + std::string mExpectationList; +}; + +const char *UnrollFlattenTest::UNROLL = "LOOP"; +const char *UnrollFlattenTest::FLATTEN = "FLATTEN"; + +// Check that the nothing is added if there is no gradient operation +// even when there is ifs and discontinuous loops +TEST_F(UnrollFlattenTest, NoGradient) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float f;\n" + "float fun(float a){\n" // 1 + " if (a > 1.0) {return f;}\n" // 2 + " else {return a + 1.0;}\n" + "}\n" + "float fun2(float a){\n" // 3 + " for (int i = 0; i < 10; i++) {\n" // 4 + " if (a > 1.0) {break;}\n" // 5 + " a = fun(a);\n" // 6 + " }\n" + " return a;\n" + "}\n" + "void main() {\n" + " float accum = 0.0;\n" + " if (f < 5.0) {accum = fun2(accum);}\n" // 7 + " gl_FragColor = vec4(accum);\n" + "}\n"; + compile(shaderString); + // 1 - shouldn't get a Lod0 version generated + // 2 - no FLATTEN because does not contain discont loop + // 3 - shouldn't get a Lod0 version generated + // 4 - no LOOP because discont, and also no gradient + // 5 - no FLATTEN because does not contain loop with a gradient + // 6 - call non-Lod0 version + // 7 - no FLATTEN + const char *expectations[] = + { + "fun(", "if", + "fun2(", "for", "if", "break", "fun(", + "main(", "if", "fun2(" + }; + expect(expectations, ArraySize(expectations)); +} + +// Check that when we have a gradient in a non-discontinuous loop +// we use the regular version of the functions. Also checks that +// LOOP is generated for the loop containing the gradient. +TEST_F(UnrollFlattenTest, GradientNotInDiscont) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float f;\n" + "uniform sampler2D tex;" + "float fun(float a){\n" // 1 + " return texture2D(tex, vec2(0.5, f)).x;\n" // 2 + "}\n" + "float fun2(float a){\n" // 3 + " for (int i = 0; i < 10; i++) {\n" // 4 + " if (a > 1.0) {}\n" // 5 + " a = fun(a);\n" // 6 + " a += texture2D(tex, vec2(a, 0.0)).x;" // 7 + " }\n" + " return a;\n" + "}\n" + "void main() {\n" + " float accum = 0.0;\n" + " if (f < 5.0) {accum = fun2(accum);}\n" // 8 + " gl_FragColor = vec4(accum);\n" + "}\n"; + // 1 - shouldn't get a Lod0 version generated + // 2 - no Lod0 version generated + // 3 - shouldn't get a Lod0 version generated (not in discont loop) + // 4 - should have LOOP because it contains a gradient operation (even if Lod0) + // 5 - no FLATTEN because doesn't contain loop with a gradient + // 6 - call non-Lod0 version + // 7 - call non-Lod0 version + // 8 - FLATTEN because it contains a loop with a gradient + compile(shaderString); + const char *expectations[] = + { + "fun(", "texture2D(", + "fun2(", "LOOP", "for", "if", "fun(", "texture2D(", + "main(", "FLATTEN", "if", "fun2(" + }; + expect(expectations, ArraySize(expectations)); +} + +// Check that when we have a gradient in a discontinuous loop +// we use the Lod0 version of the functions. +TEST_F(UnrollFlattenTest, GradientInDiscont) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float f;\n" + "uniform sampler2D tex;" + "float fun(float a){\n" // 1 + " return texture2D(tex, vec2(0.5, f)).x;\n" // 2 + "}\n" + "float fun2(float a){\n" // 3 + " for (int i = 0; i < 10; i++) {\n" // 4 + " if (a > 1.0) {break;}\n" // 5 + " a = fun(a);\n" // 6 + " a += texture2D(tex, vec2(a, 0.0)).x;" // 7 + " }\n" + " return a;\n" + "}\n" + "void main() {\n" + " float accum = 0.0;\n" + " if (f < 5.0) {accum = fun2(accum);}\n" // 8 + " gl_FragColor = vec4(accum);\n" + "}\n"; + // 1 - should get a Lod0 version generated (gradient + discont loop) + // 2 - will get the Lod0 if in funLod0 + // 3 - shouldn't get a Lod0 version generated (not in discont loop) + // 4 - should have LOOP because it contains a gradient operation (even if Lod0) + // 5 - no FLATTEN because doesn't contain a loop with a gradient + // 6 - call Lod0 version + // 7 - call Lod0 version + // 8 - FLATTEN because it contains a loop with a gradient + compile(shaderString); + const char *expectations[] = + { + "fun(", "texture2D(", + "funLod0(", "texture2DLod0(", + "fun2(", "LOOP", "for", "if", "break", "funLod0(", "texture2DLod0", + "main(", "FLATTEN", "if", "fun2(" + }; + expect(expectations, ArraySize(expectations)); +} + +} diff --git a/gfx/angle/src/tests/compiler_tests/VariablePacker_test.cpp b/gfx/angle/src/tests/compiler_tests/VariablePacker_test.cpp new file mode 100755 index 000000000..dd9b0b5b6 --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/VariablePacker_test.cpp @@ -0,0 +1,237 @@ +// +// Copyright (c) 2002-2012 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 "gtest/gtest.h" +#include "angle_gl.h" +#include "common/utilities.h" +#include "common/angleutils.h" +#include "compiler/translator/VariablePacker.h" + +static sh::GLenum types[] = { + GL_FLOAT_MAT4, // 0 + GL_FLOAT_MAT2, // 1 + GL_FLOAT_VEC4, // 2 + GL_INT_VEC4, // 3 + GL_BOOL_VEC4, // 4 + GL_FLOAT_MAT3, // 5 + GL_FLOAT_VEC3, // 6 + GL_INT_VEC3, // 7 + GL_BOOL_VEC3, // 8 + GL_FLOAT_VEC2, // 9 + GL_INT_VEC2, // 10 + GL_BOOL_VEC2, // 11 + GL_FLOAT, // 12 + GL_INT, // 13 + GL_BOOL, // 14 + GL_SAMPLER_2D, // 15 + GL_SAMPLER_CUBE, // 16 + GL_SAMPLER_EXTERNAL_OES, // 17 + GL_SAMPLER_2D_RECT_ARB, // 18 + GL_UNSIGNED_INT, // 19 + GL_UNSIGNED_INT_VEC2, // 20 + GL_UNSIGNED_INT_VEC3, // 21 + GL_UNSIGNED_INT_VEC4, // 22 + GL_FLOAT_MAT2x3, // 23 + GL_FLOAT_MAT2x4, // 24 + GL_FLOAT_MAT3x2, // 25 + GL_FLOAT_MAT3x4, // 26 + GL_FLOAT_MAT4x2, // 27 + GL_FLOAT_MAT4x3, // 28 + GL_SAMPLER_3D, // 29 + GL_SAMPLER_2D_ARRAY, // 30 + GL_SAMPLER_2D_SHADOW, // 31 + GL_SAMPLER_CUBE_SHADOW, // 32 + GL_SAMPLER_2D_ARRAY_SHADOW, // 33 + GL_INT_SAMPLER_2D, // 34 + GL_INT_SAMPLER_CUBE, // 35 + GL_INT_SAMPLER_3D, // 36 + GL_INT_SAMPLER_2D_ARRAY, // 37 + GL_UNSIGNED_INT_SAMPLER_2D, // 38 + GL_UNSIGNED_INT_SAMPLER_CUBE, // 39 + GL_UNSIGNED_INT_SAMPLER_3D, // 40 + GL_UNSIGNED_INT_SAMPLER_2D_ARRAY, // 41 +}; + +static sh::GLenum nonSqMatTypes[] = { + GL_FLOAT_MAT2x3, + GL_FLOAT_MAT2x4, + GL_FLOAT_MAT3x2, + GL_FLOAT_MAT3x4, + GL_FLOAT_MAT4x2, + GL_FLOAT_MAT4x3 +}; + +TEST(VariablePacking, Pack) { + VariablePacker packer; + std::vector vars; + const int kMaxRows = 16; + // test no vars. + EXPECT_TRUE(packer.CheckVariablesWithinPackingLimits(kMaxRows, vars)); + + for (size_t tt = 0; tt < ArraySize(types); ++tt) + { + sh::GLenum type = types[tt]; + int num_rows = VariablePacker::GetNumRows(type); + int num_components_per_row = VariablePacker::GetNumComponentsPerRow(type); + // Check 1 of the type. + vars.clear(); + vars.push_back(sh::ShaderVariable(type, 0)); + EXPECT_TRUE(packer.CheckVariablesWithinPackingLimits(kMaxRows, vars)); + + // Check exactly the right amount of 1 type as an array. + int num_vars = kMaxRows / num_rows; + vars.clear(); + vars.push_back(sh::ShaderVariable(type, num_vars == 1 ? 0 : num_vars)); + EXPECT_TRUE(packer.CheckVariablesWithinPackingLimits(kMaxRows, vars)); + + // test too many + vars.clear(); + vars.push_back(sh::ShaderVariable(type, num_vars == 0 ? 0 : (num_vars + 1))); + EXPECT_FALSE(packer.CheckVariablesWithinPackingLimits(kMaxRows, vars)); + + // Check exactly the right amount of 1 type as individual vars. + num_vars = + kMaxRows / num_rows * ((num_components_per_row > 2) ? 1 : (4 / num_components_per_row)); + vars.clear(); + for (int ii = 0; ii < num_vars; ++ii) + { + vars.push_back(sh::ShaderVariable(type, 0)); + } + EXPECT_TRUE(packer.CheckVariablesWithinPackingLimits(kMaxRows, vars)); + + // Check 1 too many. + vars.push_back(sh::ShaderVariable(type, 0)); + EXPECT_FALSE(packer.CheckVariablesWithinPackingLimits(kMaxRows, vars)); + } + + // Test example from GLSL ES 3.0 spec chapter 11. + vars.clear(); + vars.push_back(sh::ShaderVariable(GL_FLOAT_VEC4, 0)); + vars.push_back(sh::ShaderVariable(GL_FLOAT_MAT3, 0)); + vars.push_back(sh::ShaderVariable(GL_FLOAT_MAT3, 0)); + vars.push_back(sh::ShaderVariable(GL_FLOAT_VEC2, 6)); + vars.push_back(sh::ShaderVariable(GL_FLOAT_VEC2, 4)); + vars.push_back(sh::ShaderVariable(GL_FLOAT_VEC2, 0)); + vars.push_back(sh::ShaderVariable(GL_FLOAT, 3)); + vars.push_back(sh::ShaderVariable(GL_FLOAT, 2)); + vars.push_back(sh::ShaderVariable(GL_FLOAT, 0)); + EXPECT_TRUE(packer.CheckVariablesWithinPackingLimits(kMaxRows, vars)); +} + +TEST(VariablePacking, PackSizes) { + for (size_t tt = 0; tt < ArraySize(types); ++tt) { + GLenum type = types[tt]; + + int expectedComponents = gl::VariableComponentCount(type); + int expectedRows = gl::VariableRowCount(type); + + if (type == GL_FLOAT_MAT2) { + expectedComponents = 4; + } else if (gl::IsMatrixType(type)) { + int squareSize = std::max(gl::VariableRowCount(type), + gl::VariableColumnCount(type)); + expectedComponents = squareSize; + expectedRows = squareSize; + } + + EXPECT_EQ(expectedComponents, + VariablePacker::GetNumComponentsPerRow(type)); + EXPECT_EQ(expectedRows, VariablePacker::GetNumRows(type)); + } +} + +// Check special assumptions about packing non-square mats +TEST(VariablePacking, NonSquareMats) { + + for (size_t mt = 0; mt < ArraySize(nonSqMatTypes); ++mt) { + + GLenum type = nonSqMatTypes[mt]; + + int rows = gl::VariableRowCount(type); + int cols = gl::VariableColumnCount(type); + int squareSize = std::max(rows, cols); + + std::vector vars; + vars.push_back(sh::ShaderVariable(type, 0)); + + // Fill columns + for (int row = 0; row < squareSize; row++) { + for (int col = squareSize; col < 4; ++col) { + vars.push_back(sh::ShaderVariable(GL_FLOAT, 0)); + } + } + + VariablePacker packer; + + EXPECT_TRUE(packer.CheckVariablesWithinPackingLimits(squareSize, vars)); + + // and one scalar and packing should fail + vars.push_back(sh::ShaderVariable(GL_FLOAT, 0)); + EXPECT_FALSE(packer.CheckVariablesWithinPackingLimits(squareSize, vars)); + } +} + +// Scalar type variables can be packed sharing rows with other variables. +TEST(VariablePacking, ReuseRows) +{ + VariablePacker packer; + std::vector vars; + const int kMaxRows = 512; + + // uniform bool u0[129]; + // uniform bool u1[129]; + // uniform bool u2[129]; + // uniform bool u3[129]; + { + int num_arrays = 4; + int num_elements_per_array = kMaxRows / num_arrays + 1; + for (int ii = 0; ii < num_arrays; ++ii) + { + vars.push_back(sh::ShaderVariable(GL_BOOL, num_elements_per_array)); + } + EXPECT_TRUE(packer.CheckVariablesWithinPackingLimits(kMaxRows, vars)); + } + + vars.clear(); + // uniform vec2 u0[257]; + // uniform float u1[257]; + // uniform int u1[257]; + { + int num_elements_per_array = kMaxRows / 2 + 1; + vars.push_back(sh::ShaderVariable(GL_FLOAT_VEC2, num_elements_per_array)); + vars.push_back(sh::ShaderVariable(GL_FLOAT, num_elements_per_array)); + vars.push_back(sh::ShaderVariable(GL_INT, num_elements_per_array)); + EXPECT_TRUE(packer.CheckVariablesWithinPackingLimits(kMaxRows, vars)); + } +} + +// Check the packer supports and flattens structures. +TEST(VariablePacking, Struct) +{ + VariablePacker packer; + std::vector fields; + const int kMaxRows = 16; + + // Test example from GLSL ES 3.0 spec chapter 11, but with structs + std::vector vars; + vars.push_back(sh::ShaderVariable(GL_STRUCT_ANGLEX, 0)); + + sh::ShaderVariable &parentStruct = vars[0]; + parentStruct.fields.push_back(sh::ShaderVariable(GL_FLOAT_VEC4, 0)); + parentStruct.fields.push_back(sh::ShaderVariable(GL_FLOAT_MAT3, 0)); + + parentStruct.fields.push_back(sh::ShaderVariable(GL_STRUCT_ANGLEX, 0)); + sh::ShaderVariable &innerStruct = parentStruct.fields.back(); + innerStruct.fields.push_back(sh::ShaderVariable(GL_FLOAT_MAT3, 0)); + innerStruct.fields.push_back(sh::ShaderVariable(GL_FLOAT_VEC2, 6)); + innerStruct.fields.push_back(sh::ShaderVariable(GL_FLOAT_VEC2, 4)); + + parentStruct.fields.push_back(sh::ShaderVariable(GL_FLOAT_VEC2, 0)); + parentStruct.fields.push_back(sh::ShaderVariable(GL_FLOAT, 3)); + parentStruct.fields.push_back(sh::ShaderVariable(GL_FLOAT, 2)); + parentStruct.fields.push_back(sh::ShaderVariable(GL_FLOAT, 0)); + + EXPECT_TRUE(packer.CheckVariablesWithinPackingLimits(kMaxRows, vars)); +} diff --git a/gfx/angle/src/tests/compiler_tests/WorkGroupSize_test.cpp b/gfx/angle/src/tests/compiler_tests/WorkGroupSize_test.cpp new file mode 100755 index 000000000..980617f84 --- /dev/null +++ b/gfx/angle/src/tests/compiler_tests/WorkGroupSize_test.cpp @@ -0,0 +1,99 @@ +// +// Copyright (c) 2015 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. +// +// WorkGroupSize_test.cpp: +// tests for local group size in a compute shader +// + +#include "angle_gl.h" +#include "gtest/gtest.h" +#include "GLSLANG/ShaderLang.h" +#include "compiler/translator/TranslatorESSL.h" +#include "tests/test_utils/compiler_test.h" + +using namespace sh; + +class WorkGroupSizeTest : public testing::Test +{ + public: + WorkGroupSizeTest() {} + + protected: + void SetUp() override + { + ShBuiltInResources resources; + InitBuiltInResources(&resources); + + mTranslator = new TranslatorESSL(GL_COMPUTE_SHADER, SH_GLES3_1_SPEC); + ASSERT_TRUE(mTranslator->Init(resources)); + } + + void TearDown() override { SafeDelete(mTranslator); } + + // Return true when compilation succeeds + bool compile(const std::string &shaderString) + { + const char *shaderStrings[] = {shaderString.c_str()}; + bool status = mTranslator->compile(shaderStrings, 1, SH_INTERMEDIATE_TREE | SH_VARIABLES); + TInfoSink &infoSink = mTranslator->getInfoSink(); + mInfoLog = infoSink.info.c_str(); + return status; + } + + protected: + std::string mInfoLog; + TranslatorESSL *mTranslator = nullptr; +}; + +// checks whether compiler has parsed the local size layout qualifiers qcorrectly +TEST_F(WorkGroupSizeTest, OnlyLocalSizeXSpecified) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x=5) in;\n" + "void main() {\n" + "}\n"; + + compile(shaderString); + + const WorkGroupSize &localSize = mTranslator->getComputeShaderLocalSize(); + ASSERT_EQ(5, localSize[0]); + ASSERT_EQ(1, localSize[1]); + ASSERT_EQ(1, localSize[2]); +} + +// checks whether compiler has parsed the local size layout qualifiers qcorrectly +TEST_F(WorkGroupSizeTest, LocalSizeXandZ) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x=5, local_size_z=10) in;\n" + "void main() {\n" + "}\n"; + + compile(shaderString); + + const WorkGroupSize &localSize = mTranslator->getComputeShaderLocalSize(); + ASSERT_EQ(5, localSize[0]); + ASSERT_EQ(1, localSize[1]); + ASSERT_EQ(10, localSize[2]); +} + +// checks whether compiler has parsed the local size layout qualifiers qcorrectly +TEST_F(WorkGroupSizeTest, LocalSizeAll) +{ + const std::string &shaderString = + "#version 310 es\n" + "layout(local_size_x=5, local_size_z=10, local_size_y=15) in;\n" + "void main() {\n" + "}\n"; + + compile(shaderString); + + const WorkGroupSize &localSize = mTranslator->getComputeShaderLocalSize(); + ASSERT_EQ(5, localSize[0]); + ASSERT_EQ(15, localSize[1]); + ASSERT_EQ(10, localSize[2]); +} -- cgit v1.2.3