| /*------------------------------------------------------------------------- |
| * drawElements Quality Program OpenGL (ES) Module |
| * ----------------------------------------------- |
| * |
| * Copyright 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| *//*! |
| * \file |
| * \brief Precision and range tests for GLSL builtins and types. |
| * |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "glsBuiltinPrecisionTests.hpp" |
| |
| #include "deMath.h" |
| #include "deMemory.h" |
| #include "deDefs.hpp" |
| #include "deRandom.hpp" |
| #include "deSTLUtil.hpp" |
| #include "deStringUtil.hpp" |
| #include "deUniquePtr.hpp" |
| #include "deSharedPtr.hpp" |
| #include "deArrayUtil.hpp" |
| |
| #include "tcuCommandLine.hpp" |
| #include "tcuFloatFormat.hpp" |
| #include "tcuInterval.hpp" |
| #include "tcuTestCase.hpp" |
| #include "tcuTestLog.hpp" |
| #include "tcuVector.hpp" |
| #include "tcuMatrix.hpp" |
| #include "tcuResultCollector.hpp" |
| |
| #include "gluContextInfo.hpp" |
| #include "gluVarType.hpp" |
| #include "gluRenderContext.hpp" |
| #include "glwDefs.hpp" |
| |
| #include "glsShaderExecUtil.hpp" |
| |
| #include <cmath> |
| #include <string> |
| #include <sstream> |
| #include <iostream> |
| #include <map> |
| #include <utility> |
| #include <limits> |
| |
| // Uncomment this to get evaluation trace dumps to std::cerr |
| // #define GLS_ENABLE_TRACE |
| |
| // set this to true to dump even passing results |
| #define GLS_LOG_ALL_RESULTS false |
| |
| enum |
| { |
| // Computing reference intervals can take a non-trivial amount of time, especially on |
| // platforms where toggling floating-point rounding mode is slow (emulated arm on x86). |
| // As a workaround watchdog is kept happy by touching it periodically during reference |
| // interval computation. |
| TOUCH_WATCHDOG_VALUE_FREQUENCY = 4096 |
| }; |
| |
| namespace deqp |
| { |
| namespace gls |
| { |
| namespace BuiltinPrecisionTests |
| { |
| |
| using std::map; |
| using std::ostream; |
| using std::ostringstream; |
| using std::pair; |
| using std::set; |
| using std::string; |
| using std::vector; |
| |
| using de::MovePtr; |
| using de::Random; |
| using de::SharedPtr; |
| using de::UniquePtr; |
| using tcu::FloatFormat; |
| using tcu::Interval; |
| using tcu::Matrix; |
| using tcu::MessageBuilder; |
| using tcu::TestCase; |
| using tcu::TestLog; |
| using tcu::Vector; |
| namespace matrix = tcu::matrix; |
| using gls::ShaderExecUtil::Symbol; |
| using glu::ContextInfo; |
| using glu::DataType; |
| using glu::Precision; |
| using glu::RenderContext; |
| using glu::ShaderType; |
| using glu::VarType; |
| |
| typedef TestCase::IterateResult IterateResult; |
| |
| using namespace glw; |
| using namespace tcu; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Generic singleton creator. |
| * |
| * instance<T>() returns a reference to a unique default-constructed instance |
| * of T. This is mainly used for our GLSL function implementations: each |
| * function is implemented by an object, and each of the objects has a |
| * distinct class. It would be extremely toilsome to maintain a separate |
| * context object that contained individual instances of the function classes, |
| * so we have to resort to global singleton instances. |
| * |
| *//*--------------------------------------------------------------------*/ |
| template <typename T> |
| const T &instance(void) |
| { |
| static const T s_instance = T(); |
| return s_instance; |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief A placeholder type for unused template parameters. |
| * |
| * In the precision tests we are dealing with functions of different arities. |
| * To minimize code duplication, we only define templates with the maximum |
| * number of arguments, currently four. If a function's arity is less than the |
| * maximum, Void us used as the type for unused arguments. |
| * |
| * Although Voids are not used at run-time, they still must be compilable, so |
| * they must support all operations that other types do. |
| * |
| *//*--------------------------------------------------------------------*/ |
| struct Void |
| { |
| typedef Void Element; |
| enum |
| { |
| SIZE = 0, |
| }; |
| |
| template <typename T> |
| explicit Void(const T &) |
| { |
| } |
| Void(void) |
| { |
| } |
| operator double(void) const |
| { |
| return TCU_NAN; |
| } |
| |
| // These are used to make Voids usable as containers in container-generic code. |
| Void &operator[](int) |
| { |
| return *this; |
| } |
| const Void &operator[](int) const |
| { |
| return *this; |
| } |
| }; |
| |
| ostream &operator<<(ostream &os, Void) |
| { |
| return os << "()"; |
| } |
| |
| //! Returns true for all other types except Void |
| template <typename T> |
| bool isTypeValid(void) |
| { |
| return true; |
| } |
| template <> |
| bool isTypeValid<Void>(void) |
| { |
| return false; |
| } |
| |
| //! Utility function for getting the name of a data type. |
| //! This is used in vector and matrix constructors. |
| template <typename T> |
| const char *dataTypeNameOf(void) |
| { |
| return glu::getDataTypeName(glu::dataTypeOf<T>()); |
| } |
| |
| template <> |
| const char *dataTypeNameOf<Void>(void) |
| { |
| DE_FATAL("Impossible"); |
| return DE_NULL; |
| } |
| |
| //! A hack to get Void support for VarType. |
| template <typename T> |
| VarType getVarTypeOf(Precision prec = glu::PRECISION_LAST) |
| { |
| return glu::varTypeOf<T>(prec); |
| } |
| |
| template <> |
| VarType getVarTypeOf<Void>(Precision) |
| { |
| DE_FATAL("Impossible"); |
| return VarType(); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Type traits for generalized interval types. |
| * |
| * We are trying to compute sets of acceptable values not only for |
| * float-valued expressions but also for compound values: vectors and |
| * matrices. We approximate a set of vectors as a vector of intervals and |
| * likewise for matrices. |
| * |
| * We now need generalized operations for each type and its interval |
| * approximation. These are given in the type Traits<T>. |
| * |
| * The type Traits<T>::IVal is the approximation of T: it is `Interval` for |
| * scalar types, and a vector or matrix of intervals for container types. |
| * |
| * To allow template inference to take place, there are function wrappers for |
| * the actual operations in Traits<T>. Hence we can just use: |
| * |
| * makeIVal(someFloat) |
| * |
| * instead of: |
| * |
| * Traits<float>::doMakeIVal(value) |
| * |
| *//*--------------------------------------------------------------------*/ |
| |
| template <typename T> |
| struct Traits; |
| |
| //! Create container from elementwise singleton values. |
| template <typename T> |
| typename Traits<T>::IVal makeIVal(const T &value) |
| { |
| return Traits<T>::doMakeIVal(value); |
| } |
| |
| //! Elementwise union of intervals. |
| template <typename T> |
| typename Traits<T>::IVal unionIVal(const typename Traits<T>::IVal &a, const typename Traits<T>::IVal &b) |
| { |
| return Traits<T>::doUnion(a, b); |
| } |
| |
| //! Returns true iff every element of `ival` contains the corresponding element of `value`. |
| template <typename T> |
| bool contains(const typename Traits<T>::IVal &ival, const T &value) |
| { |
| return Traits<T>::doContains(ival, value); |
| } |
| |
| //! Returns true iff every element of `ival` contains corresponding element of `value` within the warning interval |
| template <typename T> |
| bool containsWarning(const typename Traits<T>::IVal &ival, const T &value) |
| { |
| return Traits<T>::doContainsWarning(ival, value); |
| } |
| |
| //! Print out an interval with the precision of `fmt`. |
| template <typename T> |
| void printIVal(const FloatFormat &fmt, const typename Traits<T>::IVal &ival, ostream &os) |
| { |
| Traits<T>::doPrintIVal(fmt, ival, os); |
| } |
| |
| template <typename T> |
| string intervalToString(const FloatFormat &fmt, const typename Traits<T>::IVal &ival) |
| { |
| ostringstream oss; |
| printIVal<T>(fmt, ival, oss); |
| return oss.str(); |
| } |
| |
| //! Print out a value with the precision of `fmt`. |
| template <typename T> |
| void printValue(const FloatFormat &fmt, const T &value, ostream &os) |
| { |
| Traits<T>::doPrintValue(fmt, value, os); |
| } |
| |
| template <typename T> |
| string valueToString(const FloatFormat &fmt, const T &val) |
| { |
| ostringstream oss; |
| printValue(fmt, val, oss); |
| return oss.str(); |
| } |
| |
| //! Approximate `value` elementwise to the float precision defined in `fmt`. |
| //! The resulting interval might not be a singleton if rounding in both |
| //! directions is allowed. |
| template <typename T> |
| typename Traits<T>::IVal round(const FloatFormat &fmt, const T &value) |
| { |
| return Traits<T>::doRound(fmt, value); |
| } |
| |
| template <typename T> |
| typename Traits<T>::IVal convert(const FloatFormat &fmt, const typename Traits<T>::IVal &value) |
| { |
| return Traits<T>::doConvert(fmt, value); |
| } |
| |
| //! Common traits for scalar types. |
| template <typename T> |
| struct ScalarTraits |
| { |
| typedef Interval IVal; |
| |
| static Interval doMakeIVal(const T &value) |
| { |
| // Thankfully all scalar types have a well-defined conversion to `double`, |
| // hence Interval can represent their ranges without problems. |
| return Interval(double(value)); |
| } |
| |
| static Interval doUnion(const Interval &a, const Interval &b) |
| { |
| return a | b; |
| } |
| |
| static bool doContains(const Interval &a, T value) |
| { |
| return a.contains(double(value)); |
| } |
| |
| static bool doContainsWarning(const Interval &a, T value) |
| { |
| return a.containsWarning(double(value)); |
| } |
| |
| static Interval doConvert(const FloatFormat &fmt, const IVal &ival) |
| { |
| return fmt.convert(ival); |
| } |
| |
| static Interval doRound(const FloatFormat &fmt, T value) |
| { |
| return fmt.roundOut(double(value), false); |
| } |
| }; |
| |
| template <> |
| struct Traits<float> : ScalarTraits<float> |
| { |
| static void doPrintIVal(const FloatFormat &fmt, const Interval &ival, ostream &os) |
| { |
| os << fmt.intervalToHex(ival); |
| } |
| |
| static void doPrintValue(const FloatFormat &fmt, const float &value, ostream &os) |
| { |
| os << fmt.floatToHex(value); |
| } |
| }; |
| |
| template <> |
| struct Traits<bool> : ScalarTraits<bool> |
| { |
| static void doPrintValue(const FloatFormat &, const float &value, ostream &os) |
| { |
| os << (value != 0.0f ? "true" : "false"); |
| } |
| |
| static void doPrintIVal(const FloatFormat &, const Interval &ival, ostream &os) |
| { |
| os << "{"; |
| if (ival.contains(false)) |
| os << "false"; |
| if (ival.contains(false) && ival.contains(true)) |
| os << ", "; |
| if (ival.contains(true)) |
| os << "true"; |
| os << "}"; |
| } |
| }; |
| |
| template <> |
| struct Traits<int> : ScalarTraits<int> |
| { |
| static void doPrintValue(const FloatFormat &, const int &value, ostream &os) |
| { |
| os << value; |
| } |
| |
| static void doPrintIVal(const FloatFormat &, const Interval &ival, ostream &os) |
| { |
| os << "[" << int(ival.lo()) << ", " << int(ival.hi()) << "]"; |
| } |
| }; |
| |
| //! Common traits for containers, i.e. vectors and matrices. |
| //! T is the container type itself, I is the same type with interval elements. |
| template <typename T, typename I> |
| struct ContainerTraits |
| { |
| typedef typename T::Element Element; |
| typedef I IVal; |
| |
| static IVal doMakeIVal(const T &value) |
| { |
| IVal ret; |
| |
| for (int ndx = 0; ndx < T::SIZE; ++ndx) |
| ret[ndx] = makeIVal(value[ndx]); |
| |
| return ret; |
| } |
| |
| static IVal doUnion(const IVal &a, const IVal &b) |
| { |
| IVal ret; |
| |
| for (int ndx = 0; ndx < T::SIZE; ++ndx) |
| ret[ndx] = unionIVal<Element>(a[ndx], b[ndx]); |
| |
| return ret; |
| } |
| |
| static bool doContains(const IVal &ival, const T &value) |
| { |
| for (int ndx = 0; ndx < T::SIZE; ++ndx) |
| if (!contains(ival[ndx], value[ndx])) |
| return false; |
| |
| return true; |
| } |
| |
| static bool doContainsWarning(const IVal &ival, const T &value) |
| { |
| for (int ndx = 0; ndx < T::SIZE; ++ndx) |
| if (!containsWarning(ival[ndx], value[ndx])) |
| return false; |
| |
| return true; |
| } |
| |
| static void doPrintIVal(const FloatFormat &fmt, const IVal ival, ostream &os) |
| { |
| os << "("; |
| |
| for (int ndx = 0; ndx < T::SIZE; ++ndx) |
| { |
| if (ndx > 0) |
| os << ", "; |
| |
| printIVal<Element>(fmt, ival[ndx], os); |
| } |
| |
| os << ")"; |
| } |
| |
| static void doPrintValue(const FloatFormat &fmt, const T &value, ostream &os) |
| { |
| os << dataTypeNameOf<T>() << "("; |
| |
| for (int ndx = 0; ndx < T::SIZE; ++ndx) |
| { |
| if (ndx > 0) |
| os << ", "; |
| |
| printValue<Element>(fmt, value[ndx], os); |
| } |
| |
| os << ")"; |
| } |
| |
| static IVal doConvert(const FloatFormat &fmt, const IVal &value) |
| { |
| IVal ret; |
| |
| for (int ndx = 0; ndx < T::SIZE; ++ndx) |
| ret[ndx] = convert<Element>(fmt, value[ndx]); |
| |
| return ret; |
| } |
| |
| static IVal doRound(const FloatFormat &fmt, T value) |
| { |
| IVal ret; |
| |
| for (int ndx = 0; ndx < T::SIZE; ++ndx) |
| ret[ndx] = round(fmt, value[ndx]); |
| |
| return ret; |
| } |
| }; |
| |
| template <typename T, int Size> |
| struct Traits<Vector<T, Size>> : ContainerTraits<Vector<T, Size>, Vector<typename Traits<T>::IVal, Size>> |
| { |
| }; |
| |
| template <typename T, int Rows, int Cols> |
| struct Traits<Matrix<T, Rows, Cols>> |
| : ContainerTraits<Matrix<T, Rows, Cols>, Matrix<typename Traits<T>::IVal, Rows, Cols>> |
| { |
| }; |
| |
| //! Void traits. These are just dummies, but technically valid: a Void is a |
| //! unit type with a single possible value. |
| template <> |
| struct Traits<Void> |
| { |
| typedef Void IVal; |
| |
| static Void doMakeIVal(const Void &value) |
| { |
| return value; |
| } |
| static Void doUnion(const Void &, const Void &) |
| { |
| return Void(); |
| } |
| static bool doContains(const Void &, Void) |
| { |
| return true; |
| } |
| static bool doContainsWarning(const Void &, Void) |
| { |
| return true; |
| } |
| static Void doRound(const FloatFormat &, const Void &value) |
| { |
| return value; |
| } |
| static Void doConvert(const FloatFormat &, const Void &value) |
| { |
| return value; |
| } |
| |
| static void doPrintValue(const FloatFormat &, const Void &, ostream &os) |
| { |
| os << "()"; |
| } |
| |
| static void doPrintIVal(const FloatFormat &, const Void &, ostream &os) |
| { |
| os << "()"; |
| } |
| }; |
| |
| //! This is needed for container-generic operations. |
| //! We want a scalar type T to be its own "one-element vector". |
| template <typename T, int Size> |
| struct ContainerOf |
| { |
| typedef Vector<T, Size> Container; |
| }; |
| |
| template <typename T> |
| struct ContainerOf<T, 1> |
| { |
| typedef T Container; |
| }; |
| template <int Size> |
| struct ContainerOf<Void, Size> |
| { |
| typedef Void Container; |
| }; |
| |
| // This is a kludge that is only needed to get the ExprP::operator[] syntactic sugar to work. |
| template <typename T> |
| struct ElementOf |
| { |
| typedef typename T::Element Element; |
| }; |
| template <> |
| struct ElementOf<float> |
| { |
| typedef void Element; |
| }; |
| template <> |
| struct ElementOf<bool> |
| { |
| typedef void Element; |
| }; |
| template <> |
| struct ElementOf<int> |
| { |
| typedef void Element; |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * |
| * \name Abstract syntax for expressions and statements. |
| * |
| * We represent GLSL programs as syntax objects: an Expr<T> represents an |
| * expression whose GLSL type corresponds to the C++ type T, and a Statement |
| * represents a statement. |
| * |
| * To ease memory management, we use shared pointers to refer to expressions |
| * and statements. ExprP<T> is a shared pointer to an Expr<T>, and StatementP |
| * is a shared pointer to a Statement. |
| * |
| * \{ |
| * |
| *//*--------------------------------------------------------------------*/ |
| |
| class ExprBase; |
| class ExpandContext; |
| class Statement; |
| class StatementP; |
| class FuncBase; |
| template <typename T> |
| class ExprP; |
| template <typename T> |
| class Variable; |
| template <typename T> |
| class VariableP; |
| template <typename T> |
| class DefaultSampling; |
| |
| typedef set<const FuncBase *> FuncSet; |
| |
| template <typename T> |
| VariableP<T> variable(const string &name); |
| StatementP compoundStatement(const vector<StatementP> &statements); |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief A variable environment. |
| * |
| * An Environment object maintains the mapping between variables of the |
| * abstract syntax tree and their values. |
| * |
| * \todo [2014-03-28 lauri] At least run-time type safety. |
| * |
| *//*--------------------------------------------------------------------*/ |
| class Environment |
| { |
| public: |
| template <typename T> |
| void bind(const Variable<T> &variable, const typename Traits<T>::IVal &value) |
| { |
| uint8_t *const data = new uint8_t[sizeof(value)]; |
| |
| deMemcpy(data, &value, sizeof(value)); |
| de::insert(m_map, variable.getName(), SharedPtr<uint8_t>(data, de::ArrayDeleter<uint8_t>())); |
| } |
| |
| template <typename T> |
| typename Traits<T>::IVal &lookup(const Variable<T> &variable) const |
| { |
| uint8_t *const data = de::lookup(m_map, variable.getName()).get(); |
| |
| return *reinterpret_cast<typename Traits<T>::IVal *>(data); |
| } |
| |
| private: |
| map<string, SharedPtr<uint8_t>> m_map; |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Evaluation context. |
| * |
| * The evaluation context contains everything that separates one execution of |
| * an expression from the next. Currently this means the desired floating |
| * point precision and the current variable environment. |
| * |
| *//*--------------------------------------------------------------------*/ |
| struct EvalContext |
| { |
| EvalContext(const FloatFormat &format_, Precision floatPrecision_, Environment &env_, int callDepth_ = 0) |
| : format(format_) |
| , floatPrecision(floatPrecision_) |
| , env(env_) |
| , callDepth(callDepth_) |
| { |
| } |
| |
| FloatFormat format; |
| Precision floatPrecision; |
| Environment &env; |
| int callDepth; |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Simple incremental counter. |
| * |
| * This is used to make sure that different ExpandContexts will not produce |
| * overlapping temporary names. |
| * |
| *//*--------------------------------------------------------------------*/ |
| class Counter |
| { |
| public: |
| Counter(int count = 0) : m_count(count) |
| { |
| } |
| int operator()(void) |
| { |
| return m_count++; |
| } |
| |
| private: |
| int m_count; |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief A statement or declaration. |
| * |
| * Statements have no values. Instead, they are executed for their side |
| * effects only: the execute() method should modify at least one variable in |
| * the environment. |
| * |
| * As a bit of a kludge, a Statement object can also represent a declaration: |
| * when it is evaluated, it can add a variable binding to the environment |
| * instead of modifying a current one. |
| * |
| *//*--------------------------------------------------------------------*/ |
| class Statement |
| { |
| public: |
| virtual ~Statement(void) |
| { |
| } |
| //! Execute the statement, modifying the environment of `ctx` |
| void execute(EvalContext &ctx) const |
| { |
| this->doExecute(ctx); |
| } |
| void print(ostream &os) const |
| { |
| this->doPrint(os); |
| } |
| //! Add the functions used in this statement to `dst`. |
| void getUsedFuncs(FuncSet &dst) const |
| { |
| this->doGetUsedFuncs(dst); |
| } |
| |
| protected: |
| virtual void doPrint(ostream &os) const = 0; |
| virtual void doExecute(EvalContext &ctx) const = 0; |
| virtual void doGetUsedFuncs(FuncSet &dst) const = 0; |
| }; |
| |
| ostream &operator<<(ostream &os, const Statement &stmt) |
| { |
| stmt.print(os); |
| return os; |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Smart pointer for statements (and declarations) |
| * |
| *//*--------------------------------------------------------------------*/ |
| class StatementP : public SharedPtr<const Statement> |
| { |
| public: |
| typedef SharedPtr<const Statement> Super; |
| |
| StatementP(void) |
| { |
| } |
| explicit StatementP(const Statement *ptr) : Super(ptr) |
| { |
| } |
| StatementP(const Super &ptr) : Super(ptr) |
| { |
| } |
| }; |
| |
| class ExpandContext |
| { |
| public: |
| ExpandContext(Counter &symCounter) : m_symCounter(symCounter) |
| { |
| } |
| ExpandContext(const ExpandContext &parent) : m_symCounter(parent.m_symCounter) |
| { |
| } |
| |
| template <typename T> |
| VariableP<T> genSym(const string &baseName) |
| { |
| return variable<T>(baseName + de::toString(m_symCounter())); |
| } |
| |
| void addStatement(const StatementP &stmt) |
| { |
| m_statements.push_back(stmt); |
| } |
| |
| vector<StatementP> getStatements(void) const |
| { |
| return m_statements; |
| } |
| |
| private: |
| Counter &m_symCounter; |
| vector<StatementP> m_statements; |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief |
| * |
| * A statement that modifies a variable or a declaration that binds a variable. |
| * |
| *//*--------------------------------------------------------------------*/ |
| template <typename T> |
| class VariableStatement : public Statement |
| { |
| public: |
| VariableStatement(const VariableP<T> &variable, const ExprP<T> &value, bool isDeclaration) |
| : m_variable(variable) |
| , m_value(value) |
| , m_isDeclaration(isDeclaration) |
| { |
| } |
| |
| protected: |
| void doPrint(ostream &os) const |
| { |
| if (m_isDeclaration) |
| os << glu::declare(getVarTypeOf<T>(), m_variable->getName()); |
| else |
| os << m_variable->getName(); |
| |
| os << " = " << *m_value << ";\n"; |
| } |
| |
| void doExecute(EvalContext &ctx) const |
| { |
| if (m_isDeclaration) |
| ctx.env.bind(*m_variable, m_value->evaluate(ctx)); |
| else |
| ctx.env.lookup(*m_variable) = m_value->evaluate(ctx); |
| } |
| |
| void doGetUsedFuncs(FuncSet &dst) const |
| { |
| m_value->getUsedFuncs(dst); |
| } |
| |
| VariableP<T> m_variable; |
| ExprP<T> m_value; |
| bool m_isDeclaration; |
| }; |
| |
| template <typename T> |
| StatementP variableStatement(const VariableP<T> &variable, const ExprP<T> &value, bool isDeclaration) |
| { |
| return StatementP(new VariableStatement<T>(variable, value, isDeclaration)); |
| } |
| |
| template <typename T> |
| StatementP variableDeclaration(const VariableP<T> &variable, const ExprP<T> &definiens) |
| { |
| return variableStatement(variable, definiens, true); |
| } |
| |
| template <typename T> |
| StatementP variableAssignment(const VariableP<T> &variable, const ExprP<T> &value) |
| { |
| return variableStatement(variable, value, false); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief A compound statement, i.e. a block. |
| * |
| * A compound statement is executed by executing its constituent statements in |
| * sequence. |
| * |
| *//*--------------------------------------------------------------------*/ |
| class CompoundStatement : public Statement |
| { |
| public: |
| CompoundStatement(const vector<StatementP> &statements) : m_statements(statements) |
| { |
| } |
| |
| protected: |
| void doPrint(ostream &os) const |
| { |
| os << "{\n"; |
| |
| for (size_t ndx = 0; ndx < m_statements.size(); ++ndx) |
| os << *m_statements[ndx]; |
| |
| os << "}\n"; |
| } |
| |
| void doExecute(EvalContext &ctx) const |
| { |
| for (size_t ndx = 0; ndx < m_statements.size(); ++ndx) |
| m_statements[ndx]->execute(ctx); |
| } |
| |
| void doGetUsedFuncs(FuncSet &dst) const |
| { |
| for (size_t ndx = 0; ndx < m_statements.size(); ++ndx) |
| m_statements[ndx]->getUsedFuncs(dst); |
| } |
| |
| vector<StatementP> m_statements; |
| }; |
| |
| StatementP compoundStatement(const vector<StatementP> &statements) |
| { |
| return StatementP(new CompoundStatement(statements)); |
| } |
| |
| //! Common base class for all expressions regardless of their type. |
| class ExprBase |
| { |
| public: |
| virtual ~ExprBase(void) |
| { |
| } |
| void printExpr(ostream &os) const |
| { |
| this->doPrintExpr(os); |
| } |
| |
| //! Output the functions that this expression refers to |
| void getUsedFuncs(FuncSet &dst) const |
| { |
| this->doGetUsedFuncs(dst); |
| } |
| |
| protected: |
| virtual void doPrintExpr(ostream &) const |
| { |
| } |
| virtual void doGetUsedFuncs(FuncSet &) const |
| { |
| } |
| }; |
| |
| //! Type-specific operations for an expression representing type T. |
| template <typename T> |
| class Expr : public ExprBase |
| { |
| public: |
| typedef T Val; |
| typedef typename Traits<T>::IVal IVal; |
| |
| IVal evaluate(const EvalContext &ctx) const; |
| |
| protected: |
| virtual IVal doEvaluate(const EvalContext &ctx) const = 0; |
| }; |
| |
| //! Evaluate an expression with the given context, optionally tracing the calls to stderr. |
| template <typename T> |
| typename Traits<T>::IVal Expr<T>::evaluate(const EvalContext &ctx) const |
| { |
| #ifdef GLS_ENABLE_TRACE |
| static const FloatFormat highpFmt(-126, 127, 23, true, tcu::MAYBE, tcu::YES, tcu::MAYBE); |
| EvalContext newCtx(ctx.format, ctx.floatPrecision, ctx.env, ctx.callDepth + 1); |
| const IVal ret = this->doEvaluate(newCtx); |
| |
| if (isTypeValid<T>()) |
| { |
| std::cerr << string(ctx.callDepth, ' '); |
| this->printExpr(std::cerr); |
| std::cerr << " -> " << intervalToString<T>(highpFmt, ret) << std::endl; |
| } |
| return ret; |
| #else |
| return this->doEvaluate(ctx); |
| #endif |
| } |
| |
| template <typename T> |
| class ExprPBase : public SharedPtr<const Expr<T>> |
| { |
| public: |
| }; |
| |
| ostream &operator<<(ostream &os, const ExprBase &expr) |
| { |
| expr.printExpr(os); |
| return os; |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Shared pointer to an expression of a container type. |
| * |
| * Container types (i.e. vectors and matrices) support the subscription |
| * operator. This class provides a bit of syntactic sugar to allow us to use |
| * the C++ subscription operator to create a subscription expression. |
| *//*--------------------------------------------------------------------*/ |
| template <typename T> |
| class ContainerExprPBase : public ExprPBase<T> |
| { |
| public: |
| ExprP<typename T::Element> operator[](int i) const; |
| }; |
| |
| template <typename T> |
| class ExprP : public ExprPBase<T> |
| { |
| }; |
| |
| // We treat Voids as containers since the unused parameters in generalized |
| // vector functions are represented as Voids. |
| template <> |
| class ExprP<Void> : public ContainerExprPBase<Void> |
| { |
| }; |
| |
| template <typename T, int Size> |
| class ExprP<Vector<T, Size>> : public ContainerExprPBase<Vector<T, Size>> |
| { |
| }; |
| |
| template <typename T, int Rows, int Cols> |
| class ExprP<Matrix<T, Rows, Cols>> : public ContainerExprPBase<Matrix<T, Rows, Cols>> |
| { |
| }; |
| |
| template <typename T> |
| ExprP<T> exprP(void) |
| { |
| return ExprP<T>(); |
| } |
| |
| template <typename T> |
| ExprP<T> exprP(const SharedPtr<const Expr<T>> &ptr) |
| { |
| ExprP<T> ret; |
| static_cast<SharedPtr<const Expr<T>> &>(ret) = ptr; |
| return ret; |
| } |
| |
| template <typename T> |
| ExprP<T> exprP(const Expr<T> *ptr) |
| { |
| return exprP(SharedPtr<const Expr<T>>(ptr)); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief A shared pointer to a variable expression. |
| * |
| * This is just a narrowing of ExprP for the operations that require a variable |
| * instead of an arbitrary expression. |
| * |
| *//*--------------------------------------------------------------------*/ |
| template <typename T> |
| class VariableP : public SharedPtr<const Variable<T>> |
| { |
| public: |
| typedef SharedPtr<const Variable<T>> Super; |
| explicit VariableP(const Variable<T> *ptr) : Super(ptr) |
| { |
| } |
| VariableP(void) |
| { |
| } |
| VariableP(const Super &ptr) : Super(ptr) |
| { |
| } |
| |
| operator ExprP<T>(void) const |
| { |
| SharedPtr<const Expr<T>> ptr = *this; |
| return exprP(ptr); |
| } |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \name Syntactic sugar operators for expressions. |
| * |
| * @{ |
| * |
| * These operators allow the use of C++ syntax to construct GLSL expressions |
| * containing operators: e.g. "a+b" creates an addition expression with |
| * operands a and b, and so on. |
| * |
| *//*--------------------------------------------------------------------*/ |
| ExprP<float> operator-(const ExprP<float> &arg0); |
| ExprP<float> operator+(const ExprP<float> &arg0, const ExprP<float> &arg1); |
| ExprP<float> operator-(const ExprP<float> &arg0, const ExprP<float> &arg1); |
| ExprP<float> operator*(const ExprP<float> &arg0, const ExprP<float> &arg1); |
| ExprP<float> operator/(const ExprP<float> &arg0, const ExprP<float> &arg1); |
| template <int Size> |
| ExprP<Vector<float, Size>> operator-(const ExprP<Vector<float, Size>> &arg0); |
| template <int Size> |
| ExprP<Vector<float, Size>> operator*(const ExprP<Vector<float, Size>> &arg0, const ExprP<float> &arg1); |
| template <int Size> |
| ExprP<Vector<float, Size>> operator*(const ExprP<Vector<float, Size>> &arg0, const ExprP<Vector<float, Size>> &arg1); |
| template <int Size> |
| ExprP<Vector<float, Size>> operator-(const ExprP<Vector<float, Size>> &arg0, const ExprP<Vector<float, Size>> &arg1); |
| template <int Left, int Mid, int Right> |
| ExprP<Matrix<float, Left, Right>> operator*(const ExprP<Matrix<float, Left, Mid>> &left, |
| const ExprP<Matrix<float, Mid, Right>> &right); |
| template <int Rows, int Cols> |
| ExprP<Vector<float, Rows>> operator*(const ExprP<Vector<float, Cols>> &left, |
| const ExprP<Matrix<float, Rows, Cols>> &right); |
| template <int Rows, int Cols> |
| ExprP<Vector<float, Cols>> operator*(const ExprP<Matrix<float, Rows, Cols>> &left, |
| const ExprP<Vector<float, Rows>> &right); |
| template <int Rows, int Cols> |
| ExprP<Matrix<float, Rows, Cols>> operator*(const ExprP<Matrix<float, Rows, Cols>> &left, const ExprP<float> &right); |
| template <int Rows, int Cols> |
| ExprP<Matrix<float, Rows, Cols>> operator+(const ExprP<Matrix<float, Rows, Cols>> &left, |
| const ExprP<Matrix<float, Rows, Cols>> &right); |
| template <int Rows, int Cols> |
| ExprP<Matrix<float, Rows, Cols>> operator-(const ExprP<Matrix<float, Rows, Cols>> &mat); |
| |
| //! @} |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Variable expression. |
| * |
| * A variable is evaluated by looking up its range of possible values from an |
| * environment. |
| *//*--------------------------------------------------------------------*/ |
| template <typename T> |
| class Variable : public Expr<T> |
| { |
| public: |
| typedef typename Expr<T>::IVal IVal; |
| |
| Variable(const string &name) : m_name(name) |
| { |
| } |
| string getName(void) const |
| { |
| return m_name; |
| } |
| |
| protected: |
| void doPrintExpr(ostream &os) const |
| { |
| os << m_name; |
| } |
| IVal doEvaluate(const EvalContext &ctx) const |
| { |
| return ctx.env.lookup<T>(*this); |
| } |
| |
| private: |
| string m_name; |
| }; |
| |
| template <typename T> |
| VariableP<T> variable(const string &name) |
| { |
| return VariableP<T>(new Variable<T>(name)); |
| } |
| |
| template <typename T> |
| VariableP<T> bindExpression(const string &name, ExpandContext &ctx, const ExprP<T> &expr) |
| { |
| VariableP<T> var = ctx.genSym<T>(name); |
| ctx.addStatement(variableDeclaration(var, expr)); |
| return var; |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Constant expression. |
| * |
| * A constant is evaluated by rounding it to a set of possible values allowed |
| * by the current floating point precision. |
| *//*--------------------------------------------------------------------*/ |
| template <typename T> |
| class Constant : public Expr<T> |
| { |
| public: |
| typedef typename Expr<T>::IVal IVal; |
| |
| Constant(const T &value) : m_value(value) |
| { |
| } |
| |
| protected: |
| void doPrintExpr(ostream &os) const |
| { |
| os << m_value; |
| } |
| IVal doEvaluate(const EvalContext &) const |
| { |
| return makeIVal(m_value); |
| } |
| |
| private: |
| T m_value; |
| }; |
| |
| template <typename T> |
| ExprP<T> constant(const T &value) |
| { |
| return exprP(new Constant<T>(value)); |
| } |
| |
| //! Return a reference to a singleton void constant. |
| const ExprP<Void> &voidP(void) |
| { |
| static const ExprP<Void> singleton = constant(Void()); |
| |
| return singleton; |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Four-element tuple. |
| * |
| * This is used for various things where we need one thing for each possible |
| * function parameter. Currently the maximum supported number of parameters is |
| * four. |
| *//*--------------------------------------------------------------------*/ |
| template <typename T0 = Void, typename T1 = Void, typename T2 = Void, typename T3 = Void> |
| struct Tuple4 |
| { |
| explicit Tuple4(const T0 e0 = T0(), const T1 e1 = T1(), const T2 e2 = T2(), const T3 e3 = T3()) |
| : a(e0) |
| , b(e1) |
| , c(e2) |
| , d(e3) |
| { |
| } |
| |
| T0 a; |
| T1 b; |
| T2 c; |
| T3 d; |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Function signature. |
| * |
| * This is a purely compile-time structure used to bundle all types in a |
| * function signature together. This makes passing the signature around in |
| * templates easier, since we only need to take and pass a single Sig instead |
| * of a bunch of parameter types and a return type. |
| * |
| *//*--------------------------------------------------------------------*/ |
| template <typename R, typename P0 = Void, typename P1 = Void, typename P2 = Void, typename P3 = Void> |
| struct Signature |
| { |
| typedef R Ret; |
| typedef P0 Arg0; |
| typedef P1 Arg1; |
| typedef P2 Arg2; |
| typedef P3 Arg3; |
| typedef typename Traits<Ret>::IVal IRet; |
| typedef typename Traits<Arg0>::IVal IArg0; |
| typedef typename Traits<Arg1>::IVal IArg1; |
| typedef typename Traits<Arg2>::IVal IArg2; |
| typedef typename Traits<Arg3>::IVal IArg3; |
| |
| typedef Tuple4<const Arg0 &, const Arg1 &, const Arg2 &, const Arg3 &> Args; |
| typedef Tuple4<const IArg0 &, const IArg1 &, const IArg2 &, const IArg3 &> IArgs; |
| typedef Tuple4<ExprP<Arg0>, ExprP<Arg1>, ExprP<Arg2>, ExprP<Arg3>> ArgExprs; |
| }; |
| |
| typedef vector<const ExprBase *> BaseArgExprs; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Type-independent operations for function objects. |
| * |
| *//*--------------------------------------------------------------------*/ |
| class FuncBase |
| { |
| public: |
| virtual ~FuncBase(void) |
| { |
| } |
| virtual string getName(void) const = 0; |
| //! Name of extension that this function requires, or empty. |
| virtual string getRequiredExtension(const RenderContext &) const |
| { |
| return ""; |
| } |
| virtual void print(ostream &, const BaseArgExprs &) const = 0; |
| //! Index of output parameter, or -1 if none of the parameters is output. |
| virtual int getOutParamIndex(void) const |
| { |
| return -1; |
| } |
| |
| void printDefinition(ostream &os) const |
| { |
| doPrintDefinition(os); |
| } |
| |
| void getUsedFuncs(FuncSet &dst) const |
| { |
| this->doGetUsedFuncs(dst); |
| } |
| |
| protected: |
| virtual void doPrintDefinition(ostream &os) const = 0; |
| virtual void doGetUsedFuncs(FuncSet &dst) const = 0; |
| }; |
| |
| typedef Tuple4<string, string, string, string> ParamNames; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Function objects. |
| * |
| * Each Func object represents a GLSL function. It can be applied to interval |
| * arguments, and it returns the an interval that is a conservative |
| * approximation of the image of the GLSL function over the argument |
| * intervals. That is, it is given a set of possible arguments and it returns |
| * the set of possible values. |
| * |
| *//*--------------------------------------------------------------------*/ |
| template <typename Sig_> |
| class Func : public FuncBase |
| { |
| public: |
| typedef Sig_ Sig; |
| typedef typename Sig::Ret Ret; |
| typedef typename Sig::Arg0 Arg0; |
| typedef typename Sig::Arg1 Arg1; |
| typedef typename Sig::Arg2 Arg2; |
| typedef typename Sig::Arg3 Arg3; |
| typedef typename Sig::IRet IRet; |
| typedef typename Sig::IArg0 IArg0; |
| typedef typename Sig::IArg1 IArg1; |
| typedef typename Sig::IArg2 IArg2; |
| typedef typename Sig::IArg3 IArg3; |
| typedef typename Sig::Args Args; |
| typedef typename Sig::IArgs IArgs; |
| typedef typename Sig::ArgExprs ArgExprs; |
| |
| void print(ostream &os, const BaseArgExprs &args) const |
| { |
| this->doPrint(os, args); |
| } |
| |
| IRet apply(const EvalContext &ctx, const IArg0 &arg0 = IArg0(), const IArg1 &arg1 = IArg1(), |
| const IArg2 &arg2 = IArg2(), const IArg3 &arg3 = IArg3()) const |
| { |
| return this->applyArgs(ctx, IArgs(arg0, arg1, arg2, arg3)); |
| } |
| IRet applyArgs(const EvalContext &ctx, const IArgs &args) const |
| { |
| return this->doApply(ctx, args); |
| } |
| ExprP<Ret> operator()(const ExprP<Arg0> &arg0 = voidP(), const ExprP<Arg1> &arg1 = voidP(), |
| const ExprP<Arg2> &arg2 = voidP(), const ExprP<Arg3> &arg3 = voidP()) const; |
| |
| const ParamNames &getParamNames(void) const |
| { |
| return this->doGetParamNames(); |
| } |
| |
| protected: |
| virtual IRet doApply(const EvalContext &, const IArgs &) const = 0; |
| virtual void doPrint(ostream &os, const BaseArgExprs &args) const |
| { |
| os << getName() << "("; |
| |
| if (isTypeValid<Arg0>()) |
| os << *args[0]; |
| |
| if (isTypeValid<Arg1>()) |
| os << ", " << *args[1]; |
| |
| if (isTypeValid<Arg2>()) |
| os << ", " << *args[2]; |
| |
| if (isTypeValid<Arg3>()) |
| os << ", " << *args[3]; |
| |
| os << ")"; |
| } |
| |
| virtual const ParamNames &doGetParamNames(void) const |
| { |
| static ParamNames names("a", "b", "c", "d"); |
| return names; |
| } |
| }; |
| |
| template <typename Sig> |
| class Apply : public Expr<typename Sig::Ret> |
| { |
| public: |
| typedef typename Sig::Ret Ret; |
| typedef typename Sig::Arg0 Arg0; |
| typedef typename Sig::Arg1 Arg1; |
| typedef typename Sig::Arg2 Arg2; |
| typedef typename Sig::Arg3 Arg3; |
| typedef typename Expr<Ret>::Val Val; |
| typedef typename Expr<Ret>::IVal IVal; |
| typedef Func<Sig> ApplyFunc; |
| typedef typename ApplyFunc::ArgExprs ArgExprs; |
| |
| Apply(const ApplyFunc &func, const ExprP<Arg0> &arg0 = voidP(), const ExprP<Arg1> &arg1 = voidP(), |
| const ExprP<Arg2> &arg2 = voidP(), const ExprP<Arg3> &arg3 = voidP()) |
| : m_func(func) |
| , m_args(arg0, arg1, arg2, arg3) |
| { |
| } |
| |
| Apply(const ApplyFunc &func, const ArgExprs &args) : m_func(func), m_args(args) |
| { |
| } |
| |
| protected: |
| void doPrintExpr(ostream &os) const |
| { |
| BaseArgExprs args; |
| args.push_back(m_args.a.get()); |
| args.push_back(m_args.b.get()); |
| args.push_back(m_args.c.get()); |
| args.push_back(m_args.d.get()); |
| m_func.print(os, args); |
| } |
| |
| IVal doEvaluate(const EvalContext &ctx) const |
| { |
| return m_func.apply(ctx, m_args.a->evaluate(ctx), m_args.b->evaluate(ctx), m_args.c->evaluate(ctx), |
| m_args.d->evaluate(ctx)); |
| } |
| |
| void doGetUsedFuncs(FuncSet &dst) const |
| { |
| m_func.getUsedFuncs(dst); |
| m_args.a->getUsedFuncs(dst); |
| m_args.b->getUsedFuncs(dst); |
| m_args.c->getUsedFuncs(dst); |
| m_args.d->getUsedFuncs(dst); |
| } |
| |
| const ApplyFunc &m_func; |
| ArgExprs m_args; |
| }; |
| |
| template <typename T> |
| class Alternatives : public Func<Signature<T, T, T>> |
| { |
| public: |
| typedef typename Alternatives::Sig Sig; |
| |
| protected: |
| typedef typename Alternatives::IRet IRet; |
| typedef typename Alternatives::IArgs IArgs; |
| |
| virtual string getName(void) const |
| { |
| return "alternatives"; |
| } |
| virtual void doPrintDefinition(std::ostream &) const |
| { |
| } |
| void doGetUsedFuncs(FuncSet &) const |
| { |
| } |
| |
| virtual IRet doApply(const EvalContext &, const IArgs &args) const |
| { |
| return unionIVal<T>(args.a, args.b); |
| } |
| |
| virtual void doPrint(ostream &os, const BaseArgExprs &args) const |
| { |
| os << "{" << *args[0] << " | " << *args[1] << "}"; |
| } |
| }; |
| |
| template <typename Sig> |
| ExprP<typename Sig::Ret> createApply(const Func<Sig> &func, const typename Func<Sig>::ArgExprs &args) |
| { |
| return exprP(new Apply<Sig>(func, args)); |
| } |
| |
| template <typename Sig> |
| ExprP<typename Sig::Ret> createApply(const Func<Sig> &func, const ExprP<typename Sig::Arg0> &arg0 = voidP(), |
| const ExprP<typename Sig::Arg1> &arg1 = voidP(), |
| const ExprP<typename Sig::Arg2> &arg2 = voidP(), |
| const ExprP<typename Sig::Arg3> &arg3 = voidP()) |
| { |
| return exprP(new Apply<Sig>(func, arg0, arg1, arg2, arg3)); |
| } |
| |
| template <typename Sig> |
| ExprP<typename Sig::Ret> Func<Sig>::operator()(const ExprP<typename Sig::Arg0> &arg0, |
| const ExprP<typename Sig::Arg1> &arg1, |
| const ExprP<typename Sig::Arg2> &arg2, |
| const ExprP<typename Sig::Arg3> &arg3) const |
| { |
| return createApply(*this, arg0, arg1, arg2, arg3); |
| } |
| |
| template <typename F> |
| ExprP<typename F::Ret> app(const ExprP<typename F::Arg0> &arg0 = voidP(), const ExprP<typename F::Arg1> &arg1 = voidP(), |
| const ExprP<typename F::Arg2> &arg2 = voidP(), const ExprP<typename F::Arg3> &arg3 = voidP()) |
| { |
| return createApply(instance<F>(), arg0, arg1, arg2, arg3); |
| } |
| |
| template <typename F> |
| typename F::IRet call(const EvalContext &ctx, const typename F::IArg0 &arg0 = Void(), |
| const typename F::IArg1 &arg1 = Void(), const typename F::IArg2 &arg2 = Void(), |
| const typename F::IArg3 &arg3 = Void()) |
| { |
| return instance<F>().apply(ctx, arg0, arg1, arg2, arg3); |
| } |
| |
| template <typename T> |
| ExprP<T> alternatives(const ExprP<T> &arg0, const ExprP<T> &arg1) |
| { |
| return createApply<typename Alternatives<T>::Sig>(instance<Alternatives<T>>(), arg0, arg1); |
| } |
| |
| template <typename Sig> |
| class ApplyVar : public Apply<Sig> |
| { |
| public: |
| typedef typename Sig::Ret Ret; |
| typedef typename Sig::Arg0 Arg0; |
| typedef typename Sig::Arg1 Arg1; |
| typedef typename Sig::Arg2 Arg2; |
| typedef typename Sig::Arg3 Arg3; |
| typedef typename Expr<Ret>::Val Val; |
| typedef typename Expr<Ret>::IVal IVal; |
| typedef Func<Sig> ApplyFunc; |
| typedef typename ApplyFunc::ArgExprs ArgExprs; |
| |
| ApplyVar(const ApplyFunc &func, const VariableP<Arg0> &arg0, const VariableP<Arg1> &arg1, |
| const VariableP<Arg2> &arg2, const VariableP<Arg3> &arg3) |
| : Apply<Sig>(func, arg0, arg1, arg2, arg3) |
| { |
| } |
| |
| protected: |
| IVal doEvaluate(const EvalContext &ctx) const |
| { |
| const Variable<Arg0> &var0 = static_cast<const Variable<Arg0> &>(*this->m_args.a); |
| const Variable<Arg1> &var1 = static_cast<const Variable<Arg1> &>(*this->m_args.b); |
| const Variable<Arg2> &var2 = static_cast<const Variable<Arg2> &>(*this->m_args.c); |
| const Variable<Arg3> &var3 = static_cast<const Variable<Arg3> &>(*this->m_args.d); |
| return this->m_func.apply(ctx, ctx.env.lookup(var0), ctx.env.lookup(var1), ctx.env.lookup(var2), |
| ctx.env.lookup(var3)); |
| } |
| }; |
| |
| template <typename Sig> |
| ExprP<typename Sig::Ret> applyVar(const Func<Sig> &func, const VariableP<typename Sig::Arg0> &arg0, |
| const VariableP<typename Sig::Arg1> &arg1, const VariableP<typename Sig::Arg2> &arg2, |
| const VariableP<typename Sig::Arg3> &arg3) |
| { |
| return exprP(new ApplyVar<Sig>(func, arg0, arg1, arg2, arg3)); |
| } |
| |
| template <typename Sig_> |
| class DerivedFunc : public Func<Sig_> |
| { |
| public: |
| typedef typename DerivedFunc::ArgExprs ArgExprs; |
| typedef typename DerivedFunc::IRet IRet; |
| typedef typename DerivedFunc::IArgs IArgs; |
| typedef typename DerivedFunc::Ret Ret; |
| typedef typename DerivedFunc::Arg0 Arg0; |
| typedef typename DerivedFunc::Arg1 Arg1; |
| typedef typename DerivedFunc::Arg2 Arg2; |
| typedef typename DerivedFunc::Arg3 Arg3; |
| typedef typename DerivedFunc::IArg0 IArg0; |
| typedef typename DerivedFunc::IArg1 IArg1; |
| typedef typename DerivedFunc::IArg2 IArg2; |
| typedef typename DerivedFunc::IArg3 IArg3; |
| |
| protected: |
| void doPrintDefinition(ostream &os) const |
| { |
| const ParamNames ¶mNames = this->getParamNames(); |
| |
| initialize(); |
| |
| os << dataTypeNameOf<Ret>() << " " << this->getName() << "("; |
| if (isTypeValid<Arg0>()) |
| os << dataTypeNameOf<Arg0>() << " " << paramNames.a; |
| if (isTypeValid<Arg1>()) |
| os << ", " << dataTypeNameOf<Arg1>() << " " << paramNames.b; |
| if (isTypeValid<Arg2>()) |
| os << ", " << dataTypeNameOf<Arg2>() << " " << paramNames.c; |
| if (isTypeValid<Arg3>()) |
| os << ", " << dataTypeNameOf<Arg3>() << " " << paramNames.d; |
| os << ")\n{\n"; |
| |
| for (size_t ndx = 0; ndx < m_body.size(); ++ndx) |
| os << *m_body[ndx]; |
| os << "return " << *m_ret << ";\n"; |
| os << "}\n"; |
| } |
| |
| IRet doApply(const EvalContext &ctx, const IArgs &args) const |
| { |
| Environment funEnv; |
| IArgs &mutArgs = const_cast<IArgs &>(args); |
| IRet ret; |
| |
| initialize(); |
| |
| funEnv.bind(*m_var0, args.a); |
| funEnv.bind(*m_var1, args.b); |
| funEnv.bind(*m_var2, args.c); |
| funEnv.bind(*m_var3, args.d); |
| |
| { |
| EvalContext funCtx(ctx.format, ctx.floatPrecision, funEnv, ctx.callDepth); |
| |
| for (size_t ndx = 0; ndx < m_body.size(); ++ndx) |
| m_body[ndx]->execute(funCtx); |
| |
| ret = m_ret->evaluate(funCtx); |
| } |
| |
| // \todo [lauri] Store references instead of values in environment |
| const_cast<IArg0 &>(mutArgs.a) = funEnv.lookup(*m_var0); |
| const_cast<IArg1 &>(mutArgs.b) = funEnv.lookup(*m_var1); |
| const_cast<IArg2 &>(mutArgs.c) = funEnv.lookup(*m_var2); |
| const_cast<IArg3 &>(mutArgs.d) = funEnv.lookup(*m_var3); |
| |
| return ret; |
| } |
| |
| void doGetUsedFuncs(FuncSet &dst) const |
| { |
| initialize(); |
| if (dst.insert(this).second) |
| { |
| for (size_t ndx = 0; ndx < m_body.size(); ++ndx) |
| m_body[ndx]->getUsedFuncs(dst); |
| m_ret->getUsedFuncs(dst); |
| } |
| } |
| |
| virtual ExprP<Ret> doExpand(ExpandContext &ctx, const ArgExprs &args_) const = 0; |
| |
| // These are transparently initialized when first needed. They cannot be |
| // initialized in the constructor because they depend on the doExpand |
| // method of the subclass. |
| |
| mutable VariableP<Arg0> m_var0; |
| mutable VariableP<Arg1> m_var1; |
| mutable VariableP<Arg2> m_var2; |
| mutable VariableP<Arg3> m_var3; |
| mutable vector<StatementP> m_body; |
| mutable ExprP<Ret> m_ret; |
| |
| private: |
| void initialize(void) const |
| { |
| if (!m_ret) |
| { |
| const ParamNames ¶mNames = this->getParamNames(); |
| Counter symCounter; |
| ExpandContext ctx(symCounter); |
| ArgExprs args; |
| |
| args.a = m_var0 = variable<Arg0>(paramNames.a); |
| args.b = m_var1 = variable<Arg1>(paramNames.b); |
| args.c = m_var2 = variable<Arg2>(paramNames.c); |
| args.d = m_var3 = variable<Arg3>(paramNames.d); |
| |
| m_ret = this->doExpand(ctx, args); |
| m_body = ctx.getStatements(); |
| } |
| } |
| }; |
| |
| template <typename Sig> |
| class PrimitiveFunc : public Func<Sig> |
| { |
| public: |
| typedef typename PrimitiveFunc::Ret Ret; |
| typedef typename PrimitiveFunc::ArgExprs ArgExprs; |
| |
| protected: |
| void doPrintDefinition(ostream &) const |
| { |
| } |
| void doGetUsedFuncs(FuncSet &) const |
| { |
| } |
| }; |
| |
| template <typename T> |
| class Cond : public PrimitiveFunc<Signature<T, bool, T, T>> |
| { |
| public: |
| typedef typename Cond::IArgs IArgs; |
| typedef typename Cond::IRet IRet; |
| |
| string getName(void) const |
| { |
| return "_cond"; |
| } |
| |
| protected: |
| void doPrint(ostream &os, const BaseArgExprs &args) const |
| { |
| os << "(" << *args[0] << " ? " << *args[1] << " : " << *args[2] << ")"; |
| } |
| |
| IRet doApply(const EvalContext &, const IArgs &iargs) const |
| { |
| IRet ret; |
| |
| if (iargs.a.contains(true)) |
| ret = unionIVal<T>(ret, iargs.b); |
| |
| if (iargs.a.contains(false)) |
| ret = unionIVal<T>(ret, iargs.c); |
| |
| return ret; |
| } |
| }; |
| |
| template <typename T> |
| class CompareOperator : public PrimitiveFunc<Signature<bool, T, T>> |
| { |
| public: |
| typedef typename CompareOperator::IArgs IArgs; |
| typedef typename CompareOperator::IArg0 IArg0; |
| typedef typename CompareOperator::IArg1 IArg1; |
| typedef typename CompareOperator::IRet IRet; |
| |
| protected: |
| void doPrint(ostream &os, const BaseArgExprs &args) const |
| { |
| os << "(" << *args[0] << getSymbol() << *args[1] << ")"; |
| } |
| |
| Interval doApply(const EvalContext &, const IArgs &iargs) const |
| { |
| const IArg0 &arg0 = iargs.a; |
| const IArg1 &arg1 = iargs.b; |
| IRet ret; |
| |
| if (canSucceed(arg0, arg1)) |
| ret |= true; |
| if (canFail(arg0, arg1)) |
| ret |= false; |
| |
| return ret; |
| } |
| |
| virtual string getSymbol(void) const = 0; |
| virtual bool canSucceed(const IArg0 &, const IArg1 &) const = 0; |
| virtual bool canFail(const IArg0 &, const IArg1 &) const = 0; |
| }; |
| |
| template <typename T> |
| class LessThan : public CompareOperator<T> |
| { |
| public: |
| string getName(void) const |
| { |
| return "lessThan"; |
| } |
| |
| protected: |
| string getSymbol(void) const |
| { |
| return "<"; |
| } |
| |
| bool canSucceed(const Interval &a, const Interval &b) const |
| { |
| return (a.lo() < b.hi()); |
| } |
| |
| bool canFail(const Interval &a, const Interval &b) const |
| { |
| return !(a.hi() < b.lo()); |
| } |
| }; |
| |
| template <typename T> |
| ExprP<bool> operator<(const ExprP<T> &a, const ExprP<T> &b) |
| { |
| return app<LessThan<T>>(a, b); |
| } |
| |
| template <typename T> |
| ExprP<T> cond(const ExprP<bool> &test, const ExprP<T> &consequent, const ExprP<T> &alternative) |
| { |
| return app<Cond<T>>(test, consequent, alternative); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * |
| * @} |
| * |
| *//*--------------------------------------------------------------------*/ |
| |
| class FloatFunc1 : public PrimitiveFunc<Signature<float, float>> |
| { |
| protected: |
| Interval doApply(const EvalContext &ctx, const IArgs &iargs) const |
| { |
| return this->applyMonotone(ctx, iargs.a); |
| } |
| |
| Interval applyMonotone(const EvalContext &ctx, const Interval &iarg0) const |
| { |
| Interval ret; |
| |
| TCU_INTERVAL_APPLY_MONOTONE1(ret, arg0, iarg0, val, |
| TCU_SET_INTERVAL(val, point, point = this->applyPoint(ctx, arg0))); |
| |
| ret |= innerExtrema(ctx, iarg0); |
| ret &= (this->getCodomain() | TCU_NAN); |
| |
| return ctx.format.convert(ret); |
| } |
| |
| virtual Interval innerExtrema(const EvalContext &, const Interval &) const |
| { |
| return Interval(); // empty interval, i.e. no extrema |
| } |
| |
| virtual Interval applyPoint(const EvalContext &ctx, double arg0) const |
| { |
| const double exact = this->applyExact(arg0); |
| const double prec = this->precision(ctx, exact, arg0); |
| const double wprec = this->warningPrecision(ctx, exact, arg0); |
| Interval ioutput = exact + Interval(-prec, prec); |
| ioutput.warning(exact - wprec, exact + wprec); |
| return ioutput; |
| } |
| |
| virtual double applyExact(double) const |
| { |
| TCU_THROW(InternalError, "Cannot apply"); |
| } |
| |
| virtual Interval getCodomain(void) const |
| { |
| return Interval::unbounded(true); |
| } |
| |
| virtual double precision(const EvalContext &ctx, double, double) const = 0; |
| |
| virtual double warningPrecision(const EvalContext &ctx, double exact, double arg0) const |
| { |
| return precision(ctx, exact, arg0); |
| } |
| }; |
| |
| class CFloatFunc1 : public FloatFunc1 |
| { |
| public: |
| CFloatFunc1(const string &name, DoubleFunc1 &func) : m_name(name), m_func(func) |
| { |
| } |
| |
| string getName(void) const |
| { |
| return m_name; |
| } |
| |
| protected: |
| double applyExact(double x) const |
| { |
| return m_func(x); |
| } |
| |
| const string m_name; |
| DoubleFunc1 &m_func; |
| }; |
| |
| class FloatFunc2 : public PrimitiveFunc<Signature<float, float, float>> |
| { |
| protected: |
| Interval doApply(const EvalContext &ctx, const IArgs &iargs) const |
| { |
| return this->applyMonotone(ctx, iargs.a, iargs.b); |
| } |
| |
| Interval applyMonotone(const EvalContext &ctx, const Interval &xi, const Interval &yi) const |
| { |
| Interval reti; |
| |
| TCU_INTERVAL_APPLY_MONOTONE2(reti, x, xi, y, yi, ret, |
| TCU_SET_INTERVAL(ret, point, point = this->applyPoint(ctx, x, y))); |
| reti |= innerExtrema(ctx, xi, yi); |
| reti &= (this->getCodomain() | TCU_NAN); |
| |
| return ctx.format.convert(reti); |
| } |
| |
| virtual Interval innerExtrema(const EvalContext &, const Interval &, const Interval &) const |
| { |
| return Interval(); // empty interval, i.e. no extrema |
| } |
| |
| virtual Interval applyPoint(const EvalContext &ctx, double x, double y) const |
| { |
| const double exact = this->applyExact(x, y); |
| const double prec = this->precision(ctx, exact, x, y); |
| |
| return exact + Interval(-prec, prec); |
| } |
| |
| virtual double applyExact(double, double) const |
| { |
| TCU_THROW(InternalError, "Cannot apply"); |
| } |
| |
| virtual Interval getCodomain(void) const |
| { |
| return Interval::unbounded(true); |
| } |
| |
| virtual double precision(const EvalContext &ctx, double ret, double x, double y) const = 0; |
| }; |
| |
| class CFloatFunc2 : public FloatFunc2 |
| { |
| public: |
| CFloatFunc2(const string &name, DoubleFunc2 &func) : m_name(name), m_func(func) |
| { |
| } |
| |
| string getName(void) const |
| { |
| return m_name; |
| } |
| |
| protected: |
| double applyExact(double x, double y) const |
| { |
| return m_func(x, y); |
| } |
| |
| const string m_name; |
| DoubleFunc2 &m_func; |
| }; |
| |
| class InfixOperator : public FloatFunc2 |
| { |
| protected: |
| virtual string getSymbol(void) const = 0; |
| |
| void doPrint(ostream &os, const BaseArgExprs &args) const |
| { |
| os << "(" << *args[0] << " " << getSymbol() << " " << *args[1] << ")"; |
| } |
| |
| Interval applyPoint(const EvalContext &ctx, double x, double y) const |
| { |
| const double exact = this->applyExact(x, y); |
| |
| // Allow either representable number on both sides of the exact value, |
| // but require exactly representable values to be preserved. |
| return ctx.format.roundOut(exact, !deIsInf(x) && !deIsInf(y)); |
| } |
| |
| double precision(const EvalContext &, double, double, double) const |
| { |
| return 0.0; |
| } |
| }; |
| |
| class FloatFunc3 : public PrimitiveFunc<Signature<float, float, float, float>> |
| { |
| protected: |
| Interval doApply(const EvalContext &ctx, const IArgs &iargs) const |
| { |
| return this->applyMonotone(ctx, iargs.a, iargs.b, iargs.c); |
| } |
| |
| Interval applyMonotone(const EvalContext &ctx, const Interval &xi, const Interval &yi, const Interval &zi) const |
| { |
| Interval reti; |
| TCU_INTERVAL_APPLY_MONOTONE3(reti, x, xi, y, yi, z, zi, ret, |
| TCU_SET_INTERVAL(ret, point, point = this->applyPoint(ctx, x, y, z))); |
| return ctx.format.convert(reti); |
| } |
| |
| virtual Interval applyPoint(const EvalContext &ctx, double x, double y, double z) const |
| { |
| const double exact = this->applyExact(x, y, z); |
| const double prec = this->precision(ctx, exact, x, y, z); |
| return exact + Interval(-prec, prec); |
| } |
| |
| virtual double applyExact(double, double, double) const |
| { |
| TCU_THROW(InternalError, "Cannot apply"); |
| } |
| |
| virtual double precision(const EvalContext &ctx, double result, double x, double y, double z) const = 0; |
| }; |
| |
| // We define syntactic sugar functions for expression constructors. Since |
| // these have the same names as ordinary mathematical operations (sin, log |
| // etc.), it's better to give them a dedicated namespace. |
| namespace Functions |
| { |
| |
| using namespace tcu; |
| |
| class Add : public InfixOperator |
| { |
| public: |
| string getName(void) const |
| { |
| return "add"; |
| } |
| string getSymbol(void) const |
| { |
| return "+"; |
| } |
| |
| Interval doApply(const EvalContext &ctx, const IArgs &iargs) const |
| { |
| // Fast-path for common case |
| if (iargs.a.isOrdinary(ctx.format.getMaxValue()) && iargs.b.isOrdinary(ctx.format.getMaxValue())) |
| { |
| Interval ret; |
| TCU_SET_INTERVAL_BOUNDS(ret, sum, sum = iargs.a.lo() + iargs.b.lo(), sum = iargs.a.hi() + iargs.b.hi()); |
| return ctx.format.convert(ctx.format.roundOut(ret, true)); |
| } |
| return this->applyMonotone(ctx, iargs.a, iargs.b); |
| } |
| |
| protected: |
| double applyExact(double x, double y) const |
| { |
| return x + y; |
| } |
| }; |
| |
| class Mul : public InfixOperator |
| { |
| public: |
| string getName(void) const |
| { |
| return "mul"; |
| } |
| string getSymbol(void) const |
| { |
| return "*"; |
| } |
| |
| Interval doApply(const EvalContext &ctx, const IArgs &iargs) const |
| { |
| Interval a = iargs.a; |
| Interval b = iargs.b; |
| |
| // Fast-path for common case |
| if (a.isOrdinary(ctx.format.getMaxValue()) && b.isOrdinary(ctx.format.getMaxValue())) |
| { |
| Interval ret; |
| if (a.hi() < 0) |
| { |
| a = -a; |
| b = -b; |
| } |
| if (a.lo() >= 0 && b.lo() >= 0) |
| { |
| TCU_SET_INTERVAL_BOUNDS(ret, prod, prod = iargs.a.lo() * iargs.b.lo(), |
| prod = iargs.a.hi() * iargs.b.hi()); |
| return ctx.format.convert(ctx.format.roundOut(ret, true)); |
| } |
| if (a.lo() >= 0 && b.hi() <= 0) |
| { |
| TCU_SET_INTERVAL_BOUNDS(ret, prod, prod = iargs.a.hi() * iargs.b.lo(), |
| prod = iargs.a.lo() * iargs.b.hi()); |
| return ctx.format.convert(ctx.format.roundOut(ret, true)); |
| } |
| } |
| return this->applyMonotone(ctx, iargs.a, iargs.b); |
| } |
| |
| protected: |
| double applyExact(double x, double y) const |
| { |
| return x * y; |
| } |
| |
| Interval innerExtrema(const EvalContext &, const Interval &xi, const Interval &yi) const |
| { |
| if (((xi.contains(-TCU_INFINITY) || xi.contains(TCU_INFINITY)) && yi.contains(0.0)) || |
| ((yi.contains(-TCU_INFINITY) || yi.contains(TCU_INFINITY)) && xi.contains(0.0))) |
| return Interval(TCU_NAN); |
| |
| return Interval(); |
| } |
| }; |
| |
| class Sub : public InfixOperator |
| { |
| public: |
| string getName(void) const |
| { |
| return "sub"; |
| } |
| string getSymbol(void) const |
| { |
| return "-"; |
| } |
| |
| Interval doApply(const EvalContext &ctx, const IArgs &iargs) const |
| { |
| // Fast-path for common case |
| if (iargs.a.isOrdinary(ctx.format.getMaxValue()) && iargs.b.isOrdinary(ctx.format.getMaxValue())) |
| { |
| Interval ret; |
| |
| TCU_SET_INTERVAL_BOUNDS(ret, diff, diff = iargs.a.lo() - iargs.b.hi(), diff = iargs.a.hi() - iargs.b.lo()); |
| return ctx.format.convert(ctx.format.roundOut(ret, true)); |
| } |
| else |
| { |
| return this->applyMonotone(ctx, iargs.a, iargs.b); |
| } |
| } |
| |
| protected: |
| double applyExact(double x, double y) const |
| { |
| return x - y; |
| } |
| }; |
| |
| class Negate : public FloatFunc1 |
| { |
| public: |
| string getName(void) const |
| { |
| return "_negate"; |
| } |
| void doPrint(ostream &os, const BaseArgExprs &args) const |
| { |
| os << "-" << *args[0]; |
| } |
| |
| protected: |
| double precision(const EvalContext &, double, double) const |
| { |
| return 0.0; |
| } |
| double applyExact(double x) const |
| { |
| return -x; |
| } |
| }; |
| |
| class Div : public InfixOperator |
| { |
| public: |
| string getName(void) const |
| { |
| return "div"; |
| } |
| |
| protected: |
| string getSymbol(void) const |
| { |
| return "/"; |
| } |
| |
| Interval innerExtrema(const EvalContext &, const Interval &nom, const Interval &den) const |
| { |
| Interval ret; |
| |
| if (den.contains(0.0)) |
| { |
| if (nom.contains(0.0)) |
| ret |= TCU_NAN; |
| |
| if (nom.lo() < 0.0 || nom.hi() > 0.0) |
| ret |= Interval::unbounded(); |
| } |
| |
| return ret; |
| } |
| |
| double applyExact(double x, double y) const |
| { |
| return x / y; |
| } |
| |
| Interval applyPoint(const EvalContext &ctx, double x, double y) const |
| { |
| Interval ret = FloatFunc2::applyPoint(ctx, x, y); |
| |
| if (!deIsInf(x) && !deIsInf(y) && y != 0.0) |
| { |
| const Interval dst = ctx.format.convert(ret); |
| if (dst.contains(-TCU_INFINITY)) |
| ret |= -ctx.format.getMaxValue(); |
| if (dst.contains(+TCU_INFINITY)) |
| ret |= +ctx.format.getMaxValue(); |
| } |
| |
| return ret; |
| } |
| |
| double precision(const EvalContext &ctx, double ret, double, double den) const |
| { |
| const FloatFormat &fmt = ctx.format; |
| |
| // \todo [2014-03-05 lauri] Check that the limits in GLSL 3.10 are actually correct. |
| // For now, we assume that division's precision is 2.5 ULP when the value is within |
| // [2^MINEXP, 2^MAXEXP-1] |
| |
| if (den == 0.0) |
| return 0.0; // Result must be exactly inf |
| else if (de::inBounds(deAbs(den), deLdExp(1.0, fmt.getMinExp()), deLdExp(1.0, fmt.getMaxExp() - 1))) |
| return fmt.ulp(ret, 2.5); |
| else |
| return TCU_INFINITY; // Can be any number, but must be a number. |
| } |
| }; |
| |
| class InverseSqrt : public FloatFunc1 |
| { |
| public: |
| string getName(void) const |
| { |
| return "inversesqrt"; |
| } |
| |
| protected: |
| double applyExact(double x) const |
| { |
| return 1.0 / deSqrt(x); |
| } |
| |
| double precision(const EvalContext &ctx, double ret, double x) const |
| { |
| return x <= 0 ? TCU_NAN : ctx.format.ulp(ret, 2.0); |
| } |
| |
| Interval getCodomain(void) const |
| { |
| return Interval(0.0, TCU_INFINITY); |
| } |
| }; |
| |
| class ExpFunc : public CFloatFunc1 |
| { |
| public: |
| ExpFunc(const string &name, DoubleFunc1 &func) : CFloatFunc1(name, func) |
| { |
| } |
| |
| protected: |
| double precision(const EvalContext &ctx, double ret, double x) const |
| { |
| switch (ctx.floatPrecision) |
| { |
| case glu::PRECISION_HIGHP: |
| return ctx.format.ulp(ret, 3.0 + 2.0 * deAbs(x)); |
| case glu::PRECISION_MEDIUMP: |
| return ctx.format.ulp(ret, 2.0 + 2.0 * deAbs(x)); |
| case glu::PRECISION_LOWP: |
| return ctx.format.ulp(ret, 2.0); |
| default: |
| DE_FATAL("Impossible"); |
| } |
| return 0; |
| } |
| |
| Interval getCodomain(void) const |
| { |
| return Interval(0.0, TCU_INFINITY); |
| } |
| }; |
| |
| class Exp2 : public ExpFunc |
| { |
| public: |
| Exp2(void) : ExpFunc("exp2", deExp2) |
| { |
| } |
| }; |
| class Exp : public ExpFunc |
| { |
| public: |
| Exp(void) : ExpFunc("exp", deExp) |
| { |
| } |
| }; |
| |
| ExprP<float> exp2(const ExprP<float> &x) |
| { |
| return app<Exp2>(x); |
| } |
| ExprP<float> exp(const ExprP<float> &x) |
| { |
| return app<Exp>(x); |
| } |
| |
| class LogFunc : public CFloatFunc1 |
| { |
| public: |
| LogFunc(const string &name, DoubleFunc1 &func) : CFloatFunc1(name, func) |
| { |
| } |
| |
| protected: |
| double precision(const EvalContext &ctx, double ret, double x) const |
| { |
| if (x <= 0) |
| return TCU_NAN; |
| |
| switch (ctx.floatPrecision) |
| { |
| case glu::PRECISION_HIGHP: |
| return (0.5 <= x && x <= 2.0) ? deLdExp(1.0, -21) : ctx.format.ulp(ret, 3.0); |
| case glu::PRECISION_MEDIUMP: |
| return (0.5 <= x && x <= 2.0) ? deLdExp(1.0, -7) : ctx.format.ulp(ret, 2.0); |
| case glu::PRECISION_LOWP: |
| return ctx.format.ulp(ret, 2.0); |
| default: |
| DE_FATAL("Impossible"); |
| } |
| |
| return 0; |
| } |
| |
| // OpenGL API Issue #57 "Clarifying the required ULP precision for GLSL built-in log()". Agreed that |
| // implementations will be allowed 4 ULPs for HIGHP Log/Log2, but CTS should generate a quality warning. |
| double warningPrecision(const EvalContext &ctx, double ret, double x) const |
| { |
| if (ctx.floatPrecision == glu::PRECISION_HIGHP && x > 0) |
| { |
| return (0.5 <= x && x <= 2.0) ? deLdExp(1.0, -21) : ctx.format.ulp(ret, 4.0); |
| } |
| else |
| { |
| return precision(ctx, ret, x); |
| } |
| } |
| }; |
| |
| class Log2 : public LogFunc |
| { |
| public: |
| Log2(void) : LogFunc("log2", deLog2) |
| { |
| } |
| }; |
| class Log : public LogFunc |
| { |
| public: |
| Log(void) : LogFunc("log", deLog) |
| { |
| } |
| }; |
| |
| ExprP<float> log2(const ExprP<float> &x) |
| { |
| return app<Log2>(x); |
| } |
| ExprP<float> log(const ExprP<float> &x) |
| { |
| return app<Log>(x); |
| } |
| |
| #define DEFINE_CONSTRUCTOR1(CLASS, TRET, NAME, T0) \ |
| ExprP<TRET> NAME(const ExprP<T0> &arg0) \ |
| { \ |
| return app<CLASS>(arg0); \ |
| } |
| |
| #define DEFINE_DERIVED1(CLASS, TRET, NAME, T0, ARG0, EXPANSION) \ |
| class CLASS : public DerivedFunc<Signature<TRET, T0>> /* NOLINT(CLASS) */ \ |
| { \ |
| public: \ |
| string getName(void) const \ |
| { \ |
| return #NAME; \ |
| } \ |
| \ |
| protected: \ |
| ExprP<TRET> doExpand(ExpandContext &, const CLASS::ArgExprs &args_) const \ |
| { \ |
| const ExprP<float> &ARG0 = args_.a; \ |
| return EXPANSION; \ |
| } \ |
| }; \ |
| DEFINE_CONSTRUCTOR1(CLASS, TRET, NAME, T0) |
| |
| #define DEFINE_DERIVED_FLOAT1(CLASS, NAME, ARG0, EXPANSION) DEFINE_DERIVED1(CLASS, float, NAME, float, ARG0, EXPANSION) |
| |
| #define DEFINE_CONSTRUCTOR2(CLASS, TRET, NAME, T0, T1) \ |
| ExprP<TRET> NAME(const ExprP<T0> &arg0, const ExprP<T1> &arg1) \ |
| { \ |
| return app<CLASS>(arg0, arg1); \ |
| } |
| |
| #define DEFINE_DERIVED2(CLASS, TRET, NAME, T0, Arg0, T1, Arg1, EXPANSION) \ |
| class CLASS : public DerivedFunc<Signature<TRET, T0, T1>> /* NOLINT(CLASS) */ \ |
| { \ |
| public: \ |
| string getName(void) const \ |
| { \ |
| return #NAME; \ |
| } \ |
| \ |
| protected: \ |
| ExprP<TRET> doExpand(ExpandContext &, const ArgExprs &args_) const \ |
| { \ |
| const ExprP<T0> &Arg0 = args_.a; \ |
| const ExprP<T1> &Arg1 = args_.b; \ |
| return EXPANSION; \ |
| } \ |
| }; \ |
| DEFINE_CONSTRUCTOR2(CLASS, TRET, NAME, T0, T1) |
| |
| #define DEFINE_DERIVED_FLOAT2(CLASS, NAME, Arg0, Arg1, EXPANSION) \ |
| DEFINE_DERIVED2(CLASS, float, NAME, float, Arg0, float, Arg1, EXPANSION) |
| |
| #define DEFINE_CONSTRUCTOR3(CLASS, TRET, NAME, T0, T1, T2) \ |
| ExprP<TRET> NAME(const ExprP<T0> &arg0, const ExprP<T1> &arg1, const ExprP<T2> &arg2) \ |
| { \ |
| return app<CLASS>(arg0, arg1, arg2); \ |
| } |
| |
| #define DEFINE_DERIVED3(CLASS, TRET, NAME, T0, ARG0, T1, ARG1, T2, ARG2, EXPANSION) \ |
| class CLASS : public DerivedFunc<Signature<TRET, T0, T1, T2>> /* NOLINT(CLASS) */ \ |
| { \ |
| public: \ |
| string getName(void) const \ |
| { \ |
| return #NAME; \ |
| } \ |
| \ |
| protected: \ |
| ExprP<TRET> doExpand(ExpandContext &, const ArgExprs &args_) const \ |
| { \ |
| const ExprP<T0> &ARG0 = args_.a; \ |
| const ExprP<T1> &ARG1 = args_.b; \ |
| const ExprP<T2> &ARG2 = args_.c; \ |
| return EXPANSION; \ |
| } \ |
| }; \ |
| DEFINE_CONSTRUCTOR3(CLASS, TRET, NAME, T0, T1, T2) |
| |
| #define DEFINE_DERIVED_FLOAT3(CLASS, NAME, ARG0, ARG1, ARG2, EXPANSION) \ |
| DEFINE_DERIVED3(CLASS, float, NAME, float, ARG0, float, ARG1, float, ARG2, EXPANSION) |
| |
| #define DEFINE_CONSTRUCTOR4(CLASS, TRET, NAME, T0, T1, T2, T3) \ |
| ExprP<TRET> NAME(const ExprP<T0> &arg0, const ExprP<T1> &arg1, const ExprP<T2> &arg2, const ExprP<T3> &arg3) \ |
| { \ |
| return app<CLASS>(arg0, arg1, arg2, arg3); \ |
| } |
| |
| DEFINE_DERIVED_FLOAT1(Sqrt, sqrt, x, (x == 0.0f ? constant(1.0f) : (constant(1.0f) / app<InverseSqrt>(x)))) |
| DEFINE_DERIVED_FLOAT2(Pow, pow, x, y, exp2(y *log2(x))) |
| DEFINE_DERIVED_FLOAT1(Radians, radians, d, (constant(DE_PI) / constant(180.0f)) * d) |
| DEFINE_DERIVED_FLOAT1(Degrees, degrees, r, (constant(180.0f) / constant(DE_PI)) * r) |
| |
| class TrigFunc : public CFloatFunc1 |
| { |
| public: |
| TrigFunc(const string &name, DoubleFunc1 &func, const Interval &loEx, const Interval &hiEx) |
| : CFloatFunc1(name, func) |
| , m_loExtremum(loEx) |
| , m_hiExtremum(hiEx) |
| { |
| } |
| |
| protected: |
| Interval innerExtrema(const EvalContext &, const Interval &angle) const |
| { |
| const double lo = angle.lo(); |
| const double hi = angle.hi(); |
| const int loSlope = doGetSlope(lo); |
| const int hiSlope = doGetSlope(hi); |
| |
| // Detect the high and low values the function can take between the |
| // interval endpoints. |
| if (angle.length() >= 2.0 * DE_PI_DOUBLE) |
| { |
| // The interval is longer than a full cycle, so it must get all possible values. |
| return m_hiExtremum | m_loExtremum; |
| } |
| else if (loSlope == 1 && hiSlope == -1) |
| { |
| // The slope can change from positive to negative only at the maximum value. |
| return m_hiExtremum; |
| } |
| else if (loSlope == -1 && hiSlope == 1) |
| { |
| // The slope can change from negative to positive only at the maximum value. |
| return m_loExtremum; |
| } |
| else if (loSlope == hiSlope && deIntSign(applyExact(hi) - applyExact(lo)) * loSlope == -1) |
| { |
| // The slope has changed twice between the endpoints, so both extrema are included. |
| return m_hiExtremum | m_loExtremum; |
| } |
| |
| return Interval(); |
| } |
| |
| Interval getCodomain(void) const |
| { |
| // Ensure that result is always within [-1, 1], or NaN (for +-inf) |
| return Interval(-1.0, 1.0) | TCU_NAN; |
| } |
| |
| double precision(const EvalContext &ctx, double ret, double arg) const |
| { |
| if (ctx.floatPrecision == glu::PRECISION_HIGHP) |
| { |
| // Use precision from OpenCL fast relaxed math |
| if (-DE_PI_DOUBLE <= arg && arg <= DE_PI_DOUBLE) |
| { |
| return deLdExp(1.0, -11); |
| } |
| else |
| { |
| // "larger otherwise", let's pick |x| * 2^-12 , which is slightly over |
| // 2^-11 at x == pi. |
| return deLdExp(deAbs(arg), -12); |
| } |
| } |
| else if (ctx.floatPrecision == glu::PRECISION_MEDIUMP) |
| { |
| if (-DE_PI_DOUBLE <= arg && arg <= DE_PI_DOUBLE) |
| { |
| // from OpenCL half-float extension specification |
| return ctx.format.ulp(ret, 2.0); |
| } |
| else |
| { |
| // |x| * 2^-10, slightly larger than 2 ULP at x == pi |
| return deLdExp(deAbs(arg), -10); |
| } |
| } |
| else |
| { |
| DE_ASSERT(ctx.floatPrecision == glu::PRECISION_LOWP); |
| |
| // from OpenCL half-float extension specification |
| return ctx.format.ulp(ret, 2.0); |
| } |
| } |
| |
| virtual int doGetSlope(double angle) const = 0; |
| |
| Interval m_loExtremum; |
| Interval m_hiExtremum; |
| }; |
| |
| class Sin : public TrigFunc |
| { |
| public: |
| Sin(void) : TrigFunc("sin", deSin, -1.0, 1.0) |
| { |
| } |
| |
| protected: |
| int doGetSlope(double angle) const |
| { |
| return deIntSign(deCos(angle)); |
| } |
| }; |
| |
| ExprP<float> sin(const ExprP<float> &x) |
| { |
| return app<Sin>(x); |
| } |
| |
| class Cos : public TrigFunc |
| { |
| public: |
| Cos(void) : TrigFunc("cos", deCos, -1.0, 1.0) |
| { |
| } |
| |
| protected: |
| int doGetSlope(double angle) const |
| { |
| return -deIntSign(deSin(angle)); |
| } |
| }; |
| |
| ExprP<float> cos(const ExprP<float> &x) |
| { |
| return app<Cos>(x); |
| } |
| |
| DEFINE_DERIVED_FLOAT1(Tan, tan, x, sin(x) * (constant(1.0f) / cos(x))) |
| |
| class ASin : public CFloatFunc1 |
| { |
| public: |
| ASin(void) : CFloatFunc1("asin", deAsin) |
| { |
| } |
| |
| protected: |
| double precision(const EvalContext &ctx, double, double x) const |
| { |
| if (!de::inBounds(x, -1.0, 1.0)) |
| return TCU_NAN; |
| |
| if (ctx.floatPrecision == glu::PRECISION_HIGHP) |
| { |
| // Absolute error of 2^-11 |
| return deLdExp(1.0, -11); |
| } |
| else |
| { |
| // Absolute error of 2^-8 |
| return deLdExp(1.0, -8); |
| } |
| } |
| }; |
| |
| class ArcTrigFunc : public CFloatFunc1 |
| { |
| public: |
| ArcTrigFunc(const string &name, DoubleFunc1 &func, double precisionULPs, const Interval &domain, |
| const Interval &codomain) |
| : CFloatFunc1(name, func) |
| , m_precision(precisionULPs) |
| , m_domain(domain) |
| , m_codomain(codomain) |
| { |
| } |
| |
| protected: |
| double precision(const EvalContext &ctx, double ret, double x) const |
| { |
| if (!m_domain.contains(x)) |
| return TCU_NAN; |
| |
| if (ctx.floatPrecision == glu::PRECISION_HIGHP) |
| { |
| // Use OpenCL's fast relaxed math precision |
| return ctx.format.ulp(ret, m_precision); |
| } |
| else |
| { |
| // Use OpenCL half-float spec |
| return ctx.format.ulp(ret, 2.0); |
| } |
| } |
| |
| // We could implement getCodomain with m_codomain, but choose not to, |
| // because it seems too strict with trascendental constants like pi. |
| |
| const double m_precision; |
| const Interval m_domain; |
| const Interval m_codomain; |
| }; |
| |
| class ACos : public ArcTrigFunc |
| { |
| public: |
| ACos(void) : ArcTrigFunc("acos", deAcos, 4096.0, Interval(-1.0, 1.0), Interval(0.0, DE_PI_DOUBLE)) |
| { |
| } |
| }; |
| |
| class ATan : public ArcTrigFunc |
| { |
| public: |
| ATan(void) |
| : ArcTrigFunc("atan", deAtanOver, 4096.0, Interval::unbounded(), |
| Interval(-DE_PI_DOUBLE * 0.5, DE_PI_DOUBLE * 0.5)) |
| { |
| } |
| }; |
| |
| class ATan2 : public CFloatFunc2 |
| { |
| public: |
| ATan2(void) : CFloatFunc2("atan", deAtan2) |
| { |
| } |
| |
| protected: |
| Interval innerExtrema(const EvalContext &ctx, const Interval &yi, const Interval &xi) const |
| { |
| Interval ret; |
| |
| if (yi.contains(0.0)) |
| { |
| if (xi.contains(0.0)) |
| ret |= TCU_NAN; |
| if (xi.intersects(Interval(-TCU_INFINITY, 0.0))) |
| ret |= Interval(-DE_PI_DOUBLE, DE_PI_DOUBLE); |
| } |
| |
| if (!yi.isFinite(ctx.format.getMaxValue()) || !xi.isFinite(ctx.format.getMaxValue())) |
| { |
| // Infinities may not be supported, allow anything, including NaN |
| ret |= TCU_NAN; |
| } |
| |
| return ret; |
| } |
| |
| double precision(const EvalContext &ctx, double ret, double, double) const |
| { |
| if (ctx.floatPrecision == glu::PRECISION_HIGHP) |
| return ctx.format.ulp(ret, 4096.0); |
| else |
| return ctx.format.ulp(ret, 2.0); |
| } |
| |
| // Codomain could be [-pi, pi], but that would probably be too strict. |
| }; |
| |
| DEFINE_DERIVED_FLOAT1(Sinh, sinh, x, (exp(x) - exp(-x)) / constant(2.0f)) |
| DEFINE_DERIVED_FLOAT1(Cosh, cosh, x, (exp(x) + exp(-x)) / constant(2.0f)) |
| DEFINE_DERIVED_FLOAT1(Tanh, tanh, x, sinh(x) / cosh(x)) |
| |
| // These are not defined as derived forms in the GLSL ES spec, but |
| // that gives us a reasonable precision. |
| DEFINE_DERIVED_FLOAT1(ASinh, asinh, x, log(x + sqrt(x * x + constant(1.0f)))) |
| DEFINE_DERIVED_FLOAT1(ACosh, acosh, x, |
| log(x + |
| sqrt(alternatives((x + constant(1.0f)) * (x - constant(1.0f)), (x * x - constant(1.0f)))))) |
| DEFINE_DERIVED_FLOAT1(ATanh, atanh, x, constant(0.5f) * log((constant(1.0f) + x) / (constant(1.0f) - x))) |
| |
| template <typename T> |
| class GetComponent : public PrimitiveFunc<Signature<typename T::Element, T, int>> |
| { |
| public: |
| typedef typename GetComponent::IRet IRet; |
| |
| string getName(void) const |
| { |
| return "_getComponent"; |
| } |
| |
| void print(ostream &os, const BaseArgExprs &args) const |
| { |
| os << *args[0] << "[" << *args[1] << "]"; |
| } |
| |
| protected: |
| IRet doApply(const EvalContext &, const typename GetComponent::IArgs &iargs) const |
| { |
| IRet ret; |
| |
| for (int compNdx = 0; compNdx < T::SIZE; ++compNdx) |
| { |
| if (iargs.b.contains(compNdx)) |
| ret = unionIVal<typename T::Element>(ret, iargs.a[compNdx]); |
| } |
| |
| return ret; |
| } |
| }; |
| |
| template <typename T> |
| ExprP<typename T::Element> getComponent(const ExprP<T> &container, int ndx) |
| { |
| DE_ASSERT(0 <= ndx && ndx < T::SIZE); |
| return app<GetComponent<T>>(container, constant(ndx)); |
| } |
| |
| template <typename T> |
| string vecNamePrefix(void); |
| template <> |
| string vecNamePrefix<float>(void) |
| { |
| return ""; |
| } |
| template <> |
| string vecNamePrefix<int>(void) |
| { |
| return "i"; |
| } |
| template <> |
| string vecNamePrefix<bool>(void) |
| { |
| return "b"; |
| } |
| |
| template <typename T, int Size> |
| string vecName(void) |
| { |
| return vecNamePrefix<T>() + "vec" + de::toString(Size); |
| } |
| |
| template <typename T, int Size> |
| class GenVec; |
| |
| template <typename T> |
| class GenVec<T, 1> : public DerivedFunc<Signature<T, T>> |
| { |
| public: |
| typedef typename GenVec<T, 1>::ArgExprs ArgExprs; |
| |
| string getName(void) const |
| { |
| return "_" + vecName<T, 1>(); |
| } |
| |
| protected: |
| ExprP<T> doExpand(ExpandContext &, const ArgExprs &args) const |
| { |
| return args.a; |
| } |
| }; |
| |
| template <typename T> |
| class GenVec<T, 2> : public PrimitiveFunc<Signature<Vector<T, 2>, T, T>> |
| { |
| public: |
| typedef typename GenVec::IRet IRet; |
| typedef typename GenVec::IArgs IArgs; |
| |
| string getName(void) const |
| { |
| return vecName<T, 2>(); |
| } |
| |
| protected: |
| IRet doApply(const EvalContext &, const IArgs &iargs) const |
| { |
| return IRet(iargs.a, iargs.b); |
| } |
| }; |
| |
| template <typename T> |
| class GenVec<T, 3> : public PrimitiveFunc<Signature<Vector<T, 3>, T, T, T>> |
| { |
| public: |
| typedef typename GenVec::IRet IRet; |
| typedef typename GenVec::IArgs IArgs; |
| |
| string getName(void) const |
| { |
| return vecName<T, 3>(); |
| } |
| |
| protected: |
| IRet doApply(const EvalContext &, const IArgs &iargs) const |
| { |
| return IRet(iargs.a, iargs.b, iargs.c); |
| } |
| }; |
| |
| template <typename T> |
| class GenVec<T, 4> : public PrimitiveFunc<Signature<Vector<T, 4>, T, T, T, T>> |
| { |
| public: |
| typedef typename GenVec::IRet IRet; |
| typedef typename GenVec::IArgs IArgs; |
| |
| string getName(void) const |
| { |
| return vecName<T, 4>(); |
| } |
| |
| protected: |
| IRet doApply(const EvalContext &, const IArgs &iargs) const |
| { |
| return IRet(iargs.a, iargs.b, iargs.c, iargs.d); |
| } |
| }; |
| |
| template <typename T, int Rows, int Columns> |
| class GenMat; |
| |
| template <typename T, int Rows> |
| class GenMat<T, Rows, 2> : public PrimitiveFunc<Signature<Matrix<T, Rows, 2>, Vector<T, Rows>, Vector<T, Rows>>> |
| { |
| public: |
| typedef typename GenMat::Ret Ret; |
| typedef typename GenMat::IRet IRet; |
| typedef typename GenMat::IArgs IArgs; |
| |
| string getName(void) const |
| { |
| return dataTypeNameOf<Ret>(); |
| } |
| |
| protected: |
| IRet doApply(const EvalContext &, const IArgs &iargs) const |
| { |
| IRet ret; |
| ret[0] = iargs.a; |
| ret[1] = iargs.b; |
| return ret; |
| } |
| }; |
| |
| template <typename T, int Rows> |
| class GenMat<T, Rows, 3> |
| : public PrimitiveFunc<Signature<Matrix<T, Rows, 3>, Vector<T, Rows>, Vector<T, Rows>, Vector<T, Rows>>> |
| { |
| public: |
| typedef typename GenMat::Ret Ret; |
| typedef typename GenMat::IRet IRet; |
| typedef typename GenMat::IArgs IArgs; |
| |
| string getName(void) const |
| { |
| return dataTypeNameOf<Ret>(); |
| } |
| |
| protected: |
| IRet doApply(const EvalContext &, const IArgs &iargs) const |
| { |
| IRet ret; |
| ret[0] = iargs.a; |
| ret[1] = iargs.b; |
| ret[2] = iargs.c; |
| return ret; |
| } |
| }; |
| |
| template <typename T, int Rows> |
| class GenMat<T, Rows, 4> |
| : public PrimitiveFunc< |
| Signature<Matrix<T, Rows, 4>, Vector<T, Rows>, Vector<T, Rows>, Vector<T, Rows>, Vector<T, Rows>>> |
| { |
| public: |
| typedef typename GenMat::Ret Ret; |
| typedef typename GenMat::IRet IRet; |
| typedef typename GenMat::IArgs IArgs; |
| |
| string getName(void) const |
| { |
| return dataTypeNameOf<Ret>(); |
| } |
| |
| protected: |
| IRet doApply(const EvalContext &, const IArgs &iargs) const |
| { |
| IRet ret; |
| ret[0] = iargs.a; |
| ret[1] = iargs.b; |
| ret[2] = iargs.c; |
| ret[3] = iargs.d; |
| return ret; |
| } |
| }; |
| |
| template <typename T, int Rows> |
| ExprP<Matrix<T, Rows, 2>> mat2(const ExprP<Vector<T, Rows>> &arg0, const ExprP<Vector<T, Rows>> &arg1) |
| { |
| return app<GenMat<T, Rows, 2>>(arg0, arg1); |
| } |
| |
| template <typename T, int Rows> |
| ExprP<Matrix<T, Rows, 3>> mat3(const ExprP<Vector<T, Rows>> &arg0, const ExprP<Vector<T, Rows>> &arg1, |
| const ExprP<Vector<T, Rows>> &arg2) |
| { |
| return app<GenMat<T, Rows, 3>>(arg0, arg1, arg2); |
| } |
| |
| template <typename T, int Rows> |
| ExprP<Matrix<T, Rows, 4>> mat4(const ExprP<Vector<T, Rows>> &arg0, const ExprP<Vector<T, Rows>> &arg1, |
| const ExprP<Vector<T, Rows>> &arg2, const ExprP<Vector<T, Rows>> &arg3) |
| { |
| return app<GenMat<T, Rows, 4>>(arg0, arg1, arg2, arg3); |
| } |
| |
| template <int Rows, int Cols> |
| class MatNeg : public PrimitiveFunc<Signature<Matrix<float, Rows, Cols>, Matrix<float, Rows, Cols>>> |
| { |
| public: |
| typedef typename MatNeg::IRet IRet; |
| typedef typename MatNeg::IArgs IArgs; |
| |
| string getName(void) const |
| { |
| return "_matNeg"; |
| } |
| |
| protected: |
| void doPrint(ostream &os, const BaseArgExprs &args) const |
| { |
| os << "-(" << *args[0] << ")"; |
| } |
| |
| IRet doApply(const EvalContext &, const IArgs &iargs) const |
| { |
| IRet ret; |
| |
| for (int col = 0; col < Cols; ++col) |
| { |
| for (int row = 0; row < Rows; ++row) |
| ret[col][row] = -iargs.a[col][row]; |
| } |
| |
| return ret; |
| } |
| }; |
| |
| template <typename T, typename Sig> |
| class CompWiseFunc : public PrimitiveFunc<Sig> |
| { |
| public: |
| typedef Func<Signature<T, T, T>> ScalarFunc; |
| |
| string getName(void) const |
| { |
| return doGetScalarFunc().getName(); |
| } |
| |
| protected: |
| void doPrint(ostream &os, const BaseArgExprs &args) const |
| { |
| doGetScalarFunc().print(os, args); |
| } |
| |
| virtual const ScalarFunc &doGetScalarFunc(void) const = 0; |
| }; |
| |
| template <int Rows, int Cols> |
| class CompMatFuncBase |
| : public CompWiseFunc<float, |
| Signature<Matrix<float, Rows, Cols>, Matrix<float, Rows, Cols>, Matrix<float, Rows, Cols>>> |
| { |
| public: |
| typedef typename CompMatFuncBase::IRet IRet; |
| typedef typename CompMatFuncBase::IArgs IArgs; |
| |
| protected: |
| IRet doApply(const EvalContext &ctx, const IArgs &iargs) const |
| { |
| IRet ret; |
| |
| for (int col = 0; col < Cols; ++col) |
| { |
| for (int row = 0; row < Rows; ++row) |
| ret[col][row] = this->doGetScalarFunc().apply(ctx, iargs.a[col][row], iargs.b[col][row]); |
| } |
| |
| return ret; |
| } |
| }; |
| |
| template <typename F, int Rows, int Cols> |
| class CompMatFunc : public CompMatFuncBase<Rows, Cols> |
| { |
| protected: |
| const typename CompMatFunc::ScalarFunc &doGetScalarFunc(void) const |
| { |
| return instance<F>(); |
| } |
| }; |
| |
| class ScalarMatrixCompMult : public Mul |
| { |
| public: |
| string getName(void) const |
| { |
| return "matrixCompMult"; |
| } |
| |
| void doPrint(ostream &os, const BaseArgExprs &args) const |
| { |
| Func<Sig>::doPrint(os, args); |
| } |
| }; |
| |
| template <int Rows, int Cols> |
| class MatrixCompMult : public CompMatFunc<ScalarMatrixCompMult, Rows, Cols> |
| { |
| }; |
| |
| template <int Rows, int Cols> |
| class ScalarMatFuncBase |
| : public CompWiseFunc<float, Signature<Matrix<float, Rows, Cols>, Matrix<float, Rows, Cols>, float>> |
| { |
| public: |
| typedef typename ScalarMatFuncBase::IRet IRet; |
| typedef typename ScalarMatFuncBase::IArgs IArgs; |
| |
| protected: |
| IRet doApply(const EvalContext &ctx, const IArgs &iargs) const |
| { |
| IRet ret; |
| |
| for (int col = 0; col < Cols; ++col) |
| { |
| for (int row = 0; row < Rows; ++row) |
| ret[col][row] = this->doGetScalarFunc().apply(ctx, iargs.a[col][row], iargs.b); |
| } |
| |
| return ret; |
| } |
| }; |
| |
| template <typename F, int Rows, int Cols> |
| class ScalarMatFunc : public ScalarMatFuncBase<Rows, Cols> |
| { |
| protected: |
| const typename ScalarMatFunc::ScalarFunc &doGetScalarFunc(void) const |
| { |
| return instance<F>(); |
| } |
| }; |
| |
| template <typename T, int Size> |
| struct GenXType; |
| |
| template <typename T> |
| struct GenXType<T, 1> |
| { |
| static ExprP<T> genXType(const ExprP<T> &x) |
| { |
| return x; |
| } |
| }; |
| |
| template <typename T> |
| struct GenXType<T, 2> |
| { |
| static ExprP<Vector<T, 2>> genXType(const ExprP<T> &x) |
| { |
| return app<GenVec<T, 2>>(x, x); |
| } |
| }; |
| |
| template <typename T> |
| struct GenXType<T, 3> |
| { |
| static ExprP<Vector<T, 3>> genXType(const ExprP<T> &x) |
| { |
| return app<GenVec<T, 3>>(x, x, x); |
| } |
| }; |
| |
| template <typename T> |
| struct GenXType<T, 4> |
| { |
| static ExprP<Vector<T, 4>> genXType(const ExprP<T> &x) |
| { |
| return app<GenVec<T, 4>>(x, x, x, x); |
| } |
| }; |
| |
| //! Returns an expression of vector of size `Size` (or scalar if Size == 1), |
| //! with each element initialized with the expression `x`. |
| template <typename T, int Size> |
| ExprP<typename ContainerOf<T, Size>::Container> genXType(const ExprP<T> &x) |
| { |
| return GenXType<T, Size>::genXType(x); |
| } |
| |
| typedef GenVec<float, 2> FloatVec2; |
| DEFINE_CONSTRUCTOR2(FloatVec2, Vec2, vec2, float, float) |
| |
| typedef GenVec<float, 3> FloatVec3; |
| DEFINE_CONSTRUCTOR3(FloatVec3, Vec3, vec3, float, float, float) |
| |
| typedef GenVec<float, 4> FloatVec4; |
| DEFINE_CONSTRUCTOR4(FloatVec4, Vec4, vec4, float, float, float, float) |
| |
| template <int Size> |
| class Dot : public DerivedFunc<Signature<float, Vector<float, Size>, Vector<float, Size>>> |
| { |
| public: |
| typedef typename Dot::ArgExprs ArgExprs; |
| |
| string getName(void) const |
| { |
| return "dot"; |
| } |
| |
| protected: |
| ExprP<float> doExpand(ExpandContext &, const ArgExprs &args) const |
| { |
| ExprP<float> op[Size]; |
| // Precompute all products. |
| for (int ndx = 0; ndx < Size; ++ndx) |
| op[ndx] = args.a[ndx] * args.b[ndx]; |
| |
| int idx[Size]; |
| //Prepare an array of indices. |
| for (int ndx = 0; ndx < Size; ++ndx) |
| idx[ndx] = ndx; |
| |
| ExprP<float> res = op[0]; |
| // Compute the first dot alternative: SUM(a[i]*b[i]), i = 0 .. Size-1 |
| for (int ndx = 1; ndx < Size; ++ndx) |
| res = res + op[ndx]; |
| |
| // Generate all permutations of indices and |
| // using a permutation compute a dot alternative. |
| // Generates all possible variants fo summation of products in the dot product expansion expression. |
| do |
| { |
| ExprP<float> alt = constant(0.0f); |
| for (int ndx = 0; ndx < Size; ++ndx) |
| alt = alt + op[idx[ndx]]; |
| res = alternatives(res, alt); |
| } while (std::next_permutation(idx, idx + Size)); |
| |
| return res; |
| } |
| }; |
| |
| template <> |
| class Dot<1> : public DerivedFunc<Signature<float, float, float>> |
| { |
| public: |
| string getName(void) const |
| { |
| return "dot"; |
| } |
| |
| ExprP<float> doExpand(ExpandContext &, const ArgExprs &args) const |
| { |
| return args.a * args.b; |
| } |
| }; |
| |
| template <int Size> |
| ExprP<float> dot(const ExprP<Vector<float, Size>> &x, const ExprP<Vector<float, Size>> &y) |
| { |
| return app<Dot<Size>>(x, y); |
| } |
| |
| ExprP<float> dot(const ExprP<float> &x, const ExprP<float> &y) |
| { |
| return app<Dot<1>>(x, y); |
| } |
| |
| template <int Size> |
| class Length : public DerivedFunc<Signature<float, typename ContainerOf<float, Size>::Container>> |
| { |
| public: |
| typedef typename Length::ArgExprs ArgExprs; |
| |
| string getName(void) const |
| { |
| return "length"; |
| } |
| |
| protected: |
| ExprP<float> doExpand(ExpandContext &, const ArgExprs &args) const |
| { |
| return sqrt(dot(args.a, args.a)); |
| } |
| }; |
| |
| template <int Size> |
| ExprP<float> length(const ExprP<typename ContainerOf<float, Size>::Container> &x) |
| { |
| return app<Length<Size>>(x); |
| } |
| |
| template <int Size> |
| class Distance |
| : public DerivedFunc< |
| Signature<float, typename ContainerOf<float, Size>::Container, typename ContainerOf<float, Size>::Container>> |
| { |
| public: |
| typedef typename Distance::Ret Ret; |
| typedef typename Distance::ArgExprs ArgExprs; |
| |
| string getName(void) const |
| { |
| return "distance"; |
| } |
| |
| protected: |
| ExprP<Ret> doExpand(ExpandContext &, const ArgExprs &args) const |
| { |
| return length<Size>(args.a - args.b); |
| } |
| }; |
| |
| // cross |
| |
| class Cross : public DerivedFunc<Signature<Vec3, Vec3, Vec3>> |
| { |
| public: |
| string getName(void) const |
| { |
| return "cross"; |
| } |
| |
| protected: |
| ExprP<Vec3> doExpand(ExpandContext &, const ArgExprs &x) const |
| { |
| return vec3(x.a[1] * x.b[2] - x.b[1] * x.a[2], x.a[2] * x.b[0] - x.b[2] * x.a[0], |
| x.a[0] * x.b[1] - x.b[0] * x.a[1]); |
| } |
| }; |
| |
| DEFINE_CONSTRUCTOR2(Cross, Vec3, cross, Vec3, Vec3) |
| |
| template <int Size> |
| class Normalize |
| : public DerivedFunc< |
| Signature<typename ContainerOf<float, Size>::Container, typename ContainerOf<float, Size>::Container>> |
| { |
| public: |
| typedef typename Normalize::Ret Ret; |
| typedef typename Normalize::ArgExprs ArgExprs; |
| |
| string getName(void) const |
| { |
| return "normalize"; |
| } |
| |
| protected: |
| ExprP<Ret> doExpand(ExpandContext &, const ArgExprs &args) const |
| { |
| return args.a / length<Size>(args.a); |
| } |
| }; |
| |
| template <int Size> |
| class FaceForward |
| : public DerivedFunc< |
| Signature<typename ContainerOf<float, Size>::Container, typename ContainerOf<float, Size>::Container, |
| typename ContainerOf<float, Size>::Container, typename ContainerOf<float, Size>::Container>> |
| { |
| public: |
| typedef typename FaceForward::Ret Ret; |
| typedef typename FaceForward::ArgExprs ArgExprs; |
| |
| string getName(void) const |
| { |
| return "faceforward"; |
| } |
| |
| protected: |
| ExprP<Ret> doExpand(ExpandContext &, const ArgExprs &args) const |
| { |
| return cond(dot(args.c, args.b) < constant(0.0f), args.a, -args.a); |
| } |
| }; |
| |
| template <int Size, typename Ret, typename Arg0, typename Arg1> |
| struct ApplyReflect |
| { |
| static ExprP<Ret> apply(ExpandContext &ctx, const ExprP<Arg0> &i, const ExprP<Arg1> &n) |
| { |
| const ExprP<float> dotNI = bindExpression("dotNI", ctx, dot(n, i)); |
| |
| return i - alternatives((n * dotNI) * constant(2.0f), n * (dotNI * constant(2.0f))); |
| } |
| }; |
| |
| template <typename Ret, typename Arg0, typename Arg1> |
| struct ApplyReflect<1, Ret, Arg0, Arg1> |
| { |
| static ExprP<Ret> apply(ExpandContext &, const ExprP<Arg0> &i, const ExprP<Arg1> &n) |
| { |
| return i - alternatives(alternatives((n * (n * i)) * constant(2.0f), n * ((n * i) * constant(2.0f))), |
| (n * n) * (i * constant(2.0f))); |
| } |
| }; |
| |
| template <int Size> |
| class Reflect |
| : public DerivedFunc< |
| Signature<typename ContainerOf<float, Size>::Container, typename ContainerOf<float, Size>::Container, |
| typename ContainerOf<float, Size>::Container>> |
| { |
| public: |
| typedef typename Reflect::Ret Ret; |
| typedef typename Reflect::Arg0 Arg0; |
| typedef typename Reflect::Arg1 Arg1; |
| typedef typename Reflect::ArgExprs ArgExprs; |
| |
| string getName(void) const |
| { |
| return "reflect"; |
| } |
| |
| protected: |
| ExprP<Ret> doExpand(ExpandContext &ctx, const ArgExprs &args) const |
| { |
| const ExprP<Arg0> &i = args.a; |
| const ExprP<Arg1> &n = args.b; |
| |
| return ApplyReflect<Size, Ret, Arg0, Arg1>::apply(ctx, i, n); |
| } |
| }; |
| |
| template <int Size> |
| class Refract |
| : public DerivedFunc< |
| Signature<typename ContainerOf<float, Size>::Container, typename ContainerOf<float, Size>::Container, |
| typename ContainerOf<float, Size>::Container, float>> |
| { |
| public: |
| typedef typename Refract::Ret Ret; |
| typedef typename Refract::Arg0 Arg0; |
| typedef typename Refract::Arg1 Arg1; |
| typedef typename Refract::ArgExprs ArgExprs; |
| |
| string getName(void) const |
| { |
| return "refract"; |
| } |
| |
| protected: |
| ExprP<Ret> doExpand(ExpandContext &ctx, const ArgExprs &args) const |
| { |
| const ExprP<Arg0> &i = args.a; |
| const ExprP<Arg1> &n = args.b; |
| const ExprP<float> &eta = args.c; |
| const ExprP<float> dotNI = bindExpression("dotNI", ctx, dot(n, i)); |
| const ExprP<float> k1 = |
| bindExpression("k1", ctx, constant(1.0f) - eta * eta * (constant(1.0f) - dotNI * dotNI)); |
| |
| const ExprP<float> k2 = |
| bindExpression("k2", ctx, (((dotNI * (-dotNI)) + constant(1.0f)) * eta) * (-eta) + constant(1.0f)); |
| const ExprP<float> k = bindExpression("k", ctx, alternatives(k1, k2)); |
| |
| return cond(k < constant(0.0f), genXType<float, Size>(constant(0.0f)), i * eta - n * (eta * dotNI + sqrt(k))); |
| } |
| }; |
| |
| class PreciseFunc1 : public CFloatFunc1 |
| { |
| public: |
| PreciseFunc1(const string &name, DoubleFunc1 &func) : CFloatFunc1(name, func) |
| { |
| } |
| |
| protected: |
| double precision(const EvalContext &, double, double) const |
| { |
| return 0.0; |
| } |
| }; |
| |
| class Abs : public PreciseFunc1 |
| { |
| public: |
| Abs(void) : PreciseFunc1("abs", deAbs) |
| { |
| } |
| }; |
| |
| class Sign : public PreciseFunc1 |
| { |
| public: |
| Sign(void) : PreciseFunc1("sign", deSign) |
| { |
| } |
| }; |
| |
| class Floor : public PreciseFunc1 |
| { |
| public: |
| Floor(void) : PreciseFunc1("floor", deFloor) |
| { |
| } |
| }; |
| |
| class Trunc : public PreciseFunc1 |
| { |
| public: |
| Trunc(void) : PreciseFunc1("trunc", deTrunc) |
| { |
| } |
| }; |
| |
| class Round : public FloatFunc1 |
| { |
| public: |
| string getName(void) const |
| { |
| return "round"; |
| } |
| |
| protected: |
| Interval applyPoint(const EvalContext &, double x) const |
| { |
| double truncated = 0.0; |
| const double fract = deModf(x, &truncated); |
| Interval ret; |
| |
| if (fabs(fract) <= 0.5) |
| ret |= truncated; |
| if (fabs(fract) >= 0.5) |
| ret |= truncated + deSign(fract); |
| |
| return ret; |
| } |
| |
| double precision(const EvalContext &, double, double) const |
| { |
| return 0.0; |
| } |
| }; |
| |
| class RoundEven : public PreciseFunc1 |
| { |
| public: |
| RoundEven(void) : PreciseFunc1("roundEven", deRoundEven) |
| { |
| } |
| }; |
| |
| class Ceil : public PreciseFunc1 |
| { |
| public: |
| Ceil(void) : PreciseFunc1("ceil", deCeil) |
| { |
| } |
| }; |
| |
| DEFINE_DERIVED_FLOAT1(Fract, fract, x, x - app<Floor>(x)) |
| |
| class PreciseFunc2 : public CFloatFunc2 |
| { |
| public: |
| PreciseFunc2(const string &name, DoubleFunc2 &func) : CFloatFunc2(name, func) |
| { |
| } |
| |
| protected: |
| double precision(const EvalContext &, double, double, double) const |
| { |
| return 0.0; |
| } |
| }; |
| |
| DEFINE_DERIVED_FLOAT2(Mod, mod, x, y, x - y * app<Floor>(x / y)) |
| |
| class Modf : public PrimitiveFunc<Signature<float, float, float>> |
| { |
| public: |
| string getName(void) const |
| { |
| return "modf"; |
| } |
| |
| protected: |
| IRet doApply(const EvalContext &ctx, const IArgs &iargs) const |
| { |
| Interval fracIV; |
| Interval &wholeIV = const_cast<Interval &>(iargs.b); |
| double intPart = 0; |
| |
| TCU_INTERVAL_APPLY_MONOTONE1(fracIV, x, iargs.a, frac, frac = deModf(x, &intPart)); |
| TCU_INTERVAL_APPLY_MONOTONE1(wholeIV, x, iargs.a, whole, deModf(x, &intPart); whole = intPart); |
| |
| if (!iargs.a.isFinite(ctx.format.getMaxValue())) |
| { |
| // Behavior on modf(Inf) not well-defined, allow anything as a fractional part |
| // See Khronos bug 13907 |
| fracIV |= TCU_NAN; |
| } |
| |
| return fracIV; |
| } |
| |
| int getOutParamIndex(void) const |
| { |
| return 1; |
| } |
| }; |
| |
| int compare(const EvalContext &ctx, double x, double y) |
| { |
| if (ctx.format.hasSubnormal() != tcu::YES) |
| { |
| const int minExp = ctx.format.getMinExp(); |
| const int fractionBits = ctx.format.getFractionBits(); |
| const double minQuantum = deLdExp(1.0f, minExp - fractionBits); |
| const double minNormalized = deLdExp(1.0f, minExp); |
| const double maxSubnormal = minNormalized - minQuantum; |
| const double minSubnormal = -maxSubnormal; |
| |
| if (minSubnormal <= x && x <= maxSubnormal && minSubnormal <= y && y <= maxSubnormal) |
| return 0; |
| } |
| |
| if (x < y) |
| return -1; |
| if (y < x) |
| return 1; |
| return 0; |
| } |
| |
| class MinMaxFunc : public FloatFunc2 |
| { |
| public: |
| MinMaxFunc(const string &name, int sign) : m_name(name), m_sign(sign) |
| { |
| } |
| |
| string getName(void) const |
| { |
| return m_name; |
| } |
| |
| protected: |
| Interval applyPoint(const EvalContext &ctx, double x, double y) const |
| { |
| const int cmp = compare(ctx, x, y) * m_sign; |
| |
| if (cmp > 0) |
| return x; |
| if (cmp < 0) |
| return y; |
| |
| // An implementation without subnormals may not be able to distinguish |
| // between x and y even when they're not equal in host arithmetic. |
| return Interval(x, y); |
| } |
| |
| double precision(const EvalContext &, double, double, double) const |
| { |
| return 0.0; |
| } |
| |
| const string m_name; |
| const int m_sign; |
| }; |
| |
| class Min : public MinMaxFunc |
| { |
| public: |
| Min(void) : MinMaxFunc("min", -1) |
| { |
| } |
| }; |
| class Max : public MinMaxFunc |
| { |
| public: |
| Max(void) : MinMaxFunc("max", 1) |
| { |
| } |
| }; |
| |
| class Clamp : public FloatFunc3 |
| { |
| public: |
| string getName(void) const |
| { |
| return "clamp"; |
| } |
| |
| protected: |
| Interval applyPoint(const EvalContext &ctx, double x, double minVal, double maxVal) const |
| { |
| if (minVal > maxVal) |
| return TCU_NAN; |
| |
| const int cmpMin = compare(ctx, x, minVal); |
| const int cmpMax = compare(ctx, x, maxVal); |
| const int cmpMinMax = compare(ctx, minVal, maxVal); |
| |
| if (cmpMin < 0) |
| { |
| if (cmpMinMax < 0) |
| return minVal; |
| else |
| return Interval(minVal, maxVal); |
| } |
| if (cmpMax > 0) |
| { |
| if (cmpMinMax < 0) |
| return maxVal; |
| else |
| return Interval(minVal, maxVal); |
| } |
| |
| Interval result = x; |
| if (cmpMin == 0) |
| result |= minVal; |
| if (cmpMax == 0) |
| result |= maxVal; |
| return result; |
| } |
| |
| double precision(const EvalContext &, double, double, double minVal, double maxVal) const |
| { |
| return minVal > maxVal ? TCU_NAN : 0.0; |
| } |
| }; |
| |
| ExprP<float> clamp(const ExprP<float> &x, const ExprP<float> &minVal, const ExprP<float> &maxVal) |
| { |
| return app<Clamp>(x, minVal, maxVal); |
| } |
| |
| DEFINE_DERIVED_FLOAT3(Mix, mix, x, y, a, alternatives((x * (constant(1.0f) - a)) + y * a, x + (y - x) * a)) |
| |
| static double step(double edge, double x) |
| { |
| return x < edge ? 0.0 : 1.0; |
| } |
| |
| class Step : public PreciseFunc2 |
| { |
| public: |
| Step(void) : PreciseFunc2("step", step) |
| { |
| } |
| }; |
| |
| class SmoothStep : public DerivedFunc<Signature<float, float, float, float>> |
| { |
| public: |
| string getName(void) const |
| { |
| return "smoothstep"; |
| } |
| |
| protected: |
| ExprP<Ret> doExpand(ExpandContext &ctx, const ArgExprs &args) const |
| { |
| const ExprP<float> &edge0 = args.a; |
| const ExprP<float> &edge1 = args.b; |
| const ExprP<float> &x = args.c; |
| const ExprP<float> tExpr = clamp((x - edge0) / (edge1 - edge0), constant(0.0f), constant(1.0f)); |
| const ExprP<float> t = bindExpression("t", ctx, tExpr); |
| |
| return (t * t * (constant(3.0f) - constant(2.0f) * t)); |
| } |
| }; |
| |
| class FrExp : public PrimitiveFunc<Signature<float, float, int>> |
| { |
| public: |
| string getName(void) const |
| { |
| return "frexp"; |
| } |
| |
| protected: |
| IRet doApply(const EvalContext &, const IArgs &iargs) const |
| { |
| IRet ret; |
| const IArg0 &x = iargs.a; |
| IArg1 &exponent = const_cast<IArg1 &>(iargs.b); |
| |
| if (x.hasNaN() || x.contains(TCU_INFINITY) || x.contains(-TCU_INFINITY)) |
| { |
| // GLSL (in contrast to IEEE) says that result of applying frexp |
| // to infinity is undefined |
| ret = Interval::unbounded() | TCU_NAN; |
| exponent = Interval(-deLdExp(1.0, 31), deLdExp(1.0, 31) - 1); |
| } |
| else if (!x.empty()) |
| { |
| int loExp = 0; |
| const double loFrac = deFrExp(x.lo(), &loExp); |
| int hiExp = 0; |
| const double hiFrac = deFrExp(x.hi(), &hiExp); |
| |
| if (deSign(loFrac) != deSign(hiFrac)) |
| { |
| exponent = Interval(-TCU_INFINITY, de::max(loExp, hiExp)); |
| ret = Interval(); |
| if (deSign(loFrac) < 0) |
| ret |= Interval(-1.0 + DBL_EPSILON * 0.5, 0.0); |
| if (deSign(hiFrac) > 0) |
| ret |= Interval(0.0, 1.0 - DBL_EPSILON * 0.5); |
| } |
| else |
| { |
| exponent = Interval(loExp, hiExp); |
| if (loExp == hiExp) |
| ret = Interval(loFrac, hiFrac); |
| else |
| ret = deSign(loFrac) * Interval(0.5, 1.0 - DBL_EPSILON * 0.5); |
| } |
| } |
| |
| return ret; |
| } |
| |
| int getOutParamIndex(void) const |
| { |
| return 1; |
| } |
| }; |
| |
| class LdExp : public PrimitiveFunc<Signature<float, float, int>> |
| { |
| public: |
| string getName(void) const |
| { |
| return "ldexp"; |
| } |
| |
| protected: |
| Interval doApply(const EvalContext &ctx, const IArgs &iargs) const |
| { |
| Interval ret = call<Exp2>(ctx, iargs.b); |
| // Khronos bug 11180 consensus: if exp2(exponent) cannot be represented, |
| // the result is undefined. |
| |
| if (ret.contains(TCU_INFINITY) || ret.contains(-TCU_INFINITY)) |
| ret |= TCU_NAN; |
| |
| return call<Mul>(ctx, iargs.a, ret); |
| } |
| }; |
| |
| template <int Rows, int Columns> |
| class Transpose : public PrimitiveFunc<Signature<Matrix<float, Rows, Columns>, Matrix<float, Columns, Rows>>> |
| { |
| public: |
| typedef typename Transpose::IRet IRet; |
| typedef typename Transpose::IArgs IArgs; |
| |
| string getName(void) const |
| { |
| return "transpose"; |
| } |
| |
| protected: |
| IRet doApply(const EvalContext &, const IArgs &iargs) const |
| { |
| IRet ret; |
| |
| for (int rowNdx = 0; rowNdx < Rows; ++rowNdx) |
| { |
| for (int colNdx = 0; colNdx < Columns; ++colNdx) |
| ret(rowNdx, colNdx) = iargs.a(colNdx, rowNdx); |
| } |
| |
| return ret; |
| } |
| }; |
| |
| template <typename Ret, typename Arg0, typename Arg1> |
| class MulFunc : public PrimitiveFunc<Signature<Ret, Arg0, Arg1>> |
| { |
| public: |
| string getName(void) const |
| { |
| return "mul"; |
| } |
| |
| protected: |
| void doPrint(ostream &os, const BaseArgExprs &args) const |
| { |
| os << "(" << *args[0] << " * " << *args[1] << ")"; |
| } |
| }; |
| |
| template <int LeftRows, int Middle, int RightCols> |
| class MatMul : public MulFunc<Matrix<float, LeftRows, RightCols>, Matrix<float, LeftRows, Middle>, |
| Matrix<float, Middle, RightCols>> |
| { |
| protected: |
| typedef typename MatMul::IRet IRet; |
| typedef typename MatMul::IArgs IArgs; |
| typedef typename MatMul::IArg0 IArg0; |
| typedef typename MatMul::IArg1 IArg1; |
| |
| IRet doApply(const EvalContext &ctx, const IArgs &iargs) const |
| { |
| const IArg0 &left = iargs.a; |
| const IArg1 &right = iargs.b; |
| IRet ret; |
| |
| for (int row = 0; row < LeftRows; ++row) |
| { |
| for (int col = 0; col < RightCols; ++col) |
| { |
| Interval element(0.0); |
| |
| for (int ndx = 0; ndx < Middle; ++ndx) |
| element = call<Add>(ctx, element, call<Mul>(ctx, left[ndx][row], right[col][ndx])); |
| |
| ret[col][row] = element; |
| } |
| } |
| |
| return ret; |
| } |
| }; |
| |
| template <int Rows, int Cols> |
| class VecMatMul : public MulFunc<Vector<float, Cols>, Vector<float, Rows>, Matrix<float, Rows, Cols>> |
| { |
| public: |
| typedef typename VecMatMul::IRet IRet; |
| typedef typename VecMatMul::IArgs IArgs; |
| typedef typename VecMatMul::IArg0 IArg0; |
| typedef typename VecMatMul::IArg1 IArg1; |
| |
| protected: |
| IRet doApply(const EvalContext &ctx, const IArgs &iargs) const |
| { |
| const IArg0 &left = iargs.a; |
| const IArg1 &right = iargs.b; |
| IRet ret; |
| |
| for (int col = 0; col < Cols; ++col) |
| { |
| Interval element(0.0); |
| |
| for (int row = 0; row < Rows; ++row) |
| element = call<Add>(ctx, element, call<Mul>(ctx, left[row], right[col][row])); |
| |
| ret[col] = element; |
| } |
| |
| return ret; |
| } |
| }; |
| |
| template <int Rows, int Cols> |
| class MatVecMul : public MulFunc<Vector<float, Rows>, Matrix<float, Rows, Cols>, Vector<float, Cols>> |
| { |
| public: |
| typedef typename MatVecMul::IRet IRet; |
| typedef typename MatVecMul::IArgs IArgs; |
| typedef typename MatVecMul::IArg0 IArg0; |
| typedef typename MatVecMul::IArg1 IArg1; |
| |
| protected: |
| IRet doApply(const EvalContext &ctx, const IArgs &iargs) const |
| { |
| const IArg0 &left = iargs.a; |
| const IArg1 &right = iargs.b; |
| |
| return call<VecMatMul<Cols, Rows>>(ctx, right, call<Transpose<Rows, Cols>>(ctx, left)); |
| } |
| }; |
| |
| template <int Rows, int Cols> |
| class OuterProduct |
| : public PrimitiveFunc<Signature<Matrix<float, Rows, Cols>, Vector<float, Rows>, Vector<float, Cols>>> |
| { |
| public: |
| typedef typename OuterProduct::IRet IRet; |
| typedef typename OuterProduct::IArgs IArgs; |
| |
| string getName(void) const |
| { |
| return "outerProduct"; |
| } |
| |
| protected: |
| IRet doApply(const EvalContext &ctx, const IArgs &iargs) const |
| { |
| IRet ret; |
| |
| for (int row = 0; row < Rows; ++row) |
| { |
| for (int col = 0; col < Cols; ++col) |
| ret[col][row] = call<Mul>(ctx, iargs.a[row], iargs.b[col]); |
| } |
| |
| return ret; |
| } |
| }; |
| |
| template <int Rows, int Cols> |
| ExprP<Matrix<float, Rows, Cols>> outerProduct(const ExprP<Vector<float, Rows>> &left, |
| const ExprP<Vector<float, Cols>> &right) |
| { |
| return app<OuterProduct<Rows, Cols>>(left, right); |
| } |
| |
| template <int Size> |
| class DeterminantBase : public DerivedFunc<Signature<float, Matrix<float, Size, Size>>> |
| { |
| public: |
| string getName(void) const |
| { |
| return "determinant"; |
| } |
| }; |
| |
| template <int Size> |
| class Determinant; |
| |
| template <int Size> |
| ExprP<float> determinant(ExprP<Matrix<float, Size, Size>> mat) |
| { |
| return app<Determinant<Size>>(mat); |
| } |
| |
| template <> |
| class Determinant<2> : public DeterminantBase<2> |
| { |
| protected: |
| ExprP<Ret> doExpand(ExpandContext &, const ArgExprs &args) const |
| { |
| ExprP<Mat2> mat = args.a; |
| |
| return mat[0][0] * mat[1][1] - mat[1][0] * mat[0][1]; |
| } |
| }; |
| |
| template <> |
| class Determinant<3> : public DeterminantBase<3> |
| { |
| protected: |
| ExprP<Ret> doExpand(ExpandContext &, const ArgExprs &args) const |
| { |
| ExprP<Mat3> mat = args.a; |
| |
| return (mat[0][0] * (mat[1][1] * mat[2][2] - mat[1][2] * mat[2][1]) + |
| mat[0][1] * (mat[1][2] * mat[2][0] - mat[1][0] * mat[2][2]) + |
| mat[0][2] * (mat[1][0] * mat[2][1] - mat[1][1] * mat[2][0])); |
| } |
| }; |
| |
| template <> |
| class Determinant<4> : public DeterminantBase<4> |
| { |
| protected: |
| ExprP<Ret> doExpand(ExpandContext &ctx, const ArgExprs &args) const |
| { |
| ExprP<Mat4> mat = args.a; |
| ExprP<Mat3> minors[4]; |
| |
| for (int ndx = 0; ndx < 4; ++ndx) |
| { |
| ExprP<Vec4> minorColumns[3]; |
| ExprP<Vec3> columns[3]; |
| |
| for (int col = 0; col < 3; ++col) |
| minorColumns[col] = mat[col < ndx ? col : col + 1]; |
| |
| for (int col = 0; col < 3; ++col) |
| columns[col] = vec3(minorColumns[0][col + 1], minorColumns[1][col + 1], minorColumns[2][col + 1]); |
| |
| minors[ndx] = bindExpression("minor", ctx, mat3(columns[0], columns[1], columns[2])); |
| } |
| |
| return (mat[0][0] * determinant(minors[0]) - mat[1][0] * determinant(minors[1]) + |
| mat[2][0] * determinant(minors[2]) - mat[3][0] * determinant(minors[3])); |
| } |
| }; |
| |
| template <int Size> |
| class Inverse; |
| |
| template <int Size> |
| ExprP<Matrix<float, Size, Size>> inverse(ExprP<Matrix<float, Size, Size>> mat) |
| { |
| return app<Inverse<Size>>(mat); |
| } |
| |
| template <> |
| class Inverse<2> : public DerivedFunc<Signature<Mat2, Mat2>> |
| { |
| public: |
| string getName(void) const |
| { |
| return "inverse"; |
| } |
| |
| protected: |
| ExprP<Ret> doExpand(ExpandContext &ctx, const ArgExprs &args) const |
| { |
| ExprP<Mat2> mat = args.a; |
| ExprP<float> det = bindExpression("det", ctx, determinant(mat)); |
| |
| return mat2(vec2(mat[1][1] / det, -mat[0][1] / det), vec2(-mat[1][0] / det, mat[0][0] / det)); |
| } |
| }; |
| |
| template <> |
| class Inverse<3> : public DerivedFunc<Signature<Mat3, Mat3>> |
| { |
| public: |
| string getName(void) const |
| { |
| return "inverse"; |
| } |
| |
| protected: |
| ExprP<Ret> doExpand(ExpandContext &ctx, const ArgExprs &args) const |
| { |
| ExprP<Mat3> mat = args.a; |
| ExprP<Mat2> invA = |
| bindExpression("invA", ctx, inverse(mat2(vec2(mat[0][0], mat[0][1]), vec2(mat[1][0], mat[1][1])))); |
| |
| ExprP<Vec2> matB = bindExpression("matB", ctx, vec2(mat[2][0], mat[2][1])); |
| ExprP<Vec2> matC = bindExpression("matC", ctx, vec2(mat[0][2], mat[1][2])); |
| ExprP<float> matD = bindExpression("matD", ctx, mat[2][2]); |
| |
| ExprP<float> schur = bindExpression("schur", ctx, constant(1.0f) / (matD - dot(matC * invA, matB))); |
| |
| ExprP<Vec2> t1 = invA * matB; |
| ExprP<Vec2> t2 = t1 * schur; |
| ExprP<Mat2> t3 = outerProduct(t2, matC); |
| ExprP<Mat2> t4 = t3 * invA; |
| ExprP<Mat2> t5 = invA + t4; |
| ExprP<Mat2> blockA = bindExpression("blockA", ctx, t5); |
| ExprP<Vec2> blockB = bindExpression("blockB", ctx, (invA * matB) * -schur); |
| ExprP<Vec2> blockC = bindExpression("blockC", ctx, (matC * invA) * -schur); |
| |
| return mat3(vec3(blockA[0][0], blockA[0][1], blockC[0]), vec3(blockA[1][0], blockA[1][1], blockC[1]), |
| vec3(blockB[0], blockB[1], schur)); |
| } |
| }; |
| |
| template <> |
| class Inverse<4> : public DerivedFunc<Signature<Mat4, Mat4>> |
| { |
| public: |
| string getName(void) const |
| { |
| return "inverse"; |
| } |
| |
| protected: |
| ExprP<Ret> doExpand(ExpandContext &ctx, const ArgExprs &args) const |
| { |
| ExprP<Mat4> mat = args.a; |
| ExprP<Mat2> invA = |
| bindExpression("invA", ctx, inverse(mat2(vec2(mat[0][0], mat[0][1]), vec2(mat[1][0], mat[1][1])))); |
| ExprP<Mat2> matB = bindExpression("matB", ctx, mat2(vec2(mat[2][0], mat[2][1]), vec2(mat[3][0], mat[3][1]))); |
| ExprP<Mat2> matC = bindExpression("matC", ctx, mat2(vec2(mat[0][2], mat[0][3]), vec2(mat[1][2], mat[1][3]))); |
| ExprP<Mat2> matD = bindExpression("matD", ctx, mat2(vec2(mat[2][2], mat[2][3]), vec2(mat[3][2], mat[3][3]))); |
| ExprP<Mat2> schur = bindExpression("schur", ctx, inverse(matD + -(matC * invA * matB))); |
| ExprP<Mat2> blockA = bindExpression("blockA", ctx, invA + (invA * matB * schur * matC * invA)); |
| ExprP<Mat2> blockB = bindExpression("blockB", ctx, (-invA) * matB * schur); |
| ExprP<Mat2> blockC = bindExpression("blockC", ctx, (-schur) * matC * invA); |
| |
| return mat4(vec4(blockA[0][0], blockA[0][1], blockC[0][0], blockC[0][1]), |
| vec4(blockA[1][0], blockA[1][1], blockC[1][0], blockC[1][1]), |
| vec4(blockB[0][0], blockB[0][1], schur[0][0], schur[0][1]), |
| vec4(blockB[1][0], blockB[1][1], schur[1][0], schur[1][1])); |
| } |
| }; |
| |
| class Fma : public DerivedFunc<Signature<float, float, float, float>> |
| { |
| public: |
| string getName(void) const |
| { |
| return "fma"; |
| } |
| |
| string getRequiredExtension(const RenderContext &context) const |
| { |
| return (glu::contextSupports(context.getType(), glu::ApiType::core(4, 5))) ? "" : "GL_EXT_gpu_shader5"; |
| } |
| |
| protected: |
| ExprP<float> doExpand(ExpandContext &, const ArgExprs &x) const |
| { |
| return x.a * x.b + x.c; |
| } |
| }; |
| |
| } // namespace Functions |
| |
| using namespace Functions; |
| |
| template <typename T> |
| ExprP<typename T::Element> ContainerExprPBase<T>::operator[](int i) const |
| { |
| return Functions::getComponent(exprP<T>(*this), i); |
| } |
| |
| ExprP<float> operator+(const ExprP<float> &arg0, const ExprP<float> &arg1) |
| { |
| return app<Add>(arg0, arg1); |
| } |
| |
| ExprP<float> operator-(const ExprP<float> &arg0, const ExprP<float> &arg1) |
| { |
| return app<Sub>(arg0, arg1); |
| } |
| |
| ExprP<float> operator-(const ExprP<float> &arg0) |
| { |
| return app<Negate>(arg0); |
| } |
| |
| ExprP<float> operator*(const ExprP<float> &arg0, const ExprP<float> &arg1) |
| { |
| return app<Mul>(arg0, arg1); |
| } |
| |
| ExprP<float> operator/(const ExprP<float> &arg0, const ExprP<float> &arg1) |
| { |
| return app<Div>(arg0, arg1); |
| } |
| |
| template <typename Sig_, int Size> |
| class GenFunc : public PrimitiveFunc<Signature<typename ContainerOf<typename Sig_::Ret, Size>::Container, |
| typename ContainerOf<typename Sig_::Arg0, Size>::Container, |
| typename ContainerOf<typename Sig_::Arg1, Size>::Container, |
| typename ContainerOf<typename Sig_::Arg2, Size>::Container, |
| typename ContainerOf<typename Sig_::Arg3, Size>::Container>> |
| { |
| public: |
| typedef typename GenFunc::IArgs IArgs; |
| typedef typename GenFunc::IRet IRet; |
| |
| GenFunc(const Func<Sig_> &scalarFunc) : m_func(scalarFunc) |
| { |
| } |
| |
| string getName(void) const |
| { |
| return m_func.getName(); |
| } |
| |
| int getOutParamIndex(void) const |
| { |
| return m_func.getOutParamIndex(); |
| } |
| |
| string getRequiredExtension(const RenderContext &context) const |
| { |
| return m_func.getRequiredExtension(context); |
| } |
| |
| protected: |
| void doPrint(ostream &os, const BaseArgExprs &args) const |
| { |
| m_func.print(os, args); |
| } |
| |
| IRet doApply(const EvalContext &ctx, const IArgs &iargs) const |
| { |
| IRet ret; |
| |
| for (int ndx = 0; ndx < Size; ++ndx) |
| { |
| ret[ndx] = m_func.apply(ctx, iargs.a[ndx], iargs.b[ndx], iargs.c[ndx], iargs.d[ndx]); |
| } |
| |
| return ret; |
| } |
| |
| void doGetUsedFuncs(FuncSet &dst) const |
| { |
| m_func.getUsedFuncs(dst); |
| } |
| |
| const Func<Sig_> &m_func; |
| }; |
| |
| template <typename F, int Size> |
| class VectorizedFunc : public GenFunc<typename F::Sig, Size> |
| { |
| public: |
| VectorizedFunc(void) : GenFunc<typename F::Sig, Size>(instance<F>()) |
| { |
| } |
| }; |
| |
| template <typename Sig_, int Size> |
| class FixedGenFunc |
| : public PrimitiveFunc<Signature<typename ContainerOf<typename Sig_::Ret, Size>::Container, |
| typename ContainerOf<typename Sig_::Arg0, Size>::Container, typename Sig_::Arg1, |
| typename ContainerOf<typename Sig_::Arg2, Size>::Container, |
| typename ContainerOf<typename Sig_::Arg3, Size>::Container>> |
| { |
| public: |
| typedef typename FixedGenFunc::IArgs IArgs; |
| typedef typename FixedGenFunc::IRet IRet; |
| |
| string getName(void) const |
| { |
| return this->doGetScalarFunc().getName(); |
| } |
| |
| protected: |
| void doPrint(ostream &os, const BaseArgExprs &args) const |
| { |
| this->doGetScalarFunc().print(os, args); |
| } |
| |
| IRet doApply(const EvalContext &ctx, const IArgs &iargs) const |
| { |
| IRet ret; |
| const Func<Sig_> &func = this->doGetScalarFunc(); |
| |
| for (int ndx = 0; ndx < Size; ++ndx) |
| ret[ndx] = func.apply(ctx, iargs.a[ndx], iargs.b, iargs.c[ndx], iargs.d[ndx]); |
| |
| return ret; |
| } |
| |
| virtual const Func<Sig_> &doGetScalarFunc(void) const = 0; |
| }; |
| |
| template <typename F, int Size> |
| class FixedVecFunc : public FixedGenFunc<typename F::Sig, Size> |
| { |
| protected: |
| const Func<typename F::Sig> &doGetScalarFunc(void) const |
| { |
| return instance<F>(); |
| } |
| }; |
| |
| template <typename Sig> |
| struct GenFuncs |
| { |
| GenFuncs(const Func<Sig> &func_, const GenFunc<Sig, 2> &func2_, const GenFunc<Sig, 3> &func3_, |
| const GenFunc<Sig, 4> &func4_) |
| : func(func_) |
| , func2(func2_) |
| , func3(func3_) |
| , func4(func4_) |
| { |
| } |
| |
| const Func<Sig> &func; |
| const GenFunc<Sig, 2> &func2; |
| const GenFunc<Sig, 3> &func3; |
| const GenFunc<Sig, 4> &func4; |
| }; |
| |
| template <typename F> |
| GenFuncs<typename F::Sig> makeVectorizedFuncs(void) |
| { |
| return GenFuncs<typename F::Sig>(instance<F>(), instance<VectorizedFunc<F, 2>>(), instance<VectorizedFunc<F, 3>>(), |
| instance<VectorizedFunc<F, 4>>()); |
| } |
| |
| template <int Size> |
| ExprP<Vector<float, Size>> operator*(const ExprP<Vector<float, Size>> &arg0, const ExprP<Vector<float, Size>> &arg1) |
| { |
| return app<VectorizedFunc<Mul, Size>>(arg0, arg1); |
| } |
| |
| template <int Size> |
| ExprP<Vector<float, Size>> operator*(const ExprP<Vector<float, Size>> &arg0, const ExprP<float> &arg1) |
| { |
| return app<FixedVecFunc<Mul, Size>>(arg0, arg1); |
| } |
| |
| template <int Size> |
| ExprP<Vector<float, Size>> operator/(const ExprP<Vector<float, Size>> &arg0, const ExprP<float> &arg1) |
| { |
| return app<FixedVecFunc<Div, Size>>(arg0, arg1); |
| } |
| |
| template <int Size> |
| ExprP<Vector<float, Size>> operator-(const ExprP<Vector<float, Size>> &arg0) |
| { |
| return app<VectorizedFunc<Negate, Size>>(arg0); |
| } |
| |
| template <int Size> |
| ExprP<Vector<float, Size>> operator-(const ExprP<Vector<float, Size>> &arg0, const ExprP<Vector<float, Size>> &arg1) |
| { |
| return app<VectorizedFunc<Sub, Size>>(arg0, arg1); |
| } |
| |
| template <int LeftRows, int Middle, int RightCols> |
| ExprP<Matrix<float, LeftRows, RightCols>> operator*(const ExprP<Matrix<float, LeftRows, Middle>> &left, |
| const ExprP<Matrix<float, Middle, RightCols>> &right) |
| { |
| return app<MatMul<LeftRows, Middle, RightCols>>(left, right); |
| } |
| |
| template <int Rows, int Cols> |
| ExprP<Vector<float, Rows>> operator*(const ExprP<Vector<float, Cols>> &left, |
| const ExprP<Matrix<float, Rows, Cols>> &right) |
| { |
| return app<VecMatMul<Rows, Cols>>(left, right); |
| } |
| |
| template <int Rows, int Cols> |
| ExprP<Vector<float, Cols>> operator*(const ExprP<Matrix<float, Rows, Cols>> &left, |
| const ExprP<Vector<float, Rows>> &right) |
| { |
| return app<MatVecMul<Rows, Cols>>(left, right); |
| } |
| |
| template <int Rows, int Cols> |
| ExprP<Matrix<float, Rows, Cols>> operator*(const ExprP<Matrix<float, Rows, Cols>> &left, const ExprP<float> &right) |
| { |
| return app<ScalarMatFunc<Mul, Rows, Cols>>(left, right); |
| } |
| |
| template <int Rows, int Cols> |
| ExprP<Matrix<float, Rows, Cols>> operator+(const ExprP<Matrix<float, Rows, Cols>> &left, |
| const ExprP<Matrix<float, Rows, Cols>> &right) |
| { |
| return app<CompMatFunc<Add, Rows, Cols>>(left, right); |
| } |
| |
| template <int Rows, int Cols> |
| ExprP<Matrix<float, Rows, Cols>> operator-(const ExprP<Matrix<float, Rows, Cols>> &mat) |
| { |
| return app<MatNeg<Rows, Cols>>(mat); |
| } |
| |
| template <typename T> |
| class Sampling |
| { |
| public: |
| virtual void genFixeds(const FloatFormat &, vector<T> &) const |
| { |
| } |
| virtual T genRandom(const FloatFormat &, Precision, Random &) const |
| { |
| return T(); |
| } |
| virtual double getWeight(void) const |
| { |
| return 0.0; |
| } |
| }; |
| |
| template <> |
| class DefaultSampling<Void> : public Sampling<Void> |
| { |
| public: |
| void genFixeds(const FloatFormat &, vector<Void> &dst) const |
| { |
| dst.push_back(Void()); |
| } |
| }; |
| |
| template <> |
| class DefaultSampling<bool> : public Sampling<bool> |
| { |
| public: |
| void genFixeds(const FloatFormat &, vector<bool> &dst) const |
| { |
| dst.push_back(true); |
| dst.push_back(false); |
| } |
| }; |
| |
| template <> |
| class DefaultSampling<int> : public Sampling<int> |
| { |
| public: |
| int genRandom(const FloatFormat &, Precision prec, Random &rnd) const |
| { |
| const int exp = rnd.getInt(0, getNumBits(prec) - 2); |
| const int sign = rnd.getBool() ? -1 : 1; |
| |
| return sign * rnd.getInt(0, (int32_t)1 << exp); |
| } |
| |
| void genFixeds(const FloatFormat &, vector<int> &dst) const |
| { |
| dst.push_back(0); |
| dst.push_back(-1); |
| dst.push_back(1); |
| } |
| double getWeight(void) const |
| { |
| return 1.0; |
| } |
| |
| private: |
| static inline int getNumBits(Precision prec) |
| { |
| switch (prec) |
| { |
| case glu::PRECISION_LOWP: |
| return 8; |
| case glu::PRECISION_MEDIUMP: |
| return 16; |
| case glu::PRECISION_HIGHP: |
| return 32; |
| default: |
| DE_ASSERT(false); |
| return 0; |
| } |
| } |
| }; |
| |
| template <> |
| class DefaultSampling<float> : public Sampling<float> |
| { |
| public: |
| float genRandom(const FloatFormat &format, Precision prec, Random &rnd) const; |
| void genFixeds(const FloatFormat &format, vector<float> &dst) const; |
| double getWeight(void) const |
| { |
| return 1.0; |
| } |
| }; |
| |
| //! Generate a random float from a reasonable general-purpose distribution. |
| float DefaultSampling<float>::genRandom(const FloatFormat &format, Precision, Random &rnd) const |
| { |
| const int minExp = format.getMinExp(); |
| const int maxExp = format.getMaxExp(); |
| const bool haveSubnormal = format.hasSubnormal() != tcu::NO; |
| |
| // Choose exponent so that the cumulative distribution is cubic. |
| // This makes the probability distribution quadratic, with the peak centered on zero. |
| const double minRoot = deCbrt(minExp - 0.5 - (haveSubnormal ? 1.0 : 0.0)); |
| const double maxRoot = deCbrt(maxExp + 0.5); |
| const int fractionBits = format.getFractionBits(); |
| const int exp = int(deRoundEven(dePow(rnd.getDouble(minRoot, maxRoot), 3.0))); |
| float base = 0.0f; // integral power of two |
| float quantum = 0.0f; // smallest representable difference in the binade |
| float significand = 0.0f; // Significand. |
| |
| DE_ASSERT(fractionBits < std::numeric_limits<float>::digits); |
| |
| // Generate some occasional special numbers |
| switch (rnd.getInt(0, 64)) |
| { |
| case 0: |
| return 0; |
| case 1: |
| return TCU_INFINITY; |
| case 2: |
| return -TCU_INFINITY; |
| case 3: |
| return TCU_NAN; |
| default: |
| break; |
| } |
| |
| if (exp >= minExp) |
| { |
| // Normal number |
| base = deFloatLdExp(1.0f, exp); |
| quantum = deFloatLdExp(1.0f, exp - fractionBits); |
| } |
| else |
| { |
| // Subnormal |
| base = 0.0f; |
| quantum = deFloatLdExp(1.0f, minExp - fractionBits); |
| } |
| |
| switch (rnd.getInt(0, 16)) |
| { |
| case 0: // The highest number in this binade, significand is all bits one. |
| significand = base - quantum; |
| break; |
| case 1: // Significand is one. |
| significand = quantum; |
| break; |
| case 2: // Significand is zero. |
| significand = 0.0; |
| break; |
| default: // Random (evenly distributed) significand. |
| { |
| uint64_t intFraction = rnd.getUint64() & ((1ull << fractionBits) - 1); |
| significand = float(intFraction) * quantum; |
| } |
| } |
| |
| // Produce positive numbers more often than negative. |
| return (rnd.getInt(0, 3) == 0 ? -1.0f : 1.0f) * (base + significand); |
| } |
| |
| //! Generate a standard set of floats that should always be tested. |
| void DefaultSampling<float>::genFixeds(const FloatFormat &format, vector<float> &dst) const |
| { |
| const int minExp = format.getMinExp(); |
| const int maxExp = format.getMaxExp(); |
| const int fractionBits = format.getFractionBits(); |
| const float minQuantum = deFloatLdExp(1.0f, minExp - fractionBits); |
| const float minNormalized = deFloatLdExp(1.0f, minExp); |
| const float maxQuantum = deFloatLdExp(1.0f, maxExp - fractionBits); |
| |
| // NaN |
| dst.push_back(TCU_NAN); |
| // Zero |
| dst.push_back(0.0f); |
| |
| for (int sign = -1; sign <= 1; sign += 2) |
| { |
| // Smallest subnormal |
| dst.push_back((float)sign * minQuantum); |
| |
| // Largest subnormal |
| dst.push_back((float)sign * (minNormalized - minQuantum)); |
| |
| // Smallest normalized |
| dst.push_back((float)sign * minNormalized); |
| |
| // Next smallest normalized |
| dst.push_back((float)sign * (minNormalized + minQuantum)); |
| |
| dst.push_back((float)sign * 0.5f); |
| dst.push_back((float)sign * 1.0f); |
| dst.push_back((float)sign * 2.0f); |
| |
| // Largest number |
| dst.push_back((float)sign * (deFloatLdExp(1.0f, maxExp) + (deFloatLdExp(1.0f, maxExp) - maxQuantum))); |
| |
| dst.push_back((float)sign * TCU_INFINITY); |
| } |
| } |
| |
| template <typename T, int Size> |
| class DefaultSampling<Vector<T, Size>> : public Sampling<Vector<T, Size>> |
| { |
| public: |
| typedef Vector<T, Size> Value; |
| |
| Value genRandom(const FloatFormat &fmt, Precision prec, Random &rnd) const |
| { |
| Value ret; |
| |
| for (int ndx = 0; ndx < Size; ++ndx) |
| ret[ndx] = instance<DefaultSampling<T>>().genRandom(fmt, prec, rnd); |
| |
| return ret; |
| } |
| |
| void genFixeds(const FloatFormat &fmt, vector<Value> &dst) const |
| { |
| vector<T> scalars; |
| |
| instance<DefaultSampling<T>>().genFixeds(fmt, scalars); |
| |
| for (size_t scalarNdx = 0; scalarNdx < scalars.size(); ++scalarNdx) |
| dst.push_back(Value(scalars[scalarNdx])); |
| } |
| |
| double getWeight(void) const |
| { |
| return dePow(instance<DefaultSampling<T>>().getWeight(), Size); |
| } |
| }; |
| |
| template <typename T, int Rows, int Columns> |
| class DefaultSampling<Matrix<T, Rows, Columns>> : public Sampling<Matrix<T, Rows, Columns>> |
| { |
| public: |
| typedef Matrix<T, Rows, Columns> Value; |
| |
| Value genRandom(const FloatFormat &fmt, Precision prec, Random &rnd) const |
| { |
| Value ret; |
| |
| for (int rowNdx = 0; rowNdx < Rows; ++rowNdx) |
| for (int colNdx = 0; colNdx < Columns; ++colNdx) |
| ret(rowNdx, colNdx) = instance<DefaultSampling<T>>().genRandom(fmt, prec, rnd); |
| |
| return ret; |
| } |
| |
| void genFixeds(const FloatFormat &fmt, vector<Value> &dst) const |
| { |
| vector<T> scalars; |
| |
| instance<DefaultSampling<T>>().genFixeds(fmt, scalars); |
| |
| for (size_t scalarNdx = 0; scalarNdx < scalars.size(); ++scalarNdx) |
| dst.push_back(Value(scalars[scalarNdx])); |
| |
| if (Columns == Rows) |
| { |
| Value mat(0.0); |
| T x = T(1.0f); |
| mat[0][0] = x; |
| for (int ndx = 0; ndx < Columns; ++ndx) |
| { |
| mat[Columns - 1 - ndx][ndx] = x; |
| x *= T(2.0f); |
| } |
| dst.push_back(mat); |
| } |
| } |
| |
| double getWeight(void) const |
| { |
| return dePow(instance<DefaultSampling<T>>().getWeight(), Rows * Columns); |
| } |
| }; |
| |
| struct Context |
| { |
| Context(const string &name_, TestContext &testContext_, RenderContext &renderContext_, |
| const FloatFormat &floatFormat_, const FloatFormat &highpFormat_, Precision precision_, |
| ShaderType shaderType_, size_t numRandoms_) |
| : name(name_) |
| , testContext(testContext_) |
| , renderContext(renderContext_) |
| , floatFormat(floatFormat_) |
| , highpFormat(highpFormat_) |
| , precision(precision_) |
| , shaderType(shaderType_) |
| , numRandoms(numRandoms_) |
| { |
| } |
| |
| string name; |
| TestContext &testContext; |
| RenderContext &renderContext; |
| FloatFormat floatFormat; |
| FloatFormat highpFormat; |
| Precision precision; |
| ShaderType shaderType; |
| size_t numRandoms; |
| }; |
| |
| template <typename In0_ = Void, typename In1_ = Void, typename In2_ = Void, typename In3_ = Void> |
| struct InTypes |
| { |
| typedef In0_ In0; |
| typedef In1_ In1; |
| typedef In2_ In2; |
| typedef In3_ In3; |
| }; |
| |
| template <typename In> |
| int numInputs(void) |
| { |
| return (!isTypeValid<typename In::In0>() ? 0 : |
| !isTypeValid<typename In::In1>() ? 1 : |
| !isTypeValid<typename In::In2>() ? 2 : |
| !isTypeValid<typename In::In3>() ? 3 : |
| 4); |
| } |
| |
| template <typename Out0_, typename Out1_ = Void> |
| struct OutTypes |
| { |
| typedef Out0_ Out0; |
| typedef Out1_ Out1; |
| }; |
| |
| template <typename Out> |
| int numOutputs(void) |
| { |
| return (!isTypeValid<typename Out::Out0>() ? 0 : !isTypeValid<typename Out::Out1>() ? 1 : 2); |
| } |
| |
| template <typename In> |
| struct Inputs |
| { |
| vector<typename In::In0> in0; |
| vector<typename In::In1> in1; |
| vector<typename In::In2> in2; |
| vector<typename In::In3> in3; |
| }; |
| |
| template <typename Out> |
| struct Outputs |
| { |
| Outputs(size_t size) : out0(size), out1(size) |
| { |
| } |
| |
| vector<typename Out::Out0> out0; |
| vector<typename Out::Out1> out1; |
| }; |
| |
| template <typename In, typename Out> |
| struct Variables |
| { |
| VariableP<typename In::In0> in0; |
| VariableP<typename In::In1> in1; |
| VariableP<typename In::In2> in2; |
| VariableP<typename In::In3> in3; |
| VariableP<typename Out::Out0> out0; |
| VariableP<typename Out::Out1> out1; |
| }; |
| |
| template <typename In> |
| struct Samplings |
| { |
| Samplings(const Sampling<typename In::In0> &in0_, const Sampling<typename In::In1> &in1_, |
| const Sampling<typename In::In2> &in2_, const Sampling<typename In::In3> &in3_) |
| : in0(in0_) |
| , in1(in1_) |
| , in2(in2_) |
| , in3(in3_) |
| { |
| } |
| |
| const Sampling<typename In::In0> &in0; |
| const Sampling<typename In::In1> &in1; |
| const Sampling<typename In::In2> &in2; |
| const Sampling<typename In::In3> &in3; |
| }; |
| |
| template <typename In> |
| struct DefaultSamplings : Samplings<In> |
| { |
| DefaultSamplings(void) |
| : Samplings<In>(instance<DefaultSampling<typename In::In0>>(), instance<DefaultSampling<typename In::In1>>(), |
| instance<DefaultSampling<typename In::In2>>(), instance<DefaultSampling<typename In::In3>>()) |
| { |
| } |
| }; |
| |
| class PrecisionCase : public TestCase |
| { |
| public: |
| IterateResult iterate(void); |
| |
| protected: |
| PrecisionCase(const Context &context, const string &name, const string &extension = "") |
| : TestCase(context.testContext, name.c_str(), name.c_str()) |
| , m_ctx(context) |
| , m_status() |
| , m_rnd(0xdeadbeefu + context.testContext.getCommandLine().getBaseSeed()) |
| , m_extension(extension) |
| { |
| } |
| |
| RenderContext &getRenderContext(void) const |
| { |
| return m_ctx.renderContext; |
| } |
| |
| const FloatFormat &getFormat(void) const |
| { |
| return m_ctx.floatFormat; |
| } |
| |
| TestLog &log(void) const |
| { |
| return m_testCtx.getLog(); |
| } |
| |
| virtual void runTest(void) = 0; |
| |
| template <typename In, typename Out> |
| void testStatement(const Variables<In, Out> &variables, const Inputs<In> &inputs, const Statement &stmt); |
| |
| template <typename T> |
| Symbol makeSymbol(const Variable<T> &variable) |
| { |
| return Symbol(variable.getName(), getVarTypeOf<T>(m_ctx.precision)); |
| } |
| |
| Context m_ctx; |
| ResultCollector m_status; |
| Random m_rnd; |
| const string m_extension; |
| }; |
| |
| IterateResult PrecisionCase::iterate(void) |
| { |
| runTest(); |
| m_status.setTestContextResult(m_testCtx); |
| return STOP; |
| } |
| |
| template <typename In, typename Out> |
| void PrecisionCase::testStatement(const Variables<In, Out> &variables, const Inputs<In> &inputs, const Statement &stmt) |
| { |
| using namespace ShaderExecUtil; |
| |
| typedef typename In::In0 In0; |
| typedef typename In::In1 In1; |
| typedef typename In::In2 In2; |
| typedef typename In::In3 In3; |
| typedef typename Out::Out0 Out0; |
| typedef typename Out::Out1 Out1; |
| |
| const FloatFormat &fmt = getFormat(); |
| const int inCount = numInputs<In>(); |
| const int outCount = numOutputs<Out>(); |
| const size_t numValues = (inCount > 0) ? inputs.in0.size() : 1; |
| Outputs<Out> outputs(numValues); |
| ShaderSpec spec; |
| const FloatFormat highpFmt = m_ctx.highpFormat; |
| const int maxMsgs = 100; |
| int numErrors = 0; |
| Environment env; // Hoisted out of the inner loop for optimization. |
| |
| switch (inCount) |
| { |
| case 4: |
| DE_ASSERT(inputs.in3.size() == numValues); |
| // Fallthrough |
| case 3: |
| DE_ASSERT(inputs.in2.size() == numValues); |
| // Fallthrough |
| case 2: |
| DE_ASSERT(inputs.in1.size() == numValues); |
| // Fallthrough |
| case 1: |
| DE_ASSERT(inputs.in0.size() == numValues); |
| // Fallthrough |
| default: |
| break; |
| } |
| |
| // Print out the statement and its definitions |
| log() << TestLog::Message << "Statement: " << stmt << TestLog::EndMessage; |
| { |
| ostringstream oss; |
| FuncSet funcs; |
| |
| stmt.getUsedFuncs(funcs); |
| for (FuncSet::const_iterator it = funcs.begin(); it != funcs.end(); ++it) |
| { |
| (*it)->printDefinition(oss); |
| } |
| if (!funcs.empty()) |
| log() << TestLog::Message << "Reference definitions:\n" << oss.str() << TestLog::EndMessage; |
| } |
| |
| // Initialize ShaderSpec from precision, variables and statement. |
| { |
| ostringstream os; |
| os << "precision " << glu::getPrecisionName(m_ctx.precision) << " float;\n"; |
| spec.globalDeclarations = os.str(); |
| } |
| |
| spec.version = getContextTypeGLSLVersion(getRenderContext().getType()); |
| |
| if (!m_extension.empty()) |
| spec.globalDeclarations = "#extension " + m_extension + " : require\n"; |
| |
| spec.inputs.resize(inCount); |
| |
| switch (inCount) |
| { |
| case 4: |
| spec.inputs[3] = makeSymbol(*variables.in3); |
| // Fallthrough |
| case 3: |
| spec.inputs[2] = makeSymbol(*variables.in2); |
| // Fallthrough |
| case 2: |
| spec.inputs[1] = makeSymbol(*variables.in1); |
| // Fallthrough |
| case 1: |
| spec.inputs[0] = makeSymbol(*variables.in0); |
| // Fallthrough |
| default: |
| break; |
| } |
| |
| spec.outputs.resize(outCount); |
| |
| switch (outCount) |
| { |
| case 2: |
| spec.outputs[1] = makeSymbol(*variables.out1); // Fallthrough |
| case 1: |
| spec.outputs[0] = makeSymbol(*variables.out0); |
| default: |
| break; |
| } |
| |
| spec.source = de::toString(stmt); |
| |
| // Run the shader with inputs. |
| { |
| UniquePtr<ShaderExecutor> executor(createExecutor(getRenderContext(), m_ctx.shaderType, spec)); |
| const void *inputArr[] = { |
| &inputs.in0.front(), |
| &inputs.in1.front(), |
| &inputs.in2.front(), |
| &inputs.in3.front(), |
| }; |
| void *outputArr[] = { |
| &outputs.out0.front(), |
| &outputs.out1.front(), |
| }; |
| |
| executor->log(log()); |
| if (!executor->isOk()) |
| TCU_FAIL("Shader compilation failed"); |
| |
| executor->useProgram(); |
| executor->execute(int(numValues), inputArr, outputArr); |
| } |
| |
| // Initialize environment with unused values so we don't need to bind in inner loop. |
| { |
| const typename Traits<In0>::IVal in0; |
| const typename Traits<In1>::IVal in1; |
| const typename Traits<In2>::IVal in2; |
| const typename Traits<In3>::IVal in3; |
| const typename Traits<Out0>::IVal reference0; |
| const typename Traits<Out1>::IVal reference1; |
| |
| env.bind(*variables.in0, in0); |
| env.bind(*variables.in1, in1); |
| env.bind(*variables.in2, in2); |
| env.bind(*variables.in3, in3); |
| env.bind(*variables.out0, reference0); |
| env.bind(*variables.out1, reference1); |
| } |
| |
| // For each input tuple, compute output reference interval and compare |
| // shader output to the reference. |
| for (size_t valueNdx = 0; valueNdx < numValues; valueNdx++) |
| { |
| bool result = true; |
| bool inExpectedRange; |
| bool inWarningRange; |
| const char *failStr = "Fail"; |
| typename Traits<Out0>::IVal reference0; |
| typename Traits<Out1>::IVal reference1; |
| |
| if (valueNdx % (size_t)TOUCH_WATCHDOG_VALUE_FREQUENCY == 0) |
| m_testCtx.touchWatchdog(); |
| |
| env.lookup(*variables.in0) = convert<In0>(fmt, round(fmt, inputs.in0[valueNdx])); |
| env.lookup(*variables.in1) = convert<In1>(fmt, round(fmt, inputs.in1[valueNdx])); |
| env.lookup(*variables.in2) = convert<In2>(fmt, round(fmt, inputs.in2[valueNdx])); |
| env.lookup(*variables.in3) = convert<In3>(fmt, round(fmt, inputs.in3[valueNdx])); |
| |
| { |
| EvalContext ctx(fmt, m_ctx.precision, env); |
| stmt.execute(ctx); |
| } |
| |
| switch (outCount) |
| { |
| case 2: |
| reference1 = convert<Out1>(highpFmt, env.lookup(*variables.out1)); |
| inExpectedRange = contains(reference1, outputs.out1[valueNdx]); |
| inWarningRange = containsWarning(reference1, outputs.out1[valueNdx]); |
| if (!inExpectedRange && inWarningRange) |
| { |
| m_status.addResult(QP_TEST_RESULT_QUALITY_WARNING, "Shader output 1 has low-quality shader precision"); |
| failStr = "QualityWarning"; |
| result = false; |
| } |
| else if (!inExpectedRange) |
| { |
| m_status.addResult(QP_TEST_RESULT_FAIL, "Shader output 1 is outside acceptable range"); |
| failStr = "Fail"; |
| result = false; |
| } |
| // Fallthrough |
| |
| case 1: |
| reference0 = convert<Out0>(highpFmt, env.lookup(*variables.out0)); |
| inExpectedRange = contains(reference0, outputs.out0[valueNdx]); |
| inWarningRange = containsWarning(reference0, outputs.out0[valueNdx]); |
| if (!inExpectedRange && inWarningRange) |
| { |
| m_status.addResult(QP_TEST_RESULT_QUALITY_WARNING, "Shader output 0 has low-quality shader precision"); |
| failStr = "QualityWarning"; |
| result = false; |
| } |
| else if (!inExpectedRange) |
| { |
| m_status.addResult(QP_TEST_RESULT_FAIL, "Shader output 0 is outside acceptable range"); |
| failStr = "Fail"; |
| result = false; |
| } |
| |
| default: |
| break; |
| } |
| |
| if (!result) |
| ++numErrors; |
| |
| if ((!result && numErrors <= maxMsgs) || GLS_LOG_ALL_RESULTS) |
| { |
| MessageBuilder builder = log().message(); |
| |
| builder << (result ? "Passed" : failStr) << " sample:\n"; |
| |
| if (inCount > 0) |
| { |
| builder << "\t" << variables.in0->getName() << " = " << valueToString(highpFmt, inputs.in0[valueNdx]) |
| << "\n"; |
| } |
| |
| if (inCount > 1) |
| { |
| builder << "\t" << variables.in1->getName() << " = " << valueToString(highpFmt, inputs.in1[valueNdx]) |
| << "\n"; |
| } |
| |
| if (inCount > 2) |
| { |
| builder << "\t" << variables.in2->getName() << " = " << valueToString(highpFmt, inputs.in2[valueNdx]) |
| << "\n"; |
| } |
| |
| if (inCount > 3) |
| { |
| builder << "\t" << variables.in3->getName() << " = " << valueToString(highpFmt, inputs.in3[valueNdx]) |
| << "\n"; |
| } |
| |
| if (outCount > 0) |
| { |
| builder << "\t" << variables.out0->getName() << " = " << valueToString(highpFmt, outputs.out0[valueNdx]) |
| << "\n" |
| << "\tExpected range: " << intervalToString<typename Out::Out0>(highpFmt, reference0) << "\n"; |
| } |
| |
| if (outCount > 1) |
| { |
| builder << "\t" << variables.out1->getName() << " = " << valueToString(highpFmt, outputs.out1[valueNdx]) |
| << "\n" |
| << "\tExpected range: " << intervalToString<typename Out::Out1>(highpFmt, reference1) << "\n"; |
| } |
| |
| builder << TestLog::EndMessage; |
| } |
| } |
| |
| if (numErrors > maxMsgs) |
| { |
| log() << TestLog::Message << "(Skipped " << (numErrors - maxMsgs) << " messages.)" << TestLog::EndMessage; |
| } |
| |
| if (numErrors == 0) |
| { |
| log() << TestLog::Message << "All " << numValues << " inputs passed." << TestLog::EndMessage; |
| } |
| else |
| { |
| log() << TestLog::Message << numErrors << "/" << numValues << " inputs failed or had quality warnings." |
| << TestLog::EndMessage; |
| } |
| } |
| |
| template <typename T> |
| struct InputLess |
| { |
| bool operator()(const T &val1, const T &val2) const |
| { |
| return val1 < val2; |
| } |
| }; |
| |
| template <typename T> |
| bool inputLess(const T &val1, const T &val2) |
| { |
| return InputLess<T>()(val1, val2); |
| } |
| |
| template <> |
| struct InputLess<float> |
| { |
| bool operator()(const float &val1, const float &val2) const |
| { |
| if (deIsNaN(val1)) |
| return false; |
| if (deIsNaN(val2)) |
| return true; |
| return val1 < val2; |
| } |
| }; |
| |
| template <typename T, int Size> |
| struct InputLess<Vector<T, Size>> |
| { |
| bool operator()(const Vector<T, Size> &vec1, const Vector<T, Size> &vec2) const |
| { |
| for (int ndx = 0; ndx < Size; ++ndx) |
| { |
| if (inputLess(vec1[ndx], vec2[ndx])) |
| return true; |
| if (inputLess(vec2[ndx], vec1[ndx])) |
| return false; |
| } |
| |
| return false; |
| } |
| }; |
| |
| template <typename T, int Rows, int Cols> |
| struct InputLess<Matrix<T, Rows, Cols>> |
| { |
| bool operator()(const Matrix<T, Rows, Cols> &mat1, const Matrix<T, Rows, Cols> &mat2) const |
| { |
| for (int col = 0; col < Cols; ++col) |
| { |
| if (inputLess(mat1[col], mat2[col])) |
| return true; |
| if (inputLess(mat2[col], mat1[col])) |
| return false; |
| } |
| |
| return false; |
| } |
| }; |
| |
| template <typename In> |
| struct InTuple : public Tuple4<typename In::In0, typename In::In1, typename In::In2, typename In::In3> |
| { |
| InTuple(const typename In::In0 &in0, const typename In::In1 &in1, const typename In::In2 &in2, |
| const typename In::In3 &in3) |
| : Tuple4<typename In::In0, typename In::In1, typename In::In2, typename In::In3>(in0, in1, in2, in3) |
| { |
| } |
| }; |
| |
| template <typename In> |
| struct InputLess<InTuple<In>> |
| { |
| bool operator()(const InTuple<In> &in1, const InTuple<In> &in2) const |
| { |
| if (inputLess(in1.a, in2.a)) |
| return true; |
| if (inputLess(in2.a, in1.a)) |
| return false; |
| if (inputLess(in1.b, in2.b)) |
| return true; |
| if (inputLess(in2.b, in1.b)) |
| return false; |
| if (inputLess(in1.c, in2.c)) |
| return true; |
| if (inputLess(in2.c, in1.c)) |
| return false; |
| if (inputLess(in1.d, in2.d)) |
| return true; |
| return false; |
| } |
| }; |
| |
| template <typename In> |
| Inputs<In> generateInputs(const Samplings<In> &samplings, const FloatFormat &floatFormat, Precision intPrecision, |
| size_t numSamples, Random &rnd) |
| { |
| Inputs<In> ret; |
| Inputs<In> fixedInputs; |
| set<InTuple<In>, InputLess<InTuple<In>>> seenInputs; |
| |
| samplings.in0.genFixeds(floatFormat, fixedInputs.in0); |
| samplings.in1.genFixeds(floatFormat, fixedInputs.in1); |
| samplings.in2.genFixeds(floatFormat, fixedInputs.in2); |
| samplings.in3.genFixeds(floatFormat, fixedInputs.in3); |
| |
| for (size_t ndx0 = 0; ndx0 < fixedInputs.in0.size(); ++ndx0) |
| { |
| for (size_t ndx1 = 0; ndx1 < fixedInputs.in1.size(); ++ndx1) |
| { |
| for (size_t ndx2 = 0; ndx2 < fixedInputs.in2.size(); ++ndx2) |
| { |
| for (size_t ndx3 = 0; ndx3 < fixedInputs.in3.size(); ++ndx3) |
| { |
| const InTuple<In> tuple(fixedInputs.in0[ndx0], fixedInputs.in1[ndx1], fixedInputs.in2[ndx2], |
| fixedInputs.in3[ndx3]); |
| |
| seenInputs.insert(tuple); |
| ret.in0.push_back(tuple.a); |
| ret.in1.push_back(tuple.b); |
| ret.in2.push_back(tuple.c); |
| ret.in3.push_back(tuple.d); |
| } |
| } |
| } |
| } |
| |
| for (size_t ndx = 0; ndx < numSamples; ++ndx) |
| { |
| const typename In::In0 in0 = samplings.in0.genRandom(floatFormat, intPrecision, rnd); |
| const typename In::In1 in1 = samplings.in1.genRandom(floatFormat, intPrecision, rnd); |
| const typename In::In2 in2 = samplings.in2.genRandom(floatFormat, intPrecision, rnd); |
| const typename In::In3 in3 = samplings.in3.genRandom(floatFormat, intPrecision, rnd); |
| const InTuple<In> tuple(in0, in1, in2, in3); |
| |
| if (de::contains(seenInputs, tuple)) |
| continue; |
| |
| seenInputs.insert(tuple); |
| ret.in0.push_back(in0); |
| ret.in1.push_back(in1); |
| ret.in2.push_back(in2); |
| ret.in3.push_back(in3); |
| } |
| |
| return ret; |
| } |
| |
| class FuncCaseBase : public PrecisionCase |
| { |
| public: |
| IterateResult iterate(void); |
| |
| protected: |
| FuncCaseBase(const Context &context, const string &name, const FuncBase &func) |
| : PrecisionCase(context, name, func.getRequiredExtension(context.renderContext)) |
| { |
| } |
| }; |
| |
| IterateResult FuncCaseBase::iterate(void) |
| { |
| MovePtr<ContextInfo> info(ContextInfo::create(getRenderContext())); |
| |
| if (!m_extension.empty() && !info->isExtensionSupported(m_extension.c_str()) && |
| !glu::contextSupports(getRenderContext().getType(), glu::ApiType::core(4, 5))) |
| throw NotSupportedError("Unsupported extension: " + m_extension); |
| |
| runTest(); |
| |
| m_status.setTestContextResult(m_testCtx); |
| return STOP; |
| } |
| |
| template <typename Sig> |
| class FuncCase : public FuncCaseBase |
| { |
| public: |
| typedef Func<Sig> CaseFunc; |
| typedef typename Sig::Ret Ret; |
| typedef typename Sig::Arg0 Arg0; |
| typedef typename Sig::Arg1 Arg1; |
| typedef typename Sig::Arg2 Arg2; |
| typedef typename Sig::Arg3 Arg3; |
| typedef InTypes<Arg0, Arg1, Arg2, Arg3> In; |
| typedef OutTypes<Ret> Out; |
| |
| FuncCase(const Context &context, const string &name, const CaseFunc &func) |
| : FuncCaseBase(context, name, func) |
| , m_func(func) |
| { |
| } |
| |
| protected: |
| void runTest(void); |
| |
| virtual const Samplings<In> &getSamplings(void) |
| { |
| return instance<DefaultSamplings<In>>(); |
| } |
| |
| private: |
| const CaseFunc &m_func; |
| }; |
| |
| template <typename Sig> |
| void FuncCase<Sig>::runTest(void) |
| { |
| const Inputs<In> inputs( |
| generateInputs(getSamplings(), m_ctx.floatFormat, m_ctx.precision, m_ctx.numRandoms, m_rnd)); |
| Variables<In, Out> variables; |
| |
| variables.out0 = variable<Ret>("out0"); |
| variables.out1 = variable<Void>("out1"); |
| variables.in0 = variable<Arg0>("in0"); |
| variables.in1 = variable<Arg1>("in1"); |
| variables.in2 = variable<Arg2>("in2"); |
| variables.in3 = variable<Arg3>("in3"); |
| |
| { |
| ExprP<Ret> expr = applyVar(m_func, variables.in0, variables.in1, variables.in2, variables.in3); |
| StatementP stmt = variableAssignment(variables.out0, expr); |
| |
| this->testStatement(variables, inputs, *stmt); |
| } |
| } |
| |
| template <typename Sig> |
| class InOutFuncCase : public FuncCaseBase |
| { |
| public: |
| typedef Func<Sig> CaseFunc; |
| typedef typename Sig::Ret Ret; |
| typedef typename Sig::Arg0 Arg0; |
| typedef typename Sig::Arg1 Arg1; |
| typedef typename Sig::Arg2 Arg2; |
| typedef typename Sig::Arg3 Arg3; |
| typedef InTypes<Arg0, Arg2, Arg3> In; |
| typedef OutTypes<Ret, Arg1> Out; |
| |
| InOutFuncCase(const Context &context, const string &name, const CaseFunc &func) |
| : FuncCaseBase(context, name, func) |
| , m_func(func) |
| { |
| } |
| |
| protected: |
| void runTest(void); |
| |
| virtual const Samplings<In> &getSamplings(void) |
| { |
| return instance<DefaultSamplings<In>>(); |
| } |
| |
| private: |
| const CaseFunc &m_func; |
| }; |
| |
| template <typename Sig> |
| void InOutFuncCase<Sig>::runTest(void) |
| { |
| const Inputs<In> inputs( |
| generateInputs(getSamplings(), m_ctx.floatFormat, m_ctx.precision, m_ctx.numRandoms, m_rnd)); |
| Variables<In, Out> variables; |
| |
| variables.out0 = variable<Ret>("out0"); |
| variables.out1 = variable<Arg1>("out1"); |
| variables.in0 = variable<Arg0>("in0"); |
| variables.in1 = variable<Arg2>("in1"); |
| variables.in2 = variable<Arg3>("in2"); |
| variables.in3 = variable<Void>("in3"); |
| |
| { |
| ExprP<Ret> expr = applyVar(m_func, variables.in0, variables.out1, variables.in1, variables.in2); |
| StatementP stmt = variableAssignment(variables.out0, expr); |
| |
| this->testStatement(variables, inputs, *stmt); |
| } |
| } |
| |
| template <typename Sig> |
| PrecisionCase *createFuncCase(const Context &context, const string &name, const Func<Sig> &func) |
| { |
| switch (func.getOutParamIndex()) |
| { |
| case -1: |
| return new FuncCase<Sig>(context, name, func); |
| case 1: |
| return new InOutFuncCase<Sig>(context, name, func); |
| default: |
| DE_FATAL("Impossible"); |
| } |
| return DE_NULL; |
| } |
| |
| class CaseFactory |
| { |
| public: |
| virtual ~CaseFactory(void) |
| { |
| } |
| virtual MovePtr<TestNode> createCase(const Context &ctx) const = 0; |
| virtual string getName(void) const = 0; |
| virtual string getDesc(void) const = 0; |
| }; |
| |
| class FuncCaseFactory : public CaseFactory |
| { |
| public: |
| virtual const FuncBase &getFunc(void) const = 0; |
| |
| string getName(void) const |
| { |
| return de::toLower(getFunc().getName()); |
| } |
| |
| string getDesc(void) const |
| { |
| return "Function '" + getFunc().getName() + "'"; |
| } |
| }; |
| |
| template <typename Sig> |
| class GenFuncCaseFactory : public CaseFactory |
| { |
| public: |
| GenFuncCaseFactory(const GenFuncs<Sig> &funcs, const string &name) : m_funcs(funcs), m_name(de::toLower(name)) |
| { |
| } |
| |
| MovePtr<TestNode> createCase(const Context &ctx) const |
| { |
| TestCaseGroup *group = new TestCaseGroup(ctx.testContext, ctx.name.c_str(), ctx.name.c_str()); |
| |
| group->addChild(createFuncCase(ctx, "scalar", m_funcs.func)); |
| group->addChild(createFuncCase(ctx, "vec2", m_funcs.func2)); |
| group->addChild(createFuncCase(ctx, "vec3", m_funcs.func3)); |
| group->addChild(createFuncCase(ctx, "vec4", m_funcs.func4)); |
| |
| return MovePtr<TestNode>(group); |
| } |
| |
| string getName(void) const |
| { |
| return m_name; |
| } |
| |
| string getDesc(void) const |
| { |
| return "Function '" + m_funcs.func.getName() + "'"; |
| } |
| |
| private: |
| const GenFuncs<Sig> m_funcs; |
| string m_name; |
| }; |
| |
| template <template <int> class GenF> |
| class TemplateFuncCaseFactory : public FuncCaseFactory |
| { |
| public: |
| MovePtr<TestNode> createCase(const Context &ctx) const |
| { |
| TestCaseGroup *group = new TestCaseGroup(ctx.testContext, ctx.name.c_str(), ctx.name.c_str()); |
| group->addChild(createFuncCase(ctx, "scalar", instance<GenF<1>>())); |
| group->addChild(createFuncCase(ctx, "vec2", instance<GenF<2>>())); |
| group->addChild(createFuncCase(ctx, "vec3", instance<GenF<3>>())); |
| group->addChild(createFuncCase(ctx, "vec4", instance<GenF<4>>())); |
| |
| return MovePtr<TestNode>(group); |
| } |
| |
| const FuncBase &getFunc(void) const |
| { |
| return instance<GenF<1>>(); |
| } |
| }; |
| |
| template <template <int> class GenF> |
| class SquareMatrixFuncCaseFactory : public FuncCaseFactory |
| { |
| public: |
| MovePtr<TestNode> createCase(const Context &ctx) const |
| { |
| TestCaseGroup *group = new TestCaseGroup(ctx.testContext, ctx.name.c_str(), ctx.name.c_str()); |
| group->addChild(createFuncCase(ctx, "mat2", instance<GenF<2>>())); |
| #if 0 |
| // disabled until we get reasonable results |
| group->addChild(createFuncCase(ctx, "mat3", instance<GenF<3> >())); |
| group->addChild(createFuncCase(ctx, "mat4", instance<GenF<4> >())); |
| #endif |
| |
| return MovePtr<TestNode>(group); |
| } |
| |
| const FuncBase &getFunc(void) const |
| { |
| return instance<GenF<2>>(); |
| } |
| }; |
| |
| template <template <int, int> class GenF> |
| class MatrixFuncCaseFactory : public FuncCaseFactory |
| { |
| public: |
| MovePtr<TestNode> createCase(const Context &ctx) const |
| { |
| TestCaseGroup *const group = new TestCaseGroup(ctx.testContext, ctx.name.c_str(), ctx.name.c_str()); |
| |
| this->addCase<2, 2>(ctx, group); |
| this->addCase<3, 2>(ctx, group); |
| this->addCase<4, 2>(ctx, group); |
| this->addCase<2, 3>(ctx, group); |
| this->addCase<3, 3>(ctx, group); |
| this->addCase<4, 3>(ctx, group); |
| this->addCase<2, 4>(ctx, group); |
| this->addCase<3, 4>(ctx, group); |
| this->addCase<4, 4>(ctx, group); |
| |
| return MovePtr<TestNode>(group); |
| } |
| |
| const FuncBase &getFunc(void) const |
| { |
| return instance<GenF<2, 2>>(); |
| } |
| |
| private: |
| template <int Rows, int Cols> |
| void addCase(const Context &ctx, TestCaseGroup *group) const |
| { |
| const char *const name = dataTypeNameOf<Matrix<float, Rows, Cols>>(); |
| |
| group->addChild(createFuncCase(ctx, name, instance<GenF<Rows, Cols>>())); |
| } |
| }; |
| |
| template <typename Sig> |
| class SimpleFuncCaseFactory : public CaseFactory |
| { |
| public: |
| SimpleFuncCaseFactory(const Func<Sig> &func) : m_func(func) |
| { |
| } |
| |
| MovePtr<TestNode> createCase(const Context &ctx) const |
| { |
| return MovePtr<TestNode>(createFuncCase(ctx, ctx.name.c_str(), m_func)); |
| } |
| |
| string getName(void) const |
| { |
| return de::toLower(m_func.getName()); |
| } |
| |
| string getDesc(void) const |
| { |
| return "Function '" + getName() + "'"; |
| } |
| |
| private: |
| const Func<Sig> &m_func; |
| }; |
| |
| template <typename F> |
| SharedPtr<SimpleFuncCaseFactory<typename F::Sig>> createSimpleFuncCaseFactory(void) |
| { |
| return SharedPtr<SimpleFuncCaseFactory<typename F::Sig>>(new SimpleFuncCaseFactory<typename F::Sig>(instance<F>())); |
| } |
| |
| class BuiltinFuncs : public CaseFactories |
| { |
| public: |
| const vector<const CaseFactory *> getFactories(void) const |
| { |
| vector<const CaseFactory *> ret; |
| |
| for (size_t ndx = 0; ndx < m_factories.size(); ++ndx) |
| ret.push_back(m_factories[ndx].get()); |
| |
| return ret; |
| } |
| |
| void addFactory(SharedPtr<const CaseFactory> fact) |
| { |
| m_factories.push_back(fact); |
| } |
| |
| private: |
| vector<SharedPtr<const CaseFactory>> m_factories; |
| }; |
| |
| template <typename F> |
| void addScalarFactory(BuiltinFuncs &funcs, string name = "") |
| { |
| if (name.empty()) |
| name = instance<F>().getName(); |
| |
| funcs.addFactory( |
| SharedPtr<const CaseFactory>(new GenFuncCaseFactory<typename F::Sig>(makeVectorizedFuncs<F>(), name))); |
| } |
| |
| MovePtr<const CaseFactories> createES3BuiltinCases(void) |
| { |
| MovePtr<BuiltinFuncs> funcs(new BuiltinFuncs()); |
| |
| addScalarFactory<Add>(*funcs); |
| addScalarFactory<Sub>(*funcs); |
| addScalarFactory<Mul>(*funcs); |
| addScalarFactory<Div>(*funcs); |
| |
| addScalarFactory<Radians>(*funcs); |
| addScalarFactory<Degrees>(*funcs); |
| addScalarFactory<Sin>(*funcs); |
| addScalarFactory<Cos>(*funcs); |
| addScalarFactory<Tan>(*funcs); |
| addScalarFactory<ASin>(*funcs); |
| addScalarFactory<ACos>(*funcs); |
| addScalarFactory<ATan2>(*funcs, "atan2"); |
| addScalarFactory<ATan>(*funcs); |
| addScalarFactory<Sinh>(*funcs); |
| addScalarFactory<Cosh>(*funcs); |
| addScalarFactory<Tanh>(*funcs); |
| addScalarFactory<ASinh>(*funcs); |
| addScalarFactory<ACosh>(*funcs); |
| addScalarFactory<ATanh>(*funcs); |
| |
| addScalarFactory<Pow>(*funcs); |
| addScalarFactory<Exp>(*funcs); |
| addScalarFactory<Log>(*funcs); |
| addScalarFactory<Exp2>(*funcs); |
| addScalarFactory<Log2>(*funcs); |
| addScalarFactory<Sqrt>(*funcs); |
| addScalarFactory<InverseSqrt>(*funcs); |
| |
| addScalarFactory<Abs>(*funcs); |
| addScalarFactory<Sign>(*funcs); |
| addScalarFactory<Floor>(*funcs); |
| addScalarFactory<Trunc>(*funcs); |
| addScalarFactory<Round>(*funcs); |
| addScalarFactory<RoundEven>(*funcs); |
| addScalarFactory<Ceil>(*funcs); |
| addScalarFactory<Fract>(*funcs); |
| addScalarFactory<Mod>(*funcs); |
| funcs->addFactory(createSimpleFuncCaseFactory<Modf>()); |
| addScalarFactory<Min>(*funcs); |
| addScalarFactory<Max>(*funcs); |
| addScalarFactory<Clamp>(*funcs); |
| addScalarFactory<Mix>(*funcs); |
| addScalarFactory<Step>(*funcs); |
| addScalarFactory<SmoothStep>(*funcs); |
| |
| funcs->addFactory(SharedPtr<const CaseFactory>(new TemplateFuncCaseFactory<Length>())); |
| funcs->addFactory(SharedPtr<const CaseFactory>(new TemplateFuncCaseFactory<Distance>())); |
| funcs->addFactory(SharedPtr<const CaseFactory>(new TemplateFuncCaseFactory<Dot>())); |
| funcs->addFactory(createSimpleFuncCaseFactory<Cross>()); |
| funcs->addFactory(SharedPtr<const CaseFactory>(new TemplateFuncCaseFactory<Normalize>())); |
| funcs->addFactory(SharedPtr<const CaseFactory>(new TemplateFuncCaseFactory<FaceForward>())); |
| funcs->addFactory(SharedPtr<const CaseFactory>(new TemplateFuncCaseFactory<Reflect>())); |
| funcs->addFactory(SharedPtr<const CaseFactory>(new TemplateFuncCaseFactory<Refract>())); |
| |
| funcs->addFactory(SharedPtr<const CaseFactory>(new MatrixFuncCaseFactory<MatrixCompMult>())); |
| funcs->addFactory(SharedPtr<const CaseFactory>(new MatrixFuncCaseFactory<OuterProduct>())); |
| funcs->addFactory(SharedPtr<const CaseFactory>(new MatrixFuncCaseFactory<Transpose>())); |
| funcs->addFactory(SharedPtr<const CaseFactory>(new SquareMatrixFuncCaseFactory<Determinant>())); |
| funcs->addFactory(SharedPtr<const CaseFactory>(new SquareMatrixFuncCaseFactory<Inverse>())); |
| |
| return MovePtr<const CaseFactories>(funcs.release()); |
| } |
| |
| MovePtr<const CaseFactories> createES31BuiltinCases(void) |
| { |
| MovePtr<BuiltinFuncs> funcs(new BuiltinFuncs()); |
| |
| addScalarFactory<FrExp>(*funcs); |
| addScalarFactory<LdExp>(*funcs); |
| addScalarFactory<Fma>(*funcs); |
| |
| return MovePtr<const CaseFactories>(funcs.release()); |
| } |
| |
| struct PrecisionTestContext |
| { |
| PrecisionTestContext(TestContext &testCtx_, RenderContext &renderCtx_, const FloatFormat &highp_, |
| const FloatFormat &mediump_, const FloatFormat &lowp_, const vector<ShaderType> &shaderTypes_, |
| int numRandoms_) |
| : testCtx(testCtx_) |
| , renderCtx(renderCtx_) |
| , shaderTypes(shaderTypes_) |
| , numRandoms(numRandoms_) |
| { |
| formats[glu::PRECISION_HIGHP] = &highp_; |
| formats[glu::PRECISION_MEDIUMP] = &mediump_; |
| formats[glu::PRECISION_LOWP] = &lowp_; |
| } |
| |
| TestContext &testCtx; |
| RenderContext &renderCtx; |
| const FloatFormat *formats[glu::PRECISION_LAST]; |
| vector<ShaderType> shaderTypes; |
| int numRandoms; |
| }; |
| |
| TestCaseGroup *createFuncGroup(const PrecisionTestContext &ctx, const CaseFactory &factory) |
| { |
| TestCaseGroup *const group = new TestCaseGroup(ctx.testCtx, factory.getName().c_str(), factory.getDesc().c_str()); |
| |
| for (int precNdx = 0; precNdx < glu::PRECISION_LAST; ++precNdx) |
| { |
| const Precision precision = Precision(precNdx); |
| const string precName(glu::getPrecisionName(precision)); |
| const FloatFormat &fmt = *de::getSizedArrayElement<glu::PRECISION_LAST>(ctx.formats, precNdx); |
| const FloatFormat &highpFmt = *de::getSizedArrayElement<glu::PRECISION_LAST>(ctx.formats, glu::PRECISION_HIGHP); |
| |
| for (size_t shaderNdx = 0; shaderNdx < ctx.shaderTypes.size(); ++shaderNdx) |
| { |
| const ShaderType shaderType = ctx.shaderTypes[shaderNdx]; |
| const string shaderName(glu::getShaderTypeName(shaderType)); |
| const string name = precName + "_" + shaderName; |
| const Context caseCtx(name, ctx.testCtx, ctx.renderCtx, fmt, highpFmt, precision, shaderType, |
| ctx.numRandoms); |
| |
| group->addChild(factory.createCase(caseCtx).release()); |
| } |
| } |
| |
| return group; |
| } |
| |
| void addBuiltinPrecisionTests(TestContext &testCtx, RenderContext &renderCtx, const CaseFactories &cases, |
| const vector<ShaderType> &shaderTypes, TestCaseGroup &dstGroup) |
| { |
| const int userRandoms = testCtx.getCommandLine().getTestIterationCount(); |
| const int defRandoms = 16384; |
| const int numRandoms = userRandoms > 0 ? userRandoms : defRandoms; |
| const FloatFormat highp(-126, 127, 23, true, |
| tcu::MAYBE, // subnormals |
| tcu::YES, // infinities |
| tcu::MAYBE); // NaN |
| // \todo [2014-04-01 lauri] Check these once Khronos bug 11840 is resolved. |
| const FloatFormat mediump(-13, 13, 9, false); |
| // A fixed-point format is just a floating point format with a fixed |
| // exponent and support for subnormals. |
| const FloatFormat lowp(0, 0, 7, false, tcu::YES); |
| const PrecisionTestContext ctx(testCtx, renderCtx, highp, mediump, lowp, shaderTypes, numRandoms); |
| |
| for (size_t ndx = 0; ndx < cases.getFactories().size(); ++ndx) |
| dstGroup.addChild(createFuncGroup(ctx, *cases.getFactories()[ndx])); |
| } |
| |
| } // namespace BuiltinPrecisionTests |
| } // namespace gls |
| } // namespace deqp |