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
33namespace deqp
34{
35namespace gles3
36{
37namespace Functional
38{
39
40using std::vector;
41using std::string;
42using tcu::TestLog;
43using namespace gls::ShaderExecUtil;
44
45using tcu::Vec2;
46using tcu::Vec3;
47using tcu::Vec4;
48using tcu::IVec2;
49using tcu::IVec3;
50using tcu::IVec4;
51
52// Utilities
53
54template<typename T, int Size>
55struct VecArrayAccess
56{
57public:
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
64private:
65	tcu::Vector<T, Size>*			m_array;
66};
67
68template<typename T>	T			randomScalar	(de::Random& rnd, T minValue, T maxValue);
69template<> inline		float		randomScalar	(de::Random& rnd, float minValue, float maxValue)		{ return rnd.getFloat(minValue, maxValue);	}
70template<> inline		deInt32		randomScalar	(de::Random& rnd, deInt32 minValue, deInt32 maxValue)	{ return rnd.getInt(minValue, maxValue);	}
71template<> inline		deUint32	randomScalar	(de::Random& rnd, deUint32 minValue, deUint32 maxValue)	{ return minValue + rnd.getUint32() % (maxValue - minValue + 1); }
72
73template<typename T, int Size>
74inline 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
82template<typename T, int Size>
83static 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
90template<typename T>
91static 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
98inline 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
106inline 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
113inline 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
123inline 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
130inline 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
136inline deUint32 getMaxUlpDiffFromBits (int numAccurateBits)
137{
138	const int		numGarbageBits	= 23-numAccurateBits;
139	const deUint32	mask			= (1u<<numGarbageBits)-1u;
140
141	return mask;
142}
143
144inline float getEpsFromBits (float value, int numAccurateBits)
145{
146	return getEpsFromMaxUlpDiff(value, getMaxUlpDiffFromBits(numAccurateBits));
147}
148
149static 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
164class CommonFunctionCase : public TestCase
165{
166public:
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
174protected:
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
187private:
188	ShaderExecutor*			m_executor;
189};
190
191CommonFunctionCase::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
200CommonFunctionCase::~CommonFunctionCase (void)
201{
202	CommonFunctionCase::deinit();
203}
204
205void 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
216void CommonFunctionCase::deinit (void)
217{
218	delete m_executor;
219	m_executor = DE_NULL;
220}
221
222static 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
230static 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
238static 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
260struct HexFloat
261{
262	const float value;
263	HexFloat (const float value_) : value(value_) {}
264};
265
266std::ostream& operator<< (std::ostream& str, const HexFloat& v)
267{
268	return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits());
269}
270
271struct HexBool
272{
273	const deUint32 value;
274	HexBool (const deUint32 value_) : value(value_) {}
275};
276
277std::ostream& operator<< (std::ostream& str, const HexBool& v)
278{
279	return str << (v.value ? "true" : "false") << " / " << tcu::toHex(v.value);
280}
281
282struct 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
290std::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
324CommonFunctionCase::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
390static 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
403static 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
414static 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
419class AbsCase : public CommonFunctionCase
420{
421public:
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
501class SignCase : public CommonFunctionCase
502{
503public:
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], (float*)values[0] + scalarSize, -1.0f);
537			std::fill((float*)values[0], (float*)values[0] + scalarSize,  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], (int*)values[0] + scalarSize, -1);
544			std::fill((int*)values[0], (int*)values[0] + scalarSize,  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
596static 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
607class RoundEvenCase : public CommonFunctionCase
608{
609public:
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
718class ModfCase : public CommonFunctionCase
719{
720public:
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
784class IsnanCase : public CommonFunctionCase
785{
786public:
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
865class IsinfCase : public CommonFunctionCase
866{
867public:
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
946class FloatBitsToUintIntCase : public CommonFunctionCase
947{
948public:
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
1006class FloatBitsToIntCase : public FloatBitsToUintIntCase
1007{
1008public:
1009	FloatBitsToIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1010		: FloatBitsToUintIntCase(context, baseType, precision, shaderType, true)
1011	{
1012	}
1013};
1014
1015class FloatBitsToUintCase : public FloatBitsToUintIntCase
1016{
1017public:
1018	FloatBitsToUintCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1019		: FloatBitsToUintIntCase(context, baseType, precision, shaderType, false)
1020	{
1021	}
1022};
1023
1024class BitsToFloatCase : public CommonFunctionCase
1025{
1026public:
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 int				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 int		ulpDiff		= de::abs((int)in0 - (int)out0);
1061
1062			if (ulpDiff > maxUlpDiff)
1063			{
1064				m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(in0) << " 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
1074class FloorCase : public CommonFunctionCase
1075{
1076public:
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
1170class TruncCase : public CommonFunctionCase
1171{
1172public:
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
1278class RoundCase : public CommonFunctionCase
1279{
1280public:
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
1406class CeilCase : public CommonFunctionCase
1407{
1408public:
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
1511class FractCase : public CommonFunctionCase
1512{
1513public:
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
1623ShaderCommonFunctionTests::ShaderCommonFunctionTests (Context& context)
1624	: TestCaseGroup(context, "common", "Common function tests")
1625{
1626}
1627
1628ShaderCommonFunctionTests::~ShaderCommonFunctionTests (void)
1629{
1630}
1631
1632template<class TestClass>
1633static 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
1665void 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