You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5574 lines
145 KiB
5574 lines
145 KiB
/*-------------------------------------------------------------------------
|
|
* 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>
|
|
|
|
// 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::string;
|
|
using std::map;
|
|
using std::ostream;
|
|
using std::ostringstream;
|
|
using std::pair;
|
|
using std::vector;
|
|
using std::set;
|
|
|
|
using de::MovePtr;
|
|
using de::Random;
|
|
using de::SharedPtr;
|
|
using de::UniquePtr;
|
|
using tcu::Interval;
|
|
using tcu::FloatFormat;
|
|
using tcu::MessageBuilder;
|
|
using tcu::TestCase;
|
|
using tcu::TestLog;
|
|
using tcu::Vector;
|
|
using tcu::Matrix;
|
|
namespace matrix = tcu::matrix;
|
|
using glu::Precision;
|
|
using glu::RenderContext;
|
|
using glu::VarType;
|
|
using glu::DataType;
|
|
using glu::ShaderType;
|
|
using glu::ContextInfo;
|
|
using gls::ShaderExecUtil::Symbol;
|
|
|
|
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 Dummy 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)
|
|
{
|
|
deUint8* const data = new deUint8[sizeof(value)];
|
|
|
|
deMemcpy(data, &value, sizeof(value));
|
|
de::insert(m_map, variable.getName(), SharedPtr<deUint8>(data, de::ArrayDeleter<deUint8>()));
|
|
}
|
|
|
|
template<typename T>
|
|
typename Traits<T>::IVal& lookup (const Variable<T>& variable) const
|
|
{
|
|
deUint8* const data = de::lookup(m_map, variable.getName()).get();
|
|
|
|
return *reinterpret_cast<typename Traits<T>::IVal*>(data);
|
|
}
|
|
|
|
private:
|
|
map<string, SharedPtr<deUint8> > 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;
|
|
};
|
|
|
|
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 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) {}
|
|
};
|
|
|
|
/*--------------------------------------------------------------------*//*!
|
|
* \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 dummy 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 { return exprP(SharedPtr<const Expr<T> >(*this)); }
|
|
};
|
|
|
|
/*--------------------------------------------------------------------*//*!
|
|
* \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 (void) 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& paramNames = 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& paramNames = 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() && iargs.b.isOrdinary())
|
|
{
|
|
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() && b.isOrdinary())
|
|
{
|
|
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() && iargs.b.isOrdinary())
|
|
{
|
|
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, 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 (ctx.format.hasInf() != YES && (!yi.isFinite() || !xi.isFinite()))
|
|
{
|
|
// 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&, 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())
|
|
{
|
|
// 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 (void) const
|
|
{
|
|
return "GL_EXT_gpu_shader5";
|
|
}
|
|
|
|
protected:
|
|
ExprP<float> doExpand (ExpandContext&, const ArgExprs& x) const
|
|
{
|
|
return x.a * x.b + x.c;
|
|
}
|
|
};
|
|
|
|
} // 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 (void) const
|
|
{
|
|
return m_func.getRequiredExtension();
|
|
}
|
|
|
|
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, (deInt32)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.
|
|
{
|
|
deUint64 intFraction = rnd.getUint64() & ((1 << 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 dummy 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()) {}
|
|
};
|
|
|
|
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]));
|
|
}
|
|
|
|
} // BuiltinPrecisionTests
|
|
} // gls
|
|
} // deqp
|