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