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