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.
2187 lines
74 KiB
2187 lines
74 KiB
/*-------------------------------------------------------------------------
|
|
* drawElements Quality Program OpenGL ES 3.1 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 Common built-in function tests.
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "es31fShaderCommonFunctionTests.hpp"
|
|
#include "gluContextInfo.hpp"
|
|
#include "glsShaderExecUtil.hpp"
|
|
#include "tcuTestLog.hpp"
|
|
#include "tcuFormatUtil.hpp"
|
|
#include "tcuFloat.hpp"
|
|
#include "tcuInterval.hpp"
|
|
#include "tcuFloatFormat.hpp"
|
|
#include "tcuVectorUtil.hpp"
|
|
#include "deRandom.hpp"
|
|
#include "deMath.h"
|
|
#include "deString.h"
|
|
#include "deArrayUtil.hpp"
|
|
|
|
namespace deqp
|
|
{
|
|
namespace gles31
|
|
{
|
|
namespace Functional
|
|
{
|
|
|
|
using std::vector;
|
|
using std::string;
|
|
using tcu::TestLog;
|
|
using namespace gls::ShaderExecUtil;
|
|
|
|
using tcu::Vec2;
|
|
using tcu::Vec3;
|
|
using tcu::Vec4;
|
|
using tcu::IVec2;
|
|
using tcu::IVec3;
|
|
using tcu::IVec4;
|
|
|
|
// Utilities
|
|
|
|
template<typename T, int Size>
|
|
struct VecArrayAccess
|
|
{
|
|
public:
|
|
VecArrayAccess (const void* ptr) : m_array((tcu::Vector<T, Size>*)ptr) {}
|
|
~VecArrayAccess (void) {}
|
|
|
|
const tcu::Vector<T, Size>& operator[] (size_t offset) const { return m_array[offset]; }
|
|
tcu::Vector<T, Size>& operator[] (size_t offset) { return m_array[offset]; }
|
|
|
|
private:
|
|
tcu::Vector<T, Size>* m_array;
|
|
};
|
|
|
|
template<typename T, int Size>
|
|
static void fillRandomVectors (de::Random& rnd, const tcu::Vector<T, Size>& minValue, const tcu::Vector<T, Size>& maxValue, void* dst, int numValues, int offset = 0)
|
|
{
|
|
VecArrayAccess<T, Size> access(dst);
|
|
for (int ndx = 0; ndx < numValues; ndx++)
|
|
access[offset + ndx] = tcu::randomVector<T, Size>(rnd, minValue, maxValue);
|
|
}
|
|
|
|
template<typename T>
|
|
static void fillRandomScalars (de::Random& rnd, T minValue, T maxValue, void* dst, int numValues, int offset = 0)
|
|
{
|
|
T* typedPtr = (T*)dst;
|
|
for (int ndx = 0; ndx < numValues; ndx++)
|
|
typedPtr[offset + ndx] = de::randomScalar<T>(rnd, minValue, maxValue);
|
|
}
|
|
|
|
inline int numBitsLostInOp (float input, float output)
|
|
{
|
|
const int inExp = tcu::Float32(input).exponent();
|
|
const int outExp = tcu::Float32(output).exponent();
|
|
|
|
return de::max(0, inExp-outExp); // Lost due to mantissa shift.
|
|
}
|
|
|
|
inline deUint32 getUlpDiff (float a, float b)
|
|
{
|
|
const deUint32 aBits = tcu::Float32(a).bits();
|
|
const deUint32 bBits = tcu::Float32(b).bits();
|
|
return aBits > bBits ? aBits - bBits : bBits - aBits;
|
|
}
|
|
|
|
inline deUint32 getUlpDiffIgnoreZeroSign (float a, float b)
|
|
{
|
|
if (tcu::Float32(a).isZero())
|
|
return getUlpDiff(tcu::Float32::construct(tcu::Float32(b).sign(), 0, 0).asFloat(), b);
|
|
else if (tcu::Float32(b).isZero())
|
|
return getUlpDiff(a, tcu::Float32::construct(tcu::Float32(a).sign(), 0, 0).asFloat());
|
|
else
|
|
return getUlpDiff(a, b);
|
|
}
|
|
|
|
inline bool supportsSignedZero (glu::Precision precision)
|
|
{
|
|
// \note GLSL ES 3.1 doesn't really require support for -0, but we require it for highp
|
|
// as it is very widely supported.
|
|
return precision == glu::PRECISION_HIGHP;
|
|
}
|
|
|
|
inline float getEpsFromMaxUlpDiff (float value, deUint32 ulpDiff)
|
|
{
|
|
const int exp = tcu::Float32(value).exponent();
|
|
return tcu::Float32::construct(+1, exp, (1u<<23) | ulpDiff).asFloat() - tcu::Float32::construct(+1, exp, 1u<<23).asFloat();
|
|
}
|
|
|
|
inline deUint32 getMaxUlpDiffFromBits (int numAccurateBits)
|
|
{
|
|
const int numGarbageBits = 23-numAccurateBits;
|
|
const deUint32 mask = (1u<<numGarbageBits)-1u;
|
|
|
|
return mask;
|
|
}
|
|
|
|
inline float getEpsFromBits (float value, int numAccurateBits)
|
|
{
|
|
return getEpsFromMaxUlpDiff(value, getMaxUlpDiffFromBits(numAccurateBits));
|
|
}
|
|
|
|
static int getMinMantissaBits (glu::Precision precision)
|
|
{
|
|
const int bits[] =
|
|
{
|
|
7, // lowp
|
|
10, // mediump
|
|
23 // highp
|
|
};
|
|
DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(bits) == glu::PRECISION_LAST);
|
|
DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(bits)));
|
|
return bits[precision];
|
|
}
|
|
|
|
static int getMaxNormalizedValueExponent (glu::Precision precision)
|
|
{
|
|
const int exponent[] =
|
|
{
|
|
0, // lowp
|
|
13, // mediump
|
|
127 // highp
|
|
};
|
|
DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(exponent) == glu::PRECISION_LAST);
|
|
DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(exponent)));
|
|
return exponent[precision];
|
|
}
|
|
|
|
static int getMinNormalizedValueExponent (glu::Precision precision)
|
|
{
|
|
const int exponent[] =
|
|
{
|
|
-7, // lowp
|
|
-13, // mediump
|
|
-126 // highp
|
|
};
|
|
DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(exponent) == glu::PRECISION_LAST);
|
|
DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(exponent)));
|
|
return exponent[precision];
|
|
}
|
|
|
|
static float makeFloatRepresentable (float f, glu::Precision precision)
|
|
{
|
|
if (precision == glu::PRECISION_HIGHP)
|
|
{
|
|
// \note: assuming f is not extended-precision
|
|
return f;
|
|
}
|
|
else
|
|
{
|
|
const int numMantissaBits = getMinMantissaBits(precision);
|
|
const int maxNormalizedValueExponent = getMaxNormalizedValueExponent(precision);
|
|
const int minNormalizedValueExponent = getMinNormalizedValueExponent(precision);
|
|
const deUint32 representableMantissaMask = ((deUint32(1) << numMantissaBits) - 1) << (23 - (deUint32)numMantissaBits);
|
|
const float largestRepresentableValue = tcu::Float32::constructBits(+1, maxNormalizedValueExponent, ((1u << numMantissaBits) - 1u) << (23u - (deUint32)numMantissaBits)).asFloat();
|
|
const bool zeroNotRepresentable = (precision == glu::PRECISION_LOWP);
|
|
|
|
// if zero is not required to be representable, use smallest positive non-subnormal value
|
|
const float zeroValue = (zeroNotRepresentable) ? (tcu::Float32::constructBits(+1, minNormalizedValueExponent, 1).asFloat()) : (0.0f);
|
|
|
|
const tcu::Float32 float32Representation (f);
|
|
|
|
if (float32Representation.exponent() < minNormalizedValueExponent)
|
|
{
|
|
// flush too small values to zero
|
|
return zeroValue;
|
|
}
|
|
else if (float32Representation.exponent() > maxNormalizedValueExponent)
|
|
{
|
|
// clamp too large values
|
|
return (float32Representation.sign() == +1) ? (largestRepresentableValue) : (-largestRepresentableValue);
|
|
}
|
|
else
|
|
{
|
|
// remove unrepresentable mantissa bits
|
|
const tcu::Float32 targetRepresentation(tcu::Float32::constructBits(float32Representation.sign(),
|
|
float32Representation.exponent(),
|
|
float32Representation.mantissaBits() & representableMantissaMask));
|
|
|
|
return targetRepresentation.asFloat();
|
|
}
|
|
}
|
|
}
|
|
|
|
// CommonFunctionCase
|
|
|
|
class CommonFunctionCase : public TestCase
|
|
{
|
|
public:
|
|
CommonFunctionCase (Context& context, const char* name, const char* description, glu::ShaderType shaderType);
|
|
~CommonFunctionCase (void);
|
|
|
|
void init (void);
|
|
void deinit (void);
|
|
IterateResult iterate (void);
|
|
|
|
protected:
|
|
CommonFunctionCase (const CommonFunctionCase& other);
|
|
CommonFunctionCase& operator= (const CommonFunctionCase& other);
|
|
|
|
virtual void getInputValues (int numValues, void* const* values) const = 0;
|
|
virtual bool compare (const void* const* inputs, const void* const* outputs) = 0;
|
|
|
|
glu::ShaderType m_shaderType;
|
|
ShaderSpec m_spec;
|
|
int m_numValues;
|
|
|
|
std::ostringstream m_failMsg; //!< Comparison failure help message.
|
|
|
|
private:
|
|
ShaderExecutor* m_executor;
|
|
};
|
|
|
|
CommonFunctionCase::CommonFunctionCase (Context& context, const char* name, const char* description, glu::ShaderType shaderType)
|
|
: TestCase (context, name, description)
|
|
, m_shaderType (shaderType)
|
|
, m_numValues (100)
|
|
, m_executor (DE_NULL)
|
|
{
|
|
}
|
|
|
|
CommonFunctionCase::~CommonFunctionCase (void)
|
|
{
|
|
CommonFunctionCase::deinit();
|
|
}
|
|
|
|
void CommonFunctionCase::init (void)
|
|
{
|
|
DE_ASSERT(!m_executor);
|
|
|
|
glu::ContextType contextType = m_context.getRenderContext().getType();
|
|
m_spec.version = glu::getContextTypeGLSLVersion(contextType);
|
|
|
|
m_executor = createExecutor(m_context.getRenderContext(), m_shaderType, m_spec);
|
|
m_testCtx.getLog() << m_executor;
|
|
|
|
if (!m_executor->isOk())
|
|
throw tcu::TestError("Compile failed");
|
|
}
|
|
|
|
void CommonFunctionCase::deinit (void)
|
|
{
|
|
delete m_executor;
|
|
m_executor = DE_NULL;
|
|
}
|
|
|
|
static vector<int> getScalarSizes (const vector<Symbol>& symbols)
|
|
{
|
|
vector<int> sizes(symbols.size());
|
|
for (int ndx = 0; ndx < (int)symbols.size(); ++ndx)
|
|
sizes[ndx] = symbols[ndx].varType.getScalarSize();
|
|
return sizes;
|
|
}
|
|
|
|
static int computeTotalScalarSize (const vector<Symbol>& symbols)
|
|
{
|
|
int totalSize = 0;
|
|
for (vector<Symbol>::const_iterator sym = symbols.begin(); sym != symbols.end(); ++sym)
|
|
totalSize += sym->varType.getScalarSize();
|
|
return totalSize;
|
|
}
|
|
|
|
static vector<void*> getInputOutputPointers (const vector<Symbol>& symbols, vector<deUint32>& data, const int numValues)
|
|
{
|
|
vector<void*> pointers (symbols.size());
|
|
int curScalarOffset = 0;
|
|
|
|
for (int varNdx = 0; varNdx < (int)symbols.size(); ++varNdx)
|
|
{
|
|
const Symbol& var = symbols[varNdx];
|
|
const int scalarSize = var.varType.getScalarSize();
|
|
|
|
// Uses planar layout as input/output specs do not support strides.
|
|
pointers[varNdx] = &data[curScalarOffset];
|
|
curScalarOffset += scalarSize*numValues;
|
|
}
|
|
|
|
DE_ASSERT(curScalarOffset == (int)data.size());
|
|
|
|
return pointers;
|
|
}
|
|
|
|
// \todo [2013-08-08 pyry] Make generic utility and move to glu?
|
|
|
|
struct HexFloat
|
|
{
|
|
const float value;
|
|
HexFloat (const float value_) : value(value_) {}
|
|
};
|
|
|
|
std::ostream& operator<< (std::ostream& str, const HexFloat& v)
|
|
{
|
|
return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits());
|
|
}
|
|
|
|
struct HexBool
|
|
{
|
|
const deUint32 value;
|
|
HexBool (const deUint32 value_) : value(value_) {}
|
|
};
|
|
|
|
std::ostream& operator<< (std::ostream& str, const HexBool& v)
|
|
{
|
|
return str << (v.value ? "true" : "false") << " / " << tcu::toHex(v.value);
|
|
}
|
|
|
|
struct VarValue
|
|
{
|
|
const glu::VarType& type;
|
|
const void* value;
|
|
|
|
VarValue (const glu::VarType& type_, const void* value_) : type(type_), value(value_) {}
|
|
};
|
|
|
|
std::ostream& operator<< (std::ostream& str, const VarValue& varValue)
|
|
{
|
|
DE_ASSERT(varValue.type.isBasicType());
|
|
|
|
const glu::DataType basicType = varValue.type.getBasicType();
|
|
const glu::DataType scalarType = glu::getDataTypeScalarType(basicType);
|
|
const int numComponents = glu::getDataTypeScalarSize(basicType);
|
|
|
|
if (numComponents > 1)
|
|
str << glu::getDataTypeName(basicType) << "(";
|
|
|
|
for (int compNdx = 0; compNdx < numComponents; compNdx++)
|
|
{
|
|
if (compNdx != 0)
|
|
str << ", ";
|
|
|
|
switch (scalarType)
|
|
{
|
|
case glu::TYPE_FLOAT: str << HexFloat(((const float*)varValue.value)[compNdx]); break;
|
|
case glu::TYPE_INT: str << ((const deInt32*)varValue.value)[compNdx]; break;
|
|
case glu::TYPE_UINT: str << tcu::toHex(((const deUint32*)varValue.value)[compNdx]); break;
|
|
case glu::TYPE_BOOL: str << HexBool(((const deUint32*)varValue.value)[compNdx]); break;
|
|
|
|
default:
|
|
DE_ASSERT(false);
|
|
}
|
|
}
|
|
|
|
if (numComponents > 1)
|
|
str << ")";
|
|
|
|
return str;
|
|
}
|
|
|
|
CommonFunctionCase::IterateResult CommonFunctionCase::iterate (void)
|
|
{
|
|
const int numInputScalars = computeTotalScalarSize(m_spec.inputs);
|
|
const int numOutputScalars = computeTotalScalarSize(m_spec.outputs);
|
|
vector<deUint32> inputData (numInputScalars * m_numValues);
|
|
vector<deUint32> outputData (numOutputScalars * m_numValues);
|
|
const vector<void*> inputPointers = getInputOutputPointers(m_spec.inputs, inputData, m_numValues);
|
|
const vector<void*> outputPointers = getInputOutputPointers(m_spec.outputs, outputData, m_numValues);
|
|
|
|
// Initialize input data.
|
|
getInputValues(m_numValues, &inputPointers[0]);
|
|
|
|
// Execute shader.
|
|
m_executor->useProgram();
|
|
m_executor->execute(m_numValues, &inputPointers[0], &outputPointers[0]);
|
|
|
|
// Compare results.
|
|
{
|
|
const vector<int> inScalarSizes = getScalarSizes(m_spec.inputs);
|
|
const vector<int> outScalarSizes = getScalarSizes(m_spec.outputs);
|
|
vector<void*> curInputPtr (inputPointers.size());
|
|
vector<void*> curOutputPtr (outputPointers.size());
|
|
int numFailed = 0;
|
|
|
|
for (int valNdx = 0; valNdx < m_numValues; valNdx++)
|
|
{
|
|
// Set up pointers for comparison.
|
|
for (int inNdx = 0; inNdx < (int)curInputPtr.size(); ++inNdx)
|
|
curInputPtr[inNdx] = (deUint32*)inputPointers[inNdx] + inScalarSizes[inNdx]*valNdx;
|
|
|
|
for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); ++outNdx)
|
|
curOutputPtr[outNdx] = (deUint32*)outputPointers[outNdx] + outScalarSizes[outNdx]*valNdx;
|
|
|
|
if (!compare(&curInputPtr[0], &curOutputPtr[0]))
|
|
{
|
|
// \todo [2013-08-08 pyry] We probably want to log reference value as well?
|
|
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: comparison failed for value " << valNdx << ":\n " << m_failMsg.str() << TestLog::EndMessage;
|
|
|
|
m_testCtx.getLog() << TestLog::Message << " inputs:" << TestLog::EndMessage;
|
|
for (int inNdx = 0; inNdx < (int)curInputPtr.size(); inNdx++)
|
|
m_testCtx.getLog() << TestLog::Message << " " << m_spec.inputs[inNdx].name << " = "
|
|
<< VarValue(m_spec.inputs[inNdx].varType, curInputPtr[inNdx])
|
|
<< TestLog::EndMessage;
|
|
|
|
m_testCtx.getLog() << TestLog::Message << " outputs:" << TestLog::EndMessage;
|
|
for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); outNdx++)
|
|
m_testCtx.getLog() << TestLog::Message << " " << m_spec.outputs[outNdx].name << " = "
|
|
<< VarValue(m_spec.outputs[outNdx].varType, curOutputPtr[outNdx])
|
|
<< TestLog::EndMessage;
|
|
|
|
m_failMsg.str("");
|
|
m_failMsg.clear();
|
|
numFailed += 1;
|
|
}
|
|
}
|
|
|
|
m_testCtx.getLog() << TestLog::Message << (m_numValues - numFailed) << " / " << m_numValues << " values passed" << TestLog::EndMessage;
|
|
|
|
m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
|
|
numFailed == 0 ? "Pass" : "Result comparison failed");
|
|
}
|
|
|
|
return STOP;
|
|
}
|
|
|
|
static std::string getCommonFuncCaseName (glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
|
|
{
|
|
return string(glu::getDataTypeName(baseType)) + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType);
|
|
}
|
|
|
|
class AbsCase : public CommonFunctionCase
|
|
{
|
|
public:
|
|
AbsCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
|
|
: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "abs", shaderType)
|
|
{
|
|
m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
|
|
m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
|
|
m_spec.source = "out0 = abs(in0);";
|
|
}
|
|
|
|
void getInputValues (int numValues, void* const* values) const
|
|
{
|
|
const Vec2 floatRanges[] =
|
|
{
|
|
Vec2(-2.0f, 2.0f), // lowp
|
|
Vec2(-1e3f, 1e3f), // mediump
|
|
Vec2(-1e7f, 1e7f) // highp
|
|
};
|
|
const IVec2 intRanges[] =
|
|
{
|
|
IVec2(-(1<<7)+1, (1<<7)-1),
|
|
IVec2(-(1<<15)+1, (1<<15)-1),
|
|
IVec2(0x80000001, 0x7fffffff)
|
|
};
|
|
|
|
de::Random rnd (deStringHash(getName()) ^ 0x235facu);
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
|
|
if (glu::isDataTypeFloatOrVec(type))
|
|
fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), values[0], numValues*scalarSize);
|
|
else
|
|
fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), values[0], numValues*scalarSize);
|
|
}
|
|
|
|
bool compare (const void* const* inputs, const void* const* outputs)
|
|
{
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
|
|
if (glu::isDataTypeFloatOrVec(type))
|
|
{
|
|
const int mantissaBits = getMinMantissaBits(precision);
|
|
const deUint32 maxUlpDiff = (1u<<(23-mantissaBits))-1u;
|
|
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const float out0 = ((const float*)outputs[0])[compNdx];
|
|
const float ref0 = de::abs(in0);
|
|
const deUint32 ulpDiff0 = getUlpDiff(out0, ref0);
|
|
|
|
if (ulpDiff0 > maxUlpDiff)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const int in0 = ((const int*)inputs[0])[compNdx];
|
|
const int out0 = ((const int*)outputs[0])[compNdx];
|
|
const int ref0 = de::abs(in0);
|
|
|
|
if (out0 != ref0)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << ref0;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class SignCase : public CommonFunctionCase
|
|
{
|
|
public:
|
|
SignCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
|
|
: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "sign", shaderType)
|
|
{
|
|
m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
|
|
m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
|
|
m_spec.source = "out0 = sign(in0);";
|
|
}
|
|
|
|
void getInputValues (int numValues, void* const* values) const
|
|
{
|
|
const Vec2 floatRanges[] =
|
|
{
|
|
Vec2(-2.0f, 2.0f), // lowp
|
|
Vec2(-1e4f, 1e4f), // mediump - note: may end up as inf
|
|
Vec2(-1e8f, 1e8f) // highp - note: may end up as inf
|
|
};
|
|
const IVec2 intRanges[] =
|
|
{
|
|
IVec2(-(1<<7), (1<<7)-1),
|
|
IVec2(-(1<<15), (1<<15)-1),
|
|
IVec2(0x80000000, 0x7fffffff)
|
|
};
|
|
|
|
de::Random rnd (deStringHash(getName()) ^ 0x324u);
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
|
|
if (glu::isDataTypeFloatOrVec(type))
|
|
{
|
|
// Special cases.
|
|
std::fill((float*)values[0], (float*)values[0] + scalarSize, +1.0f);
|
|
std::fill((float*)values[0] + scalarSize*1, (float*)values[0] + scalarSize*2, -1.0f);
|
|
std::fill((float*)values[0] + scalarSize*2, (float*)values[0] + scalarSize*3, 0.0f);
|
|
fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), (float*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
|
|
}
|
|
else
|
|
{
|
|
std::fill((int*)values[0], (int*)values[0] + scalarSize, +1);
|
|
std::fill((int*)values[0] + scalarSize*1, (int*)values[0] + scalarSize*2, -1);
|
|
std::fill((int*)values[0] + scalarSize*2, (int*)values[0] + scalarSize*3, 0);
|
|
fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), (int*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
|
|
}
|
|
}
|
|
|
|
bool compare (const void* const* inputs, const void* const* outputs)
|
|
{
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
|
|
if (glu::isDataTypeFloatOrVec(type))
|
|
{
|
|
// Both highp and mediump should be able to represent -1, 0, and +1 exactly
|
|
const deUint32 maxUlpDiff = precision == glu::PRECISION_LOWP ? getMaxUlpDiffFromBits(getMinMantissaBits(precision)) : 0;
|
|
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const float out0 = ((const float*)outputs[0])[compNdx];
|
|
const float ref0 = in0 < 0.0f ? -1.0f :
|
|
in0 > 0.0f ? +1.0f : 0.0f;
|
|
const deUint32 ulpDiff0 = getUlpDiff(out0, ref0);
|
|
|
|
if (ulpDiff0 > maxUlpDiff)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const int in0 = ((const int*)inputs[0])[compNdx];
|
|
const int out0 = ((const int*)outputs[0])[compNdx];
|
|
const int ref0 = in0 < 0 ? -1 :
|
|
in0 > 0 ? +1 : 0;
|
|
|
|
if (out0 != ref0)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << ref0;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
static float roundEven (float v)
|
|
{
|
|
const float q = deFloatFrac(v);
|
|
const int truncated = int(v-q);
|
|
const int rounded = (q > 0.5f) ? (truncated + 1) : // Rounded up
|
|
(q == 0.5f && (truncated % 2 != 0)) ? (truncated + 1) : // Round to nearest even at 0.5
|
|
truncated; // Rounded down
|
|
|
|
return float(rounded);
|
|
}
|
|
|
|
class RoundEvenCase : public CommonFunctionCase
|
|
{
|
|
public:
|
|
RoundEvenCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
|
|
: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "roundEven", shaderType)
|
|
{
|
|
m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
|
|
m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
|
|
m_spec.source = "out0 = roundEven(in0);";
|
|
}
|
|
|
|
void getInputValues (int numValues, void* const* values) const
|
|
{
|
|
const Vec2 ranges[] =
|
|
{
|
|
Vec2(-2.0f, 2.0f), // lowp
|
|
Vec2(-1e3f, 1e3f), // mediump
|
|
Vec2(-1e7f, 1e7f) // highp
|
|
};
|
|
|
|
de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
int numSpecialCases = 0;
|
|
|
|
// Special cases.
|
|
if (precision != glu::PRECISION_LOWP)
|
|
{
|
|
DE_ASSERT(numValues >= 20);
|
|
for (int ndx = 0; ndx < 20; ndx++)
|
|
{
|
|
const float v = de::clamp(float(ndx) - 10.5f, ranges[precision].x(), ranges[precision].y());
|
|
std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
|
|
numSpecialCases += 1;
|
|
}
|
|
}
|
|
|
|
// Random cases.
|
|
fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
|
|
|
|
// If precision is mediump, make sure values can be represented in fp16 exactly
|
|
if (precision == glu::PRECISION_MEDIUMP)
|
|
{
|
|
for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
|
|
((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
|
|
}
|
|
}
|
|
|
|
bool compare (const void* const* inputs, const void* const* outputs)
|
|
{
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const bool hasSignedZero = supportsSignedZero(precision);
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
|
|
if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
|
|
{
|
|
// Require exact rounding result.
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const float out0 = ((const float*)outputs[0])[compNdx];
|
|
const float ref = roundEven(in0);
|
|
|
|
const deUint32 ulpDiff = hasSignedZero ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
|
|
|
|
if (ulpDiff > 0)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const int mantissaBits = getMinMantissaBits(precision);
|
|
const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
|
|
const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
|
|
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const float out0 = ((const float*)outputs[0])[compNdx];
|
|
const int minRes = int(roundEven(in0-eps));
|
|
const int maxRes = int(roundEven(in0+eps));
|
|
bool anyOk = false;
|
|
|
|
for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
|
|
{
|
|
const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
|
|
|
|
if (ulpDiff <= maxUlpDiff)
|
|
{
|
|
anyOk = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!anyOk)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class ModfCase : public CommonFunctionCase
|
|
{
|
|
public:
|
|
ModfCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
|
|
: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "modf", shaderType)
|
|
{
|
|
m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
|
|
m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
|
|
m_spec.outputs.push_back(Symbol("out1", glu::VarType(baseType, precision)));
|
|
m_spec.source = "out0 = modf(in0, out1);";
|
|
}
|
|
|
|
void getInputValues (int numValues, void* const* values) const
|
|
{
|
|
const Vec2 ranges[] =
|
|
{
|
|
Vec2(-2.0f, 2.0f), // lowp
|
|
Vec2(-1e3f, 1e3f), // mediump
|
|
Vec2(-1e7f, 1e7f) // highp
|
|
};
|
|
|
|
de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
|
|
fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
|
|
}
|
|
|
|
bool compare (const void* const* inputs, const void* const* outputs)
|
|
{
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const bool hasZeroSign = supportsSignedZero(precision);
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
|
|
const int mantissaBits = getMinMantissaBits(precision);
|
|
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const float out0 = ((const float*)outputs[0])[compNdx];
|
|
const float out1 = ((const float*)outputs[1])[compNdx];
|
|
|
|
const float refOut1 = float(int(in0));
|
|
const float refOut0 = in0 - refOut1;
|
|
|
|
const int bitsLost = precision != glu::PRECISION_HIGHP ? numBitsLostInOp(in0, refOut0) : 0;
|
|
const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(mantissaBits - bitsLost, 0));
|
|
|
|
const float resSum = out0 + out1;
|
|
|
|
const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(resSum, in0) : getUlpDiffIgnoreZeroSign(resSum, in0);
|
|
|
|
if (ulpDiff > maxUlpDiff)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = (" << HexFloat(refOut0) << ") + (" << HexFloat(refOut1) << ") = " << HexFloat(in0) << " with ULP threshold "
|
|
<< tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class IsnanCase : public CommonFunctionCase
|
|
{
|
|
public:
|
|
IsnanCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
|
|
: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isnan", shaderType)
|
|
{
|
|
DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
|
|
|
|
const int vecSize = glu::getDataTypeScalarSize(baseType);
|
|
const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
|
|
|
|
m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
|
|
m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
|
|
m_spec.source = "out0 = isnan(in0);";
|
|
}
|
|
|
|
void getInputValues (int numValues, void* const* values) const
|
|
{
|
|
de::Random rnd (deStringHash(getName()) ^ 0xc2a39fu);
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
const int mantissaBits = getMinMantissaBits(precision);
|
|
const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
|
|
|
|
for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
|
|
{
|
|
const bool isNan = rnd.getFloat() > 0.3f;
|
|
const bool isInf = !isNan && rnd.getFloat() > 0.4f;
|
|
const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
|
|
const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
|
|
const deUint32 sign = rnd.getUint32() & 0x1u;
|
|
const deUint32 value = (sign << 31) | (exp << 23) | mantissa;
|
|
|
|
DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
|
|
|
|
((deUint32*)values[0])[valNdx] = value;
|
|
}
|
|
}
|
|
|
|
bool compare (const void* const* inputs, const void* const* outputs)
|
|
{
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
|
|
if (precision == glu::PRECISION_HIGHP)
|
|
{
|
|
// Only highp is required to support inf/nan
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
|
|
const bool ref = tcu::Float32(in0).isNaN();
|
|
|
|
if (out0 != ref)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else if (precision == glu::PRECISION_MEDIUMP || precision == glu::PRECISION_LOWP)
|
|
{
|
|
// NaN support is optional, check that inputs that are not NaN don't result in true.
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
|
|
const bool ref = tcu::Float32(in0).isNaN();
|
|
|
|
if (!ref && out0)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class IsinfCase : public CommonFunctionCase
|
|
{
|
|
public:
|
|
IsinfCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
|
|
: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isinf", shaderType)
|
|
{
|
|
DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
|
|
|
|
const int vecSize = glu::getDataTypeScalarSize(baseType);
|
|
const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
|
|
|
|
m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
|
|
m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
|
|
m_spec.source = "out0 = isinf(in0);";
|
|
}
|
|
|
|
void getInputValues (int numValues, void* const* values) const
|
|
{
|
|
de::Random rnd (deStringHash(getName()) ^ 0xc2a39fu);
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
const int mantissaBits = getMinMantissaBits(precision);
|
|
const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
|
|
|
|
for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
|
|
{
|
|
const bool isInf = rnd.getFloat() > 0.3f;
|
|
const bool isNan = !isInf && rnd.getFloat() > 0.4f;
|
|
const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
|
|
const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
|
|
const deUint32 sign = rnd.getUint32() & 0x1u;
|
|
const deUint32 value = (sign << 31) | (exp << 23) | mantissa;
|
|
|
|
DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
|
|
|
|
((deUint32*)values[0])[valNdx] = value;
|
|
}
|
|
}
|
|
|
|
bool compare (const void* const* inputs, const void* const* outputs)
|
|
{
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
|
|
if (precision == glu::PRECISION_HIGHP)
|
|
{
|
|
// Only highp is required to support inf/nan
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
|
|
const bool ref = tcu::Float32(in0).isInf();
|
|
|
|
if (out0 != ref)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else if (precision == glu::PRECISION_MEDIUMP)
|
|
{
|
|
// Inf support is optional, check that inputs that are not Inf in mediump don't result in true.
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
|
|
const bool ref = tcu::Float16(in0).isInf();
|
|
|
|
if (!ref && out0)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
// else: no verification can be performed
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class FloatBitsToUintIntCase : public CommonFunctionCase
|
|
{
|
|
public:
|
|
FloatBitsToUintIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType, bool outIsSigned)
|
|
: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), outIsSigned ? "floatBitsToInt" : "floatBitsToUint", shaderType)
|
|
{
|
|
const int vecSize = glu::getDataTypeScalarSize(baseType);
|
|
const glu::DataType intType = outIsSigned ? (vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT)
|
|
: (vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT);
|
|
|
|
m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
|
|
m_spec.outputs.push_back(Symbol("out0", glu::VarType(intType, glu::PRECISION_HIGHP)));
|
|
m_spec.source = outIsSigned ? "out0 = floatBitsToInt(in0);" : "out0 = floatBitsToUint(in0);";
|
|
}
|
|
|
|
void getInputValues (int numValues, void* const* values) const
|
|
{
|
|
const Vec2 ranges[] =
|
|
{
|
|
Vec2(-2.0f, 2.0f), // lowp
|
|
Vec2(-1e3f, 1e3f), // mediump
|
|
Vec2(-1e7f, 1e7f) // highp
|
|
};
|
|
|
|
de::Random rnd (deStringHash(getName()) ^ 0x2790au);
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
|
|
fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
|
|
}
|
|
|
|
bool compare (const void* const* inputs, const void* const* outputs)
|
|
{
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
|
|
const int mantissaBits = getMinMantissaBits(precision);
|
|
const int maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
|
|
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx];
|
|
const deUint32 refOut0 = tcu::Float32(in0).bits();
|
|
const int ulpDiff = de::abs((int)out0 - (int)refOut0);
|
|
|
|
if (ulpDiff > maxUlpDiff)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(refOut0) << " with threshold "
|
|
<< tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class FloatBitsToIntCase : public FloatBitsToUintIntCase
|
|
{
|
|
public:
|
|
FloatBitsToIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
|
|
: FloatBitsToUintIntCase(context, baseType, precision, shaderType, true)
|
|
{
|
|
}
|
|
};
|
|
|
|
class FloatBitsToUintCase : public FloatBitsToUintIntCase
|
|
{
|
|
public:
|
|
FloatBitsToUintCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
|
|
: FloatBitsToUintIntCase(context, baseType, precision, shaderType, false)
|
|
{
|
|
}
|
|
};
|
|
|
|
class BitsToFloatCase : public CommonFunctionCase
|
|
{
|
|
public:
|
|
BitsToFloatCase (Context& context, glu::DataType baseType, glu::ShaderType shaderType)
|
|
: CommonFunctionCase(context, getCommonFuncCaseName(baseType, glu::PRECISION_HIGHP, shaderType).c_str(), glu::isDataTypeIntOrIVec(baseType) ? "intBitsToFloat" : "uintBitsToFloat", shaderType)
|
|
{
|
|
const bool inIsSigned = glu::isDataTypeIntOrIVec(baseType);
|
|
const int vecSize = glu::getDataTypeScalarSize(baseType);
|
|
const glu::DataType floatType = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
|
|
|
|
m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
|
|
m_spec.outputs.push_back(Symbol("out0", glu::VarType(floatType, glu::PRECISION_HIGHP)));
|
|
m_spec.source = inIsSigned ? "out0 = intBitsToFloat(in0);" : "out0 = uintBitsToFloat(in0);";
|
|
}
|
|
|
|
void getInputValues (int numValues, void* const* values) const
|
|
{
|
|
de::Random rnd (deStringHash(getName()) ^ 0xbbb225u);
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
const Vec2 range (-1e8f, +1e8f);
|
|
|
|
// \note Filled as floats.
|
|
fillRandomScalars(rnd, range.x(), range.y(), values[0], numValues*scalarSize);
|
|
}
|
|
|
|
bool compare (const void* const* inputs, const void* const* outputs)
|
|
{
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
const deUint32 maxUlpDiff = 0;
|
|
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const float out0 = ((const float*)outputs[0])[compNdx];
|
|
const deUint32 ulpDiff = getUlpDiff(in0, out0);
|
|
|
|
if (ulpDiff > maxUlpDiff)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(tcu::Float32(in0).bits()) << " with ULP threshold "
|
|
<< tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class FloorCase : public CommonFunctionCase
|
|
{
|
|
public:
|
|
FloorCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
|
|
: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "floor", shaderType)
|
|
{
|
|
m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
|
|
m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
|
|
m_spec.source = "out0 = floor(in0);";
|
|
}
|
|
|
|
void getInputValues (int numValues, void* const* values) const
|
|
{
|
|
const Vec2 ranges[] =
|
|
{
|
|
Vec2(-2.0f, 2.0f), // lowp
|
|
Vec2(-1e3f, 1e3f), // mediump
|
|
Vec2(-1e7f, 1e7f) // highp
|
|
};
|
|
|
|
de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
// Random cases.
|
|
fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
|
|
|
|
// If precision is mediump, make sure values can be represented in fp16 exactly
|
|
if (precision == glu::PRECISION_MEDIUMP)
|
|
{
|
|
for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
|
|
((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
|
|
}
|
|
}
|
|
|
|
bool compare (const void* const* inputs, const void* const* outputs)
|
|
{
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
|
|
if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
|
|
{
|
|
// Require exact result.
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const float out0 = ((const float*)outputs[0])[compNdx];
|
|
const float ref = deFloatFloor(in0);
|
|
|
|
const deUint32 ulpDiff = getUlpDiff(out0, ref);
|
|
|
|
if (ulpDiff > 0)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const int mantissaBits = getMinMantissaBits(precision);
|
|
const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
|
|
const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
|
|
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const float out0 = ((const float*)outputs[0])[compNdx];
|
|
const int minRes = int(deFloatFloor(in0-eps));
|
|
const int maxRes = int(deFloatFloor(in0+eps));
|
|
bool anyOk = false;
|
|
|
|
for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
|
|
{
|
|
const deUint32 ulpDiff = getUlpDiff(out0, float(roundedVal));
|
|
|
|
if (ulpDiff <= maxUlpDiff)
|
|
{
|
|
anyOk = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!anyOk)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class TruncCase : public CommonFunctionCase
|
|
{
|
|
public:
|
|
TruncCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
|
|
: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "trunc", shaderType)
|
|
{
|
|
m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
|
|
m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
|
|
m_spec.source = "out0 = trunc(in0);";
|
|
}
|
|
|
|
void getInputValues (int numValues, void* const* values) const
|
|
{
|
|
const Vec2 ranges[] =
|
|
{
|
|
Vec2(-2.0f, 2.0f), // lowp
|
|
Vec2(-1e3f, 1e3f), // mediump
|
|
Vec2(-1e7f, 1e7f) // highp
|
|
};
|
|
|
|
de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
const float specialCases[] = { 0.0f, -0.0f, -0.9f, 0.9f, 1.0f, -1.0f };
|
|
const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases);
|
|
|
|
// Special cases
|
|
for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
|
|
{
|
|
for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
|
|
((float*)values[0])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx];
|
|
}
|
|
|
|
// Random cases.
|
|
fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + scalarSize*numSpecialCases, (numValues-numSpecialCases)*scalarSize);
|
|
|
|
// If precision is mediump, make sure values can be represented in fp16 exactly
|
|
if (precision == glu::PRECISION_MEDIUMP)
|
|
{
|
|
for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
|
|
((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
|
|
}
|
|
}
|
|
|
|
bool compare (const void* const* inputs, const void* const* outputs)
|
|
{
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
|
|
if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
|
|
{
|
|
// Require exact result.
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const float out0 = ((const float*)outputs[0])[compNdx];
|
|
const bool isNeg = tcu::Float32(in0).sign() < 0;
|
|
const float ref = isNeg ? (-float(int(-in0))) : float(int(in0));
|
|
|
|
// \note: trunc() function definition is a bit broad on negative zeros. Ignore result sign if zero.
|
|
const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
|
|
|
|
if (ulpDiff > 0)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const int mantissaBits = getMinMantissaBits(precision);
|
|
const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
|
|
const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
|
|
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const float out0 = ((const float*)outputs[0])[compNdx];
|
|
const int minRes = int(in0-eps);
|
|
const int maxRes = int(in0+eps);
|
|
bool anyOk = false;
|
|
|
|
for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
|
|
{
|
|
const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
|
|
|
|
if (ulpDiff <= maxUlpDiff)
|
|
{
|
|
anyOk = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!anyOk)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class RoundCase : public CommonFunctionCase
|
|
{
|
|
public:
|
|
RoundCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
|
|
: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "round", shaderType)
|
|
{
|
|
m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
|
|
m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
|
|
m_spec.source = "out0 = round(in0);";
|
|
}
|
|
|
|
void getInputValues (int numValues, void* const* values) const
|
|
{
|
|
const Vec2 ranges[] =
|
|
{
|
|
Vec2(-2.0f, 2.0f), // lowp
|
|
Vec2(-1e3f, 1e3f), // mediump
|
|
Vec2(-1e7f, 1e7f) // highp
|
|
};
|
|
|
|
de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
int numSpecialCases = 0;
|
|
|
|
// Special cases.
|
|
if (precision != glu::PRECISION_LOWP)
|
|
{
|
|
DE_ASSERT(numValues >= 10);
|
|
for (int ndx = 0; ndx < 10; ndx++)
|
|
{
|
|
const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
|
|
std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
|
|
numSpecialCases += 1;
|
|
}
|
|
}
|
|
|
|
// Random cases.
|
|
fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
|
|
|
|
// If precision is mediump, make sure values can be represented in fp16 exactly
|
|
if (precision == glu::PRECISION_MEDIUMP)
|
|
{
|
|
for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
|
|
((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
|
|
}
|
|
}
|
|
|
|
bool compare (const void* const* inputs, const void* const* outputs)
|
|
{
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const bool hasZeroSign = supportsSignedZero(precision);
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
|
|
if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
|
|
{
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const float out0 = ((const float*)outputs[0])[compNdx];
|
|
|
|
if (deFloatFrac(in0) == 0.5f)
|
|
{
|
|
// Allow both ceil(in) and floor(in)
|
|
const float ref0 = deFloatFloor(in0);
|
|
const float ref1 = deFloatCeil(in0);
|
|
const deUint32 ulpDiff0 = hasZeroSign ? getUlpDiff(out0, ref0) : getUlpDiffIgnoreZeroSign(out0, ref0);
|
|
const deUint32 ulpDiff1 = hasZeroSign ? getUlpDiff(out0, ref1) : getUlpDiffIgnoreZeroSign(out0, ref1);
|
|
|
|
if (ulpDiff0 > 0 && ulpDiff1 > 0)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " or " << HexFloat(ref1) << ", got ULP diff " << tcu::toHex(de::min(ulpDiff0, ulpDiff1));
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Require exact result
|
|
const float ref = roundEven(in0);
|
|
const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
|
|
|
|
if (ulpDiff > 0)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const int mantissaBits = getMinMantissaBits(precision);
|
|
const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
|
|
const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
|
|
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const float out0 = ((const float*)outputs[0])[compNdx];
|
|
const int minRes = int(roundEven(in0-eps));
|
|
const int maxRes = int(roundEven(in0+eps));
|
|
bool anyOk = false;
|
|
|
|
for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
|
|
{
|
|
const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
|
|
|
|
if (ulpDiff <= maxUlpDiff)
|
|
{
|
|
anyOk = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!anyOk)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class CeilCase : public CommonFunctionCase
|
|
{
|
|
public:
|
|
CeilCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
|
|
: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ceil", shaderType)
|
|
{
|
|
m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
|
|
m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
|
|
m_spec.source = "out0 = ceil(in0);";
|
|
}
|
|
|
|
void getInputValues (int numValues, void* const* values) const
|
|
{
|
|
const Vec2 ranges[] =
|
|
{
|
|
Vec2(-2.0f, 2.0f), // lowp
|
|
Vec2(-1e3f, 1e3f), // mediump
|
|
Vec2(-1e7f, 1e7f) // highp
|
|
};
|
|
|
|
de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
|
|
// Random cases.
|
|
fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
|
|
|
|
// If precision is mediump, make sure values can be represented in fp16 exactly
|
|
if (precision == glu::PRECISION_MEDIUMP)
|
|
{
|
|
for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
|
|
((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
|
|
}
|
|
}
|
|
|
|
bool compare (const void* const* inputs, const void* const* outputs)
|
|
{
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const bool hasZeroSign = supportsSignedZero(precision);
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
|
|
if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
|
|
{
|
|
// Require exact result.
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const float out0 = ((const float*)outputs[0])[compNdx];
|
|
const float ref = deFloatCeil(in0);
|
|
|
|
const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
|
|
|
|
if (ulpDiff > 0)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const int mantissaBits = getMinMantissaBits(precision);
|
|
const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
|
|
const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
|
|
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const float out0 = ((const float*)outputs[0])[compNdx];
|
|
const int minRes = int(deFloatCeil(in0-eps));
|
|
const int maxRes = int(deFloatCeil(in0+eps));
|
|
bool anyOk = false;
|
|
|
|
for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
|
|
{
|
|
const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
|
|
|
|
if (ulpDiff <= maxUlpDiff)
|
|
{
|
|
anyOk = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!anyOk && de::inRange(0, minRes, maxRes))
|
|
{
|
|
// Allow -0 as well.
|
|
const int ulpDiff = de::abs((int)tcu::Float32(out0).bits() - (int)0x80000000u);
|
|
anyOk = ((deUint32)ulpDiff <= maxUlpDiff);
|
|
}
|
|
|
|
if (!anyOk)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class FractCase : public CommonFunctionCase
|
|
{
|
|
public:
|
|
FractCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
|
|
: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fract", shaderType)
|
|
{
|
|
m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
|
|
m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
|
|
m_spec.source = "out0 = fract(in0);";
|
|
}
|
|
|
|
void getInputValues (int numValues, void* const* values) const
|
|
{
|
|
const Vec2 ranges[] =
|
|
{
|
|
Vec2(-2.0f, 2.0f), // lowp
|
|
Vec2(-1e3f, 1e3f), // mediump
|
|
Vec2(-1e7f, 1e7f) // highp
|
|
};
|
|
|
|
de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
int numSpecialCases = 0;
|
|
|
|
// Special cases.
|
|
if (precision != glu::PRECISION_LOWP)
|
|
{
|
|
DE_ASSERT(numValues >= 10);
|
|
for (int ndx = 0; ndx < 10; ndx++)
|
|
{
|
|
const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
|
|
std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
|
|
numSpecialCases += 1;
|
|
}
|
|
}
|
|
|
|
// Random cases.
|
|
fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
|
|
|
|
// If precision is mediump, make sure values can be represented in fp16 exactly
|
|
if (precision == glu::PRECISION_MEDIUMP)
|
|
{
|
|
for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
|
|
((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
|
|
}
|
|
}
|
|
|
|
bool compare (const void* const* inputs, const void* const* outputs)
|
|
{
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const bool hasZeroSign = supportsSignedZero(precision);
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
|
|
if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
|
|
{
|
|
// Require exact result.
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const float out0 = ((const float*)outputs[0])[compNdx];
|
|
const float ref = deFloatFrac(in0);
|
|
|
|
const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
|
|
|
|
if (ulpDiff > 0)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const int mantissaBits = getMinMantissaBits(precision);
|
|
const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
|
|
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const float out0 = ((const float*)outputs[0])[compNdx];
|
|
|
|
if (int(deFloatFloor(in0-eps)) == int(deFloatFloor(in0+eps)))
|
|
{
|
|
const float ref = deFloatFrac(in0);
|
|
const int bitsLost = numBitsLostInOp(in0, ref);
|
|
const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(0, mantissaBits-bitsLost)); // ULP diff for rounded integer value.
|
|
const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
|
|
|
|
if (ulpDiff > maxUlpDiff)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << " with ULP threshold " << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (out0 >= 1.0f)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] < 1.0";
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
static inline void frexp (float in, float* significand, int* exponent)
|
|
{
|
|
const tcu::Float32 fpValue(in);
|
|
|
|
if (!fpValue.isZero())
|
|
{
|
|
// Construct float that has exactly the mantissa, and exponent of -1.
|
|
*significand = tcu::Float32::construct(fpValue.sign(), -1, fpValue.mantissa()).asFloat();
|
|
*exponent = fpValue.exponent()+1;
|
|
}
|
|
else
|
|
{
|
|
*significand = fpValue.sign() < 0 ? -0.0f : 0.0f;
|
|
*exponent = 0;
|
|
}
|
|
}
|
|
|
|
static inline float ldexp (float significand, int exponent)
|
|
{
|
|
const tcu::Float32 mant(significand);
|
|
|
|
if (exponent == 0 && mant.isZero())
|
|
{
|
|
return mant.sign() < 0 ? -0.0f : 0.0f;
|
|
}
|
|
else
|
|
{
|
|
return tcu::Float32::construct(mant.sign(), exponent+mant.exponent(), mant.mantissa()).asFloat();
|
|
}
|
|
}
|
|
|
|
class FrexpCase : public CommonFunctionCase
|
|
{
|
|
public:
|
|
FrexpCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
|
|
: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "frexp", shaderType)
|
|
{
|
|
const int vecSize = glu::getDataTypeScalarSize(baseType);
|
|
const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
|
|
|
|
m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
|
|
m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
|
|
m_spec.outputs.push_back(Symbol("out1", glu::VarType(intType, glu::PRECISION_HIGHP)));
|
|
m_spec.source = "out0 = frexp(in0, out1);";
|
|
}
|
|
|
|
void getInputValues (int numValues, void* const* values) const
|
|
{
|
|
const Vec2 ranges[] =
|
|
{
|
|
Vec2(-2.0f, 2.0f), // lowp
|
|
Vec2(-1e3f, 1e3f), // mediump
|
|
Vec2(-1e7f, 1e7f) // highp
|
|
};
|
|
|
|
de::Random rnd (deStringHash(getName()) ^ 0x2790au);
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
|
|
// Special cases
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
((float*)values[0])[scalarSize*0 + compNdx] = 0.0f;
|
|
((float*)values[0])[scalarSize*1 + compNdx] = -0.0f;
|
|
((float*)values[0])[scalarSize*2 + compNdx] = 0.5f;
|
|
((float*)values[0])[scalarSize*3 + compNdx] = -0.5f;
|
|
((float*)values[0])[scalarSize*4 + compNdx] = 1.0f;
|
|
((float*)values[0])[scalarSize*5 + compNdx] = -1.0f;
|
|
((float*)values[0])[scalarSize*6 + compNdx] = 2.0f;
|
|
((float*)values[0])[scalarSize*7 + compNdx] = -2.0f;
|
|
}
|
|
|
|
fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + 8*scalarSize, (numValues-8)*scalarSize);
|
|
|
|
// Make sure the values are representable in the target format
|
|
for (int caseNdx = 0; caseNdx < numValues; ++caseNdx)
|
|
{
|
|
for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
|
|
{
|
|
float* const valuePtr = &((float*)values[0])[caseNdx * scalarSize + scalarNdx];
|
|
|
|
*valuePtr = makeFloatRepresentable(*valuePtr, precision);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool compare (const void* const* inputs, const void* const* outputs)
|
|
{
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
const bool signedZero = false;
|
|
|
|
const int mantissaBits = getMinMantissaBits(precision);
|
|
const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
|
|
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const float out0 = ((const float*)outputs[0])[compNdx];
|
|
const int out1 = ((const int*)outputs[1])[compNdx];
|
|
|
|
float refOut0;
|
|
int refOut1;
|
|
|
|
frexp(in0, &refOut0, &refOut1);
|
|
|
|
const deUint32 ulpDiff0 = signedZero ? getUlpDiff(out0, refOut0) : getUlpDiffIgnoreZeroSign(out0, refOut0);
|
|
|
|
if (ulpDiff0 > maxUlpDiff || out1 != refOut1)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(refOut0) << ", " << refOut1 << " with ULP threshold "
|
|
<< tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff0);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class LdexpCase : public CommonFunctionCase
|
|
{
|
|
public:
|
|
LdexpCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
|
|
: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ldexp", shaderType)
|
|
{
|
|
const int vecSize = glu::getDataTypeScalarSize(baseType);
|
|
const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
|
|
|
|
m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
|
|
m_spec.inputs.push_back(Symbol("in1", glu::VarType(intType, glu::PRECISION_HIGHP)));
|
|
m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
|
|
m_spec.source = "out0 = ldexp(in0, in1);";
|
|
}
|
|
|
|
void getInputValues (int numValues, void* const* values) const
|
|
{
|
|
const Vec2 ranges[] =
|
|
{
|
|
Vec2(-2.0f, 2.0f), // lowp
|
|
Vec2(-1e3f, 1e3f), // mediump
|
|
Vec2(-1e7f, 1e7f) // highp
|
|
};
|
|
|
|
de::Random rnd (deStringHash(getName()) ^ 0x2790au);
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
int valueNdx = 0;
|
|
|
|
{
|
|
const float easySpecialCases[] = { 0.0f, -0.0f, 0.5f, -0.5f, 1.0f, -1.0f, 2.0f, -2.0f };
|
|
|
|
DE_ASSERT(valueNdx + DE_LENGTH_OF_ARRAY(easySpecialCases) <= numValues);
|
|
for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(easySpecialCases); caseNdx++)
|
|
{
|
|
float in0;
|
|
int in1;
|
|
|
|
frexp(easySpecialCases[caseNdx], &in0, &in1);
|
|
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
|
|
((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
|
|
}
|
|
|
|
valueNdx += 1;
|
|
}
|
|
}
|
|
|
|
{
|
|
// \note lowp and mediump can not necessarily fit the values in hard cases, so we'll use only easy ones.
|
|
const int numEasyRandomCases = precision == glu::PRECISION_HIGHP ? 50 : (numValues-valueNdx);
|
|
|
|
DE_ASSERT(valueNdx + numEasyRandomCases <= numValues);
|
|
for (int caseNdx = 0; caseNdx < numEasyRandomCases; caseNdx++)
|
|
{
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in = rnd.getFloat(ranges[precision].x(), ranges[precision].y());
|
|
float in0;
|
|
int in1;
|
|
|
|
frexp(in, &in0, &in1);
|
|
|
|
((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
|
|
((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
|
|
}
|
|
|
|
valueNdx += 1;
|
|
}
|
|
}
|
|
|
|
{
|
|
const int numHardRandomCases = numValues-valueNdx;
|
|
DE_ASSERT(numHardRandomCases >= 0 && valueNdx + numHardRandomCases <= numValues);
|
|
|
|
for (int caseNdx = 0; caseNdx < numHardRandomCases; caseNdx++)
|
|
{
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const int fpExp = rnd.getInt(-126, 127);
|
|
const int sign = rnd.getBool() ? -1 : +1;
|
|
const deUint32 mantissa = (1u<<23) | (rnd.getUint32() & ((1u<<23)-1));
|
|
const int in1 = rnd.getInt(de::max(-126, -126-fpExp), de::min(127, 127-fpExp));
|
|
const float in0 = tcu::Float32::construct(sign, fpExp, mantissa).asFloat();
|
|
|
|
DE_ASSERT(de::inRange(in1, -126, 127)); // See Khronos bug 11180
|
|
DE_ASSERT(de::inRange(in1+fpExp, -126, 127));
|
|
|
|
const float out = ldexp(in0, in1);
|
|
|
|
DE_ASSERT(!tcu::Float32(out).isInf() && !tcu::Float32(out).isDenorm());
|
|
DE_UNREF(out);
|
|
|
|
((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
|
|
((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
|
|
}
|
|
|
|
valueNdx += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool compare (const void* const* inputs, const void* const* outputs)
|
|
{
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
|
|
const int mantissaBits = getMinMantissaBits(precision);
|
|
const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
|
|
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float in0 = ((const float*)inputs[0])[compNdx];
|
|
const int in1 = ((const int*)inputs[1])[compNdx];
|
|
const float out0 = ((const float*)outputs[0])[compNdx];
|
|
const float refOut0 = ldexp(in0, in1);
|
|
const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, refOut0);
|
|
|
|
const int inExp = tcu::Float32(in0).exponent();
|
|
|
|
if (ulpDiff > maxUlpDiff)
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(refOut0) << ", (exp = " << inExp << ") with ULP threshold "
|
|
<< tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class FmaCase : public CommonFunctionCase
|
|
{
|
|
public:
|
|
FmaCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
|
|
: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fma", shaderType)
|
|
{
|
|
m_spec.inputs.push_back(Symbol("a", glu::VarType(baseType, precision)));
|
|
m_spec.inputs.push_back(Symbol("b", glu::VarType(baseType, precision)));
|
|
m_spec.inputs.push_back(Symbol("c", glu::VarType(baseType, precision)));
|
|
m_spec.outputs.push_back(Symbol("res", glu::VarType(baseType, precision)));
|
|
m_spec.source = "res = fma(a, b, c);";
|
|
|
|
if (!glu::contextSupports(context.getRenderContext().getType(), glu::ApiType::es(3, 2)))
|
|
m_spec.globalDeclarations = "#extension GL_EXT_gpu_shader5 : require\n";
|
|
}
|
|
|
|
void init (void)
|
|
{
|
|
if (!glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2))
|
|
&& !m_context.getContextInfo().isExtensionSupported("GL_EXT_gpu_shader5")
|
|
&& !glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5)))
|
|
throw tcu::NotSupportedError("OpenGL ES 3.2, GL_EXT_gpu_shader5 not supported and OpenGL 4.5");
|
|
|
|
CommonFunctionCase::init();
|
|
}
|
|
|
|
void getInputValues (int numValues, void* const* values) const
|
|
{
|
|
const Vec2 ranges[] =
|
|
{
|
|
Vec2(-2.0f, 2.0f), // lowp
|
|
Vec2(-127.f, 127.f), // mediump
|
|
Vec2(-1e7f, 1e7f) // highp
|
|
};
|
|
|
|
de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
const float specialCases[][3] =
|
|
{
|
|
// a b c
|
|
{ 0.0f, 0.0f, 0.0f },
|
|
{ 0.0f, 1.0f, 0.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 1.0f, 1.0f, 0.0f },
|
|
{ 1.0f, 1.0f, 1.0f },
|
|
{ -1.0f, 1.0f, 0.0f },
|
|
{ 1.0f, -1.0f, 0.0f },
|
|
{ -1.0f, -1.0f, 0.0f },
|
|
{ -0.0f, 1.0f, 0.0f },
|
|
{ 1.0f, -0.0f, 0.0f }
|
|
};
|
|
const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases);
|
|
|
|
// Special cases
|
|
for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
|
|
{
|
|
for (int inputNdx = 0; inputNdx < 3; inputNdx++)
|
|
{
|
|
for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
|
|
((float*)values[inputNdx])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx][inputNdx];
|
|
}
|
|
}
|
|
|
|
// Random cases.
|
|
{
|
|
const int numScalars = (numValues-numSpecialCases)*scalarSize;
|
|
const int offs = scalarSize*numSpecialCases;
|
|
|
|
for (int inputNdx = 0; inputNdx < 3; inputNdx++)
|
|
fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[inputNdx] + offs, numScalars);
|
|
}
|
|
|
|
// Make sure the values are representable in the target format
|
|
for (int inputNdx = 0; inputNdx < 3; inputNdx++)
|
|
{
|
|
for (int caseNdx = 0; caseNdx < numValues; ++caseNdx)
|
|
{
|
|
for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
|
|
{
|
|
float* const valuePtr = &((float*)values[inputNdx])[caseNdx * scalarSize + scalarNdx];
|
|
|
|
*valuePtr = makeFloatRepresentable(*valuePtr, precision);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static tcu::Interval fma (glu::Precision precision, float a, float b, float c)
|
|
{
|
|
const tcu::FloatFormat formats[] =
|
|
{
|
|
// minExp maxExp mantissa exact, subnormals infinities NaN
|
|
tcu::FloatFormat(0, 0, 7, false, tcu::YES, tcu::MAYBE, tcu::MAYBE),
|
|
tcu::FloatFormat(-13, 13, 9, false, tcu::MAYBE, tcu::MAYBE, tcu::MAYBE),
|
|
tcu::FloatFormat(-126, 127, 23, true, tcu::MAYBE, tcu::YES, tcu::MAYBE)
|
|
};
|
|
const tcu::FloatFormat& format = de::getSizedArrayElement<glu::PRECISION_LAST>(formats, precision);
|
|
const tcu::Interval ia = format.convert(a);
|
|
const tcu::Interval ib = format.convert(b);
|
|
const tcu::Interval ic = format.convert(c);
|
|
tcu::Interval prod0;
|
|
tcu::Interval prod1;
|
|
tcu::Interval prod2;
|
|
tcu::Interval prod3;
|
|
tcu::Interval prod;
|
|
tcu::Interval res;
|
|
|
|
TCU_SET_INTERVAL(prod0, tmp, tmp = ia.lo() * ib.lo());
|
|
TCU_SET_INTERVAL(prod1, tmp, tmp = ia.lo() * ib.hi());
|
|
TCU_SET_INTERVAL(prod2, tmp, tmp = ia.hi() * ib.lo());
|
|
TCU_SET_INTERVAL(prod3, tmp, tmp = ia.hi() * ib.hi());
|
|
|
|
prod = format.convert(format.roundOut(prod0 | prod1 | prod2 | prod3, ia.isFinite() && ib.isFinite()));
|
|
|
|
TCU_SET_INTERVAL_BOUNDS(res, tmp,
|
|
tmp = prod.lo() + ic.lo(),
|
|
tmp = prod.hi() + ic.hi());
|
|
|
|
return format.convert(format.roundOut(res, prod.isFinite() && ic.isFinite()));
|
|
}
|
|
|
|
bool compare (const void* const* inputs, const void* const* outputs)
|
|
{
|
|
const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
|
|
const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
|
|
const int scalarSize = glu::getDataTypeScalarSize(type);
|
|
|
|
for (int compNdx = 0; compNdx < scalarSize; compNdx++)
|
|
{
|
|
const float a = ((const float*)inputs[0])[compNdx];
|
|
const float b = ((const float*)inputs[1])[compNdx];
|
|
const float c = ((const float*)inputs[2])[compNdx];
|
|
const float res = ((const float*)outputs[0])[compNdx];
|
|
const tcu::Interval ref = fma(precision, a, b, c);
|
|
|
|
if (!ref.contains(res))
|
|
{
|
|
m_failMsg << "Expected [" << compNdx << "] = " << ref;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
ShaderCommonFunctionTests::ShaderCommonFunctionTests (Context& context)
|
|
: TestCaseGroup(context, "common", "Common function tests")
|
|
{
|
|
}
|
|
|
|
ShaderCommonFunctionTests::~ShaderCommonFunctionTests (void)
|
|
{
|
|
}
|
|
|
|
template<class TestClass>
|
|
static void addFunctionCases (TestCaseGroup* parent, const char* functionName, bool floatTypes, bool intTypes, bool uintTypes, deUint32 shaderBits)
|
|
{
|
|
tcu::TestCaseGroup* group = new tcu::TestCaseGroup(parent->getTestContext(), functionName, functionName);
|
|
parent->addChild(group);
|
|
|
|
const glu::DataType scalarTypes[] =
|
|
{
|
|
glu::TYPE_FLOAT,
|
|
glu::TYPE_INT,
|
|
glu::TYPE_UINT
|
|
};
|
|
|
|
for (int scalarTypeNdx = 0; scalarTypeNdx < DE_LENGTH_OF_ARRAY(scalarTypes); scalarTypeNdx++)
|
|
{
|
|
const glu::DataType scalarType = scalarTypes[scalarTypeNdx];
|
|
|
|
if ((!floatTypes && scalarType == glu::TYPE_FLOAT) ||
|
|
(!intTypes && scalarType == glu::TYPE_INT) ||
|
|
(!uintTypes && scalarType == glu::TYPE_UINT))
|
|
continue;
|
|
|
|
for (int vecSize = 1; vecSize <= 4; vecSize++)
|
|
{
|
|
for (int prec = glu::PRECISION_LOWP; prec <= glu::PRECISION_HIGHP; prec++)
|
|
{
|
|
for (int shaderTypeNdx = 0; shaderTypeNdx < glu::SHADERTYPE_LAST; shaderTypeNdx++)
|
|
{
|
|
if (shaderBits & (1<<shaderTypeNdx))
|
|
group->addChild(new TestClass(parent->getContext(), glu::DataType(scalarType + vecSize - 1), glu::Precision(prec), glu::ShaderType(shaderTypeNdx)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ShaderCommonFunctionTests::init (void)
|
|
{
|
|
enum
|
|
{
|
|
VS = (1<<glu::SHADERTYPE_VERTEX),
|
|
TC = (1<<glu::SHADERTYPE_TESSELLATION_CONTROL),
|
|
TE = (1<<glu::SHADERTYPE_TESSELLATION_EVALUATION),
|
|
GS = (1<<glu::SHADERTYPE_GEOMETRY),
|
|
FS = (1<<glu::SHADERTYPE_FRAGMENT),
|
|
CS = (1<<glu::SHADERTYPE_COMPUTE),
|
|
|
|
ALL_SHADERS = VS|TC|TE|GS|FS|CS,
|
|
NEW_SHADERS = TC|TE|GS|CS,
|
|
};
|
|
|
|
// Float? Int? Uint? Shaders
|
|
addFunctionCases<AbsCase> (this, "abs", true, true, false, NEW_SHADERS);
|
|
addFunctionCases<SignCase> (this, "sign", true, true, false, NEW_SHADERS);
|
|
addFunctionCases<FloorCase> (this, "floor", true, false, false, NEW_SHADERS);
|
|
addFunctionCases<TruncCase> (this, "trunc", true, false, false, NEW_SHADERS);
|
|
addFunctionCases<RoundCase> (this, "round", true, false, false, NEW_SHADERS);
|
|
addFunctionCases<RoundEvenCase> (this, "roundeven", true, false, false, NEW_SHADERS);
|
|
addFunctionCases<CeilCase> (this, "ceil", true, false, false, NEW_SHADERS);
|
|
addFunctionCases<FractCase> (this, "fract", true, false, false, NEW_SHADERS);
|
|
// mod
|
|
addFunctionCases<ModfCase> (this, "modf", true, false, false, NEW_SHADERS);
|
|
// min
|
|
// max
|
|
// clamp
|
|
// mix
|
|
// step
|
|
// smoothstep
|
|
addFunctionCases<IsnanCase> (this, "isnan", true, false, false, NEW_SHADERS);
|
|
addFunctionCases<IsinfCase> (this, "isinf", true, false, false, NEW_SHADERS);
|
|
addFunctionCases<FloatBitsToIntCase> (this, "floatbitstoint", true, false, false, NEW_SHADERS);
|
|
addFunctionCases<FloatBitsToUintCase> (this, "floatbitstouint", true, false, false, NEW_SHADERS);
|
|
|
|
addFunctionCases<FrexpCase> (this, "frexp", true, false, false, ALL_SHADERS);
|
|
addFunctionCases<LdexpCase> (this, "ldexp", true, false, false, ALL_SHADERS);
|
|
addFunctionCases<FmaCase> (this, "fma", true, false, false, ALL_SHADERS);
|
|
|
|
// (u)intBitsToFloat()
|
|
{
|
|
const deUint32 shaderBits = NEW_SHADERS;
|
|
tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "intbitstofloat", "intBitsToFloat() Tests");
|
|
tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uintbitstofloat", "uintBitsToFloat() Tests");
|
|
|
|
addChild(intGroup);
|
|
addChild(uintGroup);
|
|
|
|
for (int vecSize = 1; vecSize < 4; vecSize++)
|
|
{
|
|
const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
|
|
const glu::DataType uintType = vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT;
|
|
|
|
for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
|
|
{
|
|
if (shaderBits & (1<<shaderType))
|
|
{
|
|
intGroup->addChild(new BitsToFloatCase(m_context, intType, glu::ShaderType(shaderType)));
|
|
uintGroup->addChild(new BitsToFloatCase(m_context, uintType, glu::ShaderType(shaderType)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // Functional
|
|
} // gles31
|
|
} // deqp
|