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