Commit 7fa3355f by Arun Patole Committed by Jamie Madill

Add constant folding support for matrix built-ins

This change adds constant folding support for following matrix built-ins: - matrixCompMult, outerProduct, transpose, determinant and inverse. BUG=angleproject:913 TEST=angle_unittests(new: MatrixUtilsTest, ConstantFoldingTest.*Matrix*), dEQP Tests: dEQP-GLES3.functional.shaders.constant_expressions.builtin_functions.matrix.* (All 54 tests started passing with this change) Change-Id: I7b9bf04b9a2cbff72c48216cab04df58c5f008d6 Reviewed-on: https://chromium-review.googlesource.com/276574Reviewed-by: 's avatarOlli Etuaho <oetuaho@nvidia.com> Tested-by: 's avatarOlli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 6e56faae
//
// Copyright 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.
//
// Matrix:
// Utility class implementing various matrix operations.
// Supports matrices with minimum 2 and maximum 4 number of rows/columns.
//
// TODO: Check if we can merge Matrix.h in sample_util with this and replace it with this implementation.
// TODO: Rename this file to Matrix.h once we remove Matrix.h in sample_util.
#ifndef COMMON_MATRIX_UTILS_H_
#define COMMON_MATRIX_UTILS_H_
#include <vector>
#include "common/debug.h"
namespace angle
{
template<typename T>
class Matrix
{
public:
Matrix(const std::vector<T> &elements, const unsigned int &numRows, const unsigned int &numCols)
: mElements(elements),
mRows(numRows),
mCols(numCols)
{
ASSERT(rows() >= 1 && rows() <= 4);
ASSERT(columns() >= 1 && columns() <= 4);
}
Matrix(const std::vector<T> &elements, const unsigned int &size)
: mElements(elements),
mRows(size),
mCols(size)
{
ASSERT(rows() >= 1 && rows() <= 4);
ASSERT(columns() >= 1 && columns() <= 4);
}
Matrix(const T *elements, const unsigned int &size)
: mRows(size),
mCols(size)
{
ASSERT(rows() >= 1 && rows() <= 4);
ASSERT(columns() >= 1 && columns() <= 4);
for (size_t i = 0; i < size * size; i++)
mElements.push_back(elements[i]);
}
const T &operator()(const unsigned int &rowIndex, const unsigned int &columnIndex) const
{
return mElements[rowIndex * columns() + columnIndex];
}
T &operator()(const unsigned int &rowIndex, const unsigned int &columnIndex)
{
return mElements[rowIndex * columns() + columnIndex];
}
const T &at(const unsigned int &rowIndex, const unsigned int &columnIndex) const
{
return operator()(rowIndex, columnIndex);
}
Matrix<T> operator*(const Matrix<T> &m)
{
ASSERT(columns() == m.rows());
size_t resultRows = rows();
size_t resultCols = m.columns();
Matrix<T> result(std::vector<T>(resultRows * resultCols), resultRows, resultCols);
for (size_t i = 0; i < resultRows; i++)
{
for (size_t j = 0; j < resultCols; j++)
{
T tmp = 0.0f;
for (size_t k = 0; k < columns(); k++)
tmp += at(i, k) * m(k, j);
result(i, j) = tmp;
}
}
return result;
}
unsigned int size() const
{
ASSERT(rows() == columns());
return rows();
}
unsigned int rows() const { return mRows; }
unsigned int columns() const { return mCols; }
std::vector<T> elements() const { return mElements; }
Matrix<T> compMult(const Matrix<T> &mat1) const
{
Matrix result(std::vector<T>(mElements.size()), size());
for (size_t i = 0; i < columns(); i++)
for (size_t j = 0; j < rows(); j++)
result(i, j) = at(i, j) * mat1(i, j);
return result;
}
Matrix<T> outerProduct(const Matrix<T> &mat1) const
{
unsigned int cols = mat1.columns();
Matrix result(std::vector<T>(rows() * cols), rows(), cols);
for (size_t i = 0; i < rows(); i++)
for (size_t j = 0; j < cols; j++)
result(i, j) = at(i, 0) * mat1(0, j);
return result;
}
Matrix<T> transpose() const
{
Matrix result(std::vector<T>(mElements.size()), columns(), rows());
for (size_t i = 0; i < columns(); i++)
for (size_t j = 0; j < rows(); j++)
result(i, j) = at(j, i);
return result;
}
T determinant() const
{
ASSERT(rows() == columns());
switch (size())
{
case 2:
return at(0, 0) * at(1, 1) - at(0, 1) * at(1, 0);
case 3:
return at(0, 0) * at(1, 1) * at(2, 2) +
at(0, 1) * at(1, 2) * at(2, 0) +
at(0, 2) * at(1, 0) * at(2, 1) -
at(0, 2) * at(1, 1) * at(2, 0) -
at(0, 1) * at(1, 0) * at(2, 2) -
at(0, 0) * at(1, 2) * at(2, 1);
case 4:
{
const float minorMatrices[4][3 * 3] =
{
{
at(1, 1), at(2, 1), at(3, 1),
at(1, 2), at(2, 2), at(3, 2),
at(1, 3), at(2, 3), at(3, 3),
},
{
at(1, 0), at(2, 0), at(3, 0),
at(1, 2), at(2, 2), at(3, 2),
at(1, 3), at(2, 3), at(3, 3),
},
{
at(1, 0), at(2, 0), at(3, 0),
at(1, 1), at(2, 1), at(3, 1),
at(1, 3), at(2, 3), at(3, 3),
},
{
at(1, 0), at(2, 0), at(3, 0),
at(1, 1), at(2, 1), at(3, 1),
at(1, 2), at(2, 2), at(3, 2),
}
};
return at(0, 0) * Matrix<T>(minorMatrices[0], 3).determinant() -
at(0, 1) * Matrix<T>(minorMatrices[1], 3).determinant() +
at(0, 2) * Matrix<T>(minorMatrices[2], 3).determinant() -
at(0, 3) * Matrix<T>(minorMatrices[3], 3).determinant();
}
default:
UNREACHABLE();
break;
}
return T();
}
Matrix<T> inverse() const
{
ASSERT(rows() == columns());
Matrix<T> cof(std::vector<T>(mElements.size()), rows(), columns());
switch (size())
{
case 2:
cof(0, 0) = at(1, 1);
cof(0, 1) = -at(1, 0);
cof(1, 0) = -at(0, 1);
cof(1, 1) = at(0, 0);
break;
case 3:
cof(0, 0) = at(1, 1) * at(2, 2) -
at(2, 1) * at(1, 2);
cof(0, 1) = -(at(1, 0) * at(2, 2) -
at(2, 0) * at(1, 2));
cof(0, 2) = at(1, 0) * at(2, 1) -
at(2, 0) * at(1, 1);
cof(1, 0) = -(at(0, 1) * at(2, 2) -
at(2, 1) * at(0, 2));
cof(1, 1) = at(0, 0) * at(2, 2) -
at(2, 0) * at(0, 2);
cof(1, 2) = -(at(0, 0) * at(2, 1) -
at(2, 0) * at(0, 1));
cof(2, 0) = at(0, 1) * at(1, 2) -
at(1, 1) * at(0, 2);
cof(2, 1) = -(at(0, 0) * at(1, 2) -
at(1, 0) * at(0, 2));
cof(2, 2) = at(0, 0) * at(1, 1) -
at(1, 0) * at(0, 1);
break;
case 4:
cof(0, 0) = at(1, 1) * at(2, 2) * at(3, 3) +
at(2, 1) * at(3, 2) * at(1, 3) +
at(3, 1) * at(1, 2) * at(2, 3) -
at(1, 1) * at(3, 2) * at(2, 3) -
at(2, 1) * at(1, 2) * at(3, 3) -
at(3, 1) * at(2, 2) * at(1, 3);
cof(0, 1) = -(at(1, 0) * at(2, 2) * at(3, 3) +
at(2, 0) * at(3, 2) * at(1, 3) +
at(3, 0) * at(1, 2) * at(2, 3) -
at(1, 0) * at(3, 2) * at(2, 3) -
at(2, 0) * at(1, 2) * at(3, 3) -
at(3, 0) * at(2, 2) * at(1, 3));
cof(0, 2) = at(1, 0) * at(2, 1) * at(3, 3) +
at(2, 0) * at(3, 1) * at(1, 3) +
at(3, 0) * at(1, 1) * at(2, 3) -
at(1, 0) * at(3, 1) * at(2, 3) -
at(2, 0) * at(1, 1) * at(3, 3) -
at(3, 0) * at(2, 1) * at(1, 3);
cof(0, 3) = -(at(1, 0) * at(2, 1) * at(3, 2) +
at(2, 0) * at(3, 1) * at(1, 2) +
at(3, 0) * at(1, 1) * at(2, 2) -
at(1, 0) * at(3, 1) * at(2, 2) -
at(2, 0) * at(1, 1) * at(3, 2) -
at(3, 0) * at(2, 1) * at(1, 2));
cof(1, 0) = -(at(0, 1) * at(2, 2) * at(3, 3) +
at(2, 1) * at(3, 2) * at(0, 3) +
at(3, 1) * at(0, 2) * at(2, 3) -
at(0, 1) * at(3, 2) * at(2, 3) -
at(2, 1) * at(0, 2) * at(3, 3) -
at(3, 1) * at(2, 2) * at(0, 3));
cof(1, 1) = at(0, 0) * at(2, 2) * at(3, 3) +
at(2, 0) * at(3, 2) * at(0, 3) +
at(3, 0) * at(0, 2) * at(2, 3) -
at(0, 0) * at(3, 2) * at(2, 3) -
at(2, 0) * at(0, 2) * at(3, 3) -
at(3, 0) * at(2, 2) * at(0, 3);
cof(1, 2) = -(at(0, 0) * at(2, 1) * at(3, 3) +
at(2, 0) * at(3, 1) * at(0, 3) +
at(3, 0) * at(0, 1) * at(2, 3) -
at(0, 0) * at(3, 1) * at(2, 3) -
at(2, 0) * at(0, 1) * at(3, 3) -
at(3, 0) * at(2, 1) * at(0, 3));
cof(1, 3) = at(0, 0) * at(2, 1) * at(3, 2) +
at(2, 0) * at(3, 1) * at(0, 2) +
at(3, 0) * at(0, 1) * at(2, 2) -
at(0, 0) * at(3, 1) * at(2, 2) -
at(2, 0) * at(0, 1) * at(3, 2) -
at(3, 0) * at(2, 1) * at(0, 2);
cof(2, 0) = at(0, 1) * at(1, 2) * at(3, 3) +
at(1, 1) * at(3, 2) * at(0, 3) +
at(3, 1) * at(0, 2) * at(1, 3) -
at(0, 1) * at(3, 2) * at(1, 3) -
at(1, 1) * at(0, 2) * at(3, 3) -
at(3, 1) * at(1, 2) * at(0, 3);
cof(2, 1) = -(at(0, 0) * at(1, 2) * at(3, 3) +
at(1, 0) * at(3, 2) * at(0, 3) +
at(3, 0) * at(0, 2) * at(1, 3) -
at(0, 0) * at(3, 2) * at(1, 3) -
at(1, 0) * at(0, 2) * at(3, 3) -
at(3, 0) * at(1, 2) * at(0, 3));
cof(2, 2) = at(0, 0) * at(1, 1) * at(3, 3) +
at(1, 0) * at(3, 1) * at(0, 3) +
at(3, 0) * at(0, 1) * at(1, 3) -
at(0, 0) * at(3, 1) * at(1, 3) -
at(1, 0) * at(0, 1) * at(3, 3) -
at(3, 0) * at(1, 1) * at(0, 3);
cof(2, 3) = -(at(0, 0) * at(1, 1) * at(3, 2) +
at(1, 0) * at(3, 1) * at(0, 2) +
at(3, 0) * at(0, 1) * at(1, 2) -
at(0, 0) * at(3, 1) * at(1, 2) -
at(1, 0) * at(0, 1) * at(3, 2) -
at(3, 0) * at(1, 1) * at(0, 2));
cof(3, 0) = -(at(0, 1) * at(1, 2) * at(2, 3) +
at(1, 1) * at(2, 2) * at(0, 3) +
at(2, 1) * at(0, 2) * at(1, 3) -
at(0, 1) * at(2, 2) * at(1, 3) -
at(1, 1) * at(0, 2) * at(2, 3) -
at(2, 1) * at(1, 2) * at(0, 3));
cof(3, 1) = at(0, 0) * at(1, 2) * at(2, 3) +
at(1, 0) * at(2, 2) * at(0, 3) +
at(2, 0) * at(0, 2) * at(1, 3) -
at(0, 0) * at(2, 2) * at(1, 3) -
at(1, 0) * at(0, 2) * at(2, 3) -
at(2, 0) * at(1, 2) * at(0, 3);
cof(3, 2) = -(at(0, 0) * at(1, 1) * at(2, 3) +
at(1, 0) * at(2, 1) * at(0, 3) +
at(2, 0) * at(0, 1) * at(1, 3) -
at(0, 0) * at(2, 1) * at(1, 3) -
at(1, 0) * at(0, 1) * at(2, 3) -
at(2, 0) * at(1, 1) * at(0, 3));
cof(3, 3) = at(0, 0) * at(1, 1) * at(2, 2) +
at(1, 0) * at(2, 1) * at(0, 2) +
at(2, 0) * at(0, 1) * at(1, 2) -
at(0, 0) * at(2, 1) * at(1, 2) -
at(1, 0) * at(0, 1) * at(2, 2) -
at(2, 0) * at(1, 1) * at(0, 2);
break;
default:
UNREACHABLE();
break;
}
// The inverse of A is the transpose of the cofactor matrix times the reciprocal of the determinant of A.
Matrix<T> adjugateMatrix(cof.transpose());
T det = determinant();
Matrix<T> result(std::vector<T>(mElements.size()), rows(), columns());
for (size_t i = 0; i < rows(); i++)
for (size_t j = 0; j < columns(); j++)
result(i, j) = det ? adjugateMatrix(i, j) / det : T();
return result;
}
private:
std::vector<T> mElements;
unsigned int mRows;
unsigned int mCols;
};
} // namespace angle
#endif // COMMON_MATRIX_UTILS_H_
//
// Copyright 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.
//
// matrix_utils_unittests:
// Unit tests for the matrix utils.
//
#include "matrix_utils.h"
#include <gtest/gtest.h>
using namespace angle;
namespace
{
const unsigned int minDimensions = 2;
const unsigned int maxDimensions = 4;
TEST(MatrixUtilsTest, MatrixConstructorTest)
{
for (size_t i = minDimensions; i <= maxDimensions; i++)
{
for (size_t j = minDimensions; j <= maxDimensions; j++)
{
unsigned int numElements = i * j;
Matrix<float> m(std::vector<float>(numElements, 1.0f), i, j);
EXPECT_EQ(m.rows(), i);
EXPECT_EQ(m.columns(), j);
EXPECT_EQ(m.elements(), std::vector<float>(numElements, 1.0f));
}
}
for (size_t i = minDimensions; i <= maxDimensions; i++)
{
unsigned int numElements = i * i;
Matrix<float> m(std::vector<float>(numElements, 1.0f), i);
EXPECT_EQ(m.size(), i);
EXPECT_EQ(m.columns(), m.columns());
EXPECT_EQ(m.elements(), std::vector<float>(numElements, 1.0f));
}
}
TEST(MatrixUtilsTest, MatrixCompMultTest)
{
for (size_t i = minDimensions; i <= maxDimensions; i++)
{
unsigned int numElements = i * i;
Matrix<float> m1(std::vector<float>(numElements, 2.0f), i);
Matrix<float> actualResult = m1.compMult(m1);
std::vector<float> actualResultElements = actualResult.elements();
std::vector<float> expectedResultElements(numElements, 4.0f);
EXPECT_EQ(expectedResultElements, actualResultElements);
}
}
TEST(MatrixUtilsTest, MatrixOuterProductTest)
{
for (size_t i = minDimensions; i <= maxDimensions; i++)
{
for (size_t j = minDimensions; j <= maxDimensions; j++)
{
unsigned int numElements = i * j;
Matrix<float> m1(std::vector<float>(numElements, 2.0f), i, 1);
Matrix<float> m2(std::vector<float>(numElements, 2.0f), 1, j);
Matrix<float> actualResult = m1.outerProduct(m2);
EXPECT_EQ(actualResult.rows(), i);
EXPECT_EQ(actualResult.columns(), j);
std::vector<float> actualResultElements = actualResult.elements();
std::vector<float> expectedResultElements(numElements, 4.0f);
EXPECT_EQ(expectedResultElements, actualResultElements);
}
}
}
TEST(MatrixUtilsTest, MatrixTransposeTest)
{
for (size_t i = minDimensions; i <= maxDimensions; i++)
{
for (size_t j = minDimensions; j <= maxDimensions; j++)
{
unsigned int numElements = i * j;
Matrix<float> m1(std::vector<float>(numElements, 2.0f), i, j);
Matrix<float> expectedResult = Matrix<float>(std::vector<float>(numElements, 2.0f), j, i);
Matrix<float> actualResult = m1.transpose();
EXPECT_EQ(expectedResult.elements(), actualResult.elements());
EXPECT_EQ(actualResult.rows(), expectedResult.rows());
EXPECT_EQ(actualResult.columns(), expectedResult.columns());
// transpose(transpose(A)) = A
Matrix<float> m2 = actualResult.transpose();
EXPECT_EQ(m1.elements(), m2.elements());
}
}
}
TEST(MatrixUtilsTest, MatrixDeterminantTest)
{
for (size_t i = minDimensions; i <= maxDimensions; i++)
{
unsigned int numElements = i * i;
Matrix<float> m(std::vector<float>(numElements, 2.0f), i);
EXPECT_EQ(m.determinant(), 0.0f);
}
}
TEST(MatrixUtilsTest, 2x2MatrixInverseTest)
{
float inputElements[] =
{
2.0f, 5.0f,
3.0f, 7.0f
};
unsigned int numElements = 4;
std::vector<float> input(inputElements, inputElements + numElements);
Matrix<float> inputMatrix(input, 2);
float identityElements[] =
{
1.0f, 0.0f,
0.0f, 1.0f
};
std::vector<float> identityMatrix(identityElements, identityElements + numElements);
// A * inverse(A) = I, where I is identity matrix.
Matrix<float> result = inputMatrix * inputMatrix.inverse();
EXPECT_EQ(identityMatrix, result.elements());
}
TEST(MatrixUtilsTest, 3x3MatrixInverseTest)
{
float inputElements[] =
{
11.0f, 23.0f, 37.0f,
13.0f, 29.0f, 41.0f,
19.0f, 31.0f, 43.0f
};
unsigned int numElements = 9;
std::vector<float> input(inputElements, inputElements + numElements);
Matrix<float> inputMatrix(input, 3);
float identityElements[] =
{
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f
};
std::vector<float> identityMatrix(identityElements, identityElements + numElements);
// A * inverse(A) = I, where I is identity matrix.
Matrix<float> result = inputMatrix * inputMatrix.inverse();
std::vector<float> resultElements = result.elements();
const float floatFaultTolarance = 0.000001f;
for (size_t i = 0; i < numElements; i++)
EXPECT_NEAR(resultElements[i], identityMatrix[i], floatFaultTolarance);
}
TEST(MatrixUtilsTest, 4x4MatrixInverseTest)
{
float inputElements[] =
{
29.0f, 43.0f, 61.0f, 79.0f,
31.0f, 47.0f, 67.0f, 83.0f,
37.0f, 53.0f, 71.0f, 89.0f,
41.0f, 59.0f, 73.0f, 97.0f
};
unsigned int numElements = 16;
std::vector<float> input(inputElements, inputElements + numElements);
Matrix<float> inputMatrix(input, 4);
float identityElements[] =
{
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
};
std::vector<float> identityMatrix(identityElements, identityElements + numElements);
// A * inverse(A) = I, where I is identity matrix.
Matrix<float> result = inputMatrix * inputMatrix.inverse();
std::vector<float> resultElements = result.elements();
const float floatFaultTolarance = 0.00001f;
for (size_t i = 0; i < numElements; i++)
EXPECT_NEAR(resultElements[i], identityMatrix[i], floatFaultTolarance);
}
}
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <vector> #include <vector>
#include "common/mathutil.h" #include "common/mathutil.h"
#include "common/matrix_utils.h"
#include "compiler/translator/HashNames.h" #include "compiler/translator/HashNames.h"
#include "compiler/translator/IntermNode.h" #include "compiler/translator/IntermNode.h"
#include "compiler/translator/SymbolTable.h" #include "compiler/translator/SymbolTable.h"
...@@ -199,6 +200,36 @@ TIntermTyped *CreateFoldedNode(TConstantUnion *constArray, const TIntermTyped *o ...@@ -199,6 +200,36 @@ TIntermTyped *CreateFoldedNode(TConstantUnion *constArray, const TIntermTyped *o
return folded; return folded;
} }
angle::Matrix<float> GetMatrix(TConstantUnion *paramArray, const unsigned int &rows, const unsigned int &cols)
{
std::vector<float> elements;
for (size_t i = 0; i < rows * cols; i++)
elements.push_back(paramArray[i].getFConst());
// Transpose is used since the Matrix constructor expects arguments in row-major order,
// whereas the paramArray is in column-major order.
return angle::Matrix<float>(elements, rows, cols).transpose();
}
angle::Matrix<float> GetMatrix(TConstantUnion *paramArray, const unsigned int &size)
{
std::vector<float> elements;
for (size_t i = 0; i < size * size; i++)
elements.push_back(paramArray[i].getFConst());
// Transpose is used since the Matrix constructor expects arguments in row-major order,
// whereas the paramArray is in column-major order.
return angle::Matrix<float>(elements, size).transpose();
}
void SetUnionArrayFromMatrix(const angle::Matrix<float> &m, TConstantUnion *resultArray)
{
// Transpose is used since the input Matrix is in row-major order,
// whereas the actual result should be in column-major order.
angle::Matrix<float> result = m.transpose();
std::vector<float> resultElements = result.elements();
for (size_t i = 0; i < resultElements.size(); i++)
resultArray[i].setFConst(resultElements[i]);
}
} // namespace anonymous } // namespace anonymous
...@@ -1156,7 +1187,8 @@ TConstantUnion *TIntermConstantUnion::foldUnary(TOperator op, TInfoSink &infoSin ...@@ -1156,7 +1187,8 @@ TConstantUnion *TIntermConstantUnion::foldUnary(TOperator op, TInfoSink &infoSin
size_t objectSize = getType().getObjectSize(); size_t objectSize = getType().getObjectSize();
if (op == EOpAny || op == EOpAll || op == EOpLength) if (op == EOpAny || op == EOpAll || op == EOpLength || op == EOpTranspose || op == EOpDeterminant ||
op == EOpInverse)
{ {
// Do operations where the return type has a different number of components compared to the operand type. // Do operations where the return type has a different number of components compared to the operand type.
TConstantUnion *resultArray = nullptr; TConstantUnion *resultArray = nullptr;
...@@ -1218,6 +1250,52 @@ TConstantUnion *TIntermConstantUnion::foldUnary(TOperator op, TInfoSink &infoSin ...@@ -1218,6 +1250,52 @@ TConstantUnion *TIntermConstantUnion::foldUnary(TOperator op, TInfoSink &infoSin
return nullptr; return nullptr;
} }
case EOpTranspose:
if (getType().getBasicType() == EbtFloat)
{
resultArray = new TConstantUnion[objectSize];
angle::Matrix<float> result =
GetMatrix(operandArray, getType().getNominalSize(), getType().getSecondarySize()).transpose();
SetUnionArrayFromMatrix(result, resultArray);
break;
}
else
{
infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
return nullptr;
}
case EOpDeterminant:
if (getType().getBasicType() == EbtFloat)
{
unsigned int size = getType().getNominalSize();
ASSERT(size >= 2 && size <= 4);
resultArray = new TConstantUnion();
resultArray->setFConst(GetMatrix(operandArray, size).determinant());
break;
}
else
{
infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
return nullptr;
}
case EOpInverse:
if (getType().getBasicType() == EbtFloat)
{
unsigned int size = getType().getNominalSize();
ASSERT(size >= 2 && size <= 4);
resultArray = new TConstantUnion[objectSize];
angle::Matrix<float> result = GetMatrix(operandArray, size).inverse();
SetUnionArrayFromMatrix(result, resultArray);
break;
}
else
{
infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
return nullptr;
}
default: default:
break; break;
} }
...@@ -1630,9 +1708,12 @@ TConstantUnion *TIntermConstantUnion::FoldAggregateBuiltIn(TIntermAggregate *agg ...@@ -1630,9 +1708,12 @@ TConstantUnion *TIntermConstantUnion::FoldAggregateBuiltIn(TIntermAggregate *agg
maxObjectSize = objectSizes[i]; maxObjectSize = objectSizes[i];
} }
if (!(*sequence)[0]->getAsTyped()->isMatrix())
{
for (unsigned int i = 0; i < paramsCount; i++) for (unsigned int i = 0; i < paramsCount; i++)
if (objectSizes[i] != maxObjectSize) if (objectSizes[i] != maxObjectSize)
unionArrays[i] = Vectorize(*unionArrays[i], maxObjectSize); unionArrays[i] = Vectorize(*unionArrays[i], maxObjectSize);
}
TConstantUnion *resultArray = nullptr; TConstantUnion *resultArray = nullptr;
if (paramsCount == 2) if (paramsCount == 2)
...@@ -1980,6 +2061,35 @@ TConstantUnion *TIntermConstantUnion::FoldAggregateBuiltIn(TIntermAggregate *agg ...@@ -1980,6 +2061,35 @@ TConstantUnion *TIntermConstantUnion::FoldAggregateBuiltIn(TIntermAggregate *agg
UNREACHABLE(); UNREACHABLE();
break; break;
case EOpMul:
if (basicType == EbtFloat && (*sequence)[0]->getAsTyped()->isMatrix() &&
(*sequence)[1]->getAsTyped()->isMatrix())
{
// Perform component-wise matrix multiplication.
resultArray = new TConstantUnion[maxObjectSize];
size_t size = (*sequence)[0]->getAsTyped()->getNominalSize();
angle::Matrix<float> result =
GetMatrix(unionArrays[0], size).compMult(GetMatrix(unionArrays[1], size));
SetUnionArrayFromMatrix(result, resultArray);
}
else
UNREACHABLE();
break;
case EOpOuterProduct:
if (basicType == EbtFloat)
{
size_t numRows = (*sequence)[0]->getAsTyped()->getType().getObjectSize();
size_t numCols = (*sequence)[1]->getAsTyped()->getType().getObjectSize();
resultArray = new TConstantUnion[numRows * numCols];
angle::Matrix<float> result =
GetMatrix(unionArrays[0], 1, numCols).outerProduct(GetMatrix(unionArrays[1], numRows, 1));
SetUnionArrayFromMatrix(result, resultArray);
}
else
UNREACHABLE();
break;
default: default:
UNREACHABLE(); UNREACHABLE();
// TODO: Add constant folding support for other built-in operations that take 2 parameters and not handled above. // TODO: Add constant folding support for other built-in operations that take 2 parameters and not handled above.
......
...@@ -454,6 +454,8 @@ TIntermTyped *TIntermediate::foldAggregateBuiltIn(TIntermAggregate *aggregate) ...@@ -454,6 +454,8 @@ TIntermTyped *TIntermediate::foldAggregateBuiltIn(TIntermAggregate *aggregate)
case EOpMix: case EOpMix:
case EOpStep: case EOpStep:
case EOpSmoothStep: case EOpSmoothStep:
case EOpMul:
case EOpOuterProduct:
case EOpLessThan: case EOpLessThan:
case EOpLessThanEqual: case EOpLessThanEqual:
case EOpGreaterThan: case EOpGreaterThan:
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
'common/debug.h', 'common/debug.h',
'common/mathutil.cpp', 'common/mathutil.cpp',
'common/mathutil.h', 'common/mathutil.h',
'common/matrix_utils.h',
'common/platform.h', 'common/platform.h',
'common/string_utils.cpp', 'common/string_utils.cpp',
'common/string_utils.h', 'common/string_utils.h',
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
'angle_unittests_sources': 'angle_unittests_sources':
[ [
'<(angle_path)/src/common/Optional_unittest.cpp', '<(angle_path)/src/common/Optional_unittest.cpp',
'<(angle_path)/src/common/matrix_utils_unittest.cpp',
'<(angle_path)/src/common/string_utils_unittest.cpp', '<(angle_path)/src/common/string_utils_unittest.cpp',
'<(angle_path)/src/common/utilities_unittest.cpp', '<(angle_path)/src/common/utilities_unittest.cpp',
'<(angle_path)/src/libANGLE/Config_unittest.cpp', '<(angle_path)/src/libANGLE/Config_unittest.cpp',
......
...@@ -22,11 +22,20 @@ class ConstantFinder : public TIntermTraverser ...@@ -22,11 +22,20 @@ class ConstantFinder : public TIntermTraverser
ConstantFinder(const std::vector<T> &constantVector) ConstantFinder(const std::vector<T> &constantVector)
: TIntermTraverser(true, false, false), : TIntermTraverser(true, false, false),
mConstantVector(constantVector), 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) mFound(false)
{} {}
ConstantFinder(const T &value) ConstantFinder(const T &value)
: TIntermTraverser(true, false, false), : TIntermTraverser(true, false, false),
mFaultTolerance(T()),
mFound(false) mFound(false)
{ {
mConstantVector.push_back(value); mConstantVector.push_back(value);
...@@ -39,7 +48,7 @@ class ConstantFinder : public TIntermTraverser ...@@ -39,7 +48,7 @@ class ConstantFinder : public TIntermTraverser
bool found = true; bool found = true;
for (size_t i = 0; i < mConstantVector.size(); i++) for (size_t i = 0; i < mConstantVector.size(); i++)
{ {
if (node->getUnionArrayPointer()[i] != mConstantVector[i]) if (!isEqual(node->getUnionArrayPointer()[i], mConstantVector[i]))
{ {
found = false; found = false;
break; break;
...@@ -55,7 +64,28 @@ class ConstantFinder : public TIntermTraverser ...@@ -55,7 +64,28 @@ class ConstantFinder : public TIntermTraverser
bool found() const { return mFound; } bool found() const { return mFound; }
private: 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
{
return mFaultTolerance >= abs(node.getIConst() - value);
}
bool isEqual(const TConstantUnion &node, const unsigned int &value) const
{
return mFaultTolerance >= abs(static_cast<int>(node.getUConst() - value));
}
bool isEqual(const TConstantUnion &node, const bool &value) const
{
return node.getBConst() == value;
}
std::vector<T> mConstantVector; std::vector<T> mConstantVector;
T mFaultTolerance;
bool mFound; bool mFound;
}; };
...@@ -111,6 +141,14 @@ class ConstantFoldingTest : public testing::Test ...@@ -111,6 +141,14 @@ class ConstantFoldingTest : public testing::Test
return finder.found(); return finder.found();
} }
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: private:
TranslatorESSL *mTranslatorESSL; TranslatorESSL *mTranslatorESSL;
TIntermNode *mASTRoot; TIntermNode *mASTRoot;
...@@ -223,3 +261,216 @@ TEST_F(ConstantFoldingTest, FoldVectorCrossProduct) ...@@ -223,3 +261,216 @@ TEST_F(ConstantFoldingTest, FoldVectorCrossProduct)
result.push_back(-2.0f); result.push_back(-2.0f);
ASSERT_TRUE(constantVectorFoundInAST(result)); 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"
"out mat2 my_Matrix;"
"void main() {\n"
" const mat2 m2 = inverse(mat2(2.0f, 3.0f,\n"
" 5.0f, 7.0f));\n"
" my_Matrix = m2;\n"
"}\n";
compile(shaderString);
float inputElements[] =
{
2.0f, 3.0f,
5.0f, 7.0f
};
std::vector<float> input(inputElements, inputElements + 4);
ASSERT_FALSE(constantVectorFoundInAST(input));
float outputElements[] =
{
-7.0f, 3.0f,
5.0f, -2.0f
};
std::vector<float> result(outputElements, outputElements + 4);
ASSERT_TRUE(constantVectorFoundInAST(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"
"out mat3 my_Matrix;"
"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"
" my_Matrix = m3;\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"
"out mat4 my_Matrix;"
"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"
" my_Matrix = m4;\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(constantVectorFoundInAST(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(constantVectorFoundInAST(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(constantVectorFoundInAST(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"
"out mat3 my_Matrix;"
"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"
" my_Matrix = m3;\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[] =
{
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(constantVectorFoundInAST(result));
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment