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