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