blob: d953314b5ac00974676efe0e3628bda92221e9a5 [file] [log] [blame]
//
// 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 "common/mathutil.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
{
if (node.getType() != EbtFloat)
{
return false;
}
if (value == std::numeric_limits<float>::infinity())
{
return gl::isInf(node.getFConst()) && node.getFConst() > 0;
}
else if (value == -std::numeric_limits<float>::infinity())
{
return gl::isInf(node.getFConst()) && node.getFConst() < 0;
}
return mFaultTolerance >= fabsf(node.getFConst() - value);
}
bool isEqual(const TConstantUnion &node, const int &value) const
{
if (node.getType() != EbtInt)
{
return false;
}
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
{
if (node.getType() != EbtUInt)
{
return false;
}
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
{
if (node.getType() != EbtBool)
{
return false;
}
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;
};
class ConstantFoldingExpressionTest : public ConstantFoldingTest
{
public:
ConstantFoldingExpressionTest() {}
void evaluateFloat(const std::string &floatExpression)
{
std::stringstream shaderStream;
shaderStream << "#version 300 es\n"
"precision mediump float;\n"
"out float my_FragColor;\n"
"void main()\n"
"{\n"
<< " my_FragColor = " << floatExpression << ";\n"
<< "}\n";
compile(shaderStream.str());
}
};
// Test that zero, true or false are not found in AST when they are not expected. This is to make
// sure that the subsequent tests run correctly.
TEST_F(ConstantFoldingExpressionTest, FoldFloatTestSanityCheck)
{
const std::string &floatString = "1.0";
evaluateFloat(floatString);
ASSERT_FALSE(constantFoundInAST(0.0f));
ASSERT_FALSE(constantFoundInAST(true));
ASSERT_FALSE(constantFoundInAST(false));
}
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));
}
// Test that folding IsInf results in true when the parameter is an out-of-range float literal.
// ESSL 3.00.6 section 4.1.4 Floats:
// "If the value of the floating point number is too large (small) to be stored as a single
// precision value, it is converted to positive (negative) infinity."
// ESSL 3.00.6 section 12.4:
// "Mandate support for signed infinities."
TEST_F(ConstantFoldingTest, FoldIsInfOutOfRangeFloatLiteral)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"out vec4 my_FragColor;\n"
"void main()\n"
"{\n"
" bool b = isinf(1.0e2048);\n"
" my_FragColor = vec4(b);\n"
"}\n";
compile(shaderString);
ASSERT_TRUE(constantFoundInAST(true));
}
// Test that floats that are too small to be represented get flushed to zero.
// ESSL 3.00.6 section 4.1.4 Floats:
// "A value with a magnitude too small to be represented as a mantissa and exponent is converted to
// zero."
TEST_F(ConstantFoldingExpressionTest, FoldTooSmallFloat)
{
const std::string &floatString = "1.0e-2048";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(0.0f));
}
// IEEE 754 dictates that behavior of infinity is derived from limiting cases of real arithmetic.
// lim radians(x) x -> inf = inf
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldRadiansInfinity)
{
const std::string &floatString = "radians(1.0e2048)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
}
// IEEE 754 dictates that behavior of infinity is derived from limiting cases of real arithmetic.
// lim degrees(x) x -> inf = inf
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldDegreesInfinity)
{
const std::string &floatString = "degrees(1.0e2048)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
}
// IEEE 754 dictates that sinh(inf) = inf.
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldSinhInfinity)
{
const std::string &floatString = "sinh(1.0e2048)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
}
// IEEE 754 dictates that sinh(-inf) = -inf.
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldSinhNegativeInfinity)
{
const std::string &floatString = "sinh(-1.0e2048)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(-std::numeric_limits<float>::infinity()));
}
// IEEE 754 dictates that cosh(inf) = inf.
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldCoshInfinity)
{
const std::string &floatString = "cosh(1.0e2048)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
}
// IEEE 754 dictates that cosh(-inf) = inf.
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldCoshNegativeInfinity)
{
const std::string &floatString = "cosh(-1.0e2048)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
}
// IEEE 754 dictates that asinh(inf) = inf.
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldAsinhInfinity)
{
const std::string &floatString = "asinh(1.0e2048)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
}
// IEEE 754 dictates that asinh(-inf) = -inf.
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldAsinhNegativeInfinity)
{
const std::string &floatString = "asinh(-1.0e2048)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(-std::numeric_limits<float>::infinity()));
}
// IEEE 754 dictates that acosh(inf) = inf.
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldAcoshInfinity)
{
const std::string &floatString = "acosh(1.0e2048)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
}
// IEEE 754 dictates that pow or powr(0, inf) = 0.
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldPowInfinity)
{
const std::string &floatString = "pow(0.0, 1.0e2048)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(0.0f));
}
// IEEE 754 dictates that exp(inf) = inf.
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldExpInfinity)
{
const std::string &floatString = "exp(1.0e2048)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
}
// IEEE 754 dictates that exp(-inf) = 0.
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldExpNegativeInfinity)
{
const std::string &floatString = "exp(-1.0e2048)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(0.0f));
}
// IEEE 754 dictates that log(inf) = inf.
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldLogInfinity)
{
const std::string &floatString = "log(1.0e2048)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
}
// IEEE 754 dictates that exp2(inf) = inf.
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldExp2Infinity)
{
const std::string &floatString = "exp2(1.0e2048)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
}
// IEEE 754 dictates that exp2(-inf) = 0.
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldExp2NegativeInfinity)
{
const std::string &floatString = "exp2(-1.0e2048)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(0.0f));
}
// IEEE 754 dictates that log2(inf) = inf.
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldLog2Infinity)
{
const std::string &floatString = "log2(1.0e2048)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
}
// IEEE 754 dictates that behavior of infinity is derived from limiting cases of real arithmetic.
// lim sqrt(x) x -> inf = inf
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldSqrtInfinity)
{
const std::string &floatString = "sqrt(1.0e2048)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
}
// IEEE 754 dictates that rSqrt(inf) = 0
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldInversesqrtInfinity)
{
const std::string &floatString = "inversesqrt(1.0e2048)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(0.0f));
}
// IEEE 754 dictates that behavior of infinity is derived from limiting cases of real arithmetic.
// lim length(x) x -> inf = inf
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldLengthInfinity)
{
const std::string &floatString = "length(1.0e2048)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
}
// IEEE 754 dictates that behavior of infinity is derived from limiting cases of real arithmetic.
// lim dot(x, y) x -> inf, y > 0 = inf
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldDotInfinity)
{
const std::string &floatString = "dot(1.0e2048, 1.0)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
}
// IEEE 754 dictates that behavior of infinity is derived from limiting cases of real arithmetic.
// lim dot(vec2(x, y), vec2(z)) x -> inf, finite y, z > 0 = inf
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldDotInfinity2)
{
const std::string &floatString = "dot(vec2(1.0e2048, -1.0), vec2(1.0))";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(std::numeric_limits<float>::infinity()));
}
// Faceforward behavior with infinity as a parameter can be derived from dot().
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldFaceForwardInfinity)
{
const std::string &floatString = "faceforward(4.0, 1.0e2048, 1.0)";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(-4.0f));
}
// Faceforward behavior with infinity as a parameter can be derived from dot().
// ESSL 3.00.6 section 4.5.1: "Infinities and zeroes are generated as dictated by IEEE".
TEST_F(ConstantFoldingExpressionTest, FoldFaceForwardInfinity2)
{
const std::string &floatString = "faceforward(vec2(4.0), vec2(1.0e2048, -1.0), vec2(1.0)).x";
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(-4.0f));
}