summaryrefslogtreecommitdiffstats
path: root/gfx/angle/src/tests/compiler_tests
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /gfx/angle/src/tests/compiler_tests
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'gfx/angle/src/tests/compiler_tests')
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/API_test.cpp26
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/CollectVariables_test.cpp742
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/ConstantFolding_test.cpp1064
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/DebugShaderPrecision_test.cpp1047
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/EXT_blend_func_extended_test.cpp285
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/EmulateGLFragColorBroadcast_test.cpp81
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/ExpressionLimit_test.cpp557
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/FragDepth_test.cpp119
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/GLSLCompatibilityOutput_test.cpp35
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/IntermNode_test.cpp232
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/MalformedShader_test.cpp3158
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/NV_draw_buffers_test.cpp41
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/Pack_Unpack_test.cpp122
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/PruneEmptyDeclarations_test.cpp60
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/PruneUnusedFunctions_test.cpp93
-rw-r--r--gfx/angle/src/tests/compiler_tests/QualificationOrderESSL31_test.cpp184
-rw-r--r--gfx/angle/src/tests/compiler_tests/QualificationOrder_test.cpp571
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/RecordConstantPrecision_test.cpp95
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/RemovePow_test.cpp158
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/ShCompile_test.cpp83
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/ShaderExtension_test.cpp218
-rw-r--r--gfx/angle/src/tests/compiler_tests/ShaderImage_test.cpp259
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/ShaderVariable_test.cpp421
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/TypeTracking_test.cpp456
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/UnrollFlatten_test.cpp209
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/VariablePacker_test.cpp237
-rwxr-xr-xgfx/angle/src/tests/compiler_tests/WorkGroupSize_test.cpp99
27 files changed, 10652 insertions, 0 deletions
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 <memory>
+
+#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<Uniform> &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<TranslatorGLSL> 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<Attribute> &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<InterfaceBlock> &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<InterfaceBlock> &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<InterfaceBlock> &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<InterfaceBlock> &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<InterfaceBlock> &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<Varying> &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<uint64_t>(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<InterfaceBlock> &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 <vector>
+
+#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 <typename T>
+class ConstantFinder : public TIntermTraverser
+{
+ public:
+ ConstantFinder(const std::vector<T> &constantVector)
+ : TIntermTraverser(true, false, false),
+ mConstantVector(constantVector),
+ mFaultTolerance(T()),
+ mFound(false)
+ {}
+
+ ConstantFinder(const std::vector<T> &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<int>::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<int>::min();
+ }
+
+ bool isEqual(const TConstantUnion &node, const unsigned int &value) const
+ {
+ ASSERT(mFaultTolerance < static_cast<unsigned int>(std::numeric_limits<int>::max()));
+ return static_cast<int>(mFaultTolerance) >=
+ abs(static_cast<int>(node.getUConst() - value)) &&
+ static_cast<int>(node.getUConst() - value) != std::numeric_limits<int>::min();
+ }
+
+ bool isEqual(const TConstantUnion &node, const bool &value) const
+ {
+ return node.getBConst() == value;
+ }
+
+ std::vector<T> 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 <typename T>
+ bool constantFoundInAST(T constant)
+ {
+ ConstantFinder<T> finder(constant);
+ mASTRoot->traverse(&finder);
+ return finder.found();
+ }
+
+ template <typename T>
+ bool constantVectorFoundInAST(const std::vector<T> &constantVector)
+ {
+ ConstantFinder<T> finder(constantVector);
+ mASTRoot->traverse(&finder);
+ return finder.found();
+ }
+
+ template <typename T>
+ bool constantColumnMajorMatrixFoundInAST(const std::vector<T> &constantMatrix)
+ {
+ return constantVectorFoundInAST(constantMatrix);
+ }
+
+ template <typename T>
+ bool constantVectorNearFoundInAST(const std::vector<T> &constantVector, const T &faultTolerance)
+ {
+ ConstantFinder<T> 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<float> input1(3, 1.0f);
+ ASSERT_FALSE(constantVectorFoundInAST(input1));
+ std::vector<float> input2;
+ input2.push_back(1.0f);
+ input2.push_back(-1.0f);
+ input2.push_back(1.0f);
+ ASSERT_FALSE(constantVectorFoundInAST(input2));
+ std::vector<float> 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<float> input(inputElements, inputElements + 4);
+ ASSERT_FALSE(constantColumnMajorMatrixFoundInAST(input));
+ float outputElements[] =
+ {
+ -7.0f, 3.0f,
+ 5.0f, -2.0f
+ };
+ std::vector<float> 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<float> 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<float> 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<float> 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<float> 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<float> 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<float> 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<float> 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<float> 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<float> 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<float> 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<float> 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<float> 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<float> 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<float> 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<float> 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<float> 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<float> 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<testing::tuple<ShShaderSpec, const char *, const char *>>
+{
+ 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 <sstream>
+#include <string>
+#include <vector>
+#include "angle_gl.h"
+#include "gtest/gtest.h"
+#include "GLSLANG/ShaderLang.h"
+
+#define SHADER(Src) #Src
+
+class ExpressionLimitTest : public testing::Test {
+protected:
+ static const int kMaxExpressionComplexity = 16;
+ static const int kMaxCallStackDepth = 16;
+ static const int kMaxFunctionParameters = 16;
+ static const char* kExpressionTooComplex;
+ static const char* kCallStackTooDeep;
+ static const char* kHasRecursion;
+ static const char *kTooManyParameters;
+
+ virtual void SetUp()
+ {
+ memset(&resources, 0, sizeof(resources));
+
+ GenerateResources(&resources);
+ }
+
+ // Set up the per compile resources
+ static void GenerateResources(ShBuiltInResources *res)
+ {
+ sh::InitBuiltInResources(res);
+
+ res->MaxVertexAttribs = 8;
+ res->MaxVertexUniformVectors = 128;
+ res->MaxVaryingVectors = 8;
+ res->MaxVertexTextureImageUnits = 0;
+ res->MaxCombinedTextureImageUnits = 8;
+ res->MaxTextureImageUnits = 8;
+ res->MaxFragmentUniformVectors = 16;
+ res->MaxDrawBuffers = 1;
+
+ res->OES_standard_derivatives = 0;
+ res->OES_EGL_image_external = 0;
+
+ res->MaxExpressionComplexity = kMaxExpressionComplexity;
+ res->MaxCallStackDepth = kMaxCallStackDepth;
+ res->MaxFunctionParameters = kMaxFunctionParameters;
+ }
+
+ void GenerateLongExpression(int length, std::stringstream* ss)
+ {
+ for (int ii = 0; ii < length; ++ii) {
+ *ss << "+ vec4(" << ii << ")";
+ }
+ }
+
+ std::string GenerateShaderWithLongExpression(int length)
+ {
+ static const char* shaderStart = SHADER(
+ precision mediump float;
+ uniform vec4 u_color;
+ void main()
+ {
+ gl_FragColor = u_color
+ );
+
+ std::stringstream ss;
+ ss << shaderStart;
+ GenerateLongExpression(length, &ss);
+ ss << "; }";
+
+ return ss.str();
+ }
+
+ std::string GenerateShaderWithUnusedLongExpression(int length)
+ {
+ static const char* shaderStart = SHADER(
+ precision mediump float;
+ uniform vec4 u_color;
+ void main()
+ {
+ gl_FragColor = u_color;
+ }
+ vec4 someFunction() {
+ return u_color
+ );
+
+ std::stringstream ss;
+
+ ss << shaderStart;
+ GenerateLongExpression(length, &ss);
+ ss << "; }";
+
+ return ss.str();
+ }
+
+ void GenerateDeepFunctionStack(int length, std::stringstream* ss)
+ {
+ static const char* shaderStart = SHADER(
+ precision mediump float;
+ uniform vec4 u_color;
+ vec4 function0() {
+ return u_color;
+ }
+ );
+
+ *ss << shaderStart;
+ for (int ii = 0; ii < length; ++ii) {
+ *ss << "vec4 function" << (ii + 1) << "() {\n"
+ << " return function" << ii << "();\n"
+ << "}\n";
+ }
+ }
+
+ std::string GenerateShaderWithDeepFunctionStack(int length)
+ {
+ std::stringstream ss;
+
+ GenerateDeepFunctionStack(length, &ss);
+
+ ss << "void main() {\n"
+ << " gl_FragColor = function" << length << "();\n"
+ << "}";
+
+ return ss.str();
+ }
+
+ std::string GenerateShaderWithUnusedDeepFunctionStack(int length)
+ {
+ std::stringstream ss;
+
+ GenerateDeepFunctionStack(length, &ss);
+
+ ss << "void main() {\n"
+ << " gl_FragColor = vec4(0,0,0,0);\n"
+ << "}";
+
+
+ return ss.str();
+ }
+
+ std::string GenerateShaderWithFunctionParameters(int parameters)
+ {
+ std::stringstream ss;
+
+ ss << "precision mediump float;\n"
+ << "\n"
+ << "float foo(";
+ for (int i = 0; i < parameters; ++i)
+ {
+ ss << "float f" << i;
+ if (i + 1 < parameters)
+ {
+ ss << ", ";
+ }
+ }
+ ss << ")\n"
+ << "{\n"
+ << " return f0;\n"
+ << "}\n"
+ << "\n"
+ << "void main()\n"
+ << "{\n"
+ << " gl_FragColor = vec4(0,0,0,0);\n"
+ << "}";
+
+ return ss.str();
+ }
+
+ // Compiles a shader and if there's an error checks for a specific
+ // substring in the error log. This way we know the error is specific
+ // to the issue we are testing.
+ bool CheckShaderCompilation(ShHandle compiler,
+ const char *source,
+ ShCompileOptions compileOptions,
+ const char *expected_error)
+ {
+ bool success = sh::Compile(compiler, &source, 1, compileOptions) != 0;
+ if (success)
+ {
+ success = !expected_error;
+ }
+ else
+ {
+ std::string log = sh::GetInfoLog(compiler);
+ if (expected_error)
+ success = log.find(expected_error) != std::string::npos;
+
+ EXPECT_TRUE(success) << log << "\n----shader----\n" << source;
+ }
+ return success;
+ }
+
+ ShBuiltInResources resources;
+};
+
+const char* ExpressionLimitTest::kExpressionTooComplex =
+ "Expression too complex";
+const char* ExpressionLimitTest::kCallStackTooDeep =
+ "Call stack too deep";
+const char* ExpressionLimitTest::kHasRecursion =
+ "Function recursion detected";
+const char* ExpressionLimitTest::kTooManyParameters =
+ "Function has too many parameters";
+
+TEST_F(ExpressionLimitTest, ExpressionComplexity)
+{
+ ShShaderSpec spec = SH_WEBGL_SPEC;
+ ShShaderOutput output = SH_ESSL_OUTPUT;
+ ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
+ ShCompileOptions compileOptions = SH_LIMIT_EXPRESSION_COMPLEXITY;
+
+ // Test expression under the limit passes.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler,
+ GenerateShaderWithLongExpression(
+ kMaxExpressionComplexity - 10).c_str(),
+ compileOptions, NULL));
+ // Test expression over the limit fails.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler,
+ GenerateShaderWithLongExpression(
+ kMaxExpressionComplexity + 10).c_str(),
+ compileOptions, kExpressionTooComplex));
+ // Test expression over the limit without a limit does not fail.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler,
+ GenerateShaderWithLongExpression(
+ kMaxExpressionComplexity + 10).c_str(),
+ compileOptions & ~SH_LIMIT_EXPRESSION_COMPLEXITY, NULL));
+ sh::Destruct(vertexCompiler);
+}
+
+TEST_F(ExpressionLimitTest, UnusedExpressionComplexity)
+{
+ ShShaderSpec spec = SH_WEBGL_SPEC;
+ ShShaderOutput output = SH_ESSL_OUTPUT;
+ ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
+ ShCompileOptions compileOptions = SH_LIMIT_EXPRESSION_COMPLEXITY;
+
+ // Test expression under the limit passes.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler,
+ GenerateShaderWithUnusedLongExpression(
+ kMaxExpressionComplexity - 10).c_str(),
+ compileOptions, NULL));
+ // Test expression over the limit fails.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler,
+ GenerateShaderWithUnusedLongExpression(
+ kMaxExpressionComplexity + 10).c_str(),
+ compileOptions, kExpressionTooComplex));
+ // Test expression over the limit without a limit does not fail.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler,
+ GenerateShaderWithUnusedLongExpression(
+ kMaxExpressionComplexity + 10).c_str(),
+ compileOptions & ~SH_LIMIT_EXPRESSION_COMPLEXITY, NULL));
+ sh::Destruct(vertexCompiler);
+}
+
+TEST_F(ExpressionLimitTest, CallStackDepth)
+{
+ ShShaderSpec spec = SH_WEBGL_SPEC;
+ ShShaderOutput output = SH_ESSL_OUTPUT;
+ ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
+ ShCompileOptions compileOptions = SH_LIMIT_CALL_STACK_DEPTH;
+
+ // Test call stack under the limit passes.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler,
+ GenerateShaderWithDeepFunctionStack(
+ kMaxCallStackDepth - 10).c_str(),
+ compileOptions, NULL));
+ // Test call stack over the limit fails.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler,
+ GenerateShaderWithDeepFunctionStack(
+ kMaxCallStackDepth + 10).c_str(),
+ compileOptions, kCallStackTooDeep));
+ // Test call stack over the limit without limit does not fail.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler,
+ GenerateShaderWithDeepFunctionStack(
+ kMaxCallStackDepth + 10).c_str(),
+ compileOptions & ~SH_LIMIT_CALL_STACK_DEPTH, NULL));
+ sh::Destruct(vertexCompiler);
+}
+
+TEST_F(ExpressionLimitTest, UnusedCallStackDepth)
+{
+ ShShaderSpec spec = SH_WEBGL_SPEC;
+ ShShaderOutput output = SH_ESSL_OUTPUT;
+ ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
+ ShCompileOptions compileOptions = SH_LIMIT_CALL_STACK_DEPTH;
+
+ // Test call stack under the limit passes.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler,
+ GenerateShaderWithUnusedDeepFunctionStack(
+ kMaxCallStackDepth - 10).c_str(),
+ compileOptions, NULL));
+ // Test call stack over the limit fails.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler,
+ GenerateShaderWithUnusedDeepFunctionStack(
+ kMaxCallStackDepth + 10).c_str(),
+ compileOptions, kCallStackTooDeep));
+ // Test call stack over the limit without limit does not fail.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler,
+ GenerateShaderWithUnusedDeepFunctionStack(
+ kMaxCallStackDepth + 10).c_str(),
+ compileOptions & ~SH_LIMIT_CALL_STACK_DEPTH, NULL));
+ sh::Destruct(vertexCompiler);
+}
+
+TEST_F(ExpressionLimitTest, Recursion)
+{
+ ShShaderSpec spec = SH_WEBGL_SPEC;
+ ShShaderOutput output = SH_ESSL_OUTPUT;
+ ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
+ ShCompileOptions compileOptions = 0;
+
+ static const char* shaderWithRecursion0 = SHADER(
+ precision mediump float;
+ uniform vec4 u_color;
+ vec4 someFunc() {
+ return someFunc();
+ }
+
+ void main() {
+ gl_FragColor = u_color * someFunc();
+ }
+ );
+
+ static const char* shaderWithRecursion1 = SHADER(
+ precision mediump float;
+ uniform vec4 u_color;
+
+ vec4 someFunc();
+
+ vec4 someFunc1() {
+ return someFunc();
+ }
+
+ vec4 someFunc() {
+ return someFunc1();
+ }
+
+ void main() {
+ gl_FragColor = u_color * someFunc();
+ }
+ );
+
+ static const char* shaderWithRecursion2 = SHADER(
+ precision mediump float;
+ uniform vec4 u_color;
+ vec4 someFunc() {
+ if (u_color.x > 0.5) {
+ return someFunc();
+ } else {
+ return vec4(1);
+ }
+ }
+
+ void main() {
+ gl_FragColor = someFunc();
+ }
+ );
+
+ static const char* shaderWithRecursion3 = SHADER(
+ precision mediump float;
+ uniform vec4 u_color;
+ vec4 someFunc() {
+ if (u_color.x > 0.5) {
+ return vec4(1);
+ } else {
+ return someFunc();
+ }
+ }
+
+ void main() {
+ gl_FragColor = someFunc();
+ }
+ );
+
+ static const char* shaderWithRecursion4 = SHADER(
+ precision mediump float;
+ uniform vec4 u_color;
+ vec4 someFunc() {
+ return (u_color.x > 0.5) ? vec4(1) : someFunc();
+ }
+
+ void main() {
+ gl_FragColor = someFunc();
+ }
+ );
+
+ static const char* shaderWithRecursion5 = SHADER(
+ precision mediump float;
+ uniform vec4 u_color;
+ vec4 someFunc() {
+ return (u_color.x > 0.5) ? someFunc() : vec4(1);
+ }
+
+ void main() {
+ gl_FragColor = someFunc();
+ }
+ );
+
+ static const char* shaderWithRecursion6 = SHADER(
+ precision mediump float;
+ uniform vec4 u_color;
+ vec4 someFunc() {
+ return someFunc();
+ }
+
+ void main() {
+ gl_FragColor = u_color;
+ }
+ );
+
+ static const char* shaderWithNoRecursion = SHADER(
+ precision mediump float;
+ uniform vec4 u_color;
+
+ vec3 rgb(int r, int g, int b) {
+ return vec3(float(r) / 255.0, float(g) / 255.0, float(b) / 255.0);
+ }
+
+ void main() {
+ vec3 hairColor0 = rgb(151, 200, 234);
+ vec3 faceColor2 = rgb(183, 148, 133);
+ gl_FragColor = u_color + vec4(hairColor0 + faceColor2, 0);
+ }
+ );
+
+ static const char* shaderWithRecursion7 = SHADER(
+ precision mediump float;
+ uniform vec4 u_color;
+
+ vec4 function2() {
+ return u_color;
+ }
+
+ vec4 function1() {
+ vec4 a = function2();
+ vec4 b = function1();
+ return a + b;
+ }
+
+ void main() {
+ gl_FragColor = function1();
+ }
+ );
+
+ static const char* shaderWithRecursion8 = SHADER(
+ precision mediump float;
+ uniform vec4 u_color;
+
+ vec4 function1();
+
+ vec4 function3() {
+ return function1();
+ }
+
+ vec4 function2() {
+ return function3();
+ }
+
+ vec4 function1() {
+ return function2();
+ }
+
+ void main() {
+ gl_FragColor = function1();
+ }
+ );
+
+ // Check simple recursions fails.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler, shaderWithRecursion0,
+ compileOptions, kHasRecursion));
+ // Check simple recursions fails.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler, shaderWithRecursion1,
+ compileOptions, kHasRecursion));
+ // Check if recursions fails.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler, shaderWithRecursion2,
+ compileOptions, kHasRecursion));
+ // Check if recursions fails.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler, shaderWithRecursion3,
+ compileOptions, kHasRecursion));
+ // Check ternary recursions fails.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler, shaderWithRecursion4,
+ compileOptions, kHasRecursion));
+ // Check ternary recursions fails.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler, shaderWithRecursion5,
+ compileOptions, kHasRecursion));
+
+ // Check some more forms of recursion
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler, shaderWithRecursion6,
+ compileOptions, kHasRecursion));
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler, shaderWithRecursion7,
+ compileOptions, kHasRecursion));
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler, shaderWithRecursion8,
+ compileOptions, kHasRecursion));
+ // Check unused recursions fails if limiting call stack
+ // since we check all paths.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler, shaderWithRecursion6,
+ compileOptions | SH_LIMIT_CALL_STACK_DEPTH, kHasRecursion));
+
+ // Check unused recursions passes.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler, shaderWithNoRecursion,
+ compileOptions, NULL));
+ // Check unused recursions passes if limiting call stack.
+ EXPECT_TRUE(CheckShaderCompilation(
+ vertexCompiler, shaderWithNoRecursion,
+ compileOptions | SH_LIMIT_CALL_STACK_DEPTH, NULL));
+ sh::Destruct(vertexCompiler);
+}
+
+TEST_F(ExpressionLimitTest, FunctionParameterCount)
+{
+ ShShaderSpec spec = SH_WEBGL_SPEC;
+ ShShaderOutput output = SH_ESSL_OUTPUT;
+ ShHandle compiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
+ ShCompileOptions compileOptions = SH_LIMIT_EXPRESSION_COMPLEXITY;
+
+ // Test parameters under the limit succeeds.
+ EXPECT_TRUE(CheckShaderCompilation(
+ compiler, GenerateShaderWithFunctionParameters(kMaxFunctionParameters).c_str(),
+ compileOptions, nullptr));
+ // Test parameters over the limit fails.
+ EXPECT_TRUE(CheckShaderCompilation(
+ compiler, GenerateShaderWithFunctionParameters(kMaxFunctionParameters + 1).c_str(),
+ compileOptions, kTooManyParameters));
+ // Test parameters over the limit without limit does not fail.
+ EXPECT_TRUE(CheckShaderCompilation(
+ compiler, GenerateShaderWithFunctionParameters(kMaxFunctionParameters + 1).c_str(),
+ compileOptions & ~SH_LIMIT_EXPRESSION_COMPLEXITY, nullptr));
+ sh::Destruct(compiler);
+}
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<bool>
+{
+ 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 &copy)
+ {
+ 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 <out variable name>".
+// 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 <out variable name>".
+// 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 <out variable name>".
+// 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 <class T>
+ 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<FindPow>
+{
+ 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<FindPowWorkaround>
+{
+ 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<FindNestedPowWorkaround>
+{
+ 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<FindPow>());
+ ASSERT_TRUE(foundInAST<FindPowWorkaround>());
+ ASSERT_FALSE(foundInAST<FindNestedPowWorkaround>());
+}
+
+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<FindPow>());
+ ASSERT_TRUE(foundInAST<FindNestedPowWorkaround>());
+}
+
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<sh::Uniform> &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<ShHandle>(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<ShHandle>(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<ShHandle>(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<sh::Varying> *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<ShHandle>(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<sh::Varying> *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<ShHandle>(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<sh::Varying> *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<int>(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<int>(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<sh::ShaderVariable> 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<sh::ShaderVariable> 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<sh::ShaderVariable> 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<sh::ShaderVariable> fields;
+ const int kMaxRows = 16;
+
+ // Test example from GLSL ES 3.0 spec chapter 11, but with structs
+ std::vector<sh::ShaderVariable> 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]);
+}