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