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::__anon2c2bd4020111::VecArrayAccess67 									VecArrayAccess	(const void* ptr) : m_array((tcu::Vector<T, Size>*)ptr) {}
~VecArrayAccessvkt::shaderexecutor::__anon2c2bd4020111::VecArrayAccess68 									~VecArrayAccess	(void) {}
69 
operator []vkt::shaderexecutor::__anon2c2bd4020111::VecArrayAccess70 	const tcu::Vector<T, Size>&		operator[]		(size_t offset) const	{ return m_array[offset];	}
operator []vkt::shaderexecutor::__anon2c2bd4020111::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::__anon2c2bd4020111::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::__anon2c2bd4020111::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::__anon2c2bd4020111::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 											m_executor->setShaderSources(programCollection);
451 										}
452 
453 	virtual TestInstance*				createInstance				(Context& context) const = 0;
454 
455 	void								init						(void);
456 
457 protected:
458 										CommonFunctionCase			(const CommonFunctionCase& other);
459 	CommonFunctionCase&					operator=					(const CommonFunctionCase& other);
460 
461 	const glu::ShaderType				m_shaderType;
462 	ShaderSpec							m_spec;
463 	const int							m_numValues;
464 	de::MovePtr<ShaderExecutor>			m_executor;
465 };
466 
CommonFunctionCase(tcu::TestContext & testCtx,const char * name,const char * description,glu::ShaderType shaderType)467 CommonFunctionCase::CommonFunctionCase (tcu::TestContext& testCtx, const char* name, const char* description, glu::ShaderType shaderType)
468 	: TestCase		(testCtx, name, description)
469 	, m_shaderType	(shaderType)
470 	, m_numValues	(100)
471 	, m_executor	(DE_NULL)
472 {
473 }
474 
~CommonFunctionCase(void)475 CommonFunctionCase::~CommonFunctionCase (void)
476 {
477 }
478 
init(void)479 void CommonFunctionCase::init (void)
480 {
481 	DE_ASSERT(!m_executor);
482 
483 	m_executor = de::MovePtr<ShaderExecutor>(createExecutor(m_shaderType, m_spec));
484 	m_testCtx.getLog() << *m_executor;
485 }
486 
487 // CommonFunctionTestInstance
488 
489 class CommonFunctionTestInstance : public TestInstance
490 {
491 public:
CommonFunctionTestInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)492 										CommonFunctionTestInstance	(Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
493 											: TestInstance	(context)
494 											, m_shaderType	(shaderType)
495 											, m_spec		(spec)
496 											, m_numValues	(numValues)
497 											, m_name		(name)
498 											, m_executor	(executor)
499 										{
500 										}
501 	virtual tcu::TestStatus				iterate						(void);
502 
503 protected:
504 	virtual void						getInputValues				(int numValues, void* const* values) const = 0;
505 	virtual bool						compare						(const void* const* inputs, const void* const* outputs) = 0;
506 
507 	const glu::ShaderType				m_shaderType;
508 	ShaderSpec							m_spec;
509 	const int							m_numValues;
510 
511 	const char*							m_name;
512 
513 	std::ostringstream					m_failMsg;					//!< Comparison failure help message.
514 
515 	ShaderExecutor&						m_executor;
516 };
517 
iterate(void)518 tcu::TestStatus CommonFunctionTestInstance::iterate (void)
519 {
520 	const int				numInputScalars			= computeTotalScalarSize(m_spec.inputs);
521 	const int				numOutputScalars		= computeTotalScalarSize(m_spec.outputs);
522 	vector<deUint32>		inputData				(numInputScalars * m_numValues);
523 	vector<deUint32>		outputData				(numOutputScalars * m_numValues);
524 	const vector<void*>		inputPointers			= getInputOutputPointers(m_spec.inputs, inputData, m_numValues);
525 	const vector<void*>		outputPointers			= getInputOutputPointers(m_spec.outputs, outputData, m_numValues);
526 
527 	// Initialize input data.
528 	getInputValues(m_numValues, &inputPointers[0]);
529 
530 	// Execute shader.
531 	m_executor.execute(m_context, m_numValues, &inputPointers[0], &outputPointers[0]);
532 
533 	// Compare results.
534 	{
535 		const vector<int>		inScalarSizes		= getScalarSizes(m_spec.inputs);
536 		const vector<int>		outScalarSizes		= getScalarSizes(m_spec.outputs);
537 		vector<void*>			curInputPtr			(inputPointers.size());
538 		vector<void*>			curOutputPtr		(outputPointers.size());
539 		int						numFailed			= 0;
540 		tcu::TestContext&		testCtx				= m_context.getTestContext();
541 
542 		for (int valNdx = 0; valNdx < m_numValues; valNdx++)
543 		{
544 			// Set up pointers for comparison.
545 			for (int inNdx = 0; inNdx < (int)curInputPtr.size(); ++inNdx)
546 				curInputPtr[inNdx] = (deUint32*)inputPointers[inNdx] + inScalarSizes[inNdx]*valNdx;
547 
548 			for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); ++outNdx)
549 				curOutputPtr[outNdx] = (deUint32*)outputPointers[outNdx] + outScalarSizes[outNdx]*valNdx;
550 
551 			if (!compare(&curInputPtr[0], &curOutputPtr[0]))
552 			{
553 				// \todo [2013-08-08 pyry] We probably want to log reference value as well?
554 
555 				testCtx.getLog() << TestLog::Message << "ERROR: comparison failed for value " << valNdx << ":\n  " << m_failMsg.str() << TestLog::EndMessage;
556 
557 				testCtx.getLog() << TestLog::Message << "  inputs:" << TestLog::EndMessage;
558 				for (int inNdx = 0; inNdx < (int)curInputPtr.size(); inNdx++)
559 					testCtx.getLog() << TestLog::Message << "    " << m_spec.inputs[inNdx].name << " = "
560 														   << VarValue(m_spec.inputs[inNdx].varType, curInputPtr[inNdx])
561 									   << TestLog::EndMessage;
562 
563 				testCtx.getLog() << TestLog::Message << "  outputs:" << TestLog::EndMessage;
564 				for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); outNdx++)
565 					testCtx.getLog() << TestLog::Message << "    " << m_spec.outputs[outNdx].name << " = "
566 														   << VarValue(m_spec.outputs[outNdx].varType, curOutputPtr[outNdx])
567 									   << TestLog::EndMessage;
568 
569 				m_failMsg.str("");
570 				m_failMsg.clear();
571 				numFailed += 1;
572 			}
573 		}
574 
575 		testCtx.getLog() << TestLog::Message << (m_numValues - numFailed) << " / " << m_numValues << " values passed" << TestLog::EndMessage;
576 
577 		if (numFailed == 0)
578 			return tcu::TestStatus::pass("Pass");
579 		else
580 			return tcu::TestStatus::fail("Result comparison failed");
581 	}
582 }
583 
584 // Test cases
585 
586 class AbsCaseInstance : public CommonFunctionTestInstance
587 {
588 public:
AbsCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)589 	AbsCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
590 		: CommonFunctionTestInstance	(context, shaderType, spec, executor, numValues, name)
591 	{
592 	}
593 
getInputValues(int numValues,void * const * values) const594 	void getInputValues (int numValues, void* const* values) const
595 	{
596 		const Vec2 floatRanges[] =
597 		{
598 			Vec2(-2.0f,		2.0f),	// lowp
599 			Vec2(-1e3f,		1e3f),	// mediump
600 			Vec2(-1e7f,		1e7f)	// highp
601 		};
602 		const IVec2 intRanges[] =
603 		{
604 			IVec2(-(1<<7)+1,	(1<<7)-1),
605 			IVec2(-(1<<15)+1,	(1<<15)-1),
606 			IVec2(0x80000001,	0x7fffffff)
607 		};
608 
609 		de::Random				rnd			(deStringHash(m_name) ^ 0x235facu);
610 		const glu::DataType		type		= m_spec.inputs[0].varType.getBasicType();
611 		const glu::Precision	precision	= m_spec.inputs[0].varType.getPrecision();
612 		const int				scalarSize	= glu::getDataTypeScalarSize(type);
613 
614 		if (glu::isDataTypeFloatOrVec(type))
615 			fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), values[0], numValues*scalarSize);
616 		else
617 			fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), values[0], numValues*scalarSize);
618 	}
619 
compare(const void * const * inputs,const void * const * outputs)620 	bool compare (const void* const* inputs, const void* const* outputs)
621 	{
622 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
623 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
624 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
625 
626 		if (glu::isDataTypeFloatOrVec(type))
627 		{
628 			const int		mantissaBits	= getMinMantissaBits(precision);
629 			const deUint32	maxUlpDiff		= (1u<<(23-mantissaBits))-1u;
630 
631 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
632 			{
633 				const float		in0			= ((const float*)inputs[0])[compNdx];
634 				const float		out0		= ((const float*)outputs[0])[compNdx];
635 				const float		ref0		= de::abs(in0);
636 				const deUint32	ulpDiff0	= getUlpDiff(out0, ref0);
637 
638 				if (ulpDiff0 > maxUlpDiff)
639 				{
640 					m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
641 					return false;
642 				}
643 			}
644 		}
645 		else
646 		{
647 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
648 			{
649 				const int	in0		= ((const int*)inputs[0])[compNdx];
650 				const int	out0	= ((const int*)outputs[0])[compNdx];
651 				const int	ref0	= de::abs(in0);
652 
653 				if (out0 != ref0)
654 				{
655 					m_failMsg << "Expected [" << compNdx << "] = " << ref0;
656 					return false;
657 				}
658 			}
659 		}
660 
661 		return true;
662 	}
663 };
664 
665 class AbsCase : public CommonFunctionCase
666 {
667 public:
AbsCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)668 	AbsCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
669 		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "abs", shaderType)
670 	{
671 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
672 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
673 		m_spec.source = "out0 = abs(in0);";
674 		init();
675 	}
676 
createInstance(Context & ctx) const677 	TestInstance* createInstance (Context& ctx) const
678 	{
679 		return new AbsCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
680 	}
681 };
682 
683 class SignCaseInstance : public CommonFunctionTestInstance
684 {
685 public:
SignCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)686 	SignCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
687 		: CommonFunctionTestInstance	(context, shaderType, spec, executor, numValues, name)
688 	{
689 	}
690 
getInputValues(int numValues,void * const * values) const691 	void getInputValues (int numValues, void* const* values) const
692 	{
693 		const Vec2 floatRanges[] =
694 		{
695 			Vec2(-2.0f,		2.0f),	// lowp
696 			Vec2(-1e4f,		1e4f),	// mediump	- note: may end up as inf
697 			Vec2(-1e8f,		1e8f)	// highp	- note: may end up as inf
698 		};
699 		const IVec2 intRanges[] =
700 		{
701 			IVec2(-(1<<7),		(1<<7)-1),
702 			IVec2(-(1<<15),		(1<<15)-1),
703 			IVec2(0x80000000,	0x7fffffff)
704 		};
705 
706 		de::Random				rnd			(deStringHash(m_name) ^ 0x324u);
707 		const glu::DataType		type		= m_spec.inputs[0].varType.getBasicType();
708 		const glu::Precision	precision	= m_spec.inputs[0].varType.getPrecision();
709 		const int				scalarSize	= glu::getDataTypeScalarSize(type);
710 
711 		if (glu::isDataTypeFloatOrVec(type))
712 		{
713 			// Special cases.
714 			std::fill((float*)values[0], (float*)values[0] + scalarSize, +1.0f);
715 			std::fill((float*)values[0], (float*)values[0] + scalarSize, -1.0f);
716 			std::fill((float*)values[0], (float*)values[0] + scalarSize,  0.0f);
717 			fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), (float*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
718 		}
719 		else
720 		{
721 			std::fill((int*)values[0], (int*)values[0] + scalarSize, +1);
722 			std::fill((int*)values[0], (int*)values[0] + scalarSize, -1);
723 			std::fill((int*)values[0], (int*)values[0] + scalarSize,  0);
724 			fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), (int*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
725 		}
726 	}
727 
compare(const void * const * inputs,const void * const * outputs)728 	bool compare (const void* const* inputs, const void* const* outputs)
729 	{
730 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
731 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
732 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
733 
734 		if (glu::isDataTypeFloatOrVec(type))
735 		{
736 			// Both highp and mediump should be able to represent -1, 0, and +1 exactly
737 			const deUint32 maxUlpDiff = precision == glu::PRECISION_LOWP ? getMaxUlpDiffFromBits(getMinMantissaBits(precision)) : 0;
738 
739 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
740 			{
741 				const float		in0			= ((const float*)inputs[0])[compNdx];
742 				const float		out0		= ((const float*)outputs[0])[compNdx];
743 				const float		ref0		= in0 < 0.0f ? -1.0f :
744 											  in0 > 0.0f ? +1.0f : 0.0f;
745 				const deUint32	ulpDiff0	= getUlpDiff(out0, ref0);
746 
747 				if (ulpDiff0 > maxUlpDiff)
748 				{
749 					m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
750 					return false;
751 				}
752 			}
753 		}
754 		else
755 		{
756 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
757 			{
758 				const int	in0		= ((const int*)inputs[0])[compNdx];
759 				const int	out0	= ((const int*)outputs[0])[compNdx];
760 				const int	ref0	= in0 < 0 ? -1 :
761 									  in0 > 0 ? +1 : 0;
762 
763 				if (out0 != ref0)
764 				{
765 					m_failMsg << "Expected [" << compNdx << "] = " << ref0;
766 					return false;
767 				}
768 			}
769 		}
770 
771 		return true;
772 	}
773 };
774 
775 class SignCase : public CommonFunctionCase
776 {
777 public:
SignCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)778 	SignCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
779 		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "sign", shaderType)
780 	{
781 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
782 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
783 		m_spec.source = "out0 = sign(in0);";
784 		init();
785 	}
786 
createInstance(Context & ctx) const787 	TestInstance* createInstance (Context& ctx) const
788 	{
789 		return new SignCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
790 	}
791 };
792 
roundEven(float v)793 static float roundEven (float v)
794 {
795 	const float		q			= deFloatFrac(v);
796 	const int		truncated	= int(v-q);
797 	const int		rounded		= (q > 0.5f)							? (truncated + 1) :	// Rounded up
798 									(q == 0.5f && (truncated % 2 != 0))	? (truncated + 1) :	// Round to nearest even at 0.5
799 									truncated;												// Rounded down
800 
801 	return float(rounded);
802 }
803 
804 class RoundEvenCaseInstance : public CommonFunctionTestInstance
805 {
806 public:
RoundEvenCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)807 	RoundEvenCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
808 		: CommonFunctionTestInstance	(context, shaderType, spec, executor, numValues, name)
809 	{
810 	}
811 
getInputValues(int numValues,void * const * values) const812 	void getInputValues (int numValues, void* const* values) const
813 	{
814 		const Vec2 ranges[] =
815 		{
816 			Vec2(-2.0f,		2.0f),	// lowp
817 			Vec2(-1e3f,		1e3f),	// mediump
818 			Vec2(-1e7f,		1e7f)	// highp
819 		};
820 
821 		de::Random				rnd				(deStringHash(m_name) ^ 0xac23fu);
822 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
823 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
824 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
825 		int						numSpecialCases	= 0;
826 
827 		// Special cases.
828 		if (precision != glu::PRECISION_LOWP)
829 		{
830 			DE_ASSERT(numValues >= 20);
831 			for (int ndx = 0; ndx < 20; ndx++)
832 			{
833 				const float v = de::clamp(float(ndx) - 10.5f, ranges[precision].x(), ranges[precision].y());
834 				std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
835 				numSpecialCases += 1;
836 			}
837 		}
838 
839 		// Random cases.
840 		fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
841 
842 		// If precision is mediump, make sure values can be represented in fp16 exactly
843 		if (precision == glu::PRECISION_MEDIUMP)
844 		{
845 			for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
846 				((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
847 		}
848 	}
849 
compare(const void * const * inputs,const void * const * outputs)850 	bool compare (const void* const* inputs, const void* const* outputs)
851 	{
852 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
853 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
854 		const bool				hasSignedZero	= supportsSignedZero(precision);
855 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
856 
857 		if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
858 		{
859 			// Require exact rounding result.
860 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
861 			{
862 				const float		in0			= ((const float*)inputs[0])[compNdx];
863 				const float		out0		= ((const float*)outputs[0])[compNdx];
864 				const float		ref			= roundEven(in0);
865 
866 				const deUint32	ulpDiff		= hasSignedZero ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
867 
868 				if (ulpDiff > 0)
869 				{
870 					m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
871 					return false;
872 				}
873 			}
874 		}
875 		else
876 		{
877 			const int		mantissaBits	= getMinMantissaBits(precision);
878 			const deUint32	maxUlpDiff		= getMaxUlpDiffFromBits(mantissaBits);	// ULP diff for rounded integer value.
879 			const float		eps				= getEpsFromBits(1.0f, mantissaBits);	// epsilon for rounding bounds
880 
881 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
882 			{
883 				const float		in0			= ((const float*)inputs[0])[compNdx];
884 				const float		out0		= ((const float*)outputs[0])[compNdx];
885 				const int		minRes		= int(roundEven(in0-eps));
886 				const int		maxRes		= int(roundEven(in0+eps));
887 				bool			anyOk		= false;
888 
889 				for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
890 				{
891 					const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
892 
893 					if (ulpDiff <= maxUlpDiff)
894 					{
895 						anyOk = true;
896 						break;
897 					}
898 				}
899 
900 				if (!anyOk)
901 				{
902 					m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
903 					return false;
904 				}
905 			}
906 		}
907 
908 		return true;
909 	}
910 };
911 
912 class RoundEvenCase : public CommonFunctionCase
913 {
914 public:
RoundEvenCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)915 	RoundEvenCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
916 		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "roundEven", shaderType)
917 	{
918 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
919 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
920 		m_spec.source = "out0 = roundEven(in0);";
921 		init();
922 	}
923 
createInstance(Context & ctx) const924 	TestInstance* createInstance (Context& ctx) const
925 	{
926 		return new RoundEvenCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
927 	}
928 };
929 
930 class ModfCaseInstance : public CommonFunctionTestInstance
931 {
932 public:
ModfCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)933 	ModfCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
934 		: CommonFunctionTestInstance	(context, shaderType, spec, executor, numValues, name)
935 	{
936 	}
937 
getInputValues(int numValues,void * const * values) const938 	void getInputValues (int numValues, void* const* values) const
939 	{
940 		const Vec2 ranges[] =
941 		{
942 			Vec2(-2.0f,		2.0f),	// lowp
943 			Vec2(-1e3f,		1e3f),	// mediump
944 			Vec2(-1e7f,		1e7f)	// highp
945 		};
946 
947 		de::Random				rnd			(deStringHash(m_name) ^ 0xac23fu);
948 		const glu::DataType		type		= m_spec.inputs[0].varType.getBasicType();
949 		const glu::Precision	precision	= m_spec.inputs[0].varType.getPrecision();
950 		const int				scalarSize	= glu::getDataTypeScalarSize(type);
951 
952 		fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
953 	}
954 
compare(const void * const * inputs,const void * const * outputs)955 	bool compare (const void* const* inputs, const void* const* outputs)
956 	{
957 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
958 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
959 		const bool				hasZeroSign		= supportsSignedZero(precision);
960 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
961 
962 		const int				mantissaBits	= getMinMantissaBits(precision);
963 
964 		for (int compNdx = 0; compNdx < scalarSize; compNdx++)
965 		{
966 			const float		in0			= ((const float*)inputs[0])[compNdx];
967 			const float		out0		= ((const float*)outputs[0])[compNdx];
968 			const float		out1		= ((const float*)outputs[1])[compNdx];
969 
970 			const float		refOut1		= float(int(in0));
971 			const float		refOut0		= in0 - refOut1;
972 
973 			const int		bitsLost	= precision != glu::PRECISION_HIGHP ? numBitsLostInOp(in0, refOut0) : 0;
974 			const deUint32	maxUlpDiff	= getMaxUlpDiffFromBits(de::max(mantissaBits - bitsLost, 0));
975 
976 			const float		resSum		= out0 + out1;
977 
978 			const deUint32	ulpDiff		= hasZeroSign ? getUlpDiff(resSum, in0) : getUlpDiffIgnoreZeroSign(resSum, in0);
979 
980 			if (ulpDiff > maxUlpDiff)
981 			{
982 				m_failMsg << "Expected [" << compNdx << "] = (" << HexFloat(refOut0) << ") + (" << HexFloat(refOut1) << ") = " << HexFloat(in0) << " with ULP threshold "
983 							<< tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
984 				return false;
985 			}
986 		}
987 
988 		return true;
989 	}
990 };
991 
992 class ModfCase : public CommonFunctionCase
993 {
994 public:
ModfCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)995 	ModfCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
996 		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "modf", shaderType)
997 	{
998 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
999 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1000 		m_spec.outputs.push_back(Symbol("out1", glu::VarType(baseType, precision)));
1001 		m_spec.source = "out0 = modf(in0, out1);";
1002 		init();
1003 	}
1004 
createInstance(Context & ctx) const1005 	TestInstance* createInstance (Context& ctx) const
1006 	{
1007 		return new ModfCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
1008 	}
1009 };
1010 
1011 class IsnanCaseInstance : public CommonFunctionTestInstance
1012 {
1013 public:
IsnanCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1014 	IsnanCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1015 		: CommonFunctionTestInstance	(context, shaderType, spec, executor, numValues, name)
1016 	{
1017 	}
1018 
getInputValues(int numValues,void * const * values) const1019 	void getInputValues (int numValues, void* const* values) const
1020 	{
1021 		de::Random				rnd				(deStringHash(m_name) ^ 0xc2a39fu);
1022 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
1023 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
1024 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
1025 		const int				mantissaBits	= getMinMantissaBits(precision);
1026 		const deUint32			mantissaMask	= ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
1027 
1028 		for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
1029 		{
1030 			const bool		isNan		= rnd.getFloat() > 0.3f;
1031 			const bool		isInf		= !isNan && rnd.getFloat() > 0.4f;
1032 			const deUint32	mantissa	= !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
1033 			const deUint32	exp			= !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
1034 			const deUint32	sign		= rnd.getUint32() & 0x1u;
1035 			const deUint32	value		= (sign << 31) | (exp << 23) | mantissa;
1036 
1037 			DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
1038 
1039 			((deUint32*)values[0])[valNdx] = value;
1040 		}
1041 	}
1042 
compare(const void * const * inputs,const void * const * outputs)1043 	bool compare (const void* const* inputs, const void* const* outputs)
1044 	{
1045 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
1046 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
1047 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
1048 
1049 		if (precision == glu::PRECISION_HIGHP)
1050 		{
1051 			// Only highp is required to support inf/nan
1052 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1053 			{
1054 				const float		in0		= ((const float*)inputs[0])[compNdx];
1055 				const bool		out0	= ((const deUint32*)outputs[0])[compNdx] != 0;
1056 				const bool		ref		= tcu::Float32(in0).isNaN();
1057 
1058 				if (out0 != ref)
1059 				{
1060 					m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
1061 					return false;
1062 				}
1063 			}
1064 		}
1065 		else if (precision == glu::PRECISION_MEDIUMP || precision == glu::PRECISION_LOWP)
1066 		{
1067 			// NaN support is optional, check that inputs that are not NaN don't result in true.
1068 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1069 			{
1070 				const float		in0		= ((const float*)inputs[0])[compNdx];
1071 				const bool		out0	= ((const deUint32*)outputs[0])[compNdx] != 0;
1072 				const bool		ref		= tcu::Float32(in0).isNaN();
1073 
1074 				if (!ref && out0)
1075 				{
1076 					m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
1077 					return false;
1078 				}
1079 			}
1080 		}
1081 
1082 		return true;
1083 	}
1084 };
1085 
1086 class IsnanCase : public CommonFunctionCase
1087 {
1088 public:
IsnanCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1089 	IsnanCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1090 		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isnan", shaderType)
1091 	{
1092 		DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
1093 
1094 		const int			vecSize		= glu::getDataTypeScalarSize(baseType);
1095 		const glu::DataType	boolType	= vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
1096 
1097 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1098 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
1099 		m_spec.source = "out0 = isnan(in0);";
1100 		init();
1101 	}
1102 
createInstance(Context & ctx) const1103 	TestInstance* createInstance (Context& ctx) const
1104 	{
1105 		return new IsnanCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
1106 	}
1107 };
1108 
1109 class IsinfCaseInstance : public CommonFunctionTestInstance
1110 {
1111 public:
IsinfCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1112 	IsinfCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1113 		: CommonFunctionTestInstance	(context, shaderType, spec, executor, numValues, name)
1114 	{
1115 	}
1116 
getInputValues(int numValues,void * const * values) const1117 	void getInputValues (int numValues, void* const* values) const
1118 	{
1119 		de::Random				rnd				(deStringHash(m_name) ^ 0xc2a39fu);
1120 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
1121 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
1122 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
1123 		const int				mantissaBits	= getMinMantissaBits(precision);
1124 		const deUint32			mantissaMask	= ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
1125 
1126 		for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
1127 		{
1128 			const bool		isInf		= rnd.getFloat() > 0.3f;
1129 			const bool		isNan		= !isInf && rnd.getFloat() > 0.4f;
1130 			const deUint32	mantissa	= !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
1131 			const deUint32	exp			= !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
1132 			const deUint32	sign		= rnd.getUint32() & 0x1u;
1133 			const deUint32	value		= (sign << 31) | (exp << 23) | mantissa;
1134 
1135 			DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
1136 
1137 			((deUint32*)values[0])[valNdx] = value;
1138 		}
1139 	}
1140 
compare(const void * const * inputs,const void * const * outputs)1141 	bool compare (const void* const* inputs, const void* const* outputs)
1142 	{
1143 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
1144 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
1145 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
1146 
1147 		if (precision == glu::PRECISION_HIGHP)
1148 		{
1149 			// Only highp is required to support inf/nan
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::Float32(in0).isInf();
1155 
1156 				if (out0 != ref)
1157 				{
1158 					m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
1159 					return false;
1160 				}
1161 			}
1162 		}
1163 		else if (precision == glu::PRECISION_MEDIUMP)
1164 		{
1165 			// Inf support is optional, check that inputs that are not Inf in mediump don't result in true.
1166 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1167 			{
1168 				const float		in0		= ((const float*)inputs[0])[compNdx];
1169 				const bool		out0	= ((const deUint32*)outputs[0])[compNdx] != 0;
1170 				const bool		ref		= tcu::Float16(in0).isInf();
1171 
1172 				if (!ref && out0)
1173 				{
1174 					m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
1175 					return false;
1176 				}
1177 			}
1178 		}
1179 		// else: no verification can be performed
1180 
1181 		return true;
1182 	}
1183 };
1184 
1185 class IsinfCase : public CommonFunctionCase
1186 {
1187 public:
IsinfCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1188 	IsinfCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1189 		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isinf", shaderType)
1190 	{
1191 		DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
1192 
1193 		const int			vecSize		= glu::getDataTypeScalarSize(baseType);
1194 		const glu::DataType	boolType	= vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
1195 
1196 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1197 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
1198 		m_spec.source = "out0 = isinf(in0);";
1199 		init();
1200 	}
1201 
createInstance(Context & ctx) const1202 	TestInstance* createInstance (Context& ctx) const
1203 	{
1204 		return new IsinfCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
1205 	}
1206 };
1207 
1208 class FloatBitsToUintIntCaseInstance : public CommonFunctionTestInstance
1209 {
1210 public:
FloatBitsToUintIntCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1211 	FloatBitsToUintIntCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1212 		: CommonFunctionTestInstance	(context, shaderType, spec, executor, numValues, name)
1213 	{
1214 	}
1215 
getInputValues(int numValues,void * const * values) const1216 	void getInputValues (int numValues, void* const* values) const
1217 	{
1218 		const Vec2 ranges[] =
1219 		{
1220 			Vec2(-2.0f,		2.0f),	// lowp
1221 			Vec2(-1e3f,		1e3f),	// mediump
1222 			Vec2(-1e7f,		1e7f)	// highp
1223 		};
1224 
1225 		de::Random				rnd			(deStringHash(m_name) ^ 0x2790au);
1226 		const glu::DataType		type		= m_spec.inputs[0].varType.getBasicType();
1227 		const glu::Precision	precision	= m_spec.inputs[0].varType.getPrecision();
1228 		const int				scalarSize	= glu::getDataTypeScalarSize(type);
1229 
1230 		fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
1231 	}
1232 
compare(const void * const * inputs,const void * const * outputs)1233 	bool compare (const void* const* inputs, const void* const* outputs)
1234 	{
1235 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
1236 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
1237 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
1238 
1239 		const int				mantissaBits	= getMinMantissaBits(precision);
1240 		const int				maxUlpDiff		= getMaxUlpDiffFromBits(mantissaBits);
1241 
1242 		for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1243 		{
1244 			const float		in0			= ((const float*)inputs[0])[compNdx];
1245 			const deUint32	out0		= ((const deUint32*)outputs[0])[compNdx];
1246 			const deUint32	refOut0		= tcu::Float32(in0).bits();
1247 			const int		ulpDiff		= de::abs((int)out0 - (int)refOut0);
1248 
1249 			if (ulpDiff > maxUlpDiff)
1250 			{
1251 				m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(refOut0) << " with threshold "
1252 							<< tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
1253 				return false;
1254 			}
1255 		}
1256 
1257 		return true;
1258 	}
1259 };
1260 
1261 class FloatBitsToUintIntCase : public CommonFunctionCase
1262 {
1263 public:
FloatBitsToUintIntCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType,bool outIsSigned)1264 	FloatBitsToUintIntCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType, bool outIsSigned)
1265 		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), outIsSigned ? "floatBitsToInt" : "floatBitsToUint", shaderType)
1266 	{
1267 		const int			vecSize		= glu::getDataTypeScalarSize(baseType);
1268 		const glu::DataType	intType		= outIsSigned ? (vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT)
1269 													  : (vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT);
1270 
1271 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1272 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(intType, glu::PRECISION_HIGHP)));
1273 		m_spec.source = outIsSigned ? "out0 = floatBitsToInt(in0);" : "out0 = floatBitsToUint(in0);";
1274 		init();
1275 	}
1276 
createInstance(Context & ctx) const1277 	TestInstance* createInstance (Context& ctx) const
1278 	{
1279 		return new FloatBitsToUintIntCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
1280 	}
1281 };
1282 
1283 class FloatBitsToIntCaseInstance : public FloatBitsToUintIntCaseInstance
1284 {
1285 public:
FloatBitsToIntCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1286 	FloatBitsToIntCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1287 		: FloatBitsToUintIntCaseInstance	(context, shaderType, spec, executor, numValues, name)
1288 	{
1289 	}
1290 };
1291 
1292 class FloatBitsToIntCase : public FloatBitsToUintIntCase
1293 {
1294 public:
FloatBitsToIntCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1295 	FloatBitsToIntCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1296 		: FloatBitsToUintIntCase	(testCtx, baseType, precision, shaderType, true)
1297 	{
1298 	}
1299 
1300 };
1301 
1302 class FloatBitsToUintCaseInstance : public FloatBitsToUintIntCaseInstance
1303 {
1304 public:
FloatBitsToUintCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1305 	FloatBitsToUintCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1306 		: FloatBitsToUintIntCaseInstance	(context, shaderType, spec, executor, numValues, name)
1307 	{
1308 	}
1309 };
1310 
1311 class FloatBitsToUintCase : public FloatBitsToUintIntCase
1312 {
1313 public:
FloatBitsToUintCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1314 	FloatBitsToUintCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1315 		: FloatBitsToUintIntCase	(testCtx, baseType, precision, shaderType, false)
1316 	{
1317 	}
1318 };
1319 
1320 class BitsToFloatCaseInstance : public CommonFunctionTestInstance
1321 {
1322 public:
BitsToFloatCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1323 	BitsToFloatCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1324 		: CommonFunctionTestInstance	(context, shaderType, spec, executor, numValues, name)
1325 	{
1326 	}
1327 
getInputValues(int numValues,void * const * values) const1328 	void getInputValues (int numValues, void* const* values) const
1329 	{
1330 		de::Random				rnd			(deStringHash(m_name) ^ 0xbbb225u);
1331 		const glu::DataType		type		= m_spec.inputs[0].varType.getBasicType();
1332 		const int				scalarSize	= glu::getDataTypeScalarSize(type);
1333 		const Vec2				range		(-1e8f, +1e8f);
1334 
1335 		// \note Filled as floats.
1336 		fillRandomScalars(rnd, range.x(), range.y(), values[0], numValues*scalarSize);
1337 	}
1338 
compare(const void * const * inputs,const void * const * outputs)1339 	bool compare (const void* const* inputs, const void* const* outputs)
1340 	{
1341 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
1342 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
1343 		const deUint32			maxUlpDiff		= 0;
1344 
1345 		for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1346 		{
1347 			const float		in0			= ((const float*)inputs[0])[compNdx];
1348 			const float		out0		= ((const float*)outputs[0])[compNdx];
1349 			const deUint32	ulpDiff		= getUlpDiff(in0, out0);
1350 
1351 			if (ulpDiff > maxUlpDiff)
1352 			{
1353 				m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(tcu::Float32(in0).bits()) << " with ULP threshold "
1354 							<< tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
1355 				return false;
1356 			}
1357 		}
1358 
1359 		return true;
1360 	}
1361 };
1362 
1363 class BitsToFloatCase : public CommonFunctionCase
1364 {
1365 public:
BitsToFloatCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::ShaderType shaderType)1366 	BitsToFloatCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::ShaderType shaderType)
1367 		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, glu::PRECISION_HIGHP, shaderType).c_str(), glu::isDataTypeIntOrIVec(baseType) ? "intBitsToFloat" : "uintBitsToFloat", shaderType)
1368 	{
1369 		const bool			inIsSigned	= glu::isDataTypeIntOrIVec(baseType);
1370 		const int			vecSize		= glu::getDataTypeScalarSize(baseType);
1371 		const glu::DataType	floatType	= vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1372 
1373 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
1374 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(floatType, glu::PRECISION_HIGHP)));
1375 		m_spec.source = inIsSigned ? "out0 = intBitsToFloat(in0);" : "out0 = uintBitsToFloat(in0);";
1376 		init();
1377 	}
1378 
createInstance(Context & ctx) const1379 	TestInstance* createInstance (Context& ctx) const
1380 	{
1381 		return new BitsToFloatCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
1382 	}
1383 };
1384 
1385 class FloorCaseInstance : public CommonFunctionTestInstance
1386 {
1387 public:
FloorCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1388 	FloorCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1389 		: CommonFunctionTestInstance	(context, shaderType, spec, executor, numValues, name)
1390 	{
1391 	}
1392 
getInputValues(int numValues,void * const * values) const1393 	void getInputValues (int numValues, void* const* values) const
1394 	{
1395 		const Vec2 ranges[] =
1396 		{
1397 			Vec2(-2.0f,		2.0f),	// lowp
1398 			Vec2(-1e3f,		1e3f),	// mediump
1399 			Vec2(-1e7f,		1e7f)	// highp
1400 		};
1401 
1402 		de::Random				rnd			(deStringHash(m_name) ^ 0xac23fu);
1403 		const glu::DataType		type		= m_spec.inputs[0].varType.getBasicType();
1404 		const glu::Precision	precision	= m_spec.inputs[0].varType.getPrecision();
1405 		const int				scalarSize	= glu::getDataTypeScalarSize(type);
1406 		// Random cases.
1407 		fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
1408 
1409 		// If precision is mediump, make sure values can be represented in fp16 exactly
1410 		if (precision == glu::PRECISION_MEDIUMP)
1411 		{
1412 			for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1413 				((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1414 		}
1415 	}
1416 
compare(const void * const * inputs,const void * const * outputs)1417 	bool compare (const void* const* inputs, const void* const* outputs)
1418 	{
1419 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
1420 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
1421 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
1422 
1423 		if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1424 		{
1425 			// Require exact result.
1426 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1427 			{
1428 				const float		in0			= ((const float*)inputs[0])[compNdx];
1429 				const float		out0		= ((const float*)outputs[0])[compNdx];
1430 				const float		ref			= deFloatFloor(in0);
1431 
1432 				const deUint32	ulpDiff		= getUlpDiff(out0, ref);
1433 
1434 				if (ulpDiff > 0)
1435 				{
1436 					m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1437 					return false;
1438 				}
1439 			}
1440 		}
1441 		else
1442 		{
1443 			const int		mantissaBits	= getMinMantissaBits(precision);
1444 			const deUint32	maxUlpDiff		= getMaxUlpDiffFromBits(mantissaBits);	// ULP diff for rounded integer value.
1445 			const float		eps				= getEpsFromBits(1.0f, mantissaBits);	// epsilon for rounding bounds
1446 
1447 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1448 			{
1449 				const float		in0			= ((const float*)inputs[0])[compNdx];
1450 				const float		out0		= ((const float*)outputs[0])[compNdx];
1451 				const int		minRes		= int(deFloatFloor(in0-eps));
1452 				const int		maxRes		= int(deFloatFloor(in0+eps));
1453 				bool			anyOk		= false;
1454 
1455 				for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1456 				{
1457 					const deUint32 ulpDiff = getUlpDiff(out0, float(roundedVal));
1458 
1459 					if (ulpDiff <= maxUlpDiff)
1460 					{
1461 						anyOk = true;
1462 						break;
1463 					}
1464 				}
1465 
1466 				if (!anyOk)
1467 				{
1468 					m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1469 					return false;
1470 				}
1471 			}
1472 		}
1473 
1474 		return true;
1475 	}
1476 };
1477 
1478 class FloorCase : public CommonFunctionCase
1479 {
1480 public:
FloorCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1481 	FloorCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1482 		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "floor", shaderType)
1483 	{
1484 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1485 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1486 		m_spec.source = "out0 = floor(in0);";
1487 		init();
1488 	}
1489 
createInstance(Context & ctx) const1490 	TestInstance* createInstance (Context& ctx) const
1491 	{
1492 		return new FloorCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
1493 	}
1494 };
1495 
1496 class TruncCaseInstance : public CommonFunctionTestInstance
1497 {
1498 public:
TruncCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1499 	TruncCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1500 		: CommonFunctionTestInstance	(context, shaderType, spec, executor, numValues, name)
1501 	{
1502 	}
1503 
getInputValues(int numValues,void * const * values) const1504 	void getInputValues (int numValues, void* const* values) const
1505 	{
1506 		const Vec2 ranges[] =
1507 		{
1508 			Vec2(-2.0f,		2.0f),	// lowp
1509 			Vec2(-1e3f,		1e3f),	// mediump
1510 			Vec2(-1e7f,		1e7f)	// highp
1511 		};
1512 
1513 		de::Random				rnd				(deStringHash(m_name) ^ 0xac23fu);
1514 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
1515 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
1516 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
1517 		const float				specialCases[]	= { 0.0f, -0.0f, -0.9f, 0.9f, 1.0f, -1.0f };
1518 		const int				numSpecialCases	= DE_LENGTH_OF_ARRAY(specialCases);
1519 
1520 		// Special cases
1521 		for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
1522 		{
1523 			for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
1524 				((float*)values[0])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx];
1525 		}
1526 
1527 		// Random cases.
1528 		fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + scalarSize*numSpecialCases, (numValues-numSpecialCases)*scalarSize);
1529 
1530 		// If precision is mediump, make sure values can be represented in fp16 exactly
1531 		if (precision == glu::PRECISION_MEDIUMP)
1532 		{
1533 			for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1534 				((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1535 		}
1536 	}
1537 
compare(const void * const * inputs,const void * const * outputs)1538 	bool compare (const void* const* inputs, const void* const* outputs)
1539 	{
1540 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
1541 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
1542 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
1543 
1544 		if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1545 		{
1546 			// Require exact result.
1547 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1548 			{
1549 				const float		in0			= ((const float*)inputs[0])[compNdx];
1550 				const float		out0		= ((const float*)outputs[0])[compNdx];
1551 				const bool		isNeg		= tcu::Float32(in0).sign() < 0;
1552 				const float		ref			= isNeg ? (-float(int(-in0))) : float(int(in0));
1553 
1554 				// \note: trunc() function definition is a bit broad on negative zeros. Ignore result sign if zero.
1555 				const deUint32	ulpDiff		= getUlpDiffIgnoreZeroSign(out0, ref);
1556 
1557 				if (ulpDiff > 0)
1558 				{
1559 					m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1560 					return false;
1561 				}
1562 			}
1563 		}
1564 		else
1565 		{
1566 			const int		mantissaBits	= getMinMantissaBits(precision);
1567 			const deUint32	maxUlpDiff		= getMaxUlpDiffFromBits(mantissaBits);	// ULP diff for rounded integer value.
1568 			const float		eps				= getEpsFromBits(1.0f, mantissaBits);	// epsilon for rounding bounds
1569 
1570 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1571 			{
1572 				const float		in0			= ((const float*)inputs[0])[compNdx];
1573 				const float		out0		= ((const float*)outputs[0])[compNdx];
1574 				const int		minRes		= int(in0-eps);
1575 				const int		maxRes		= int(in0+eps);
1576 				bool			anyOk		= false;
1577 
1578 				for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1579 				{
1580 					const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1581 
1582 					if (ulpDiff <= maxUlpDiff)
1583 					{
1584 						anyOk = true;
1585 						break;
1586 					}
1587 				}
1588 
1589 				if (!anyOk)
1590 				{
1591 					m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1592 					return false;
1593 				}
1594 			}
1595 		}
1596 
1597 		return true;
1598 	}
1599 };
1600 
1601 class TruncCase : public CommonFunctionCase
1602 {
1603 public:
TruncCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1604 	TruncCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1605 		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "trunc", shaderType)
1606 	{
1607 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1608 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1609 		m_spec.source = "out0 = trunc(in0);";
1610 		init();
1611 	}
1612 
createInstance(Context & ctx) const1613 	TestInstance* createInstance (Context& ctx) const
1614 	{
1615 		return new TruncCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
1616 	}
1617 };
1618 
1619 class RoundCaseInstance : public CommonFunctionTestInstance
1620 {
1621 public:
RoundCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1622 	RoundCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1623 		: CommonFunctionTestInstance	(context, shaderType, spec, executor, numValues, name)
1624 	{
1625 	}
1626 
getInputValues(int numValues,void * const * values) const1627 	void getInputValues (int numValues, void* const* values) const
1628 	{
1629 		const Vec2 ranges[] =
1630 		{
1631 			Vec2(-2.0f,		2.0f),	// lowp
1632 			Vec2(-1e3f,		1e3f),	// mediump
1633 			Vec2(-1e7f,		1e7f)	// highp
1634 		};
1635 
1636 		de::Random				rnd				(deStringHash(m_name) ^ 0xac23fu);
1637 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
1638 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
1639 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
1640 		int						numSpecialCases	= 0;
1641 
1642 		// Special cases.
1643 		if (precision != glu::PRECISION_LOWP)
1644 		{
1645 			DE_ASSERT(numValues >= 10);
1646 			for (int ndx = 0; ndx < 10; ndx++)
1647 			{
1648 				const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1649 				std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
1650 				numSpecialCases += 1;
1651 			}
1652 		}
1653 
1654 		// Random cases.
1655 		fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
1656 
1657 		// If precision is mediump, make sure values can be represented in fp16 exactly
1658 		if (precision == glu::PRECISION_MEDIUMP)
1659 		{
1660 			for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1661 				((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1662 		}
1663 	}
1664 
compare(const void * const * inputs,const void * const * outputs)1665 	bool compare (const void* const* inputs, const void* const* outputs)
1666 	{
1667 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
1668 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
1669 		const bool				hasZeroSign		= supportsSignedZero(precision);
1670 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
1671 
1672 		if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1673 		{
1674 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1675 			{
1676 				const float		in0			= ((const float*)inputs[0])[compNdx];
1677 				const float		out0		= ((const float*)outputs[0])[compNdx];
1678 
1679 				if (deFloatFrac(in0) == 0.5f)
1680 				{
1681 					// Allow both ceil(in) and floor(in)
1682 					const float		ref0		= deFloatFloor(in0);
1683 					const float		ref1		= deFloatCeil(in0);
1684 					const deUint32	ulpDiff0	= hasZeroSign ? getUlpDiff(out0, ref0) : getUlpDiffIgnoreZeroSign(out0, ref0);
1685 					const deUint32	ulpDiff1	= hasZeroSign ? getUlpDiff(out0, ref1) : getUlpDiffIgnoreZeroSign(out0, ref1);
1686 
1687 					if (ulpDiff0 > 0 && ulpDiff1 > 0)
1688 					{
1689 						m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " or " << HexFloat(ref1) << ", got ULP diff " << tcu::toHex(de::min(ulpDiff0, ulpDiff1));
1690 						return false;
1691 					}
1692 				}
1693 				else
1694 				{
1695 					// Require exact result
1696 					const float		ref		= roundEven(in0);
1697 					const deUint32	ulpDiff	= hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1698 
1699 					if (ulpDiff > 0)
1700 					{
1701 						m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1702 						return false;
1703 					}
1704 				}
1705 			}
1706 		}
1707 		else
1708 		{
1709 			const int		mantissaBits	= getMinMantissaBits(precision);
1710 			const deUint32	maxUlpDiff		= getMaxUlpDiffFromBits(mantissaBits);	// ULP diff for rounded integer value.
1711 			const float		eps				= getEpsFromBits(1.0f, mantissaBits);	// epsilon for rounding bounds
1712 
1713 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1714 			{
1715 				const float		in0			= ((const float*)inputs[0])[compNdx];
1716 				const float		out0		= ((const float*)outputs[0])[compNdx];
1717 				const int		minRes		= int(roundEven(in0-eps));
1718 				const int		maxRes		= int(roundEven(in0+eps));
1719 				bool			anyOk		= false;
1720 
1721 				for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1722 				{
1723 					const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1724 
1725 					if (ulpDiff <= maxUlpDiff)
1726 					{
1727 						anyOk = true;
1728 						break;
1729 					}
1730 				}
1731 
1732 				if (!anyOk)
1733 				{
1734 					m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1735 					return false;
1736 				}
1737 			}
1738 		}
1739 
1740 		return true;
1741 	}
1742 };
1743 
1744 class RoundCase : public CommonFunctionCase
1745 {
1746 public:
RoundCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1747 	RoundCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1748 		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "round", shaderType)
1749 	{
1750 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1751 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1752 		m_spec.source = "out0 = round(in0);";
1753 		init();
1754 	}
1755 
createInstance(Context & ctx) const1756 	TestInstance* createInstance (Context& ctx) const
1757 	{
1758 		return new RoundCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
1759 	}
1760 };
1761 
1762 class CeilCaseInstance : public CommonFunctionTestInstance
1763 {
1764 public:
CeilCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1765 	CeilCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1766 		: CommonFunctionTestInstance	(context, shaderType, spec, executor, numValues, name)
1767 	{
1768 	}
1769 
getInputValues(int numValues,void * const * values) const1770 	void getInputValues (int numValues, void* const* values) const
1771 	{
1772 		const Vec2 ranges[] =
1773 		{
1774 			Vec2(-2.0f,		2.0f),	// lowp
1775 			Vec2(-1e3f,		1e3f),	// mediump
1776 			Vec2(-1e7f,		1e7f)	// highp
1777 		};
1778 
1779 		de::Random				rnd			(deStringHash(m_name) ^ 0xac23fu);
1780 		const glu::DataType		type		= m_spec.inputs[0].varType.getBasicType();
1781 		const glu::Precision	precision	= m_spec.inputs[0].varType.getPrecision();
1782 		const int				scalarSize	= glu::getDataTypeScalarSize(type);
1783 
1784 		// Random cases.
1785 		fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
1786 
1787 		// If precision is mediump, make sure values can be represented in fp16 exactly
1788 		if (precision == glu::PRECISION_MEDIUMP)
1789 		{
1790 			for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1791 				((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1792 		}
1793 	}
1794 
compare(const void * const * inputs,const void * const * outputs)1795 	bool compare (const void* const* inputs, const void* const* outputs)
1796 	{
1797 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
1798 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
1799 		const bool				hasZeroSign		= supportsSignedZero(precision);
1800 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
1801 
1802 		if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1803 		{
1804 			// Require exact result.
1805 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1806 			{
1807 				const float		in0			= ((const float*)inputs[0])[compNdx];
1808 				const float		out0		= ((const float*)outputs[0])[compNdx];
1809 				const float		ref			= deFloatCeil(in0);
1810 
1811 				const deUint32	ulpDiff		= hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1812 
1813 				if (ulpDiff > 0)
1814 				{
1815 					m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1816 					return false;
1817 				}
1818 			}
1819 		}
1820 		else
1821 		{
1822 			const int		mantissaBits	= getMinMantissaBits(precision);
1823 			const deUint32	maxUlpDiff		= getMaxUlpDiffFromBits(mantissaBits);	// ULP diff for rounded integer value.
1824 			const float		eps				= getEpsFromBits(1.0f, mantissaBits);	// epsilon for rounding bounds
1825 
1826 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1827 			{
1828 				const float		in0			= ((const float*)inputs[0])[compNdx];
1829 				const float		out0		= ((const float*)outputs[0])[compNdx];
1830 				const int		minRes		= int(deFloatCeil(in0-eps));
1831 				const int		maxRes		= int(deFloatCeil(in0+eps));
1832 				bool			anyOk		= false;
1833 
1834 				for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1835 				{
1836 					const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1837 
1838 					if (ulpDiff <= maxUlpDiff)
1839 					{
1840 						anyOk = true;
1841 						break;
1842 					}
1843 				}
1844 
1845 				if (!anyOk && de::inRange(0, minRes, maxRes))
1846 				{
1847 					// Allow -0 as well.
1848 					const int ulpDiff = de::abs((int)tcu::Float32(out0).bits() - (int)0x80000000u);
1849 					anyOk = ((deUint32)ulpDiff <= maxUlpDiff);
1850 				}
1851 
1852 				if (!anyOk)
1853 				{
1854 					m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1855 					return false;
1856 				}
1857 			}
1858 		}
1859 
1860 		return true;
1861 	}
1862 };
1863 
1864 class CeilCase : public CommonFunctionCase
1865 {
1866 public:
CeilCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1867 	CeilCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1868 		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ceil", shaderType)
1869 	{
1870 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1871 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1872 		m_spec.source = "out0 = ceil(in0);";
1873 		init();
1874 	}
1875 
createInstance(Context & ctx) const1876 	TestInstance* createInstance (Context& ctx) const
1877 	{
1878 		return new CeilCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
1879 	}
1880 };
1881 
1882 class FractCaseInstance : public CommonFunctionTestInstance
1883 {
1884 public:
FractCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1885 	FractCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1886 		: CommonFunctionTestInstance	(context, shaderType, spec, executor, numValues, name)
1887 	{
1888 	}
1889 
getInputValues(int numValues,void * const * values) const1890 	void getInputValues (int numValues, void* const* values) const
1891 	{
1892 		const Vec2 ranges[] =
1893 		{
1894 			Vec2(-2.0f,		2.0f),	// lowp
1895 			Vec2(-1e3f,		1e3f),	// mediump
1896 			Vec2(-1e7f,		1e7f)	// highp
1897 		};
1898 
1899 		de::Random				rnd				(deStringHash(m_name) ^ 0xac23fu);
1900 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
1901 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
1902 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
1903 		int						numSpecialCases	= 0;
1904 
1905 		// Special cases.
1906 		if (precision != glu::PRECISION_LOWP)
1907 		{
1908 			DE_ASSERT(numValues >= 10);
1909 			for (int ndx = 0; ndx < 10; ndx++)
1910 			{
1911 				const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1912 				std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
1913 				numSpecialCases += 1;
1914 			}
1915 		}
1916 
1917 		// Random cases.
1918 		fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
1919 
1920 		// If precision is mediump, make sure values can be represented in fp16 exactly
1921 		if (precision == glu::PRECISION_MEDIUMP)
1922 		{
1923 			for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1924 				((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1925 		}
1926 	}
1927 
compare(const void * const * inputs,const void * const * outputs)1928 	bool compare (const void* const* inputs, const void* const* outputs)
1929 	{
1930 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
1931 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
1932 		const bool				hasZeroSign		= supportsSignedZero(precision);
1933 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
1934 
1935 		if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1936 		{
1937 			// Require exact result.
1938 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1939 			{
1940 				const float		in0			= ((const float*)inputs[0])[compNdx];
1941 				const float		out0		= ((const float*)outputs[0])[compNdx];
1942 				const float		ref			= deFloatFrac(in0);
1943 
1944 				const deUint32	ulpDiff		= hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1945 
1946 				if (ulpDiff > 0)
1947 				{
1948 					m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1949 					return false;
1950 				}
1951 			}
1952 		}
1953 		else
1954 		{
1955 			const int		mantissaBits	= getMinMantissaBits(precision);
1956 			const float		eps				= getEpsFromBits(1.0f, mantissaBits);	// epsilon for rounding bounds
1957 
1958 			for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1959 			{
1960 				const float		in0			= ((const float*)inputs[0])[compNdx];
1961 				const float		out0		= ((const float*)outputs[0])[compNdx];
1962 
1963 				if (int(deFloatFloor(in0-eps)) == int(deFloatFloor(in0+eps)))
1964 				{
1965 					const float		ref			= deFloatFrac(in0);
1966 					const int		bitsLost	= numBitsLostInOp(in0, ref);
1967 					const deUint32	maxUlpDiff	= getMaxUlpDiffFromBits(de::max(0, mantissaBits-bitsLost));	// ULP diff for rounded integer value.
1968 					const deUint32	ulpDiff		= getUlpDiffIgnoreZeroSign(out0, ref);
1969 
1970 					if (ulpDiff > maxUlpDiff)
1971 					{
1972 						m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << " with ULP threshold " << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
1973 						return false;
1974 					}
1975 				}
1976 				else
1977 				{
1978 					if (out0 >= 1.0f)
1979 					{
1980 						m_failMsg << "Expected [" << compNdx << "] < 1.0";
1981 						return false;
1982 					}
1983 				}
1984 			}
1985 		}
1986 
1987 		return true;
1988 	}
1989 };
1990 
1991 class FractCase : public CommonFunctionCase
1992 {
1993 public:
FractCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1994 	FractCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1995 		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fract", shaderType)
1996 	{
1997 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1998 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1999 		m_spec.source = "out0 = fract(in0);";
2000 		init();
2001 	}
2002 
createInstance(Context & ctx) const2003 	TestInstance* createInstance (Context& ctx) const
2004 	{
2005 		return new FractCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
2006 	}
2007 };
2008 
2009 class FrexpCaseInstance : public CommonFunctionTestInstance
2010 {
2011 public:
FrexpCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)2012 	FrexpCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
2013 		: CommonFunctionTestInstance	(context, shaderType, spec, executor, numValues, name)
2014 	{
2015 	}
2016 
getInputValues(int numValues,void * const * values) const2017 	void getInputValues (int numValues, void* const* values) const
2018 	{
2019 		const Vec2 ranges[] =
2020 		{
2021 			Vec2(-2.0f,		2.0f),	// lowp
2022 			Vec2(-1e3f,		1e3f),	// mediump
2023 			Vec2(-1e7f,		1e7f)	// highp
2024 		};
2025 
2026 		de::Random				rnd			(deStringHash(m_name) ^ 0x2790au);
2027 		const glu::DataType		type		= m_spec.inputs[0].varType.getBasicType();
2028 		const glu::Precision	precision	= m_spec.inputs[0].varType.getPrecision();
2029 		const int				scalarSize	= glu::getDataTypeScalarSize(type);
2030 
2031 		// Special cases
2032 		for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2033 		{
2034 			((float*)values[0])[scalarSize*0 + compNdx] = 0.0f;
2035 			((float*)values[0])[scalarSize*1 + compNdx] = -0.0f;
2036 			((float*)values[0])[scalarSize*2 + compNdx] = 0.5f;
2037 			((float*)values[0])[scalarSize*3 + compNdx] = -0.5f;
2038 			((float*)values[0])[scalarSize*4 + compNdx] = 1.0f;
2039 			((float*)values[0])[scalarSize*5 + compNdx] = -1.0f;
2040 			((float*)values[0])[scalarSize*6 + compNdx] = 2.0f;
2041 			((float*)values[0])[scalarSize*7 + compNdx] = -2.0f;
2042 		}
2043 
2044 		fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + 8*scalarSize, (numValues-8)*scalarSize);
2045 
2046 		// Make sure the values are representable in the target format
2047 		for (int caseNdx = 0; caseNdx < numValues; ++caseNdx)
2048 		{
2049 			for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
2050 			{
2051 				float* const valuePtr = &((float*)values[0])[caseNdx * scalarSize + scalarNdx];
2052 
2053 				*valuePtr = makeFloatRepresentable(*valuePtr, precision);
2054 			}
2055 		}
2056 	}
2057 
compare(const void * const * inputs,const void * const * outputs)2058 	bool compare (const void* const* inputs, const void* const* outputs)
2059 	{
2060 		const glu::DataType		type						= m_spec.inputs[0].varType.getBasicType();
2061 		const glu::Precision	precision					= m_spec.inputs[0].varType.getPrecision();
2062 		const int				scalarSize					= glu::getDataTypeScalarSize(type);
2063 		const bool				transitSupportsSignedZero	= (m_shaderType != glu::SHADERTYPE_FRAGMENT); // executor cannot reliably transit negative zero to fragment stage
2064 		const bool				signedZero					= supportsSignedZero(precision) && transitSupportsSignedZero;
2065 
2066 		const int				mantissaBits				= getMinMantissaBits(precision);
2067 		const deUint32			maxUlpDiff					= getMaxUlpDiffFromBits(mantissaBits);
2068 
2069 		for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2070 		{
2071 			const float		in0			= ((const float*)inputs[0])[compNdx];
2072 			const float		out0		= ((const float*)outputs[0])[compNdx];
2073 			const int		out1		= ((const int*)outputs[1])[compNdx];
2074 
2075 			float			refOut0;
2076 			int				refOut1;
2077 
2078 			frexp(in0, &refOut0, &refOut1);
2079 
2080 			const deUint32	ulpDiff0	= signedZero ? getUlpDiff(out0, refOut0) : getUlpDiffIgnoreZeroSign(out0, refOut0);
2081 
2082 			if (ulpDiff0 > maxUlpDiff || out1 != refOut1)
2083 			{
2084 				m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(refOut0) << ", " << refOut1 << " with ULP threshold "
2085 						  << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff0);
2086 				return false;
2087 			}
2088 		}
2089 
2090 		return true;
2091 	}
2092 };
2093 
2094 class FrexpCase : public CommonFunctionCase
2095 {
2096 public:
FrexpCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)2097 	FrexpCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
2098 		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "frexp", shaderType)
2099 	{
2100 		const int			vecSize		= glu::getDataTypeScalarSize(baseType);
2101 		const glu::DataType	intType		= vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
2102 
2103 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
2104 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
2105 		m_spec.outputs.push_back(Symbol("out1", glu::VarType(intType, glu::PRECISION_HIGHP)));
2106 		m_spec.source = "out0 = frexp(in0, out1);";
2107 		init();
2108 	}
2109 
createInstance(Context & ctx) const2110 	TestInstance* createInstance (Context& ctx) const
2111 	{
2112 		return new FrexpCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
2113 	}
2114 };
2115 
2116 class LdexpCaseInstance : public CommonFunctionTestInstance
2117 {
2118 public:
LdexpCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)2119 	LdexpCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
2120 		: CommonFunctionTestInstance	(context, shaderType, spec, executor, numValues, name)
2121 	{
2122 	}
2123 
getInputValues(int numValues,void * const * values) const2124 	void getInputValues (int numValues, void* const* values) const
2125 	{
2126 		const Vec2 ranges[] =
2127 		{
2128 			Vec2(-2.0f,		2.0f),	// lowp
2129 			Vec2(-1e3f,		1e3f),	// mediump
2130 			Vec2(-1e7f,		1e7f)	// highp
2131 		};
2132 
2133 		de::Random				rnd					(deStringHash(m_name) ^ 0x2790au);
2134 		const glu::DataType		type				= m_spec.inputs[0].varType.getBasicType();
2135 		const glu::Precision	precision			= m_spec.inputs[0].varType.getPrecision();
2136 		const int				scalarSize			= glu::getDataTypeScalarSize(type);
2137 		int						valueNdx			= 0;
2138 
2139 		{
2140 			const float easySpecialCases[] = { 0.0f, -0.0f, 0.5f, -0.5f, 1.0f, -1.0f, 2.0f, -2.0f };
2141 
2142 			DE_ASSERT(valueNdx + DE_LENGTH_OF_ARRAY(easySpecialCases) <= numValues);
2143 			for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(easySpecialCases); caseNdx++)
2144 			{
2145 				float	in0;
2146 				int		in1;
2147 
2148 				frexp(easySpecialCases[caseNdx], &in0, &in1);
2149 
2150 				for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2151 				{
2152 					((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
2153 					((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
2154 				}
2155 
2156 				valueNdx += 1;
2157 			}
2158 		}
2159 
2160 		{
2161 			// \note lowp and mediump can not necessarily fit the values in hard cases, so we'll use only easy ones.
2162 			const int numEasyRandomCases = precision == glu::PRECISION_HIGHP ? 50 : (numValues-valueNdx);
2163 
2164 			DE_ASSERT(valueNdx + numEasyRandomCases <= numValues);
2165 			for (int caseNdx = 0; caseNdx < numEasyRandomCases; caseNdx++)
2166 			{
2167 				for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2168 				{
2169 					const float	in	= rnd.getFloat(ranges[precision].x(), ranges[precision].y());
2170 					float		in0;
2171 					int			in1;
2172 
2173 					frexp(in, &in0, &in1);
2174 
2175 					((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
2176 					((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
2177 				}
2178 
2179 				valueNdx += 1;
2180 			}
2181 		}
2182 
2183 		{
2184 			const int numHardRandomCases = numValues-valueNdx;
2185 			DE_ASSERT(numHardRandomCases >= 0 && valueNdx + numHardRandomCases <= numValues);
2186 
2187 			for (int caseNdx = 0; caseNdx < numHardRandomCases; caseNdx++)
2188 			{
2189 				for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2190 				{
2191 					const int		fpExp		= rnd.getInt(-126, 127);
2192 					const int		sign		= rnd.getBool() ? -1 : +1;
2193 					const deUint32	mantissa	= (1u<<23) | (rnd.getUint32() & ((1u<<23)-1));
2194 					const int		in1			= rnd.getInt(de::max(-126, -126-fpExp), de::min(127, 127-fpExp));
2195 					const float		in0			= tcu::Float32::construct(sign, fpExp, mantissa).asFloat();
2196 
2197 					DE_ASSERT(de::inRange(in1, -126, 127)); // See Khronos bug 11180
2198 					DE_ASSERT(de::inRange(in1+fpExp, -126, 127));
2199 
2200 					const float		out			= ldexp(in0, in1);
2201 
2202 					DE_ASSERT(!tcu::Float32(out).isInf() && !tcu::Float32(out).isDenorm());
2203 					DE_UNREF(out);
2204 
2205 					((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
2206 					((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
2207 				}
2208 
2209 				valueNdx += 1;
2210 			}
2211 		}
2212 	}
2213 
compare(const void * const * inputs,const void * const * outputs)2214 	bool compare (const void* const* inputs, const void* const* outputs)
2215 	{
2216 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
2217 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
2218 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
2219 
2220 		const int				mantissaBits	= getMinMantissaBits(precision);
2221 		const deUint32			maxUlpDiff		= getMaxUlpDiffFromBits(mantissaBits);
2222 
2223 		for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2224 		{
2225 			const float		in0			= ((const float*)inputs[0])[compNdx];
2226 			const int		in1			= ((const int*)inputs[1])[compNdx];
2227 			const float		out0		= ((const float*)outputs[0])[compNdx];
2228 			const float		refOut0		= ldexp(in0, in1);
2229 			const deUint32	ulpDiff		= getUlpDiffIgnoreZeroSign(out0, refOut0);
2230 
2231 			const int		inExp		= tcu::Float32(in0).exponent();
2232 
2233 			if (ulpDiff > maxUlpDiff)
2234 			{
2235 				m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(refOut0) << ", (exp = " << inExp << ") with ULP threshold "
2236 						  << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
2237 				return false;
2238 			}
2239 		}
2240 
2241 		return true;
2242 	}
2243 };
2244 
2245 class LdexpCase : public CommonFunctionCase
2246 {
2247 public:
LdexpCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)2248 	LdexpCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
2249 		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ldexp", shaderType)
2250 	{
2251 		const int			vecSize		= glu::getDataTypeScalarSize(baseType);
2252 		const glu::DataType	intType		= vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
2253 
2254 		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
2255 		m_spec.inputs.push_back(Symbol("in1", glu::VarType(intType, glu::PRECISION_HIGHP)));
2256 		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
2257 		m_spec.source = "out0 = ldexp(in0, in1);";
2258 		init();
2259 	}
2260 
createInstance(Context & ctx) const2261 	TestInstance* createInstance (Context& ctx) const
2262 	{
2263 		return new LdexpCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
2264 	}
2265 };
2266 
2267 class FmaCaseInstance : public CommonFunctionTestInstance
2268 {
2269 public:
FmaCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)2270 	FmaCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
2271 		: CommonFunctionTestInstance	(context, shaderType, spec, executor, numValues, name)
2272 	{
2273 	}
2274 
getInputValues(int numValues,void * const * values) const2275 	void getInputValues (int numValues, void* const* values) const
2276 	{
2277 		const Vec2 ranges[] =
2278 		{
2279 			Vec2(-2.0f,		2.0f),	// lowp
2280 			Vec2(-127.f,	127.f),	// mediump
2281 			Vec2(-1e7f,		1e7f)	// highp
2282 		};
2283 
2284 		de::Random				rnd							(deStringHash(m_name) ^ 0xac23fu);
2285 		const glu::DataType		type						= m_spec.inputs[0].varType.getBasicType();
2286 		const glu::Precision	precision					= m_spec.inputs[0].varType.getPrecision();
2287 		const int				scalarSize					= glu::getDataTypeScalarSize(type);
2288 		const float				specialCases[][3]			=
2289 		{
2290 			// a		b		c
2291 			{ 0.0f,		0.0f,	0.0f },
2292 			{ 0.0f,		1.0f,	0.0f },
2293 			{ 0.0f,		0.0f,	-1.0f },
2294 			{ 1.0f,		1.0f,	0.0f },
2295 			{ 1.0f,		1.0f,	1.0f },
2296 			{ -1.0f,	1.0f,	0.0f },
2297 			{ 1.0f,		-1.0f,	0.0f },
2298 			{ -1.0f,	-1.0f,	0.0f },
2299 			{ -0.0f,	1.0f,	0.0f },
2300 			{ 1.0f,		-0.0f,	0.0f }
2301 		};
2302 		const int				numSpecialCases				= DE_LENGTH_OF_ARRAY(specialCases);
2303 
2304 		// Special cases
2305 		for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
2306 		{
2307 			for (int inputNdx = 0; inputNdx < 3; inputNdx++)
2308 			{
2309 				for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
2310 					((float*)values[inputNdx])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx][inputNdx];
2311 			}
2312 		}
2313 
2314 		// Random cases.
2315 		{
2316 			const int	numScalars	= (numValues-numSpecialCases)*scalarSize;
2317 			const int	offs		= scalarSize*numSpecialCases;
2318 
2319 			for (int inputNdx = 0; inputNdx < 3; inputNdx++)
2320 				fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[inputNdx] + offs, numScalars);
2321 		}
2322 
2323 		// Make sure the values are representable in the target format
2324 		for (int inputNdx = 0; inputNdx < 3; inputNdx++)
2325 		{
2326 			for (int caseNdx = 0; caseNdx < numValues; ++caseNdx)
2327 			{
2328 				for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
2329 				{
2330 					float* const valuePtr = &((float*)values[inputNdx])[caseNdx * scalarSize + scalarNdx];
2331 
2332 					*valuePtr = makeFloatRepresentable(*valuePtr, precision);
2333 				}
2334 			}
2335 		}
2336 	}
2337 
fma(glu::Precision precision,float a,float b,float c)2338 	static tcu::Interval fma (glu::Precision precision, float a, float b, float c)
2339 	{
2340 		const tcu::FloatFormat formats[] =
2341 		{
2342 			//				 minExp		maxExp		mantissa	exact,		subnormals	infinities	NaN
2343 			tcu::FloatFormat(0,			0,			7,			false,		tcu::YES,	tcu::MAYBE,	tcu::MAYBE),
2344 			tcu::FloatFormat(-13,		13,			9,			false,		tcu::MAYBE,	tcu::MAYBE,	tcu::MAYBE),
2345 			tcu::FloatFormat(-126,		127,		23,			true,		tcu::MAYBE, tcu::YES,	tcu::MAYBE)
2346 		};
2347 		const tcu::FloatFormat&	format	= de::getSizedArrayElement<glu::PRECISION_LAST>(formats, precision);
2348 		const tcu::Interval		ia		= format.convert(a);
2349 		const tcu::Interval		ib		= format.convert(b);
2350 		const tcu::Interval		ic		= format.convert(c);
2351 		tcu::Interval			prod0;
2352 		tcu::Interval			prod1;
2353 		tcu::Interval			prod2;
2354 		tcu::Interval			prod3;
2355 		tcu::Interval			prod;
2356 		tcu::Interval			res;
2357 
2358 		TCU_SET_INTERVAL(prod0, tmp, tmp = ia.lo() * ib.lo());
2359 		TCU_SET_INTERVAL(prod1, tmp, tmp = ia.lo() * ib.hi());
2360 		TCU_SET_INTERVAL(prod2, tmp, tmp = ia.hi() * ib.lo());
2361 		TCU_SET_INTERVAL(prod3, tmp, tmp = ia.hi() * ib.hi());
2362 
2363 		prod = format.convert(format.roundOut(prod0 | prod1 | prod2 | prod3, ia.isFinite() && ib.isFinite()));
2364 
2365 		TCU_SET_INTERVAL_BOUNDS(res, tmp,
2366 								tmp = prod.lo() + ic.lo(),
2367 								tmp = prod.hi() + ic.hi());
2368 
2369 		return format.convert(format.roundOut(res, prod.isFinite() && ic.isFinite()));
2370 	}
2371 
compare(const void * const * inputs,const void * const * outputs)2372 	bool compare (const void* const* inputs, const void* const* outputs)
2373 	{
2374 		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
2375 		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
2376 		const int				scalarSize		= glu::getDataTypeScalarSize(type);
2377 
2378 		for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2379 		{
2380 			const float			a			= ((const float*)inputs[0])[compNdx];
2381 			const float			b			= ((const float*)inputs[1])[compNdx];
2382 			const float			c			= ((const float*)inputs[2])[compNdx];
2383 			const float			res			= ((const float*)outputs[0])[compNdx];
2384 			const tcu::Interval	ref			= fma(precision, a, b, c);
2385 
2386 			if (!ref.contains(res))
2387 			{
2388 				m_failMsg << "Expected [" << compNdx << "] = " << ref;
2389 				return false;
2390 			}
2391 		}
2392 
2393 		return true;
2394 	}
2395 };
2396 
2397 class FmaCase : public CommonFunctionCase
2398 {
2399 public:
init(void)2400 	void init (void)
2401 	{
2402 		CommonFunctionCase::init();
2403 	}
2404 
FmaCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)2405 	FmaCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
2406 		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fma", shaderType)
2407 	{
2408 		m_spec.inputs.push_back(Symbol("a", glu::VarType(baseType, precision)));
2409 		m_spec.inputs.push_back(Symbol("b", glu::VarType(baseType, precision)));
2410 		m_spec.inputs.push_back(Symbol("c", glu::VarType(baseType, precision)));
2411 		m_spec.outputs.push_back(Symbol("res", glu::VarType(baseType, precision)));
2412 		m_spec.source = "res = fma(a, b, c);";
2413 		m_spec.globalDeclarations = "#extension GL_EXT_gpu_shader5 : require\n";
2414 		init();
2415 	}
2416 
createInstance(Context & ctx) const2417 	TestInstance* createInstance (Context& ctx) const
2418 	{
2419 		return new FmaCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
2420 	}
2421 };
2422 
2423 } // anonymous
2424 
ShaderCommonFunctionTests(tcu::TestContext & testCtx)2425 ShaderCommonFunctionTests::ShaderCommonFunctionTests (tcu::TestContext& testCtx)
2426 	: tcu::TestCaseGroup	(testCtx, "common", "Common function tests")
2427 {
2428 }
2429 
~ShaderCommonFunctionTests(void)2430 ShaderCommonFunctionTests::~ShaderCommonFunctionTests (void)
2431 {
2432 }
2433 
init(void)2434 void ShaderCommonFunctionTests::init (void)
2435 {
2436 	enum
2437 	{
2438 		VS = (1<<glu::SHADERTYPE_VERTEX),
2439 		TC = (1<<glu::SHADERTYPE_TESSELLATION_CONTROL),
2440 		TE = (1<<glu::SHADERTYPE_TESSELLATION_EVALUATION),
2441 		GS = (1<<glu::SHADERTYPE_GEOMETRY),
2442 		FS = (1<<glu::SHADERTYPE_FRAGMENT),
2443 		CS = (1<<glu::SHADERTYPE_COMPUTE),
2444 
2445 		ALL_SHADERS = VS|TC|TE|GS|FS|CS,
2446 		NEW_SHADERS = TC|TE|GS|CS,
2447 	};
2448 
2449 	//																	Float?	Int?	Uint?	Shaders
2450 	addFunctionCases<AbsCase>				(this,	"abs",				true,	true,	false,	ALL_SHADERS);
2451 	addFunctionCases<SignCase>				(this,	"sign",				true,	true,	false,	ALL_SHADERS);
2452 	addFunctionCases<FloorCase>				(this,	"floor",			true,	false,	false,	ALL_SHADERS);
2453 	addFunctionCases<TruncCase>				(this,	"trunc",			true,	false,	false,	ALL_SHADERS);
2454 	addFunctionCases<RoundCase>				(this,	"round",			true,	false,	false,	ALL_SHADERS);
2455 	addFunctionCases<RoundEvenCase>			(this,	"roundeven",		true,	false,	false,	ALL_SHADERS);
2456 	addFunctionCases<CeilCase>				(this,	"ceil",				true,	false,	false,	ALL_SHADERS);
2457 	addFunctionCases<FractCase>				(this,	"fract",			true,	false,	false,	ALL_SHADERS);
2458 	// mod
2459 	addFunctionCases<ModfCase>				(this,	"modf",				true,	false,	false,	ALL_SHADERS);
2460 	// min
2461 	// max
2462 	// clamp
2463 	// mix
2464 	// step
2465 	// smoothstep
2466 	addFunctionCases<IsnanCase>				(this,	"isnan",			true,	false,	false,	ALL_SHADERS);
2467 	addFunctionCases<IsinfCase>				(this,	"isinf",			true,	false,	false,	ALL_SHADERS);
2468 	addFunctionCases<FloatBitsToIntCase>	(this,	"floatbitstoint",	true,	false,	false,	ALL_SHADERS);
2469 	addFunctionCases<FloatBitsToUintCase>	(this,	"floatbitstouint",	true,	false,	false,	ALL_SHADERS);
2470 
2471 	addFunctionCases<FrexpCase>				(this,	"frexp",			true,	false,	false,	ALL_SHADERS);
2472 	addFunctionCases<LdexpCase>				(this,	"ldexp",			true,	false,	false,	ALL_SHADERS);
2473 	addFunctionCases<FmaCase>				(this,	"fma",				true,	false,	false,	ALL_SHADERS);
2474 
2475 	// (u)intBitsToFloat()
2476 	{
2477 		const deUint32		shaderBits	= NEW_SHADERS;
2478 		tcu::TestCaseGroup* intGroup	= new tcu::TestCaseGroup(m_testCtx, "intbitstofloat",	"intBitsToFloat() Tests");
2479 		tcu::TestCaseGroup* uintGroup	= new tcu::TestCaseGroup(m_testCtx, "uintbitstofloat",	"uintBitsToFloat() Tests");
2480 
2481 		addChild(intGroup);
2482 		addChild(uintGroup);
2483 
2484 		for (int vecSize = 1; vecSize < 4; vecSize++)
2485 		{
2486 			const glu::DataType		intType		= vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
2487 			const glu::DataType		uintType	= vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT;
2488 
2489 			for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
2490 			{
2491 				if (shaderBits & (1<<shaderType))
2492 				{
2493 					intGroup->addChild(new BitsToFloatCase(getTestContext(), intType, glu::ShaderType(shaderType)));
2494 					uintGroup->addChild(new BitsToFloatCase(getTestContext(), uintType, glu::ShaderType(shaderType)));
2495 				}
2496 			}
2497 		}
2498 	}
2499 }
2500 
2501 } // shaderexecutor
2502 } // vkt
2503