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