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