Home | History | Annotate | Download | only in functional
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program OpenGL ES 3.0 Module
      3  * -------------------------------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief Common built-in function tests.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "es3fShaderCommonFunctionTests.hpp"
     25 #include "glsShaderExecUtil.hpp"
     26 #include "tcuTestLog.hpp"
     27 #include "tcuFormatUtil.hpp"
     28 #include "tcuFloat.hpp"
     29 #include "deRandom.hpp"
     30 #include "deMath.h"
     31 #include "deString.h"
     32 
     33 namespace deqp
     34 {
     35 namespace gles3
     36 {
     37 namespace Functional
     38 {
     39 
     40 using std::vector;
     41 using std::string;
     42 using tcu::TestLog;
     43 using namespace gls::ShaderExecUtil;
     44 
     45 using tcu::Vec2;
     46 using tcu::Vec3;
     47 using tcu::Vec4;
     48 using tcu::IVec2;
     49 using tcu::IVec3;
     50 using tcu::IVec4;
     51 
     52 // Utilities
     53 
     54 template<typename T, int Size>
     55 struct VecArrayAccess
     56 {
     57 public:
     58 									VecArrayAccess	(const void* ptr) : m_array((tcu::Vector<T, Size>*)ptr) {}
     59 									~VecArrayAccess	(void) {}
     60 
     61 	const tcu::Vector<T, Size>&		operator[]		(size_t offset) const	{ return m_array[offset];	}
     62 	tcu::Vector<T, Size>&			operator[]		(size_t offset)			{ return m_array[offset];	}
     63 
     64 private:
     65 	tcu::Vector<T, Size>*			m_array;
     66 };
     67 
     68 template<typename T>	T			randomScalar	(de::Random& rnd, T minValue, T maxValue);
     69 template<> inline		float		randomScalar	(de::Random& rnd, float minValue, float maxValue)		{ return rnd.getFloat(minValue, maxValue);	}
     70 template<> inline		deInt32		randomScalar	(de::Random& rnd, deInt32 minValue, deInt32 maxValue)	{ return rnd.getInt(minValue, maxValue);	}
     71 template<> inline		deUint32	randomScalar	(de::Random& rnd, deUint32 minValue, deUint32 maxValue)	{ return minValue + rnd.getUint32() % (maxValue - minValue + 1); }
     72 
     73 template<typename T, int Size>
     74 inline tcu::Vector<T, Size> randomVector (de::Random& rnd, const tcu::Vector<T, Size>& minValue, const tcu::Vector<T, Size>& maxValue)
     75 {
     76 	tcu::Vector<T, Size> res;
     77 	for (int ndx = 0; ndx < Size; ndx++)
     78 		res[ndx] = randomScalar<T>(rnd, minValue[ndx], maxValue[ndx]);
     79 	return res;
     80 }
     81 
     82 template<typename T, int Size>
     83 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)
     84 {
     85 	VecArrayAccess<T, Size> access(dst);
     86 	for (int ndx = 0; ndx < numValues; ndx++)
     87 		access[offset + ndx] = randomVector<T, Size>(rnd, minValue, maxValue);
     88 }
     89 
     90 template<typename T>
     91 static void fillRandomScalars (de::Random& rnd, T minValue, T maxValue, void* dst, int numValues, int offset = 0)
     92 {
     93 	T* typedPtr = (T*)dst;
     94 	for (int ndx = 0; ndx < numValues; ndx++)
     95 		typedPtr[offset + ndx] = randomScalar<T>(rnd, minValue, maxValue);
     96 }
     97 
     98 inline int numBitsLostInOp (float input, float output)
     99 {
    100 	const int	inExp		= tcu::Float32(input).exponent();
    101 	const int	outExp		= tcu::Float32(output).exponent();
    102 
    103 	return de::max(0, inExp-outExp); // Lost due to mantissa shift.
    104 }
    105 
    106 inline deUint32 getUlpDiff (float a, float b)
    107 {
    108 	const deUint32	aBits	= tcu::Float32(a).bits();
    109 	const deUint32	bBits	= tcu::Float32(b).bits();
    110 	return aBits > bBits ? aBits - bBits : bBits - aBits;
    111 }
    112 
    113 inline deUint32 getUlpDiffIgnoreZeroSign (float a, float b)
    114 {
    115 	if (tcu::Float32(a).isZero())
    116 		return getUlpDiff(tcu::Float32::construct(tcu::Float32(b).sign(), 0, 0).asFloat(), b);
    117 	else if (tcu::Float32(b).isZero())
    118 		return getUlpDiff(a, tcu::Float32::construct(tcu::Float32(a).sign(), 0, 0).asFloat());
    119 	else
    120 		return getUlpDiff(a, b);
    121 }
    122 
    123 inline bool supportsSignedZero (glu::Precision precision)
    124 {
    125 	// \note GLSL ES 3.0 doesn't really require support for -0, but we require it for highp
    126 	//		 as it is very widely supported.
    127 	return precision == glu::PRECISION_HIGHP;
    128 }
    129 
    130 inline float getEpsFromMaxUlpDiff (float value, deUint32 ulpDiff)
    131 {
    132 	const int exp = tcu::Float32(value).exponent();
    133 	return tcu::Float32::construct(+1, exp, (1u<<23) | ulpDiff).asFloat() - tcu::Float32::construct(+1, exp, 1u<<23).asFloat();
    134 }
    135 
    136 inline deUint32 getMaxUlpDiffFromBits (int numAccurateBits)
    137 {
    138 	const int		numGarbageBits	= 23-numAccurateBits;
    139 	const deUint32	mask			= (1u<<numGarbageBits)-1u;
    140 
    141 	return mask;
    142 }
    143 
    144 inline float getEpsFromBits (float value, int numAccurateBits)
    145 {
    146 	return getEpsFromMaxUlpDiff(value, getMaxUlpDiffFromBits(numAccurateBits));
    147 }
    148 
    149 static int getMinMantissaBits (glu::Precision precision)
    150 {
    151 	const int bits[] =
    152 	{
    153 		7,		// lowp
    154 		10,		// mediump
    155 		23		// highp
    156 	};
    157 	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(bits) == glu::PRECISION_LAST);
    158 	DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(bits)));
    159 	return bits[precision];
    160 }
    161 
    162 // CommonFunctionCase
    163 
    164 class CommonFunctionCase : public TestCase
    165 {
    166 public:
    167 							CommonFunctionCase		(Context& context, const char* name, const char* description, glu::ShaderType shaderType);
    168 							~CommonFunctionCase		(void);
    169 
    170 	void					init					(void);
    171 	void					deinit					(void);
    172 	IterateResult			iterate					(void);
    173 
    174 protected:
    175 							CommonFunctionCase		(const CommonFunctionCase& other);
    176 	CommonFunctionCase&		operator=				(const CommonFunctionCase& other);
    177 
    178 	virtual void			getInputValues			(int numValues, void* const* values) const = 0;
    179 	virtual bool			compare					(const void* const* inputs, const void* const* outputs) = 0;
    180 
    181 	glu::ShaderType			m_shaderType;
    182 	ShaderSpec				m_spec;
    183 	int						m_numValues;
    184 
    185 	std::ostringstream		m_failMsg;				//!< Comparison failure help message.
    186 
    187 private:
    188 	ShaderExecutor*			m_executor;
    189 };
    190 
    191 CommonFunctionCase::CommonFunctionCase (Context& context, const char* name, const char* description, glu::ShaderType shaderType)
    192 	: TestCase		(context, name, description)
    193 	, m_shaderType	(shaderType)
    194 	, m_numValues	(100)
    195 	, m_executor	(DE_NULL)
    196 {
    197 	m_spec.version = glu::GLSL_VERSION_300_ES;
    198 }
    199 
    200 CommonFunctionCase::~CommonFunctionCase (void)
    201 {
    202 	CommonFunctionCase::deinit();
    203 }
    204 
    205 void CommonFunctionCase::init (void)
    206 {
    207 	DE_ASSERT(!m_executor);
    208 
    209 	m_executor = createExecutor(m_context.getRenderContext(), m_shaderType, m_spec);
    210 	m_testCtx.getLog() << m_executor;
    211 
    212 	if (!m_executor->isOk())
    213 		throw tcu::TestError("Compile failed");
    214 }
    215 
    216 void CommonFunctionCase::deinit (void)
    217 {
    218 	delete m_executor;
    219 	m_executor = DE_NULL;
    220 }
    221 
    222 static vector<int> getScalarSizes (const vector<Symbol>& symbols)
    223 {
    224 	vector<int> sizes(symbols.size());
    225 	for (int ndx = 0; ndx < (int)symbols.size(); ++ndx)
    226 		sizes[ndx] = symbols[ndx].varType.getScalarSize();
    227 	return sizes;
    228 }
    229 
    230 static int computeTotalScalarSize (const vector<Symbol>& symbols)
    231 {
    232 	int totalSize = 0;
    233 	for (vector<Symbol>::const_iterator sym = symbols.begin(); sym != symbols.end(); ++sym)
    234 		totalSize += sym->varType.getScalarSize();
    235 	return totalSize;
    236 }
    237 
    238 static vector<void*> getInputOutputPointers (const vector<Symbol>& symbols, vector<deUint32>& data, const int numValues)
    239 {
    240 	vector<void*>	pointers		(symbols.size());
    241 	int				curScalarOffset	= 0;
    242 
    243 	for (int varNdx = 0; varNdx < (int)symbols.size(); ++varNdx)
    244 	{
    245 		const Symbol&	var				= symbols[varNdx];
    246 		const int		scalarSize		= var.varType.getScalarSize();
    247 
    248 		// Uses planar layout as input/output specs do not support strides.
    249 		pointers[varNdx] = &data[curScalarOffset];
    250 		curScalarOffset += scalarSize*numValues;
    251 	}
    252 
    253 	DE_ASSERT(curScalarOffset == (int)data.size());
    254 
    255 	return pointers;
    256 }
    257 
    258 // \todo [2013-08-08 pyry] Make generic utility and move to glu?
    259 
    260 struct HexFloat
    261 {
    262 	const float value;
    263 	HexFloat (const float value_) : value(value_) {}
    264 };
    265 
    266 std::ostream& operator<< (std::ostream& str, const HexFloat& v)
    267 {
    268 	return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits());
    269 }
    270 
    271 struct HexBool
    272 {
    273 	const deUint32 value;
    274 	HexBool (const deUint32 value_) : value(value_) {}
    275 };
    276 
    277 std::ostream& operator<< (std::ostream& str, const HexBool& v)
    278 {
    279 	return str << (v.value ? "true" : "false") << " / " << tcu::toHex(v.value);
    280 }
    281 
    282 struct VarValue
    283 {
    284 	const glu::VarType&	type;
    285 	const void*			value;
    286 
    287 	VarValue (const glu::VarType& type_, const void* value_) : type(type_), value(value_) {}
    288 };
    289 
    290 std::ostream& operator<< (std::ostream& str, const VarValue& varValue)
    291 {
    292 	DE_ASSERT(varValue.type.isBasicType());
    293 
    294 	const glu::DataType		basicType		= varValue.type.getBasicType();
    295 	const glu::DataType		scalarType		= glu::getDataTypeScalarType(basicType);
    296 	const int				numComponents	= glu::getDataTypeScalarSize(basicType);
    297 
    298 	if (numComponents > 1)
    299 		str << glu::getDataTypeName(basicType) << "(";
    300 
    301 	for (int compNdx = 0; compNdx < numComponents; compNdx++)
    302 	{
    303 		if (compNdx != 0)
    304 			str << ", ";
    305 
    306 		switch (scalarType)
    307 		{
    308 			case glu::TYPE_FLOAT:	str << HexFloat(((const float*)varValue.value)[compNdx]);			break;
    309 			case glu::TYPE_INT:		str << ((const deInt32*)varValue.value)[compNdx];					break;
    310 			case glu::TYPE_UINT:	str << tcu::toHex(((const deUint32*)varValue.value)[compNdx]);		break;
    311 			case glu::TYPE_BOOL:	str << HexBool(((const deUint32*)varValue.value)[compNdx]);			break;
    312 
    313 			default:
    314 				DE_ASSERT(false);
    315 		}
    316 	}
    317 
    318 	if (numComponents > 1)
    319 		str << ")";
    320 
    321 	return str;
    322 }
    323 
    324 CommonFunctionCase::IterateResult CommonFunctionCase::iterate (void)
    325 {
    326 	const int				numInputScalars			= computeTotalScalarSize(m_spec.inputs);
    327 	const int				numOutputScalars		= computeTotalScalarSize(m_spec.outputs);
    328 	vector<deUint32>		inputData				(numInputScalars * m_numValues);
    329 	vector<deUint32>		outputData				(numOutputScalars * m_numValues);
    330 	const vector<void*>		inputPointers			= getInputOutputPointers(m_spec.inputs, inputData, m_numValues);
    331 	const vector<void*>		outputPointers			= getInputOutputPointers(m_spec.outputs, outputData, m_numValues);
    332 
    333 	// Initialize input data.
    334 	getInputValues(m_numValues, &inputPointers[0]);
    335 
    336 	// Execute shader.
    337 	m_executor->useProgram();
    338 	m_executor->execute(m_numValues, &inputPointers[0], &outputPointers[0]);
    339 
    340 	// Compare results.
    341 	{
    342 		const vector<int>		inScalarSizes		= getScalarSizes(m_spec.inputs);
    343 		const vector<int>		outScalarSizes		= getScalarSizes(m_spec.outputs);
    344 		vector<void*>			curInputPtr			(inputPointers.size());
    345 		vector<void*>			curOutputPtr		(outputPointers.size());
    346 		int						numFailed			= 0;
    347 
    348 		for (int valNdx = 0; valNdx < m_numValues; valNdx++)
    349 		{
    350 			// Set up pointers for comparison.
    351 			for (int inNdx = 0; inNdx < (int)curInputPtr.size(); ++inNdx)
    352 				curInputPtr[inNdx] = (deUint32*)inputPointers[inNdx] + inScalarSizes[inNdx]*valNdx;
    353 
    354 			for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); ++outNdx)
    355 				curOutputPtr[outNdx] = (deUint32*)outputPointers[outNdx] + outScalarSizes[outNdx]*valNdx;
    356 
    357 			if (!compare(&curInputPtr[0], &curOutputPtr[0]))
    358 			{
    359 				// \todo [2013-08-08 pyry] We probably want to log reference value as well?
    360 
    361 				m_testCtx.getLog() << TestLog::Message << "ERROR: comparison failed for value " << valNdx << ":\n  " << m_failMsg.str() << TestLog::EndMessage;
    362 
    363 				m_testCtx.getLog() << TestLog::Message << "  inputs:" << TestLog::EndMessage;
    364 				for (int inNdx = 0; inNdx < (int)curInputPtr.size(); inNdx++)
    365 					m_testCtx.getLog() << TestLog::Message << "    " << m_spec.inputs[inNdx].name << " = "
    366 														   << VarValue(m_spec.inputs[inNdx].varType, curInputPtr[inNdx])
    367 									   << TestLog::EndMessage;
    368 
    369 				m_testCtx.getLog() << TestLog::Message << "  outputs:" << TestLog::EndMessage;
    370 				for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); outNdx++)
    371 					m_testCtx.getLog() << TestLog::Message << "    " << m_spec.outputs[outNdx].name << " = "
    372 														   << VarValue(m_spec.outputs[outNdx].varType, curOutputPtr[outNdx])
    373 									   << TestLog::EndMessage;
    374 
    375 				m_failMsg.str("");
    376 				m_failMsg.clear();
    377 				numFailed += 1;
    378 			}
    379 		}
    380 
    381 		m_testCtx.getLog() << TestLog::Message << (m_numValues - numFailed) << " / " << m_numValues << " values passed" << TestLog::EndMessage;
    382 
    383 		m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
    384 								numFailed == 0 ? "Pass"					: "Result comparison failed");
    385 	}
    386 
    387 	return STOP;
    388 }
    389 
    390 static const char* getPrecisionPostfix (glu::Precision precision)
    391 {
    392 	static const char* s_postfix[] =
    393 	{
    394 		"_lowp",
    395 		"_mediump",
    396 		"_highp"
    397 	};
    398 	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_postfix) == glu::PRECISION_LAST);
    399 	DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
    400 	return s_postfix[precision];
    401 }
    402 
    403 static const char* getShaderTypePostfix (glu::ShaderType shaderType)
    404 {
    405 	static const char* s_postfix[] =
    406 	{
    407 		"_vertex",
    408 		"_fragment"
    409 	};
    410 	DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
    411 	return s_postfix[shaderType];
    412 }
    413 
    414 static std::string getCommonFuncCaseName (glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
    415 {
    416 	return string(glu::getDataTypeName(baseType)) + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType);
    417 }
    418 
    419 class AbsCase : public CommonFunctionCase
    420 {
    421 public:
    422 	AbsCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
    423 		: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "abs", shaderType)
    424 	{
    425 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
    426 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
    427 		m_spec.source = "out0 = abs(in0);";
    428 	}
    429 
    430 	void getInputValues (int numValues, void* const* values) const
    431 	{
    432 		const Vec2 floatRanges[] =
    433 		{
    434 			Vec2(-2.0f,		2.0f),	// lowp
    435 			Vec2(-1e3f,		1e3f),	// mediump
    436 			Vec2(-1e7f,		1e7f)	// highp
    437 		};
    438 		const IVec2 intRanges[] =
    439 		{
    440 			IVec2(-(1<<7)+1,	(1<<7)-1),
    441 			IVec2(-(1<<15)+1,	(1<<15)-1),
    442 			IVec2(0x80000001,	0x7fffffff)
    443 		};
    444 
    445 		de::Random				rnd			(deStringHash(getName()) ^ 0x235facu);
    446 		const glu::DataType		type		= m_spec.inputs[0].varType.getBasicType();
    447 		const glu::Precision	precision	= m_spec.inputs[0].varType.getPrecision();
    448 		const int				scalarSize	= glu::getDataTypeScalarSize(type);
    449 
    450 		if (glu::isDataTypeFloatOrVec(type))
    451 			fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), values[0], numValues*scalarSize);
    452 		else
    453 			fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), values[0], numValues*scalarSize);
    454 	}
    455 
    456 	bool compare (const void* const* inputs, const void* const* outputs)
    457 	{
    458 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
    459 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
    460 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
    461 
    462 		if (glu::isDataTypeFloatOrVec(type))
    463 		{
    464 			const int		mantissaBits	= getMinMantissaBits(precision);
    465 			const deUint32	maxUlpDiff		= (1u<<(23-mantissaBits))-1u;
    466 
    467 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
    468 			{
    469 				const float		in0			= ((const float*)inputs[0])[compNdx];
    470 				const float		out0		= ((const float*)outputs[0])[compNdx];
    471 				const float		ref0		= de::abs(in0);
    472 				const deUint32	ulpDiff0	= getUlpDiff(out0, ref0);
    473 
    474 				if (ulpDiff0 > maxUlpDiff)
    475 				{
    476 					m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
    477 					return false;
    478 				}
    479 			}
    480 		}
    481 		else
    482 		{
    483 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
    484 			{
    485 				const int	in0		= ((const int*)inputs[0])[compNdx];
    486 				const int	out0	= ((const int*)outputs[0])[compNdx];
    487 				const int	ref0	= de::abs(in0);
    488 
    489 				if (out0 != ref0)
    490 				{
    491 					m_failMsg << "Expected [" << compNdx << "] = " << ref0;
    492 					return false;
    493 				}
    494 			}
    495 		}
    496 
    497 		return true;
    498 	}
    499 };
    500 
    501 class SignCase : public CommonFunctionCase
    502 {
    503 public:
    504 	SignCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
    505 		: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "sign", shaderType)
    506 	{
    507 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
    508 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
    509 		m_spec.source = "out0 = sign(in0);";
    510 	}
    511 
    512 	void getInputValues (int numValues, void* const* values) const
    513 	{
    514 		const Vec2 floatRanges[] =
    515 		{
    516 			Vec2(-2.0f,		2.0f),	// lowp
    517 			Vec2(-1e4f,		1e4f),	// mediump	- note: may end up as inf
    518 			Vec2(-1e8f,		1e8f)	// highp	- note: may end up as inf
    519 		};
    520 		const IVec2 intRanges[] =
    521 		{
    522 			IVec2(-(1<<7),		(1<<7)-1),
    523 			IVec2(-(1<<15),		(1<<15)-1),
    524 			IVec2(0x80000000,	0x7fffffff)
    525 		};
    526 
    527 		de::Random				rnd			(deStringHash(getName()) ^ 0x324u);
    528 		const glu::DataType		type		= m_spec.inputs[0].varType.getBasicType();
    529 		const glu::Precision	precision	= m_spec.inputs[0].varType.getPrecision();
    530 		const int				scalarSize	= glu::getDataTypeScalarSize(type);
    531 
    532 		if (glu::isDataTypeFloatOrVec(type))
    533 		{
    534 			// Special cases.
    535 			std::fill((float*)values[0],				(float*)values[0] + scalarSize,		+1.0f);
    536 			std::fill((float*)values[0] + scalarSize*1,	(float*)values[0] + scalarSize*2,	-1.0f);
    537 			std::fill((float*)values[0] + scalarSize*2,	(float*)values[0] + scalarSize*3,	0.0f);
    538 			fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), (float*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
    539 		}
    540 		else
    541 		{
    542 			std::fill((int*)values[0],					(int*)values[0] + scalarSize,		+1);
    543 			std::fill((int*)values[0] + scalarSize*1,	(int*)values[0] + scalarSize*2,		-1);
    544 			std::fill((int*)values[0] + scalarSize*2,	(int*)values[0] + scalarSize*3,		0);
    545 			fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), (int*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
    546 		}
    547 	}
    548 
    549 	bool compare (const void* const* inputs, const void* const* outputs)
    550 	{
    551 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
    552 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
    553 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
    554 
    555 		if (glu::isDataTypeFloatOrVec(type))
    556 		{
    557 			// Both highp and mediump should be able to represent -1, 0, and +1 exactly
    558 			const deUint32 maxUlpDiff = precision == glu::PRECISION_LOWP ? getMaxUlpDiffFromBits(getMinMantissaBits(precision)) : 0;
    559 
    560 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
    561 			{
    562 				const float		in0			= ((const float*)inputs[0])[compNdx];
    563 				const float		out0		= ((const float*)outputs[0])[compNdx];
    564 				const float		ref0		= in0 < 0.0f ? -1.0f :
    565 											  in0 > 0.0f ? +1.0f : 0.0f;
    566 				const deUint32	ulpDiff0	= getUlpDiff(out0, ref0);
    567 
    568 				if (ulpDiff0 > maxUlpDiff)
    569 				{
    570 					m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
    571 					return false;
    572 				}
    573 			}
    574 		}
    575 		else
    576 		{
    577 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
    578 			{
    579 				const int	in0		= ((const int*)inputs[0])[compNdx];
    580 				const int	out0	= ((const int*)outputs[0])[compNdx];
    581 				const int	ref0	= in0 < 0 ? -1 :
    582 									  in0 > 0 ? +1 : 0;
    583 
    584 				if (out0 != ref0)
    585 				{
    586 					m_failMsg << "Expected [" << compNdx << "] = " << ref0;
    587 					return false;
    588 				}
    589 			}
    590 		}
    591 
    592 		return true;
    593 	}
    594 };
    595 
    596 static float roundEven (float v)
    597 {
    598 	const float		q			= deFloatFrac(v);
    599 	const int		truncated	= int(v-q);
    600 	const int		rounded		= (q > 0.5f)							? (truncated + 1) :	// Rounded up
    601 									(q == 0.5f && (truncated % 2 != 0))	? (truncated + 1) :	// Round to nearest even at 0.5
    602 									truncated;												// Rounded down
    603 
    604 	return float(rounded);
    605 }
    606 
    607 class RoundEvenCase : public CommonFunctionCase
    608 {
    609 public:
    610 	RoundEvenCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
    611 		: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "roundEven", shaderType)
    612 	{
    613 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
    614 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
    615 		m_spec.source = "out0 = roundEven(in0);";
    616 	}
    617 
    618 	void getInputValues (int numValues, void* const* values) const
    619 	{
    620 		const Vec2 ranges[] =
    621 		{
    622 			Vec2(-2.0f,		2.0f),	// lowp
    623 			Vec2(-1e3f,		1e3f),	// mediump
    624 			Vec2(-1e7f,		1e7f)	// highp
    625 		};
    626 
    627 		de::Random				rnd				(deStringHash(getName()) ^ 0xac23fu);
    628 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
    629 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
    630 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
    631 		int						numSpecialCases	= 0;
    632 
    633 		// Special cases.
    634 		if (precision != glu::PRECISION_LOWP)
    635 		{
    636 			DE_ASSERT(numValues >= 20);
    637 			for (int ndx = 0; ndx < 20; ndx++)
    638 			{
    639 				const float v = de::clamp(float(ndx) - 10.5f, ranges[precision].x(), ranges[precision].y());
    640 				std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
    641 				numSpecialCases += 1;
    642 			}
    643 		}
    644 
    645 		// Random cases.
    646 		fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
    647 
    648 		// If precision is mediump, make sure values can be represented in fp16 exactly
    649 		if (precision == glu::PRECISION_MEDIUMP)
    650 		{
    651 			for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
    652 				((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
    653 		}
    654 	}
    655 
    656 	bool compare (const void* const* inputs, const void* const* outputs)
    657 	{
    658 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
    659 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
    660 		const bool				hasSignedZero	= supportsSignedZero(precision);
    661 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
    662 
    663 		if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
    664 		{
    665 			// Require exact rounding result.
    666 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
    667 			{
    668 				const float		in0			= ((const float*)inputs[0])[compNdx];
    669 				const float		out0		= ((const float*)outputs[0])[compNdx];
    670 				const float		ref			= roundEven(in0);
    671 
    672 				const deUint32	ulpDiff		= hasSignedZero ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
    673 
    674 				if (ulpDiff > 0)
    675 				{
    676 					m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
    677 					return false;
    678 				}
    679 			}
    680 		}
    681 		else
    682 		{
    683 			const int		mantissaBits	= getMinMantissaBits(precision);
    684 			const deUint32	maxUlpDiff		= getMaxUlpDiffFromBits(mantissaBits);	// ULP diff for rounded integer value.
    685 			const float		eps				= getEpsFromBits(1.0f, mantissaBits);	// epsilon for rounding bounds
    686 
    687 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
    688 			{
    689 				const float		in0			= ((const float*)inputs[0])[compNdx];
    690 				const float		out0		= ((const float*)outputs[0])[compNdx];
    691 				const int		minRes		= int(roundEven(in0-eps));
    692 				const int		maxRes		= int(roundEven(in0+eps));
    693 				bool			anyOk		= false;
    694 
    695 				for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
    696 				{
    697 					const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
    698 
    699 					if (ulpDiff <= maxUlpDiff)
    700 					{
    701 						anyOk = true;
    702 						break;
    703 					}
    704 				}
    705 
    706 				if (!anyOk)
    707 				{
    708 					m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
    709 					return false;
    710 				}
    711 			}
    712 		}
    713 
    714 		return true;
    715 	}
    716 };
    717 
    718 class ModfCase : public CommonFunctionCase
    719 {
    720 public:
    721 	ModfCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
    722 		: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "modf", shaderType)
    723 	{
    724 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
    725 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
    726 		m_spec.outputs.push_back(Symbol("out1", glu::VarType(baseType, precision)));
    727 		m_spec.source = "out0 = modf(in0, out1);";
    728 	}
    729 
    730 	void getInputValues (int numValues, void* const* values) const
    731 	{
    732 		const Vec2 ranges[] =
    733 		{
    734 			Vec2(-2.0f,		2.0f),	// lowp
    735 			Vec2(-1e3f,		1e3f),	// mediump
    736 			Vec2(-1e7f,		1e7f)	// highp
    737 		};
    738 
    739 		de::Random				rnd			(deStringHash(getName()) ^ 0xac23fu);
    740 		const glu::DataType		type		= m_spec.inputs[0].varType.getBasicType();
    741 		const glu::Precision	precision	= m_spec.inputs[0].varType.getPrecision();
    742 		const int				scalarSize	= glu::getDataTypeScalarSize(type);
    743 
    744 		fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
    745 	}
    746 
    747 	bool compare (const void* const* inputs, const void* const* outputs)
    748 	{
    749 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
    750 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
    751 		const bool				hasZeroSign		= supportsSignedZero(precision);
    752 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
    753 
    754 		const int				mantissaBits	= getMinMantissaBits(precision);
    755 
    756 		for (int compNdx = 0; compNdx < scalarSize; compNdx++)
    757 		{
    758 			const float		in0			= ((const float*)inputs[0])[compNdx];
    759 			const float		out0		= ((const float*)outputs[0])[compNdx];
    760 			const float		out1		= ((const float*)outputs[1])[compNdx];
    761 
    762 			const float		refOut1		= float(int(in0));
    763 			const float		refOut0		= in0 - refOut1;
    764 
    765 			const int		bitsLost	= precision != glu::PRECISION_HIGHP ? numBitsLostInOp(in0, refOut0) : 0;
    766 			const deUint32	maxUlpDiff	= getMaxUlpDiffFromBits(de::max(mantissaBits - bitsLost, 0));
    767 
    768 			const float		resSum		= out0 + out1;
    769 
    770 			const deUint32	ulpDiff		= hasZeroSign ? getUlpDiff(resSum, in0) : getUlpDiffIgnoreZeroSign(resSum, in0);
    771 
    772 			if (ulpDiff > maxUlpDiff)
    773 			{
    774 				m_failMsg << "Expected [" << compNdx << "] = (" << HexFloat(refOut0) << ") + (" << HexFloat(refOut1) << ") = " << HexFloat(in0) << " with ULP threshold "
    775 							<< tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
    776 				return false;
    777 			}
    778 		}
    779 
    780 		return true;
    781 	}
    782 };
    783 
    784 class IsnanCase : public CommonFunctionCase
    785 {
    786 public:
    787 	IsnanCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
    788 		: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isnan", shaderType)
    789 	{
    790 		DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
    791 
    792 		const int			vecSize		= glu::getDataTypeScalarSize(baseType);
    793 		const glu::DataType	boolType	= vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
    794 
    795 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
    796 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
    797 		m_spec.source = "out0 = isnan(in0);";
    798 	}
    799 
    800 	void getInputValues (int numValues, void* const* values) const
    801 	{
    802 		de::Random				rnd				(deStringHash(getName()) ^ 0xc2a39fu);
    803 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
    804 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
    805 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
    806 		const int				mantissaBits	= getMinMantissaBits(precision);
    807 		const deUint32			mantissaMask	= ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
    808 
    809 		for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
    810 		{
    811 			const bool		isNan		= rnd.getFloat() > 0.3f;
    812 			const bool		isInf		= !isNan && rnd.getFloat() > 0.4f;
    813 			const deUint32	mantissa	= !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
    814 			const deUint32	exp			= !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
    815 			const deUint32	sign		= rnd.getUint32() & 0x1u;
    816 			const deUint32	value		= (sign << 31) | (exp << 23) | mantissa;
    817 
    818 			DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
    819 
    820 			((deUint32*)values[0])[valNdx] = value;
    821 		}
    822 	}
    823 
    824 	bool compare (const void* const* inputs, const void* const* outputs)
    825 	{
    826 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
    827 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
    828 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
    829 
    830 		if (precision == glu::PRECISION_HIGHP)
    831 		{
    832 			// Only highp is required to support inf/nan
    833 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
    834 			{
    835 				const float		in0		= ((const float*)inputs[0])[compNdx];
    836 				const deUint32	out0	= ((const deUint32*)outputs[0])[compNdx];
    837 				const deUint32	ref		= tcu::Float32(in0).isNaN() ? 1u : 0u;
    838 
    839 				if (out0 != ref)
    840 				{
    841 					m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
    842 					return false;
    843 				}
    844 			}
    845 		}
    846 		else
    847 		{
    848 			// Value can be either 0 or 1
    849 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
    850 			{
    851 				const int out0 = ((const int*)outputs[0])[compNdx];
    852 
    853 				if (out0 != 0 && out0 != 1)
    854 				{
    855 					m_failMsg << "Expected [" << compNdx << "] = 0 / 1";
    856 					return false;
    857 				}
    858 			}
    859 		}
    860 
    861 		return true;
    862 	}
    863 };
    864 
    865 class IsinfCase : public CommonFunctionCase
    866 {
    867 public:
    868 	IsinfCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
    869 		: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isinf", shaderType)
    870 	{
    871 		DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
    872 
    873 		const int			vecSize		= glu::getDataTypeScalarSize(baseType);
    874 		const glu::DataType	boolType	= vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
    875 
    876 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
    877 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
    878 		m_spec.source = "out0 = isinf(in0);";
    879 	}
    880 
    881 	void getInputValues (int numValues, void* const* values) const
    882 	{
    883 		de::Random				rnd				(deStringHash(getName()) ^ 0xc2a39fu);
    884 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
    885 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
    886 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
    887 		const int				mantissaBits	= getMinMantissaBits(precision);
    888 		const deUint32			mantissaMask	= ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
    889 
    890 		for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
    891 		{
    892 			const bool		isInf		= rnd.getFloat() > 0.3f;
    893 			const bool		isNan		= !isInf && rnd.getFloat() > 0.4f;
    894 			const deUint32	mantissa	= !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
    895 			const deUint32	exp			= !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
    896 			const deUint32	sign		= rnd.getUint32() & 0x1u;
    897 			const deUint32	value		= (sign << 31) | (exp << 23) | mantissa;
    898 
    899 			DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
    900 
    901 			((deUint32*)values[0])[valNdx] = value;
    902 		}
    903 	}
    904 
    905 	bool compare (const void* const* inputs, const void* const* outputs)
    906 	{
    907 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
    908 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
    909 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
    910 
    911 		if (precision == glu::PRECISION_HIGHP)
    912 		{
    913 			// Only highp is required to support inf/nan
    914 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
    915 			{
    916 				const float		in0		= ((const float*)inputs[0])[compNdx];
    917 				const deUint32	out0	= ((const deUint32*)outputs[0])[compNdx];
    918 				const deUint32	ref		= tcu::Float32(in0).isInf() ? 1u : 0u;
    919 
    920 				if (out0 != ref)
    921 				{
    922 					m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
    923 					return false;
    924 				}
    925 			}
    926 		}
    927 		else
    928 		{
    929 			// Value can be either 0 or 1
    930 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
    931 			{
    932 				const int out0 = ((const int*)outputs[0])[compNdx];
    933 
    934 				if (out0 != 0 && out0 != 1)
    935 				{
    936 					m_failMsg << "Expected [" << compNdx << "] = 0 / 1";
    937 					return false;
    938 				}
    939 			}
    940 		}
    941 
    942 		return true;
    943 	}
    944 };
    945 
    946 class FloatBitsToUintIntCase : public CommonFunctionCase
    947 {
    948 public:
    949 	FloatBitsToUintIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType, bool outIsSigned)
    950 		: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), outIsSigned ? "floatBitsToInt" : "floatBitsToUint", shaderType)
    951 	{
    952 		const int			vecSize		= glu::getDataTypeScalarSize(baseType);
    953 		const glu::DataType	intType		= outIsSigned ? (vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT)
    954 													  : (vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT);
    955 
    956 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
    957 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(intType, glu::PRECISION_HIGHP)));
    958 		m_spec.source = outIsSigned ? "out0 = floatBitsToInt(in0);" : "out0 = floatBitsToUint(in0);";
    959 	}
    960 
    961 	void getInputValues (int numValues, void* const* values) const
    962 	{
    963 		const Vec2 ranges[] =
    964 		{
    965 			Vec2(-2.0f,		2.0f),	// lowp
    966 			Vec2(-1e3f,		1e3f),	// mediump
    967 			Vec2(-1e7f,		1e7f)	// highp
    968 		};
    969 
    970 		de::Random				rnd			(deStringHash(getName()) ^ 0x2790au);
    971 		const glu::DataType		type		= m_spec.inputs[0].varType.getBasicType();
    972 		const glu::Precision	precision	= m_spec.inputs[0].varType.getPrecision();
    973 		const int				scalarSize	= glu::getDataTypeScalarSize(type);
    974 
    975 		fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
    976 	}
    977 
    978 	bool compare (const void* const* inputs, const void* const* outputs)
    979 	{
    980 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
    981 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
    982 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
    983 
    984 		const int				mantissaBits	= getMinMantissaBits(precision);
    985 		const int				maxUlpDiff		= getMaxUlpDiffFromBits(mantissaBits);
    986 
    987 		for (int compNdx = 0; compNdx < scalarSize; compNdx++)
    988 		{
    989 			const float		in0			= ((const float*)inputs[0])[compNdx];
    990 			const deUint32	out0		= ((const deUint32*)outputs[0])[compNdx];
    991 			const deUint32	refOut0		= tcu::Float32(in0).bits();
    992 			const int		ulpDiff		= de::abs((int)out0 - (int)refOut0);
    993 
    994 			if (ulpDiff > maxUlpDiff)
    995 			{
    996 				m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(refOut0) << " with threshold "
    997 							<< tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
    998 				return false;
    999 			}
   1000 		}
   1001 
   1002 		return true;
   1003 	}
   1004 };
   1005 
   1006 class FloatBitsToIntCase : public FloatBitsToUintIntCase
   1007 {
   1008 public:
   1009 	FloatBitsToIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
   1010 		: FloatBitsToUintIntCase(context, baseType, precision, shaderType, true)
   1011 	{
   1012 	}
   1013 };
   1014 
   1015 class FloatBitsToUintCase : public FloatBitsToUintIntCase
   1016 {
   1017 public:
   1018 	FloatBitsToUintCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
   1019 		: FloatBitsToUintIntCase(context, baseType, precision, shaderType, false)
   1020 	{
   1021 	}
   1022 };
   1023 
   1024 class BitsToFloatCase : public CommonFunctionCase
   1025 {
   1026 public:
   1027 	BitsToFloatCase (Context& context, glu::DataType baseType, glu::ShaderType shaderType)
   1028 		: CommonFunctionCase(context, getCommonFuncCaseName(baseType, glu::PRECISION_HIGHP, shaderType).c_str(), glu::isDataTypeIntOrIVec(baseType) ? "intBitsToFloat" : "uintBitsToFloat", shaderType)
   1029 	{
   1030 		const bool			inIsSigned	= glu::isDataTypeIntOrIVec(baseType);
   1031 		const int			vecSize		= glu::getDataTypeScalarSize(baseType);
   1032 		const glu::DataType	floatType	= vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
   1033 
   1034 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
   1035 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(floatType, glu::PRECISION_HIGHP)));
   1036 		m_spec.source = inIsSigned ? "out0 = intBitsToFloat(in0);" : "out0 = uintBitsToFloat(in0);";
   1037 	}
   1038 
   1039 	void getInputValues (int numValues, void* const* values) const
   1040 	{
   1041 		de::Random				rnd			(deStringHash(getName()) ^ 0xbbb225u);
   1042 		const glu::DataType		type		= m_spec.inputs[0].varType.getBasicType();
   1043 		const int				scalarSize	= glu::getDataTypeScalarSize(type);
   1044 		const Vec2				range		(-1e8f, +1e8f);
   1045 
   1046 		// \note Filled as floats.
   1047 		fillRandomScalars(rnd, range.x(), range.y(), values[0], numValues*scalarSize);
   1048 	}
   1049 
   1050 	bool compare (const void* const* inputs, const void* const* outputs)
   1051 	{
   1052 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
   1053 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
   1054 		const deUint32			maxUlpDiff		= 0;
   1055 
   1056 		for (int compNdx = 0; compNdx < scalarSize; compNdx++)
   1057 		{
   1058 			const float		in0			= ((const float*)inputs[0])[compNdx];
   1059 			const float		out0		= ((const float*)outputs[0])[compNdx];
   1060 			const deUint32	ulpDiff		= getUlpDiff(in0, out0);
   1061 
   1062 			if (ulpDiff > maxUlpDiff)
   1063 			{
   1064 				m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(tcu::Float32(in0).bits()) << " with ULP threshold "
   1065 							<< tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
   1066 				return false;
   1067 			}
   1068 		}
   1069 
   1070 		return true;
   1071 	}
   1072 };
   1073 
   1074 class FloorCase : public CommonFunctionCase
   1075 {
   1076 public:
   1077 	FloorCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
   1078 		: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "floor", shaderType)
   1079 	{
   1080 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
   1081 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
   1082 		m_spec.source = "out0 = floor(in0);";
   1083 	}
   1084 
   1085 	void getInputValues (int numValues, void* const* values) const
   1086 	{
   1087 		const Vec2 ranges[] =
   1088 		{
   1089 			Vec2(-2.0f,		2.0f),	// lowp
   1090 			Vec2(-1e3f,		1e3f),	// mediump
   1091 			Vec2(-1e7f,		1e7f)	// highp
   1092 		};
   1093 
   1094 		de::Random				rnd			(deStringHash(getName()) ^ 0xac23fu);
   1095 		const glu::DataType		type		= m_spec.inputs[0].varType.getBasicType();
   1096 		const glu::Precision	precision	= m_spec.inputs[0].varType.getPrecision();
   1097 		const int				scalarSize	= glu::getDataTypeScalarSize(type);
   1098 		// Random cases.
   1099 		fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
   1100 
   1101 		// If precision is mediump, make sure values can be represented in fp16 exactly
   1102 		if (precision == glu::PRECISION_MEDIUMP)
   1103 		{
   1104 			for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
   1105 				((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
   1106 		}
   1107 	}
   1108 
   1109 	bool compare (const void* const* inputs, const void* const* outputs)
   1110 	{
   1111 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
   1112 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
   1113 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
   1114 
   1115 		if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
   1116 		{
   1117 			// Require exact result.
   1118 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
   1119 			{
   1120 				const float		in0			= ((const float*)inputs[0])[compNdx];
   1121 				const float		out0		= ((const float*)outputs[0])[compNdx];
   1122 				const float		ref			= deFloatFloor(in0);
   1123 
   1124 				const deUint32	ulpDiff		= getUlpDiff(out0, ref);
   1125 
   1126 				if (ulpDiff > 0)
   1127 				{
   1128 					m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
   1129 					return false;
   1130 				}
   1131 			}
   1132 		}
   1133 		else
   1134 		{
   1135 			const int		mantissaBits	= getMinMantissaBits(precision);
   1136 			const deUint32	maxUlpDiff		= getMaxUlpDiffFromBits(mantissaBits);	// ULP diff for rounded integer value.
   1137 			const float		eps				= getEpsFromBits(1.0f, mantissaBits);	// epsilon for rounding bounds
   1138 
   1139 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
   1140 			{
   1141 				const float		in0			= ((const float*)inputs[0])[compNdx];
   1142 				const float		out0		= ((const float*)outputs[0])[compNdx];
   1143 				const int		minRes		= int(deFloatFloor(in0-eps));
   1144 				const int		maxRes		= int(deFloatFloor(in0+eps));
   1145 				bool			anyOk		= false;
   1146 
   1147 				for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
   1148 				{
   1149 					const deUint32 ulpDiff = getUlpDiff(out0, float(roundedVal));
   1150 
   1151 					if (ulpDiff <= maxUlpDiff)
   1152 					{
   1153 						anyOk = true;
   1154 						break;
   1155 					}
   1156 				}
   1157 
   1158 				if (!anyOk)
   1159 				{
   1160 					m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
   1161 					return false;
   1162 				}
   1163 			}
   1164 		}
   1165 
   1166 		return true;
   1167 	}
   1168 };
   1169 
   1170 class TruncCase : public CommonFunctionCase
   1171 {
   1172 public:
   1173 	TruncCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
   1174 		: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "trunc", shaderType)
   1175 	{
   1176 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
   1177 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
   1178 		m_spec.source = "out0 = trunc(in0);";
   1179 	}
   1180 
   1181 	void getInputValues (int numValues, void* const* values) const
   1182 	{
   1183 		const Vec2 ranges[] =
   1184 		{
   1185 			Vec2(-2.0f,		2.0f),	// lowp
   1186 			Vec2(-1e3f,		1e3f),	// mediump
   1187 			Vec2(-1e7f,		1e7f)	// highp
   1188 		};
   1189 
   1190 		de::Random				rnd				(deStringHash(getName()) ^ 0xac23fu);
   1191 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
   1192 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
   1193 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
   1194 		const float				specialCases[]	= { 0.0f, -0.0f, -0.9f, 0.9f, 1.0f, -1.0f };
   1195 		const int				numSpecialCases	= DE_LENGTH_OF_ARRAY(specialCases);
   1196 
   1197 		// Special cases
   1198 		for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
   1199 		{
   1200 			for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
   1201 				((float*)values[0])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx];
   1202 		}
   1203 
   1204 		// Random cases.
   1205 		fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + scalarSize*numSpecialCases, (numValues-numSpecialCases)*scalarSize);
   1206 
   1207 		// If precision is mediump, make sure values can be represented in fp16 exactly
   1208 		if (precision == glu::PRECISION_MEDIUMP)
   1209 		{
   1210 			for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
   1211 				((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
   1212 		}
   1213 	}
   1214 
   1215 	bool compare (const void* const* inputs, const void* const* outputs)
   1216 	{
   1217 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
   1218 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
   1219 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
   1220 
   1221 		if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
   1222 		{
   1223 			// Require exact result.
   1224 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
   1225 			{
   1226 				const float		in0			= ((const float*)inputs[0])[compNdx];
   1227 				const float		out0		= ((const float*)outputs[0])[compNdx];
   1228 				const bool		isNeg		= tcu::Float32(in0).sign() < 0;
   1229 				const float		ref			= isNeg ? (-float(int(-in0))) : float(int(in0));
   1230 
   1231 				// \note: trunc() function definition is a bit broad on negative zeros. Ignore result sign if zero.
   1232 				const deUint32	ulpDiff		= getUlpDiffIgnoreZeroSign(out0, ref);
   1233 
   1234 				if (ulpDiff > 0)
   1235 				{
   1236 					m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
   1237 					return false;
   1238 				}
   1239 			}
   1240 		}
   1241 		else
   1242 		{
   1243 			const int		mantissaBits	= getMinMantissaBits(precision);
   1244 			const deUint32	maxUlpDiff		= getMaxUlpDiffFromBits(mantissaBits);	// ULP diff for rounded integer value.
   1245 			const float		eps				= getEpsFromBits(1.0f, mantissaBits);	// epsilon for rounding bounds
   1246 
   1247 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
   1248 			{
   1249 				const float		in0			= ((const float*)inputs[0])[compNdx];
   1250 				const float		out0		= ((const float*)outputs[0])[compNdx];
   1251 				const int		minRes		= int(in0-eps);
   1252 				const int		maxRes		= int(in0+eps);
   1253 				bool			anyOk		= false;
   1254 
   1255 				for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
   1256 				{
   1257 					const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
   1258 
   1259 					if (ulpDiff <= maxUlpDiff)
   1260 					{
   1261 						anyOk = true;
   1262 						break;
   1263 					}
   1264 				}
   1265 
   1266 				if (!anyOk)
   1267 				{
   1268 					m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
   1269 					return false;
   1270 				}
   1271 			}
   1272 		}
   1273 
   1274 		return true;
   1275 	}
   1276 };
   1277 
   1278 class RoundCase : public CommonFunctionCase
   1279 {
   1280 public:
   1281 	RoundCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
   1282 		: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "round", shaderType)
   1283 	{
   1284 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
   1285 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
   1286 		m_spec.source = "out0 = round(in0);";
   1287 	}
   1288 
   1289 	void getInputValues (int numValues, void* const* values) const
   1290 	{
   1291 		const Vec2 ranges[] =
   1292 		{
   1293 			Vec2(-2.0f,		2.0f),	// lowp
   1294 			Vec2(-1e3f,		1e3f),	// mediump
   1295 			Vec2(-1e7f,		1e7f)	// highp
   1296 		};
   1297 
   1298 		de::Random				rnd				(deStringHash(getName()) ^ 0xac23fu);
   1299 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
   1300 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
   1301 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
   1302 		int						numSpecialCases	= 0;
   1303 
   1304 		// Special cases.
   1305 		if (precision != glu::PRECISION_LOWP)
   1306 		{
   1307 			DE_ASSERT(numValues >= 10);
   1308 			for (int ndx = 0; ndx < 10; ndx++)
   1309 			{
   1310 				const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
   1311 				std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
   1312 				numSpecialCases += 1;
   1313 			}
   1314 		}
   1315 
   1316 		// Random cases.
   1317 		fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
   1318 
   1319 		// If precision is mediump, make sure values can be represented in fp16 exactly
   1320 		if (precision == glu::PRECISION_MEDIUMP)
   1321 		{
   1322 			for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
   1323 				((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
   1324 		}
   1325 	}
   1326 
   1327 	bool compare (const void* const* inputs, const void* const* outputs)
   1328 	{
   1329 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
   1330 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
   1331 		const bool				hasZeroSign		= supportsSignedZero(precision);
   1332 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
   1333 
   1334 		if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
   1335 		{
   1336 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
   1337 			{
   1338 				const float		in0			= ((const float*)inputs[0])[compNdx];
   1339 				const float		out0		= ((const float*)outputs[0])[compNdx];
   1340 
   1341 				if (deFloatFrac(in0) == 0.5f)
   1342 				{
   1343 					// Allow both ceil(in) and floor(in)
   1344 					const float		ref0		= deFloatFloor(in0);
   1345 					const float		ref1		= deFloatCeil(in0);
   1346 					const deUint32	ulpDiff0	= hasZeroSign ? getUlpDiff(out0, ref0) : getUlpDiffIgnoreZeroSign(out0, ref0);
   1347 					const deUint32	ulpDiff1	= hasZeroSign ? getUlpDiff(out0, ref1) : getUlpDiffIgnoreZeroSign(out0, ref1);
   1348 
   1349 					if (ulpDiff0 > 0 && ulpDiff1 > 0)
   1350 					{
   1351 						m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " or " << HexFloat(ref1) << ", got ULP diff " << tcu::toHex(de::min(ulpDiff0, ulpDiff1));
   1352 						return false;
   1353 					}
   1354 				}
   1355 				else
   1356 				{
   1357 					// Require exact result
   1358 					const float		ref		= roundEven(in0);
   1359 					const deUint32	ulpDiff	= hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
   1360 
   1361 					if (ulpDiff > 0)
   1362 					{
   1363 						m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
   1364 						return false;
   1365 					}
   1366 				}
   1367 			}
   1368 		}
   1369 		else
   1370 		{
   1371 			const int		mantissaBits	= getMinMantissaBits(precision);
   1372 			const deUint32	maxUlpDiff		= getMaxUlpDiffFromBits(mantissaBits);	// ULP diff for rounded integer value.
   1373 			const float		eps				= getEpsFromBits(1.0f, mantissaBits);	// epsilon for rounding bounds
   1374 
   1375 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
   1376 			{
   1377 				const float		in0			= ((const float*)inputs[0])[compNdx];
   1378 				const float		out0		= ((const float*)outputs[0])[compNdx];
   1379 				const int		minRes		= int(roundEven(in0-eps));
   1380 				const int		maxRes		= int(roundEven(in0+eps));
   1381 				bool			anyOk		= false;
   1382 
   1383 				for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
   1384 				{
   1385 					const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
   1386 
   1387 					if (ulpDiff <= maxUlpDiff)
   1388 					{
   1389 						anyOk = true;
   1390 						break;
   1391 					}
   1392 				}
   1393 
   1394 				if (!anyOk)
   1395 				{
   1396 					m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
   1397 					return false;
   1398 				}
   1399 			}
   1400 		}
   1401 
   1402 		return true;
   1403 	}
   1404 };
   1405 
   1406 class CeilCase : public CommonFunctionCase
   1407 {
   1408 public:
   1409 	CeilCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
   1410 		: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ceil", shaderType)
   1411 	{
   1412 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
   1413 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
   1414 		m_spec.source = "out0 = ceil(in0);";
   1415 	}
   1416 
   1417 	void getInputValues (int numValues, void* const* values) const
   1418 	{
   1419 		const Vec2 ranges[] =
   1420 		{
   1421 			Vec2(-2.0f,		2.0f),	// lowp
   1422 			Vec2(-1e3f,		1e3f),	// mediump
   1423 			Vec2(-1e7f,		1e7f)	// highp
   1424 		};
   1425 
   1426 		de::Random				rnd			(deStringHash(getName()) ^ 0xac23fu);
   1427 		const glu::DataType		type		= m_spec.inputs[0].varType.getBasicType();
   1428 		const glu::Precision	precision	= m_spec.inputs[0].varType.getPrecision();
   1429 		const int				scalarSize	= glu::getDataTypeScalarSize(type);
   1430 
   1431 		// Random cases.
   1432 		fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
   1433 
   1434 		// If precision is mediump, make sure values can be represented in fp16 exactly
   1435 		if (precision == glu::PRECISION_MEDIUMP)
   1436 		{
   1437 			for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
   1438 				((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
   1439 		}
   1440 	}
   1441 
   1442 	bool compare (const void* const* inputs, const void* const* outputs)
   1443 	{
   1444 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
   1445 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
   1446 		const bool				hasZeroSign		= supportsSignedZero(precision);
   1447 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
   1448 
   1449 		if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
   1450 		{
   1451 			// Require exact result.
   1452 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
   1453 			{
   1454 				const float		in0			= ((const float*)inputs[0])[compNdx];
   1455 				const float		out0		= ((const float*)outputs[0])[compNdx];
   1456 				const float		ref			= deFloatCeil(in0);
   1457 
   1458 				const deUint32	ulpDiff		= hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
   1459 
   1460 				if (ulpDiff > 0)
   1461 				{
   1462 					m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
   1463 					return false;
   1464 				}
   1465 			}
   1466 		}
   1467 		else
   1468 		{
   1469 			const int		mantissaBits	= getMinMantissaBits(precision);
   1470 			const deUint32	maxUlpDiff		= getMaxUlpDiffFromBits(mantissaBits);	// ULP diff for rounded integer value.
   1471 			const float		eps				= getEpsFromBits(1.0f, mantissaBits);	// epsilon for rounding bounds
   1472 
   1473 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
   1474 			{
   1475 				const float		in0			= ((const float*)inputs[0])[compNdx];
   1476 				const float		out0		= ((const float*)outputs[0])[compNdx];
   1477 				const int		minRes		= int(deFloatCeil(in0-eps));
   1478 				const int		maxRes		= int(deFloatCeil(in0+eps));
   1479 				bool			anyOk		= false;
   1480 
   1481 				for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
   1482 				{
   1483 					const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
   1484 
   1485 					if (ulpDiff <= maxUlpDiff)
   1486 					{
   1487 						anyOk = true;
   1488 						break;
   1489 					}
   1490 				}
   1491 
   1492 				if (!anyOk && de::inRange(0, minRes, maxRes))
   1493 				{
   1494 					// Allow -0 as well.
   1495 					const int ulpDiff = de::abs((int)tcu::Float32(out0).bits() - (int)0x80000000u);
   1496 					anyOk = ((deUint32)ulpDiff <= maxUlpDiff);
   1497 				}
   1498 
   1499 				if (!anyOk)
   1500 				{
   1501 					m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
   1502 					return false;
   1503 				}
   1504 			}
   1505 		}
   1506 
   1507 		return true;
   1508 	}
   1509 };
   1510 
   1511 class FractCase : public CommonFunctionCase
   1512 {
   1513 public:
   1514 	FractCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
   1515 		: CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fract", shaderType)
   1516 	{
   1517 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
   1518 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
   1519 		m_spec.source = "out0 = fract(in0);";
   1520 	}
   1521 
   1522 	void getInputValues (int numValues, void* const* values) const
   1523 	{
   1524 		const Vec2 ranges[] =
   1525 		{
   1526 			Vec2(-2.0f,		2.0f),	// lowp
   1527 			Vec2(-1e3f,		1e3f),	// mediump
   1528 			Vec2(-1e7f,		1e7f)	// highp
   1529 		};
   1530 
   1531 		de::Random				rnd				(deStringHash(getName()) ^ 0xac23fu);
   1532 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
   1533 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
   1534 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
   1535 		int						numSpecialCases	= 0;
   1536 
   1537 		// Special cases.
   1538 		if (precision != glu::PRECISION_LOWP)
   1539 		{
   1540 			DE_ASSERT(numValues >= 10);
   1541 			for (int ndx = 0; ndx < 10; ndx++)
   1542 			{
   1543 				const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
   1544 				std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
   1545 				numSpecialCases += 1;
   1546 			}
   1547 		}
   1548 
   1549 		// Random cases.
   1550 		fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
   1551 
   1552 		// If precision is mediump, make sure values can be represented in fp16 exactly
   1553 		if (precision == glu::PRECISION_MEDIUMP)
   1554 		{
   1555 			for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
   1556 				((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
   1557 		}
   1558 	}
   1559 
   1560 	bool compare (const void* const* inputs, const void* const* outputs)
   1561 	{
   1562 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
   1563 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
   1564 		const bool				hasZeroSign		= supportsSignedZero(precision);
   1565 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
   1566 
   1567 		if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
   1568 		{
   1569 			// Require exact result.
   1570 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
   1571 			{
   1572 				const float		in0			= ((const float*)inputs[0])[compNdx];
   1573 				const float		out0		= ((const float*)outputs[0])[compNdx];
   1574 				const float		ref			= deFloatFrac(in0);
   1575 
   1576 				const deUint32	ulpDiff		= hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
   1577 
   1578 				if (ulpDiff > 0)
   1579 				{
   1580 					m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
   1581 					return false;
   1582 				}
   1583 			}
   1584 		}
   1585 		else
   1586 		{
   1587 			const int		mantissaBits	= getMinMantissaBits(precision);
   1588 			const float		eps				= getEpsFromBits(1.0f, mantissaBits);	// epsilon for rounding bounds
   1589 
   1590 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
   1591 			{
   1592 				const float		in0			= ((const float*)inputs[0])[compNdx];
   1593 				const float		out0		= ((const float*)outputs[0])[compNdx];
   1594 
   1595 				if (int(deFloatFloor(in0-eps)) == int(deFloatFloor(in0+eps)))
   1596 				{
   1597 					const float		ref			= deFloatFrac(in0);
   1598 					const int		bitsLost	= numBitsLostInOp(in0, ref);
   1599 					const deUint32	maxUlpDiff	= getMaxUlpDiffFromBits(de::max(0, mantissaBits-bitsLost));	// ULP diff for rounded integer value.
   1600 					const deUint32	ulpDiff		= getUlpDiffIgnoreZeroSign(out0, ref);
   1601 
   1602 					if (ulpDiff > maxUlpDiff)
   1603 					{
   1604 						m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << " with ULP threshold " << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
   1605 						return false;
   1606 					}
   1607 				}
   1608 				else
   1609 				{
   1610 					if (out0 >= 1.0f)
   1611 					{
   1612 						m_failMsg << "Expected [" << compNdx << "] < 1.0";
   1613 						return false;
   1614 					}
   1615 				}
   1616 			}
   1617 		}
   1618 
   1619 		return true;
   1620 	}
   1621 };
   1622 
   1623 ShaderCommonFunctionTests::ShaderCommonFunctionTests (Context& context)
   1624 	: TestCaseGroup(context, "common", "Common function tests")
   1625 {
   1626 }
   1627 
   1628 ShaderCommonFunctionTests::~ShaderCommonFunctionTests (void)
   1629 {
   1630 }
   1631 
   1632 template<class TestClass>
   1633 static void addFunctionCases (TestCaseGroup* parent, const char* functionName, bool floatTypes, bool intTypes, bool uintTypes)
   1634 {
   1635 	tcu::TestCaseGroup* group = new tcu::TestCaseGroup(parent->getTestContext(), functionName, functionName);
   1636 	parent->addChild(group);
   1637 
   1638 	const glu::DataType scalarTypes[] =
   1639 	{
   1640 		glu::TYPE_FLOAT,
   1641 		glu::TYPE_INT,
   1642 		glu::TYPE_UINT
   1643 	};
   1644 
   1645 	for (int scalarTypeNdx = 0; scalarTypeNdx < DE_LENGTH_OF_ARRAY(scalarTypes); scalarTypeNdx++)
   1646 	{
   1647 		const glu::DataType scalarType = scalarTypes[scalarTypeNdx];
   1648 
   1649 		if ((!floatTypes && scalarType == glu::TYPE_FLOAT)	||
   1650 			(!intTypes && scalarType == glu::TYPE_INT)		||
   1651 			(!uintTypes && scalarType == glu::TYPE_UINT))
   1652 			continue;
   1653 
   1654 		for (int vecSize = 1; vecSize <= 4; vecSize++)
   1655 		{
   1656 			for (int prec = glu::PRECISION_LOWP; prec <= glu::PRECISION_HIGHP; prec++)
   1657 			{
   1658 				for (int shaderType = glu::SHADERTYPE_VERTEX; shaderType <= glu::SHADERTYPE_FRAGMENT; shaderType++)
   1659 					group->addChild(new TestClass(parent->getContext(), glu::DataType(scalarType + vecSize - 1), glu::Precision(prec), glu::ShaderType(shaderType)));
   1660 			}
   1661 		}
   1662 	}
   1663 }
   1664 
   1665 void ShaderCommonFunctionTests::init (void)
   1666 {
   1667 	//																	Float?	Int?	Uint?
   1668 	addFunctionCases<AbsCase>				(this,	"abs",				true,	true,	false);
   1669 	addFunctionCases<SignCase>				(this,	"sign",				true,	true,	false);
   1670 	addFunctionCases<FloorCase>				(this,	"floor",			true,	false,	false);
   1671 	addFunctionCases<TruncCase>				(this,	"trunc",			true,	false,	false);
   1672 	addFunctionCases<RoundCase>				(this,	"round",			true,	false,	false);
   1673 	addFunctionCases<RoundEvenCase>			(this,	"roundeven",		true,	false,	false);
   1674 	addFunctionCases<CeilCase>				(this,	"ceil",				true,	false,	false);
   1675 	addFunctionCases<FractCase>				(this,	"fract",			true,	false,	false);
   1676 	// mod
   1677 	addFunctionCases<ModfCase>				(this,	"modf",				true,	false,	false);
   1678 	// min
   1679 	// max
   1680 	// clamp
   1681 	// mix
   1682 	// step
   1683 	// smoothstep
   1684 	addFunctionCases<IsnanCase>				(this,	"isnan",			true,	false,	false);
   1685 	addFunctionCases<IsinfCase>				(this,	"isinf",			true,	false,	false);
   1686 	addFunctionCases<FloatBitsToIntCase>	(this,	"floatbitstoint",	true,	false,	false);
   1687 	addFunctionCases<FloatBitsToUintCase>	(this,	"floatbitstouint",	true,	false,	false);
   1688 
   1689 	// (u)intBitsToFloat()
   1690 	{
   1691 		tcu::TestCaseGroup* intGroup	= new tcu::TestCaseGroup(m_testCtx, "intbitstofloat",	"intBitsToFloat() Tests");
   1692 		tcu::TestCaseGroup* uintGroup	= new tcu::TestCaseGroup(m_testCtx, "uintbitstofloat",	"uintBitsToFloat() Tests");
   1693 
   1694 		addChild(intGroup);
   1695 		addChild(uintGroup);
   1696 
   1697 		for (int vecSize = 1; vecSize < 4; vecSize++)
   1698 		{
   1699 			const glu::DataType		intType		= vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
   1700 			const glu::DataType		uintType	= vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT;
   1701 
   1702 			for (int shaderType = glu::SHADERTYPE_VERTEX; shaderType <= glu::SHADERTYPE_FRAGMENT; shaderType++)
   1703 			{
   1704 				intGroup->addChild(new BitsToFloatCase(m_context, intType, glu::ShaderType(shaderType)));
   1705 				uintGroup->addChild(new BitsToFloatCase(m_context, uintType, glu::ShaderType(shaderType)));
   1706 			}
   1707 		}
   1708 	}
   1709 }
   1710 
   1711 } // Functional
   1712 } // gles3
   1713 } // deqp
   1714