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