1/*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2015 The Khronos Group Inc.
6 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
7 * Copyright (c) 2016 The Android Open Source Project
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 *
21 *//*!
22 * \file
23 * \brief Common built-in function tests.
24 *//*--------------------------------------------------------------------*/
25
26#include "vktShaderCommonFunctionTests.hpp"
27#include "vktShaderExecutor.hpp"
28#include "vkQueryUtil.hpp"
29#include "gluContextInfo.hpp"
30#include "tcuTestLog.hpp"
31#include "tcuFormatUtil.hpp"
32#include "tcuFloat.hpp"
33#include "tcuInterval.hpp"
34#include "tcuFloatFormat.hpp"
35#include "tcuVectorUtil.hpp"
36#include "deRandom.hpp"
37#include "deMath.h"
38#include "deString.h"
39#include "deArrayUtil.hpp"
40#include "deSharedPtr.hpp"
41#include <algorithm>
42
43namespace vkt
44{
45
46namespace shaderexecutor
47{
48
49
50using std::vector;
51using std::string;
52using tcu::TestLog;
53
54using tcu::Vec2;
55using tcu::Vec3;
56using tcu::Vec4;
57using tcu::IVec2;
58using tcu::IVec3;
59using tcu::IVec4;
60
61namespace
62{
63
64// Utilities
65
66template<typename T, int Size>
67struct VecArrayAccess
68{
69public:
70									VecArrayAccess	(const void* ptr) : m_array((tcu::Vector<T, Size>*)ptr) {}
71									~VecArrayAccess	(void) {}
72
73	const tcu::Vector<T, Size>&		operator[]		(size_t offset) const	{ return m_array[offset];	}
74	tcu::Vector<T, Size>&			operator[]		(size_t offset)			{ return m_array[offset];	}
75
76private:
77	tcu::Vector<T, Size>*			m_array;
78};
79
80template<typename T, int Size>
81static void fillRandomVectors (de::Random& rnd, const tcu::Vector<T, Size>& minValue, const tcu::Vector<T, Size>& maxValue, void* dst, int numValues, int offset = 0)
82{
83	VecArrayAccess<T, Size> access(dst);
84	for (int ndx = 0; ndx < numValues; ndx++)
85		access[offset + ndx] = tcu::randomVector<T, Size>(rnd, minValue, maxValue);
86}
87
88template<typename T>
89static void fillRandomScalars (de::Random& rnd, T minValue, T maxValue, void* dst, int numValues, int offset = 0)
90{
91	T* typedPtr = (T*)dst;
92	for (int ndx = 0; ndx < numValues; ndx++)
93		typedPtr[offset + ndx] = de::randomScalar<T>(rnd, minValue, maxValue);
94}
95
96inline deUint32 getUlpDiff (float a, float b)
97{
98	const deUint32	aBits	= tcu::Float32(a).bits();
99	const deUint32	bBits	= tcu::Float32(b).bits();
100	return aBits > bBits ? aBits - bBits : bBits - aBits;
101}
102
103inline deUint32 getUlpDiffIgnoreZeroSign (float a, float b)
104{
105	if (tcu::Float32(a).isZero())
106		return getUlpDiff(tcu::Float32::construct(tcu::Float32(b).sign(), 0, 0).asFloat(), b);
107	else if (tcu::Float32(b).isZero())
108		return getUlpDiff(a, tcu::Float32::construct(tcu::Float32(a).sign(), 0, 0).asFloat());
109	else
110		return getUlpDiff(a, b);
111}
112
113inline deUint64 getMaxUlpDiffFromBits (int numAccurateBits, int numTotalBits)
114{
115	const int		numGarbageBits	= numTotalBits-numAccurateBits;
116	const deUint64	mask			= (1ull<<numGarbageBits)-1ull;
117
118	return mask;
119}
120
121static int getNumMantissaBits (glu::DataType type)
122{
123	DE_ASSERT(glu::isDataTypeFloatOrVec(type) || glu::isDataTypeDoubleOrDVec(type));
124	return (glu::isDataTypeFloatOrVec(type) ? 23 : 52);
125}
126
127static int getMinMantissaBits (glu::DataType type, glu::Precision precision)
128{
129	if (glu::isDataTypeDoubleOrDVec(type))
130	{
131		return tcu::Float64::MANTISSA_BITS;
132	}
133
134	// Float case.
135	const int bits[] =
136	{
137		7,								// lowp
138		tcu::Float16::MANTISSA_BITS,	// mediump
139		tcu::Float32::MANTISSA_BITS,	// highp
140	};
141	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(bits) == glu::PRECISION_LAST);
142	DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(bits)));
143	return bits[precision];
144}
145
146static int getExponentBits (glu::DataType type)
147{
148	DE_ASSERT(glu::isDataTypeFloatOrVec(type) || glu::isDataTypeDoubleOrDVec(type));
149	return (glu::isDataTypeFloatOrVec(type) ? static_cast<int>(tcu::Float32::EXPONENT_BITS) : static_cast<int>(tcu::Float64::EXPONENT_BITS));
150}
151
152static deUint32 getExponentMask (int exponentBits)
153{
154	DE_ASSERT(exponentBits > 0);
155	return ((1u<<exponentBits) - 1u);
156}
157
158static int getComponentByteSize (glu::DataType type)
159{
160	const glu::DataType scalarType = glu::getDataTypeScalarType(type);
161
162	DE_ASSERT(	scalarType == glu::TYPE_FLOAT	||
163				scalarType == glu::TYPE_FLOAT16	||
164				scalarType == glu::TYPE_DOUBLE	||
165				scalarType == glu::TYPE_INT		||
166				scalarType == glu::TYPE_UINT	||
167				scalarType == glu::TYPE_INT8	||
168				scalarType == glu::TYPE_UINT8	||
169				scalarType == glu::TYPE_INT16	||
170				scalarType == glu::TYPE_UINT16	||
171				scalarType == glu::TYPE_BOOL	);
172
173	switch (scalarType)
174	{
175	case glu::TYPE_INT8:
176	case glu::TYPE_UINT8:
177		return 1;
178	case glu::TYPE_INT16:
179	case glu::TYPE_UINT16:
180	case glu::TYPE_FLOAT16:
181		return 2;
182	case glu::TYPE_BOOL:
183	case glu::TYPE_INT:
184	case glu::TYPE_UINT:
185	case glu::TYPE_FLOAT:
186		return 4;
187	case glu::TYPE_DOUBLE:
188		return 8;
189	default:
190		DE_ASSERT(false); break;
191	}
192	// Unreachable.
193	return 0;
194}
195
196static vector<int> getScalarSizes (const vector<Symbol>& symbols)
197{
198	vector<int> sizes(symbols.size());
199	for (int ndx = 0; ndx < (int)symbols.size(); ++ndx)
200		sizes[ndx] = symbols[ndx].varType.getScalarSize();
201	return sizes;
202}
203
204static vector<int> getComponentByteSizes (const vector<Symbol>& symbols)
205{
206	vector<int> sizes;
207	sizes.reserve(symbols.size());
208	for (const auto& sym : symbols)
209		sizes.push_back(getComponentByteSize(sym.varType.getBasicType()));
210	return sizes;
211}
212
213static int computeTotalByteSize (const vector<Symbol>& symbols)
214{
215	int totalSize = 0;
216	for (const auto& sym : symbols)
217		totalSize += getComponentByteSize(sym.varType.getBasicType()) * sym.varType.getScalarSize();
218	return totalSize;
219}
220
221static vector<void*> getInputOutputPointers (const vector<Symbol>& symbols, vector<deUint8>& data, const int numValues)
222{
223	vector<void*>	pointers		(symbols.size());
224	int				curScalarOffset	= 0;
225
226	for (int varNdx = 0; varNdx < (int)symbols.size(); ++varNdx)
227	{
228		const Symbol&	var				= symbols[varNdx];
229		const int		scalarSize		= var.varType.getScalarSize();
230		const auto		componentBytes	= getComponentByteSize(var.varType.getBasicType());
231
232		// Uses planar layout as input/output specs do not support strides.
233		pointers[varNdx] = &data[curScalarOffset];
234		curScalarOffset += scalarSize*numValues*componentBytes;
235	}
236
237	DE_ASSERT(curScalarOffset == (int)data.size());
238
239	return pointers;
240}
241
242void checkTypeSupport (Context& context, glu::DataType dataType)
243{
244	if (glu::isDataTypeDoubleOrDVec(dataType))
245	{
246		const auto&	vki				= context.getInstanceInterface();
247		const auto	physicalDevice	= context.getPhysicalDevice();
248
249		const auto features = vk::getPhysicalDeviceFeatures(vki, physicalDevice);
250		if (!features.shaderFloat64)
251			TCU_THROW(NotSupportedError, "64-bit floats not supported by the implementation");
252	}
253}
254
255// \todo [2013-08-08 pyry] Make generic utility and move to glu?
256
257struct HexFloat
258{
259	const float value;
260	HexFloat (const float value_) : value(value_) {}
261};
262
263std::ostream& operator<< (std::ostream& str, const HexFloat& v)
264{
265	return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits());
266}
267
268struct HexDouble
269{
270	const double value;
271	HexDouble (const double value_) : value(value_) {}
272};
273
274std::ostream& operator<< (std::ostream& str, const HexDouble& v)
275{
276	return str << v.value << " / " << tcu::toHex(tcu::Float64(v.value).bits());
277}
278
279struct HexBool
280{
281	const deUint32 value;
282	HexBool (const deUint32 value_) : value(value_) {}
283};
284
285std::ostream& operator<< (std::ostream& str, const HexBool& v)
286{
287	return str << (v.value ? "true" : "false") << " / " << tcu::toHex(v.value);
288}
289
290struct VarValue
291{
292	const glu::VarType&	type;
293	const void*			value;
294
295	VarValue (const glu::VarType& type_, const void* value_) : type(type_), value(value_) {}
296};
297
298std::ostream& operator<< (std::ostream& str, const VarValue& varValue)
299{
300	DE_ASSERT(varValue.type.isBasicType());
301
302	const glu::DataType		basicType		= varValue.type.getBasicType();
303	const glu::DataType		scalarType		= glu::getDataTypeScalarType(basicType);
304	const int				numComponents	= glu::getDataTypeScalarSize(basicType);
305
306	if (numComponents > 1)
307		str << glu::getDataTypeName(basicType) << "(";
308
309	for (int compNdx = 0; compNdx < numComponents; compNdx++)
310	{
311		if (compNdx != 0)
312			str << ", ";
313
314		switch (scalarType)
315		{
316			case glu::TYPE_FLOAT:	str << HexFloat(((const float*)varValue.value)[compNdx]);			break;
317			case glu::TYPE_INT:		str << ((const deInt32*)varValue.value)[compNdx];					break;
318			case glu::TYPE_UINT:	str << tcu::toHex(((const deUint32*)varValue.value)[compNdx]);		break;
319			case glu::TYPE_BOOL:	str << HexBool(((const deUint32*)varValue.value)[compNdx]);			break;
320			case glu::TYPE_DOUBLE:	str << HexDouble(((const double*)varValue.value)[compNdx]);			break;
321
322			default:
323				DE_ASSERT(false);
324		}
325	}
326
327	if (numComponents > 1)
328		str << ")";
329
330	return str;
331}
332
333static std::string getCommonFuncCaseName (glu::DataType baseType, glu::Precision precision)
334{
335	const bool isDouble = glu::isDataTypeDoubleOrDVec(baseType);
336	return string(glu::getDataTypeName(baseType)) + (isDouble ? "" : getPrecisionPostfix(precision)) + "_compute";
337}
338
339template<class TestClass>
340static void addFunctionCases (tcu::TestCaseGroup* parent, const char* functionName, const std::vector<glu::DataType>& scalarTypes)
341{
342	tcu::TestCaseGroup* group = new tcu::TestCaseGroup(parent->getTestContext(), functionName);
343	parent->addChild(group);
344
345	for (const auto scalarType : scalarTypes)
346	{
347		const bool	isDouble	= glu::isDataTypeDoubleOrDVec(scalarType);
348		const int	lowestPrec	= (isDouble ? glu::PRECISION_LAST : glu::PRECISION_MEDIUMP);
349		const int	highestPrec	= (isDouble ? glu::PRECISION_LAST : glu::PRECISION_HIGHP);
350
351		for (int vecSize = 1; vecSize <= 4; vecSize++)
352		{
353			for (int prec = lowestPrec; prec <= highestPrec; prec++)
354			{
355				group->addChild(new TestClass(parent->getTestContext(), glu::DataType(scalarType + vecSize - 1), glu::Precision(prec)));
356			}
357		}
358	}
359}
360
361// CommonFunctionCase
362
363class CommonFunctionCase : public TestCase
364{
365public:
366										CommonFunctionCase			(tcu::TestContext& testCtx, const char* name);
367										~CommonFunctionCase			(void);
368	virtual	void						initPrograms				(vk::SourceCollections& programCollection) const
369										{
370											generateSources(glu::SHADERTYPE_COMPUTE, m_spec, programCollection);
371										}
372
373	virtual TestInstance*				createInstance				(Context& context) const = 0;
374
375protected:
376										CommonFunctionCase			(const CommonFunctionCase&);
377	CommonFunctionCase&					operator=					(const CommonFunctionCase&);
378
379	ShaderSpec							m_spec;
380	const int							m_numValues;
381};
382
383CommonFunctionCase::CommonFunctionCase (tcu::TestContext& testCtx, const char* name)
384	: TestCase		(testCtx, name)
385	, m_numValues	(100)
386{
387}
388
389CommonFunctionCase::~CommonFunctionCase (void)
390{
391}
392
393// CommonFunctionTestInstance
394
395class CommonFunctionTestInstance : public TestInstance
396{
397public:
398										CommonFunctionTestInstance	(Context& context, const ShaderSpec& spec, int numValues, const char* name)
399											: TestInstance	(context)
400											, m_spec		(spec)
401											, m_numValues	(numValues)
402											, m_name		(name)
403											, m_executor	(createExecutor(context, glu::SHADERTYPE_COMPUTE, spec))
404										{
405										}
406	virtual tcu::TestStatus				iterate						(void);
407
408protected:
409	virtual void						getInputValues				(int numValues, void* const* values) const = 0;
410	virtual bool						compare						(const void* const* inputs, const void* const* outputs) = 0;
411
412	const ShaderSpec					m_spec;
413	const int							m_numValues;
414
415	// \todo [2017-03-07 pyry] Hack used to generate seeds for test cases - get rid of this.
416	const char*							m_name;
417
418	std::ostringstream					m_failMsg;					//!< Comparison failure help message.
419
420	de::UniquePtr<ShaderExecutor>		m_executor;
421};
422
423tcu::TestStatus CommonFunctionTestInstance::iterate (void)
424{
425	const int				numInputBytes			= computeTotalByteSize(m_spec.inputs);
426	const int				numOutputBytes			= computeTotalByteSize(m_spec.outputs);
427	vector<deUint8>			inputData				(numInputBytes * m_numValues);
428	vector<deUint8>			outputData				(numOutputBytes * m_numValues);
429	const vector<void*>		inputPointers			= getInputOutputPointers(m_spec.inputs, inputData, m_numValues);
430	const vector<void*>		outputPointers			= getInputOutputPointers(m_spec.outputs, outputData, m_numValues);
431
432	// Initialize input data.
433	getInputValues(m_numValues, &inputPointers[0]);
434
435	// Execute shader.
436	m_executor->execute(m_numValues, &inputPointers[0], &outputPointers[0]);
437
438	// Compare results.
439	{
440		const vector<int>		inScalarSizes		= getScalarSizes(m_spec.inputs);
441		const vector<int>		outScalarSizes		= getScalarSizes(m_spec.outputs);
442		const vector<int>		inCompByteSizes		= getComponentByteSizes(m_spec.inputs);
443		const vector<int>		outCompByteSizes	= getComponentByteSizes(m_spec.outputs);
444		vector<void*>			curInputPtr			(inputPointers.size());
445		vector<void*>			curOutputPtr		(outputPointers.size());
446		int						numFailed			= 0;
447		tcu::TestContext&		testCtx				= m_context.getTestContext();
448
449		for (int valNdx = 0; valNdx < m_numValues; valNdx++)
450		{
451			// Set up pointers for comparison.
452			for (int inNdx = 0; inNdx < (int)curInputPtr.size(); ++inNdx)
453				curInputPtr[inNdx] = (deUint8*)inputPointers[inNdx] + inScalarSizes[inNdx]*inCompByteSizes[inNdx]*valNdx;
454
455			for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); ++outNdx)
456				curOutputPtr[outNdx] = (deUint8*)outputPointers[outNdx] + outScalarSizes[outNdx]*outCompByteSizes[outNdx]*valNdx;
457
458			if (!compare(&curInputPtr[0], &curOutputPtr[0]))
459			{
460				// \todo [2013-08-08 pyry] We probably want to log reference value as well?
461
462				testCtx.getLog() << TestLog::Message << "ERROR: comparison failed for value " << valNdx << ":\n  " << m_failMsg.str() << TestLog::EndMessage;
463
464				testCtx.getLog() << TestLog::Message << "  inputs:" << TestLog::EndMessage;
465				for (int inNdx = 0; inNdx < (int)curInputPtr.size(); inNdx++)
466					testCtx.getLog() << TestLog::Message << "    " << m_spec.inputs[inNdx].name << " = "
467														   << VarValue(m_spec.inputs[inNdx].varType, curInputPtr[inNdx])
468									   << TestLog::EndMessage;
469
470				testCtx.getLog() << TestLog::Message << "  outputs:" << TestLog::EndMessage;
471				for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); outNdx++)
472					testCtx.getLog() << TestLog::Message << "    " << m_spec.outputs[outNdx].name << " = "
473														   << VarValue(m_spec.outputs[outNdx].varType, curOutputPtr[outNdx])
474									   << TestLog::EndMessage;
475
476				m_failMsg.str("");
477				m_failMsg.clear();
478				numFailed += 1;
479			}
480		}
481
482		testCtx.getLog() << TestLog::Message << (m_numValues - numFailed) << " / " << m_numValues << " values passed" << TestLog::EndMessage;
483
484		if (numFailed == 0)
485			return tcu::TestStatus::pass("Pass");
486		else
487			return tcu::TestStatus::fail("Result comparison failed");
488	}
489}
490
491// Test cases
492
493class AbsCaseInstance : public CommonFunctionTestInstance
494{
495public:
496	AbsCaseInstance (Context& context, const ShaderSpec& spec, int numValues, const char* name)
497		: CommonFunctionTestInstance	(context, spec, numValues, name)
498	{
499	}
500
501	void getInputValues (int numValues, void* const* values) const
502	{
503		const IVec2 intRanges[] =
504		{
505			IVec2(-(1<<7)+1,	(1<<7)-1),
506			IVec2(-(1<<15)+1,	(1<<15)-1),
507			IVec2(0x80000001,	0x7fffffff)
508		};
509
510		de::Random				rnd			(deStringHash(m_name) ^ 0x235facu);
511		const glu::DataType		type		= m_spec.inputs[0].varType.getBasicType();
512		const glu::Precision	precision	= m_spec.inputs[0].varType.getPrecision();
513		const int				scalarSize	= glu::getDataTypeScalarSize(type);
514
515		DE_ASSERT(!glu::isDataTypeFloatOrVec(type));
516
517		fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), values[0], numValues*scalarSize);
518	}
519
520	bool compare (const void* const* inputs, const void* const* outputs)
521	{
522		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
523		const int				scalarSize		= glu::getDataTypeScalarSize(type);
524
525		DE_ASSERT(!glu::isDataTypeFloatOrVec(type));
526
527		for (int compNdx = 0; compNdx < scalarSize; compNdx++)
528		{
529			const int	in0		= ((const int*)inputs[0])[compNdx];
530			const int	out0	= ((const int*)outputs[0])[compNdx];
531			const int	ref0	= de::abs(in0);
532
533			if (out0 != ref0)
534			{
535				m_failMsg << "Expected [" << compNdx << "] = " << ref0;
536				return false;
537			}
538		}
539
540		return true;
541	}
542};
543
544class AbsCase : public CommonFunctionCase
545{
546public:
547	AbsCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision)
548		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, precision).c_str())
549	{
550		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
551		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
552		m_spec.source = "out0 = abs(in0);";
553	}
554
555	TestInstance* createInstance (Context& ctx) const
556	{
557		return new AbsCaseInstance(ctx, m_spec, m_numValues, getName());
558	}
559};
560
561class SignCaseInstance : public CommonFunctionTestInstance
562{
563public:
564	SignCaseInstance (Context& context, const ShaderSpec& spec, int numValues, const char* name)
565		: CommonFunctionTestInstance	(context, spec, numValues, name)
566	{
567	}
568
569	void getInputValues (int numValues, void* const* values) const
570	{
571		const IVec2 intRanges[] =
572		{
573			IVec2(-(1<<7),		(1<<7)-1),
574			IVec2(-(1<<15),		(1<<15)-1),
575			IVec2(0x80000000,	0x7fffffff)
576		};
577
578		de::Random				rnd			(deStringHash(m_name) ^ 0x324u);
579		const glu::DataType		type		= m_spec.inputs[0].varType.getBasicType();
580		const glu::Precision	precision	= m_spec.inputs[0].varType.getPrecision();
581		const int				scalarSize	= glu::getDataTypeScalarSize(type);
582
583		DE_ASSERT(!glu::isDataTypeFloatOrVec(type));
584
585		std::fill((int*)values[0] + scalarSize*0, (int*)values[0] + scalarSize*1, +1);
586		std::fill((int*)values[0] + scalarSize*1, (int*)values[0] + scalarSize*2, -1);
587		std::fill((int*)values[0] + scalarSize*2, (int*)values[0] + scalarSize*3,  0);
588		fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), (int*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
589	}
590
591	bool compare (const void* const* inputs, const void* const* outputs)
592	{
593		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
594		const int				scalarSize		= glu::getDataTypeScalarSize(type);
595
596		DE_ASSERT(!glu::isDataTypeFloatOrVec(type));
597
598		for (int compNdx = 0; compNdx < scalarSize; compNdx++)
599		{
600			const int	in0		= ((const int*)inputs[0])[compNdx];
601			const int	out0	= ((const int*)outputs[0])[compNdx];
602			const int	ref0	= in0 < 0 ? -1 :
603								  in0 > 0 ? +1 : 0;
604
605			if (out0 != ref0)
606			{
607				m_failMsg << "Expected [" << compNdx << "] = " << ref0;
608				return false;
609			}
610		}
611
612		return true;
613	}
614};
615
616class SignCase : public CommonFunctionCase
617{
618public:
619	SignCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision)
620		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, precision).c_str())
621	{
622		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
623		m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
624		m_spec.source = "out0 = sign(in0);";
625	}
626
627	TestInstance* createInstance (Context& ctx) const
628	{
629		return new SignCaseInstance(ctx, m_spec, m_numValues, getName());
630	}
631};
632
633static void infNanRandomFloats(int numValues, void* const* values, const char *name, const ShaderSpec& spec)
634{
635	constexpr deUint64		kOne			= 1;
636	de::Random				rnd				(deStringHash(name) ^ 0xc2a39fu);
637	const glu::DataType		type			= spec.inputs[0].varType.getBasicType();
638	const glu::Precision	precision		= spec.inputs[0].varType.getPrecision();
639	const int				scalarSize		= glu::getDataTypeScalarSize(type);
640	const int				minMantissaBits	= getMinMantissaBits(type, precision);
641	const int				numMantissaBits	= getNumMantissaBits(type);
642	const deUint64			mantissaMask	= ~getMaxUlpDiffFromBits(minMantissaBits, numMantissaBits) & ((kOne<<numMantissaBits)-kOne);
643	const int				exponentBits	= getExponentBits(type);
644	const deUint32			exponentMask	= getExponentMask(exponentBits);
645	const bool				isDouble		= glu::isDataTypeDoubleOrDVec(type);
646	const deUint64			exponentBias	= (isDouble ? static_cast<deUint64>(tcu::Float64::EXPONENT_BIAS) : static_cast<deUint64>(tcu::Float32::EXPONENT_BIAS));
647
648	int numInf = 0;
649	int numNan = 0;
650	for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
651	{
652		// Roughly 25% chance of each of Inf and NaN
653		const bool		isInf		= rnd.getFloat() > 0.75f;
654		const bool		isNan		= !isInf && rnd.getFloat() > 0.66f;
655		const deUint64	m			= rnd.getUint64() & mantissaMask;
656		const deUint64	e			= static_cast<deUint64>(rnd.getUint32() & exponentMask);
657		const deUint64	sign		= static_cast<deUint64>(rnd.getUint32() & 0x1u);
658		// Ensure the 'quiet' bit is set on NaNs (also ensures we don't generate inf by mistake)
659		const deUint64	mantissa	= isInf ? 0 : (isNan ? ((kOne<<(numMantissaBits-1)) | m) : m);
660		const deUint64	exp			= (isNan || isInf) ? exponentMask : std::min(e, exponentBias);
661		const deUint64	value		= (sign << (numMantissaBits + exponentBits)) | (exp << numMantissaBits) | static_cast<deUint32>(mantissa);
662		if (isInf) numInf++;
663		if (isNan) numNan++;
664
665		if (isDouble)
666		{
667			DE_ASSERT(tcu::Float64(value).isInf() == isInf && tcu::Float64(value).isNaN() == isNan);
668			((deUint64*)values[0])[valNdx] = value;
669		}
670		else
671		{
672			const auto value32 = static_cast<deUint32>(value);
673			DE_ASSERT(tcu::Float32(value32).isInf() == isInf && tcu::Float32(value32).isNaN() == isNan);
674			((deUint32*)values[0])[valNdx] = value32;
675		}
676	}
677	// Check for minimal coverage of intended cases.
678	DE_ASSERT(0 < numInf);
679	DE_ASSERT(0 < numNan);
680	DE_ASSERT(numInf + numNan < numValues*scalarSize);
681
682	// Release build does not use them
683	DE_UNREF(numInf);
684	DE_UNREF(numNan);
685}
686
687class IsnanCaseInstance : public CommonFunctionTestInstance
688{
689public:
690	IsnanCaseInstance (Context& context, const ShaderSpec& spec, int numValues, const char* name)
691		: CommonFunctionTestInstance	(context, spec, numValues, name)
692	{
693	}
694
695	void getInputValues (int numValues, void* const* values) const
696	{
697		infNanRandomFloats(numValues, values, m_name, m_spec);
698	}
699
700	bool compare (const void* const* inputs, const void* const* outputs)
701	{
702		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
703		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
704		const int				scalarSize		= glu::getDataTypeScalarSize(type);
705		const bool				isDouble		= glu::isDataTypeDoubleOrDVec(type);
706
707		for (int compNdx = 0; compNdx < scalarSize; compNdx++)
708		{
709			const bool	out0	= reinterpret_cast<const deUint32*>(outputs[0])[compNdx] != 0;
710			bool		ok;
711			bool		ref;
712
713			if (isDouble)
714			{
715				const double in0 = reinterpret_cast<const double*>(inputs[0])[compNdx];
716				ref	= tcu::Float64(in0).isNaN();
717				ok = (out0 == ref);
718			}
719			else
720			{
721				const float	in0	= reinterpret_cast<const float*>(inputs[0])[compNdx];
722				ref	= tcu::Float32(in0).isNaN();
723
724				// NaN support only required for highp. Otherwise just check for false positives.
725				if (precision == glu::PRECISION_HIGHP)
726					ok = (out0 == ref);
727				else
728					ok = ref || !out0;
729			}
730
731			if (!ok)
732			{
733				m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
734				return false;
735			}
736		}
737
738		return true;
739	}
740};
741
742class IsnanCase : public CommonFunctionCase
743{
744public:
745	IsnanCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision)
746		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, precision).c_str())
747	{
748		DE_ASSERT(glu::isDataTypeFloatOrVec(baseType) || glu::isDataTypeDoubleOrDVec(baseType));
749
750		const int			vecSize		= glu::getDataTypeScalarSize(baseType);
751		const glu::DataType	boolType	= vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
752
753		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
754		m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
755		m_spec.source = "out0 = isnan(in0);";
756	}
757
758	void checkSupport (Context& context) const
759	{
760		checkTypeSupport(context, m_spec.inputs[0].varType.getBasicType());
761	}
762
763	TestInstance* createInstance (Context& ctx) const
764	{
765		return new IsnanCaseInstance(ctx, m_spec, m_numValues, getName());
766	}
767};
768
769class IsinfCaseInstance : public CommonFunctionTestInstance
770{
771public:
772	IsinfCaseInstance (Context& context, const ShaderSpec& spec, int numValues, const char* name)
773		: CommonFunctionTestInstance(context, spec, numValues, name)
774	{
775	}
776
777	void getInputValues (int numValues, void* const* values) const
778	{
779		infNanRandomFloats(numValues, values, m_name, m_spec);
780	}
781
782	bool compare (const void* const* inputs, const void* const* outputs)
783	{
784		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
785		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
786		const int				scalarSize		= glu::getDataTypeScalarSize(type);
787		const bool				isDouble		= glu::isDataTypeDoubleOrDVec(type);
788
789		for (int compNdx = 0; compNdx < scalarSize; compNdx++)
790		{
791			const bool	out0 = reinterpret_cast<const deUint32*>(outputs[0])[compNdx] != 0;
792			bool		ref;
793			bool		ok;
794
795			if (isDouble)
796			{
797				const double in0 = reinterpret_cast<const double*>(inputs[0])[compNdx];
798				ref = tcu::Float64(in0).isInf();
799				ok = (out0 == ref);
800			}
801			else
802			{
803				const float in0 = reinterpret_cast<const float*>(inputs[0])[compNdx];
804				if (precision == glu::PRECISION_HIGHP)
805				{
806					// Only highp is required to support inf/nan
807					ref = tcu::Float32(in0).isInf();
808					ok = (out0 == ref);
809				}
810				else
811				{
812					// Inf support is optional, check that inputs that are not Inf in mediump don't result in true.
813					ref = tcu::Float16(in0).isInf();
814					ok = (out0 || !ref);
815				}
816			}
817
818			if (!ok)
819			{
820				m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
821				return false;
822			}
823		}
824
825		return true;
826	}
827};
828
829class IsinfCase : public CommonFunctionCase
830{
831public:
832	IsinfCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision)
833		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, precision).c_str())
834	{
835		DE_ASSERT(glu::isDataTypeFloatOrVec(baseType) || glu::isDataTypeDoubleOrDVec(baseType));
836
837		const int			vecSize		= glu::getDataTypeScalarSize(baseType);
838		const glu::DataType	boolType	= vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
839
840		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
841		m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
842		m_spec.source = "out0 = isinf(in0);";
843	}
844
845	void checkSupport (Context& context) const
846	{
847		checkTypeSupport(context, m_spec.inputs[0].varType.getBasicType());
848	}
849
850	TestInstance* createInstance (Context& ctx) const
851	{
852		return new IsinfCaseInstance(ctx, m_spec, m_numValues, getName());
853	}
854};
855
856class FloatBitsToUintIntCaseInstance : public CommonFunctionTestInstance
857{
858public:
859	FloatBitsToUintIntCaseInstance (Context& context, const ShaderSpec& spec, int numValues, const char* name)
860		: CommonFunctionTestInstance	(context, spec, numValues, name)
861	{
862	}
863
864	void getInputValues (int numValues, void* const* values) const
865	{
866		const Vec2 ranges[] =
867		{
868			Vec2(-2.0f,		2.0f),	// lowp
869			Vec2(-1e3f,		1e3f),	// mediump
870			Vec2(-1e7f,		1e7f)	// highp
871		};
872
873		de::Random				rnd			(deStringHash(m_name) ^ 0x2790au);
874		const glu::DataType		type		= m_spec.inputs[0].varType.getBasicType();
875		const glu::Precision	precision	= m_spec.inputs[0].varType.getPrecision();
876		const int				scalarSize	= glu::getDataTypeScalarSize(type);
877
878		fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
879	}
880
881	bool compare (const void* const* inputs, const void* const* outputs)
882	{
883		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
884		const glu::Precision	precision		= m_spec.inputs[0].varType.getPrecision();
885		const int				scalarSize		= glu::getDataTypeScalarSize(type);
886
887		const int				minMantissaBits	= getMinMantissaBits(type, precision);
888		const int				numMantissaBits	= getNumMantissaBits(type);
889		const int				maxUlpDiff		= static_cast<int>(getMaxUlpDiffFromBits(minMantissaBits, numMantissaBits));
890
891		for (int compNdx = 0; compNdx < scalarSize; compNdx++)
892		{
893			const float		in0			= ((const float*)inputs[0])[compNdx];
894			const deUint32	out0		= ((const deUint32*)outputs[0])[compNdx];
895			const deUint32	refOut0		= tcu::Float32(in0).bits();
896			const int		ulpDiff		= de::abs((int)out0 - (int)refOut0);
897
898			if (ulpDiff > maxUlpDiff)
899			{
900				m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(refOut0) << " with threshold "
901							<< tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
902				return false;
903			}
904		}
905
906		return true;
907	}
908};
909
910class FloatBitsToUintIntCase : public CommonFunctionCase
911{
912public:
913	FloatBitsToUintIntCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, bool outIsSigned)
914		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, precision).c_str())
915	{
916		const int			vecSize		= glu::getDataTypeScalarSize(baseType);
917		const glu::DataType	intType		= outIsSigned ? (vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT)
918													  : (vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT);
919
920		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
921		m_spec.outputs.push_back(Symbol("out0", glu::VarType(intType, glu::PRECISION_HIGHP)));
922		m_spec.source = outIsSigned ? "out0 = floatBitsToInt(in0);" : "out0 = floatBitsToUint(in0);";
923	}
924
925	TestInstance* createInstance (Context& ctx) const
926	{
927		return new FloatBitsToUintIntCaseInstance(ctx, m_spec, m_numValues, getName());
928	}
929};
930
931class FloatBitsToIntCase : public FloatBitsToUintIntCase
932{
933public:
934	FloatBitsToIntCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision)
935		: FloatBitsToUintIntCase	(testCtx, baseType, precision, true)
936	{
937	}
938
939};
940
941class FloatBitsToUintCase : public FloatBitsToUintIntCase
942{
943public:
944	FloatBitsToUintCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision)
945		: FloatBitsToUintIntCase	(testCtx, baseType, precision, false)
946	{
947	}
948};
949
950class BitsToFloatCaseInstance : public CommonFunctionTestInstance
951{
952public:
953	BitsToFloatCaseInstance (Context& context, const ShaderSpec& spec, int numValues, const char* name)
954		: CommonFunctionTestInstance	(context, spec, numValues, name)
955	{
956	}
957
958	void getInputValues (int numValues, void* const* values) const
959	{
960		de::Random				rnd			(deStringHash(m_name) ^ 0xbbb225u);
961		const glu::DataType		type		= m_spec.inputs[0].varType.getBasicType();
962		const int				scalarSize	= glu::getDataTypeScalarSize(type);
963		const Vec2				range		(-1e8f, +1e8f);
964
965		// \note Filled as floats.
966		fillRandomScalars(rnd, range.x(), range.y(), values[0], numValues*scalarSize);
967	}
968
969	bool compare (const void* const* inputs, const void* const* outputs)
970	{
971		const glu::DataType		type			= m_spec.inputs[0].varType.getBasicType();
972		const int				scalarSize		= glu::getDataTypeScalarSize(type);
973		const deUint32			maxUlpDiff		= 0;
974
975		for (int compNdx = 0; compNdx < scalarSize; compNdx++)
976		{
977			const float		in0			= ((const float*)inputs[0])[compNdx];
978			const float		out0		= ((const float*)outputs[0])[compNdx];
979			const deUint32	ulpDiff		= getUlpDiffIgnoreZeroSign(in0, out0);
980
981			if (ulpDiff > maxUlpDiff)
982			{
983				m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(tcu::Float32(in0).bits()) << " with ULP threshold "
984							<< tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
985				return false;
986			}
987		}
988
989		return true;
990	}
991};
992
993class BitsToFloatCase : public CommonFunctionCase
994{
995public:
996	BitsToFloatCase (tcu::TestContext& testCtx, glu::DataType baseType)
997		: CommonFunctionCase	(testCtx, getCommonFuncCaseName(baseType, glu::PRECISION_HIGHP).c_str())
998	{
999		const bool			inIsSigned	= glu::isDataTypeIntOrIVec(baseType);
1000		const int			vecSize		= glu::getDataTypeScalarSize(baseType);
1001		const glu::DataType	floatType	= vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1002
1003		m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
1004		m_spec.outputs.push_back(Symbol("out0", glu::VarType(floatType, glu::PRECISION_HIGHP)));
1005		m_spec.source = inIsSigned ? "out0 = intBitsToFloat(in0);" : "out0 = uintBitsToFloat(in0);";
1006	}
1007
1008	TestInstance* createInstance (Context& ctx) const
1009	{
1010		return new BitsToFloatCaseInstance(ctx, m_spec, m_numValues, getName());
1011	}
1012};
1013
1014} // anonymous
1015
1016ShaderCommonFunctionTests::ShaderCommonFunctionTests (tcu::TestContext& testCtx)
1017	: tcu::TestCaseGroup	(testCtx, "common")
1018{
1019}
1020
1021ShaderCommonFunctionTests::~ShaderCommonFunctionTests (void)
1022{
1023}
1024
1025void ShaderCommonFunctionTests::init (void)
1026{
1027	static const std::vector<glu::DataType>	kIntOnly		(1u, glu::TYPE_INT);
1028	static const std::vector<glu::DataType>	kFloatOnly		(1u, glu::TYPE_FLOAT);
1029	static const std::vector<glu::DataType>	kFloatAndDouble	{glu::TYPE_FLOAT, glu::TYPE_DOUBLE};
1030
1031	addFunctionCases<AbsCase>				(this,	"abs",				kIntOnly);
1032	addFunctionCases<SignCase>				(this,	"sign",				kIntOnly);
1033	addFunctionCases<IsnanCase>				(this,	"isnan",			kFloatAndDouble);
1034	addFunctionCases<IsinfCase>				(this,	"isinf",			kFloatAndDouble);
1035	addFunctionCases<FloatBitsToIntCase>	(this,	"floatbitstoint",	kFloatOnly);
1036	addFunctionCases<FloatBitsToUintCase>	(this,	"floatbitstouint",	kFloatOnly);
1037
1038	// (u)intBitsToFloat()
1039	{
1040		tcu::TestCaseGroup* intGroup	= new tcu::TestCaseGroup(m_testCtx, "intbitstofloat");
1041		tcu::TestCaseGroup* uintGroup	= new tcu::TestCaseGroup(m_testCtx, "uintbitstofloat");
1042
1043		addChild(intGroup);
1044		addChild(uintGroup);
1045
1046		for (int vecSize = 1; vecSize < 4; vecSize++)
1047		{
1048			const glu::DataType		intType		= vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
1049			const glu::DataType		uintType	= vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT;
1050
1051			intGroup->addChild(new BitsToFloatCase(getTestContext(), intType));
1052			uintGroup->addChild(new BitsToFloatCase(getTestContext(), uintType));
1053		}
1054	}
1055}
1056
1057} // shaderexecutor
1058} // vkt
1059