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 Floating-point packing and unpacking function tests.
24 *//*--------------------------------------------------------------------*/
25
26#include "vktShaderPackingFunctionTests.hpp"
27#include "vktShaderExecutor.hpp"
28#include "tcuTestLog.hpp"
29#include "tcuFormatUtil.hpp"
30#include "tcuFloat.hpp"
31#include "deRandom.hpp"
32#include "deMath.h"
33#include "deString.h"
34#include "deSharedPtr.hpp"
35
36namespace vkt
37{
38namespace shaderexecutor
39{
40
41using namespace shaderexecutor;
42
43using std::string;
44using tcu::TestLog;
45
46namespace
47{
48
49inline deUint32 getUlpDiff (float a, float b)
50{
51	const deUint32	aBits	= tcu::Float32(a).bits();
52	const deUint32	bBits	= tcu::Float32(b).bits();
53	return aBits > bBits ? aBits - bBits : bBits - aBits;
54}
55
56struct HexFloat
57{
58	const float value;
59	HexFloat (const float value_) : value(value_) {}
60};
61
62std::ostream& operator<< (std::ostream& str, const HexFloat& v)
63{
64	return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits());
65}
66
67static const char* getPrecisionPostfix (glu::Precision precision)
68{
69	static const char* s_postfix[] =
70	{
71		"_lowp",
72		"_mediump",
73		"_highp"
74	};
75	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_postfix) == glu::PRECISION_LAST);
76	DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
77	return s_postfix[precision];
78}
79
80static const char* getShaderTypePostfix (glu::ShaderType shaderType)
81{
82	static const char* s_postfix[] =
83	{
84		"_vertex",
85		"_fragment",
86		"_geometry",
87		"_tess_control",
88		"_tess_eval",
89		"_compute"
90	};
91	DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
92	return s_postfix[shaderType];
93}
94
95} // anonymous
96
97// ShaderPackingFunctionCase
98
99class ShaderPackingFunctionCase : public TestCase
100{
101public:
102										ShaderPackingFunctionCase			(tcu::TestContext& testCtx, const char* name, const char* description, glu::ShaderType shaderType);
103										~ShaderPackingFunctionCase			(void);
104
105	virtual	void						initPrograms						(vk::SourceCollections& programCollection) const
106										{
107											generateSources(m_shaderType, m_spec, programCollection);
108										}
109
110protected:
111	const glu::ShaderType				m_shaderType;
112	ShaderSpec							m_spec;
113
114private:
115										ShaderPackingFunctionCase			(const ShaderPackingFunctionCase& other);
116	ShaderPackingFunctionCase&			operator=							(const ShaderPackingFunctionCase& other);
117};
118
119ShaderPackingFunctionCase::ShaderPackingFunctionCase (tcu::TestContext& testCtx, const char* name, const char* description, glu::ShaderType shaderType)
120	: TestCase		(testCtx, name, description)
121	, m_shaderType	(shaderType)
122{
123}
124
125ShaderPackingFunctionCase::~ShaderPackingFunctionCase (void)
126{
127}
128
129// ShaderPackingFunctionTestInstance
130
131class ShaderPackingFunctionTestInstance : public TestInstance
132{
133public:
134										ShaderPackingFunctionTestInstance	(Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, const char* name)
135											: TestInstance	(context)
136											, m_testCtx		(context.getTestContext())
137											, m_shaderType	(shaderType)
138											, m_spec		(spec)
139											, m_name		(name)
140											, m_executor	(createExecutor(context, m_shaderType, m_spec))
141										{
142										}
143	virtual tcu::TestStatus				iterate								(void) = 0;
144protected:
145	tcu::TestContext&					m_testCtx;
146	const glu::ShaderType				m_shaderType;
147	ShaderSpec							m_spec;
148	const char*							m_name;
149	de::UniquePtr<ShaderExecutor>		m_executor;
150};
151
152// Test cases
153
154class PackSnorm2x16CaseInstance: public ShaderPackingFunctionTestInstance
155{
156public:
157	PackSnorm2x16CaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, glu::Precision precision, const char* name)
158		: ShaderPackingFunctionTestInstance	(context, shaderType, spec, name)
159		, m_precision						(precision)
160	{
161	}
162
163	tcu::TestStatus iterate (void)
164	{
165		de::Random					rnd			(deStringHash(m_name) ^ 0x776002);
166		std::vector<tcu::Vec2>		inputs;
167		std::vector<deUint32>		outputs;
168		const int					maxDiff		= m_precision == glu::PRECISION_HIGHP	? 1		:		// Rounding only.
169												  m_precision == glu::PRECISION_MEDIUMP	? 33	:		// (2^-10) * (2^15) + 1
170												  m_precision == glu::PRECISION_LOWP	? 129	: 0;	// (2^-8) * (2^15) + 1
171
172		// Special values to check.
173		inputs.push_back(tcu::Vec2(0.0f, 0.0f));
174		inputs.push_back(tcu::Vec2(-1.0f, 1.0f));
175		inputs.push_back(tcu::Vec2(0.5f, -0.5f));
176		inputs.push_back(tcu::Vec2(-1.5f, 1.5f));
177		inputs.push_back(tcu::Vec2(0.25f, -0.75f));
178
179		// Random values, mostly in range.
180		for (int ndx = 0; ndx < 15; ndx++)
181		{
182			const float x = rnd.getFloat()*2.5f - 1.25f;
183			const float y = rnd.getFloat()*2.5f - 1.25f;
184			inputs.push_back(tcu::Vec2(x, y));
185		}
186
187		// Large random values.
188		for (int ndx = 0; ndx < 80; ndx++)
189		{
190			const float x = rnd.getFloat()*1e6f - 0.5e6f;
191			const float y = rnd.getFloat()*1e6f - 0.5e6f;
192			inputs.push_back(tcu::Vec2(x, y));
193		}
194
195		outputs.resize(inputs.size());
196
197		m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
198
199		{
200			const void*	in	= &inputs[0];
201			void*		out	= &outputs[0];
202
203			m_executor->execute((int)inputs.size(), &in, &out);
204		}
205
206		// Verify
207		{
208			const int	numValues	= (int)inputs.size();
209			const int	maxPrints	= 10;
210			int			numFailed	= 0;
211
212			for (int valNdx = 0; valNdx < numValues; valNdx++)
213			{
214				const deUint16	ref0	= (deUint16)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].x(), -1.0f, 1.0f) * 32767.0f), -(1<<15), (1<<15)-1);
215				const deUint16	ref1	= (deUint16)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].y(), -1.0f, 1.0f) * 32767.0f), -(1<<15), (1<<15)-1);
216				const deUint32	ref		= (ref1 << 16) | ref0;
217				const deUint32	res		= outputs[valNdx];
218				const deUint16	res0	= (deUint16)(res & 0xffff);
219				const deUint16	res1	= (deUint16)(res >> 16);
220				const int		diff0	= de::abs((int)ref0 - (int)res0);
221				const int		diff1	= de::abs((int)ref1 - (int)res1);
222
223				if (diff0 > maxDiff || diff1 > maxDiff)
224				{
225					if (numFailed < maxPrints)
226					{
227						m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
228															   << ", expected packSnorm2x16(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
229															   << ", got " << tcu::toHex(res)
230															   << "\n  diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
231										   << TestLog::EndMessage;
232					}
233					else if (numFailed == maxPrints)
234						m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
235
236					numFailed += 1;
237				}
238			}
239
240			m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
241
242			if (numFailed == 0)
243				return tcu::TestStatus::pass("Pass");
244			else
245				return tcu::TestStatus::fail("Result comparison failed");
246
247		}
248	}
249
250private:
251	const glu::Precision m_precision;
252};
253
254class PackSnorm2x16Case : public ShaderPackingFunctionCase
255{
256public:
257	PackSnorm2x16Case (tcu::TestContext& testCtx, glu::ShaderType shaderType, glu::Precision precision)
258		: ShaderPackingFunctionCase	(testCtx, (string("packsnorm2x16") + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType)).c_str(), "packSnorm2x16", shaderType)
259		, m_precision				(precision)
260	{
261		m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC2, precision)));
262		m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
263
264		m_spec.source = "out0 = packSnorm2x16(in0);";
265	}
266
267	TestInstance* createInstance (Context& ctx) const
268	{
269		return new PackSnorm2x16CaseInstance(ctx, m_shaderType, m_spec, m_precision, getName());
270	}
271
272private:
273	const glu::Precision m_precision;
274};
275
276class UnpackSnorm2x16CaseInstance : public ShaderPackingFunctionTestInstance
277{
278public:
279	UnpackSnorm2x16CaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, const char* name)
280	: ShaderPackingFunctionTestInstance (context, shaderType, spec, name)
281	{
282	}
283
284	tcu::TestStatus iterate (void)
285	{
286		const deUint32				maxDiff		= 1; // Rounding error.
287		de::Random					rnd			(deStringHash(m_name) ^ 0x776002);
288		std::vector<deUint32>		inputs;
289		std::vector<tcu::Vec2>		outputs;
290
291		inputs.push_back(0x00000000u);
292		inputs.push_back(0x7fff8000u);
293		inputs.push_back(0x80007fffu);
294		inputs.push_back(0xffffffffu);
295		inputs.push_back(0x0001fffeu);
296
297		// Random values.
298		for (int ndx = 0; ndx < 95; ndx++)
299			inputs.push_back(rnd.getUint32());
300
301		outputs.resize(inputs.size());
302
303		m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
304
305		{
306			const void*	in	= &inputs[0];
307			void*		out	= &outputs[0];
308
309			m_executor->execute((int)inputs.size(), &in, &out);
310		}
311
312		// Verify
313		{
314			const int	numValues	= (int)inputs.size();
315			const int	maxPrints	= 10;
316			int			numFailed	= 0;
317
318			for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
319			{
320				const deInt16	in0			= (deInt16)(deUint16)(inputs[valNdx] & 0xffff);
321				const deInt16	in1			= (deInt16)(deUint16)(inputs[valNdx] >> 16);
322				const float		ref0		= de::clamp(float(in0) / 32767.f, -1.0f, 1.0f);
323				const float		ref1		= de::clamp(float(in1) / 32767.f, -1.0f, 1.0f);
324				const float		res0		= outputs[valNdx].x();
325				const float		res1		= outputs[valNdx].y();
326
327				const deUint32	diff0	= getUlpDiff(ref0, res0);
328				const deUint32	diff1	= getUlpDiff(ref1, res1);
329
330				if (diff0 > maxDiff || diff1 > maxDiff)
331				{
332					if (numFailed < maxPrints)
333					{
334						m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
335															   << "  expected unpackSnorm2x16(" << tcu::toHex(inputs[valNdx]) << ") = "
336															   << "vec2(" << HexFloat(ref0) << ", " << HexFloat(ref1) << ")"
337															   << ", got vec2(" << HexFloat(res0) << ", " << HexFloat(res1) << ")"
338															   << "\n  ULP diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
339										   << TestLog::EndMessage;
340					}
341					else if (numFailed == maxPrints)
342						m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
343
344					numFailed += 1;
345				}
346			}
347
348			m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
349
350			if (numFailed == 0)
351				return tcu::TestStatus::pass("Pass");
352			else
353				return tcu::TestStatus::fail("Result comparison failed");
354
355		}
356	}
357};
358
359class UnpackSnorm2x16Case : public ShaderPackingFunctionCase
360{
361public:
362	UnpackSnorm2x16Case (tcu::TestContext& testCtx, glu::ShaderType shaderType)
363		: ShaderPackingFunctionCase	(testCtx, (string("unpacksnorm2x16") + getShaderTypePostfix(shaderType)).c_str(), "unpackSnorm2x16", shaderType)
364	{
365		m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
366		m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_HIGHP)));
367
368		m_spec.source = "out0 = unpackSnorm2x16(in0);";
369	}
370
371	TestInstance* createInstance (Context& ctx) const
372	{
373		return new UnpackSnorm2x16CaseInstance(ctx, m_shaderType, m_spec, getName());
374	}
375};
376
377class PackUnorm2x16CaseInstance : public ShaderPackingFunctionTestInstance
378{
379public:
380	PackUnorm2x16CaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, glu::Precision precision, const char* name)
381	: ShaderPackingFunctionTestInstance	(context, shaderType, spec, name)
382	, m_precision						(precision)
383	{
384	}
385
386	tcu::TestStatus iterate (void)
387	{
388		de::Random					rnd			(deStringHash(m_name) ^ 0x776002);
389		std::vector<tcu::Vec2>		inputs;
390		std::vector<deUint32>		outputs;
391		const int					maxDiff		= m_precision == glu::PRECISION_HIGHP	? 1		:		// Rounding only.
392												  m_precision == glu::PRECISION_MEDIUMP	? 65	:		// (2^-10) * (2^16) + 1
393												  m_precision == glu::PRECISION_LOWP	? 257	: 0;	// (2^-8) * (2^16) + 1
394
395		// Special values to check.
396		inputs.push_back(tcu::Vec2(0.0f, 0.0f));
397		inputs.push_back(tcu::Vec2(0.5f, 1.0f));
398		inputs.push_back(tcu::Vec2(1.0f, 0.5f));
399		inputs.push_back(tcu::Vec2(-0.5f, 1.5f));
400		inputs.push_back(tcu::Vec2(0.25f, 0.75f));
401
402		// Random values, mostly in range.
403		for (int ndx = 0; ndx < 15; ndx++)
404		{
405			const float x = rnd.getFloat()*1.25f;
406			const float y = rnd.getFloat()*1.25f;
407			inputs.push_back(tcu::Vec2(x, y));
408		}
409
410		// Large random values.
411		for (int ndx = 0; ndx < 80; ndx++)
412		{
413			const float x = rnd.getFloat()*1e6f - 1e5f;
414			const float y = rnd.getFloat()*1e6f - 1e5f;
415			inputs.push_back(tcu::Vec2(x, y));
416		}
417
418		outputs.resize(inputs.size());
419
420		m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
421
422		{
423			const void*	in	= &inputs[0];
424			void*		out	= &outputs[0];
425
426			m_executor->execute((int)inputs.size(), &in, &out);
427		}
428
429		// Verify
430		{
431			const int	numValues	= (int)inputs.size();
432			const int	maxPrints	= 10;
433			int			numFailed	= 0;
434
435			for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
436			{
437				const deUint16	ref0	= (deUint16)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].x(), 0.0f, 1.0f) * 65535.0f), 0, (1<<16)-1);
438				const deUint16	ref1	= (deUint16)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].y(), 0.0f, 1.0f) * 65535.0f), 0, (1<<16)-1);
439				const deUint32	ref		= (ref1 << 16) | ref0;
440				const deUint32	res		= outputs[valNdx];
441				const deUint16	res0	= (deUint16)(res & 0xffff);
442				const deUint16	res1	= (deUint16)(res >> 16);
443				const int		diff0	= de::abs((int)ref0 - (int)res0);
444				const int		diff1	= de::abs((int)ref1 - (int)res1);
445
446				if (diff0 > maxDiff || diff1 > maxDiff)
447				{
448					if (numFailed < maxPrints)
449					{
450						m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
451															   << ", expected packUnorm2x16(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
452															   << ", got " << tcu::toHex(res)
453															   << "\n  diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
454										   << TestLog::EndMessage;
455					}
456					else if (numFailed == maxPrints)
457						m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
458
459					numFailed += 1;
460				}
461			}
462
463			m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
464
465			if (numFailed == 0)
466				return tcu::TestStatus::pass("Pass");
467			else
468				return tcu::TestStatus::fail("Result comparison failed");
469
470		}
471	}
472
473private:
474	const glu::Precision m_precision;
475};
476
477class PackUnorm2x16Case : public ShaderPackingFunctionCase
478{
479public:
480	PackUnorm2x16Case (tcu::TestContext& testCtx, glu::ShaderType shaderType, glu::Precision precision)
481		: ShaderPackingFunctionCase	(testCtx, (string("packunorm2x16") + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType)).c_str(), "packUnorm2x16", shaderType)
482		, m_precision				(precision)
483	{
484		m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC2, precision)));
485		m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
486
487		m_spec.source = "out0 = packUnorm2x16(in0);";
488	}
489
490	TestInstance* createInstance (Context& ctx) const
491	{
492		return new PackUnorm2x16CaseInstance(ctx, m_shaderType, m_spec, m_precision, getName());
493	}
494
495private:
496	const glu::Precision m_precision;
497};
498
499class UnpackUnorm2x16CaseInstance : public ShaderPackingFunctionTestInstance
500{
501public:
502	UnpackUnorm2x16CaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, const char* name)
503		: ShaderPackingFunctionTestInstance (context, shaderType, spec, name)
504	{
505	}
506
507	tcu::TestStatus iterate (void)
508	{
509		const deUint32				maxDiff		= 1; // Rounding error.
510		de::Random					rnd			(deStringHash(m_name) ^ 0x776002);
511		std::vector<deUint32>		inputs;
512		std::vector<tcu::Vec2>		outputs;
513
514		inputs.push_back(0x00000000u);
515		inputs.push_back(0x7fff8000u);
516		inputs.push_back(0x80007fffu);
517		inputs.push_back(0xffffffffu);
518		inputs.push_back(0x0001fffeu);
519
520		// Random values.
521		for (int ndx = 0; ndx < 95; ndx++)
522			inputs.push_back(rnd.getUint32());
523
524		outputs.resize(inputs.size());
525
526		m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
527
528		{
529			const void*	in	= &inputs[0];
530			void*		out	= &outputs[0];
531
532			m_executor->execute((int)inputs.size(), &in, &out);
533		}
534
535		// Verify
536		{
537			const int	numValues	= (int)inputs.size();
538			const int	maxPrints	= 10;
539			int			numFailed	= 0;
540
541			for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
542			{
543				const deUint16	in0			= (deUint16)(inputs[valNdx] & 0xffff);
544				const deUint16	in1			= (deUint16)(inputs[valNdx] >> 16);
545				const float		ref0		= float(in0) / 65535.0f;
546				const float		ref1		= float(in1) / 65535.0f;
547				const float		res0		= outputs[valNdx].x();
548				const float		res1		= outputs[valNdx].y();
549
550				const deUint32	diff0		= getUlpDiff(ref0, res0);
551				const deUint32	diff1		= getUlpDiff(ref1, res1);
552
553				if (diff0 > maxDiff || diff1 > maxDiff)
554				{
555					if (numFailed < maxPrints)
556					{
557						m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
558															   << "  expected unpackUnorm2x16(" << tcu::toHex(inputs[valNdx]) << ") = "
559															   << "vec2(" << HexFloat(ref0) << ", " << HexFloat(ref1) << ")"
560															   << ", got vec2(" << HexFloat(res0) << ", " << HexFloat(res1) << ")"
561															   << "\n  ULP diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
562										   << TestLog::EndMessage;
563					}
564					else if (numFailed == maxPrints)
565						m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
566
567					numFailed += 1;
568				}
569			}
570
571			m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
572
573			if (numFailed == 0)
574				return tcu::TestStatus::pass("Pass");
575			else
576				return tcu::TestStatus::fail("Result comparison failed");
577
578		}
579	}
580};
581
582
583class UnpackUnorm2x16Case : public ShaderPackingFunctionCase
584{
585public:
586	UnpackUnorm2x16Case (tcu::TestContext& testCtx, glu::ShaderType shaderType)
587		: ShaderPackingFunctionCase(testCtx, (string("unpackunorm2x16") + getShaderTypePostfix(shaderType)).c_str(), "unpackUnorm2x16", shaderType)
588	{
589		m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
590		m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_HIGHP)));
591
592		m_spec.source = "out0 = unpackUnorm2x16(in0);";
593	}
594
595	TestInstance* createInstance (Context& ctx) const
596	{
597		return new UnpackUnorm2x16CaseInstance(ctx, m_shaderType, m_spec, getName());
598	}
599
600};
601
602class PackHalf2x16CaseInstance : public ShaderPackingFunctionTestInstance
603{
604public:
605	PackHalf2x16CaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, const char* name)
606	: ShaderPackingFunctionTestInstance (context, shaderType, spec, name)
607	{
608	}
609
610	tcu::TestStatus iterate (void)
611	{
612		const int					maxDiff		= 0; // Values can be represented exactly in mediump.
613		de::Random					rnd			(deStringHash(m_name) ^ 0x776002);
614		std::vector<tcu::Vec2>		inputs;
615		std::vector<deUint32>		outputs;
616
617		// Special values to check.
618		inputs.push_back(tcu::Vec2(0.0f, 0.0f));
619		inputs.push_back(tcu::Vec2(0.5f, 1.0f));
620		inputs.push_back(tcu::Vec2(1.0f, 0.5f));
621		inputs.push_back(tcu::Vec2(-0.5f, 1.5f));
622		inputs.push_back(tcu::Vec2(0.25f, 0.75f));
623
624		// Random values.
625		{
626			const int	minExp	= -14;
627			const int	maxExp	= 15;
628
629			for (int ndx = 0; ndx < 95; ndx++)
630			{
631				tcu::Vec2 v;
632				for (int c = 0; c < 2; c++)
633				{
634					const int		s			= rnd.getBool() ? 1 : -1;
635					const int		exp			= rnd.getInt(minExp, maxExp);
636					const deUint32	mantissa	= rnd.getUint32() & ((1<<23)-1);
637
638					v[c] = tcu::Float32::construct(s, exp ? exp : 1 /* avoid denormals */, (1u<<23) | mantissa).asFloat();
639				}
640				inputs.push_back(v);
641			}
642		}
643
644		// Convert input values to fp16 and back to make sure they can be represented exactly in mediump.
645		for (std::vector<tcu::Vec2>::iterator inVal = inputs.begin(); inVal != inputs.end(); ++inVal)
646			*inVal = tcu::Vec2(tcu::Float16(inVal->x()).asFloat(), tcu::Float16(inVal->y()).asFloat());
647
648		outputs.resize(inputs.size());
649
650		m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
651
652		{
653			const void*	in	= &inputs[0];
654			void*		out	= &outputs[0];
655
656			m_executor->execute((int)inputs.size(), &in, &out);
657		}
658
659		// Verify
660		{
661			const int	numValues	= (int)inputs.size();
662			const int	maxPrints	= 10;
663			int			numFailed	= 0;
664
665			for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
666			{
667				const deUint16	ref0	= (deUint16)tcu::Float16(inputs[valNdx].x()).bits();
668				const deUint16	ref1	= (deUint16)tcu::Float16(inputs[valNdx].y()).bits();
669				const deUint32	ref		= (ref1 << 16) | ref0;
670				const deUint32	res		= outputs[valNdx];
671				const deUint16	res0	= (deUint16)(res & 0xffff);
672				const deUint16	res1	= (deUint16)(res >> 16);
673				const int		diff0	= de::abs((int)ref0 - (int)res0);
674				const int		diff1	= de::abs((int)ref1 - (int)res1);
675
676				if (diff0 > maxDiff || diff1 > maxDiff)
677				{
678					if (numFailed < maxPrints)
679					{
680						m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
681															   << ", expected packHalf2x16(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
682															   << ", got " << tcu::toHex(res)
683															   << "\n  diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
684										   << TestLog::EndMessage;
685					}
686					else if (numFailed == maxPrints)
687						m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
688
689					numFailed += 1;
690				}
691			}
692
693			m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
694
695			if (numFailed == 0)
696				return tcu::TestStatus::pass("Pass");
697			else
698				return tcu::TestStatus::fail("Result comparison failed");
699
700		}
701	}
702};
703
704class PackHalf2x16Case : public ShaderPackingFunctionCase
705{
706public:
707	PackHalf2x16Case (tcu::TestContext& testCtx, glu::ShaderType shaderType)
708		: ShaderPackingFunctionCase	(testCtx, (string("packhalf2x16") + getShaderTypePostfix(shaderType)).c_str(), "packHalf2x16", shaderType)
709	{
710		m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_HIGHP)));
711		m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
712
713		m_spec.source = "out0 = packHalf2x16(in0);";
714	}
715
716	TestInstance* createInstance (Context& ctx) const
717	{
718		return new PackHalf2x16CaseInstance(ctx, m_shaderType, m_spec, getName());
719	}
720
721};
722
723class UnpackHalf2x16CaseInstance : public ShaderPackingFunctionTestInstance
724{
725public:
726	UnpackHalf2x16CaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, const char* name)
727	: ShaderPackingFunctionTestInstance (context, shaderType, spec, name)
728	{
729	}
730
731	tcu::TestStatus iterate (void)
732	{
733		const int					maxDiff		= 0; // All bits must be accurate.
734		de::Random					rnd			(deStringHash(m_name) ^ 0x776002);
735		std::vector<deUint32>		inputs;
736		std::vector<tcu::Vec2>		outputs;
737
738		// Special values.
739		inputs.push_back((tcu::Float16( 0.0f).bits() << 16) | tcu::Float16( 1.0f).bits());
740		inputs.push_back((tcu::Float16( 1.0f).bits() << 16) | tcu::Float16( 0.0f).bits());
741		inputs.push_back((tcu::Float16(-1.0f).bits() << 16) | tcu::Float16( 0.5f).bits());
742		inputs.push_back((tcu::Float16( 0.5f).bits() << 16) | tcu::Float16(-0.5f).bits());
743
744		// Construct random values.
745		{
746			const int	minExp		= -14;
747			const int	maxExp		= 15;
748			const int	mantBits	= 10;
749
750			for (int ndx = 0; ndx < 96; ndx++)
751			{
752				deUint32 inVal = 0;
753				for (int c = 0; c < 2; c++)
754				{
755					const int		s			= rnd.getBool() ? 1 : -1;
756					const int		exp			= rnd.getInt(minExp, maxExp);
757					const deUint32	mantissa	= rnd.getUint32() & ((1<<mantBits)-1);
758					const deUint16	value		= tcu::Float16::construct(s, exp ? exp : 1 /* avoid denorm */, (deUint16)((1u<<10) | mantissa)).bits();
759
760					inVal |= value << (16*c);
761				}
762				inputs.push_back(inVal);
763			}
764		}
765
766		outputs.resize(inputs.size());
767
768		m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
769
770		{
771			const void*	in	= &inputs[0];
772			void*		out	= &outputs[0];
773
774			m_executor->execute((int)inputs.size(), &in, &out);
775		}
776
777		// Verify
778		{
779			const int	numValues	= (int)inputs.size();
780			const int	maxPrints	= 10;
781			int			numFailed	= 0;
782
783			for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
784			{
785				const deUint16	in0			= (deUint16)(inputs[valNdx] & 0xffff);
786				const deUint16	in1			= (deUint16)(inputs[valNdx] >> 16);
787				const float		ref0		= tcu::Float16(in0).asFloat();
788				const float		ref1		= tcu::Float16(in1).asFloat();
789				const float		res0		= outputs[valNdx].x();
790				const float		res1		= outputs[valNdx].y();
791
792				const deUint32	refBits0	= tcu::Float32(ref0).bits();
793				const deUint32	refBits1	= tcu::Float32(ref1).bits();
794				const deUint32	resBits0	= tcu::Float32(res0).bits();
795				const deUint32	resBits1	= tcu::Float32(res1).bits();
796
797				const int		diff0	= de::abs((int)refBits0 - (int)resBits0);
798				const int		diff1	= de::abs((int)refBits1 - (int)resBits1);
799
800				if (diff0 > maxDiff || diff1 > maxDiff)
801				{
802					if (numFailed < maxPrints)
803					{
804						m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
805															   << "  expected unpackHalf2x16(" << tcu::toHex(inputs[valNdx]) << ") = "
806															   << "vec2(" << ref0 << " / " << tcu::toHex(refBits0) << ", " << ref1 << " / " << tcu::toHex(refBits1) << ")"
807															   << ", got vec2(" << res0 << " / " << tcu::toHex(resBits0) << ", " << res1 << " / " << tcu::toHex(resBits1) << ")"
808															   << "\n  ULP diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
809										   << TestLog::EndMessage;
810					}
811					else if (numFailed == maxPrints)
812						m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
813
814					numFailed += 1;
815				}
816			}
817
818			m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
819
820			if (numFailed == 0)
821				return tcu::TestStatus::pass("Pass");
822			else
823				return tcu::TestStatus::fail("Result comparison failed");
824
825		}
826	}
827};
828
829class UnpackHalf2x16Case : public ShaderPackingFunctionCase
830{
831public:
832	UnpackHalf2x16Case (tcu::TestContext& testCtx, glu::ShaderType shaderType)
833		: ShaderPackingFunctionCase	(testCtx, (string("unpackhalf2x16") + getShaderTypePostfix(shaderType)).c_str(), "unpackHalf2x16", shaderType)
834	{
835		m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
836		m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_MEDIUMP)));
837
838		m_spec.source = "out0 = unpackHalf2x16(in0);";
839	}
840
841	TestInstance* createInstance (Context& ctx) const
842	{
843		return new UnpackHalf2x16CaseInstance(ctx, m_shaderType, m_spec, getName());
844	}
845
846};
847
848class PackSnorm4x8CaseInstance : public ShaderPackingFunctionTestInstance
849{
850public:
851	PackSnorm4x8CaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, glu::Precision precision, const char* name)
852		: ShaderPackingFunctionTestInstance	(context, shaderType, spec, name)
853		, m_precision						(precision)
854	{
855	}
856
857	tcu::TestStatus iterate (void)
858	{
859		de::Random					rnd			(deStringHash(m_name) ^ 0x42f2c0);
860		std::vector<tcu::Vec4>		inputs;
861		std::vector<deUint32>		outputs;
862		const int					maxDiff		= m_precision == glu::PRECISION_HIGHP	? 1	:		// Rounding only.
863												  m_precision == glu::PRECISION_MEDIUMP	? 1	:		// (2^-10) * (2^7) + 1
864												  m_precision == glu::PRECISION_LOWP	? 2	: 0;	// (2^-8) * (2^7) + 1
865
866		// Special values to check.
867		inputs.push_back(tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f));
868		inputs.push_back(tcu::Vec4(-1.0f, 1.0f, -1.0f, 1.0f));
869		inputs.push_back(tcu::Vec4(0.5f, -0.5f, -0.5f, 0.5f));
870		inputs.push_back(tcu::Vec4(-1.5f, 1.5f, -1.5f, 1.5f));
871		inputs.push_back(tcu::Vec4(0.25f, -0.75f, -0.25f, 0.75f));
872
873		// Random values, mostly in range.
874		for (int ndx = 0; ndx < 15; ndx++)
875		{
876			const float x = rnd.getFloat()*2.5f - 1.25f;
877			const float y = rnd.getFloat()*2.5f - 1.25f;
878			const float z = rnd.getFloat()*2.5f - 1.25f;
879			const float w = rnd.getFloat()*2.5f - 1.25f;
880			inputs.push_back(tcu::Vec4(x, y, z, w));
881		}
882
883		// Large random values.
884		for (int ndx = 0; ndx < 80; ndx++)
885		{
886			const float x = rnd.getFloat()*1e6f - 0.5e6f;
887			const float y = rnd.getFloat()*1e6f - 0.5e6f;
888			const float z = rnd.getFloat()*1e6f - 0.5e6f;
889			const float w = rnd.getFloat()*1e6f - 0.5e6f;
890			inputs.push_back(tcu::Vec4(x, y, z, w));
891		}
892
893		outputs.resize(inputs.size());
894
895		m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
896
897		{
898			const void*	in	= &inputs[0];
899			void*		out	= &outputs[0];
900
901			m_executor->execute((int)inputs.size(), &in, &out);
902		}
903
904		// Verify
905		{
906			const int	numValues	= (int)inputs.size();
907			const int	maxPrints	= 10;
908			int			numFailed	= 0;
909
910			for (int valNdx = 0; valNdx < numValues; valNdx++)
911			{
912				const deUint16	ref0	= (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].x(), -1.0f, 1.0f) * 127.0f), -(1<<7), (1<<7)-1);
913				const deUint16	ref1	= (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].y(), -1.0f, 1.0f) * 127.0f), -(1<<7), (1<<7)-1);
914				const deUint16	ref2	= (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].z(), -1.0f, 1.0f) * 127.0f), -(1<<7), (1<<7)-1);
915				const deUint16	ref3	= (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].w(), -1.0f, 1.0f) * 127.0f), -(1<<7), (1<<7)-1);
916				const deUint32	ref		= (deUint32(ref3) << 24) | (deUint32(ref2) << 16) | (deUint32(ref1) << 8) | deUint32(ref0);
917				const deUint32	res		= outputs[valNdx];
918				const deUint16	res0	= (deUint8)(res & 0xff);
919				const deUint16	res1	= (deUint8)((res >> 8) & 0xff);
920				const deUint16	res2	= (deUint8)((res >> 16) & 0xff);
921				const deUint16	res3	= (deUint8)((res >> 24) & 0xff);
922				const int		diff0	= de::abs((int)ref0 - (int)res0);
923				const int		diff1	= de::abs((int)ref1 - (int)res1);
924				const int		diff2	= de::abs((int)ref2 - (int)res2);
925				const int		diff3	= de::abs((int)ref3 - (int)res3);
926
927				if (diff0 > maxDiff || diff1 > maxDiff || diff2 > maxDiff || diff3 > maxDiff)
928				{
929					if (numFailed < maxPrints)
930					{
931						m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
932															   << ", expected packSnorm4x8(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
933															   << ", got " << tcu::toHex(res)
934															   << "\n  diffs = " << tcu::IVec4(diff0, diff1, diff2, diff3) << ", max diff = " << maxDiff
935										   << TestLog::EndMessage;
936					}
937					else if (numFailed == maxPrints)
938						m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
939
940					numFailed += 1;
941				}
942			}
943
944			m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
945
946			if (numFailed == 0)
947				return tcu::TestStatus::pass("Pass");
948			else
949				return tcu::TestStatus::fail("Result comparison failed");
950
951		}
952	}
953
954private:
955	const glu::Precision m_precision;
956};
957
958class PackSnorm4x8Case : public ShaderPackingFunctionCase
959{
960public:
961	PackSnorm4x8Case (tcu::TestContext& testCtx, glu::ShaderType shaderType, glu::Precision precision)
962		: ShaderPackingFunctionCase	(testCtx, (string("packsnorm4x8") + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType)).c_str(), "packSnorm4x8", shaderType)
963		, m_precision				(precision)
964	{
965		m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC4, precision)));
966		m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
967
968		m_spec.source = "out0 = packSnorm4x8(in0);";
969	}
970
971	TestInstance* createInstance (Context& ctx) const
972	{
973		return new PackSnorm4x8CaseInstance(ctx, m_shaderType, m_spec, m_precision, getName());
974	}
975
976private:
977	const glu::Precision m_precision;
978};
979
980class UnpackSnorm4x8CaseInstance : public ShaderPackingFunctionTestInstance
981{
982public:
983	UnpackSnorm4x8CaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, const char* name)
984		: ShaderPackingFunctionTestInstance	(context, shaderType, spec, name)
985	{
986	}
987
988	tcu::TestStatus iterate (void)
989	{
990		const deUint32				maxDiff		= 1; // Rounding error.
991		de::Random					rnd			(deStringHash(m_name) ^ 0x776002);
992		std::vector<deUint32>		inputs;
993		std::vector<tcu::Vec4>		outputs;
994
995		inputs.push_back(0x00000000u);
996		inputs.push_back(0x7fff8000u);
997		inputs.push_back(0x80007fffu);
998		inputs.push_back(0xffffffffu);
999		inputs.push_back(0x0001fffeu);
1000
1001		// Random values.
1002		for (int ndx = 0; ndx < 95; ndx++)
1003			inputs.push_back(rnd.getUint32());
1004
1005		outputs.resize(inputs.size());
1006
1007		m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
1008
1009		{
1010			const void*	in	= &inputs[0];
1011			void*		out	= &outputs[0];
1012
1013			m_executor->execute((int)inputs.size(), &in, &out);
1014		}
1015
1016		// Verify
1017		{
1018			const int	numValues	= (int)inputs.size();
1019			const int	maxPrints	= 10;
1020			int			numFailed	= 0;
1021
1022			for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
1023			{
1024				const deInt8	in0		= (deInt8)(deUint8)(inputs[valNdx] & 0xff);
1025				const deInt8	in1		= (deInt8)(deUint8)((inputs[valNdx] >> 8) & 0xff);
1026				const deInt8	in2		= (deInt8)(deUint8)((inputs[valNdx] >> 16) & 0xff);
1027				const deInt8	in3		= (deInt8)(deUint8)(inputs[valNdx] >> 24);
1028				const float		ref0	= de::clamp(float(in0) / 127.f, -1.0f, 1.0f);
1029				const float		ref1	= de::clamp(float(in1) / 127.f, -1.0f, 1.0f);
1030				const float		ref2	= de::clamp(float(in2) / 127.f, -1.0f, 1.0f);
1031				const float		ref3	= de::clamp(float(in3) / 127.f, -1.0f, 1.0f);
1032				const float		res0	= outputs[valNdx].x();
1033				const float		res1	= outputs[valNdx].y();
1034				const float		res2	= outputs[valNdx].z();
1035				const float		res3	= outputs[valNdx].w();
1036
1037				const deUint32	diff0	= getUlpDiff(ref0, res0);
1038				const deUint32	diff1	= getUlpDiff(ref1, res1);
1039				const deUint32	diff2	= getUlpDiff(ref2, res2);
1040				const deUint32	diff3	= getUlpDiff(ref3, res3);
1041
1042				if (diff0 > maxDiff || diff1 > maxDiff || diff2 > maxDiff || diff3 > maxDiff)
1043				{
1044					if (numFailed < maxPrints)
1045					{
1046						m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
1047															   << "  expected unpackSnorm4x8(" << tcu::toHex(inputs[valNdx]) << ") = "
1048															   << "vec4(" << HexFloat(ref0) << ", " << HexFloat(ref1) << ", " << HexFloat(ref2) << ", " << HexFloat(ref3) << ")"
1049															   << ", got vec4(" << HexFloat(res0) << ", " << HexFloat(res1) << ", " << HexFloat(res2) << ", " << HexFloat(res3) << ")"
1050															   << "\n  ULP diffs = (" << diff0 << ", " << diff1 << ", " << diff2 << ", " << diff3 << "), max diff = " << maxDiff
1051										   << TestLog::EndMessage;
1052					}
1053					else if (numFailed == maxPrints)
1054						m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
1055
1056					numFailed += 1;
1057				}
1058			}
1059
1060			m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
1061
1062			if (numFailed == 0)
1063				return tcu::TestStatus::pass("Pass");
1064			else
1065				return tcu::TestStatus::fail("Result comparison failed");
1066
1067		}
1068	}
1069};
1070
1071
1072class UnpackSnorm4x8Case : public ShaderPackingFunctionCase
1073{
1074public:
1075	UnpackSnorm4x8Case (tcu::TestContext& testCtx, glu::ShaderType shaderType)
1076		: ShaderPackingFunctionCase	(testCtx, (string("unpacksnorm4x8") + getShaderTypePostfix(shaderType)).c_str(), "unpackSnorm4x8", shaderType)
1077	{
1078		m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
1079		m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP)));
1080
1081		m_spec.source = "out0 = unpackSnorm4x8(in0);";
1082	}
1083
1084	TestInstance* createInstance (Context& ctx) const
1085	{
1086		return new UnpackSnorm4x8CaseInstance(ctx, m_shaderType, m_spec, getName());
1087	}
1088
1089};
1090
1091class PackUnorm4x8CaseInstance : public ShaderPackingFunctionTestInstance
1092{
1093public:
1094	PackUnorm4x8CaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, glu::Precision precision, const char* name)
1095		: ShaderPackingFunctionTestInstance	(context, shaderType, spec, name)
1096		, m_precision						(precision)
1097	{
1098	}
1099
1100	tcu::TestStatus iterate (void)
1101	{
1102		de::Random					rnd			(deStringHash(m_name) ^ 0x776002);
1103		std::vector<tcu::Vec4>		inputs;
1104		std::vector<deUint32>		outputs;
1105		const int					maxDiff		= m_precision == glu::PRECISION_HIGHP	? 1	:		// Rounding only.
1106												  m_precision == glu::PRECISION_MEDIUMP	? 1	:		// (2^-10) * (2^8) + 1
1107												  m_precision == glu::PRECISION_LOWP	? 2	: 0;	// (2^-8) * (2^8) + 1
1108
1109		// Special values to check.
1110		inputs.push_back(tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f));
1111		inputs.push_back(tcu::Vec4(-1.0f, 1.0f, -1.0f, 1.0f));
1112		inputs.push_back(tcu::Vec4(0.5f, -0.5f, -0.5f, 0.5f));
1113		inputs.push_back(tcu::Vec4(-1.5f, 1.5f, -1.5f, 1.5f));
1114		inputs.push_back(tcu::Vec4(0.25f, -0.75f, -0.25f, 0.75f));
1115
1116		// Random values, mostly in range.
1117		for (int ndx = 0; ndx < 15; ndx++)
1118		{
1119			const float x = rnd.getFloat()*1.25f - 0.125f;
1120			const float y = rnd.getFloat()*1.25f - 0.125f;
1121			const float z = rnd.getFloat()*1.25f - 0.125f;
1122			const float w = rnd.getFloat()*1.25f - 0.125f;
1123			inputs.push_back(tcu::Vec4(x, y, z, w));
1124		}
1125
1126		// Large random values.
1127		for (int ndx = 0; ndx < 80; ndx++)
1128		{
1129			const float x = rnd.getFloat()*1e6f - 1e5f;
1130			const float y = rnd.getFloat()*1e6f - 1e5f;
1131			const float z = rnd.getFloat()*1e6f - 1e5f;
1132			const float w = rnd.getFloat()*1e6f - 1e5f;
1133			inputs.push_back(tcu::Vec4(x, y, z, w));
1134		}
1135
1136		outputs.resize(inputs.size());
1137
1138		m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
1139
1140		{
1141			const void*	in	= &inputs[0];
1142			void*		out	= &outputs[0];
1143
1144			m_executor->execute((int)inputs.size(), &in, &out);
1145		}
1146
1147		// Verify
1148		{
1149			const int	numValues	= (int)inputs.size();
1150			const int	maxPrints	= 10;
1151			int			numFailed	= 0;
1152
1153			for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
1154			{
1155				const deUint16	ref0	= (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].x(), 0.0f, 1.0f) * 255.0f), 0, (1<<8)-1);
1156				const deUint16	ref1	= (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].y(), 0.0f, 1.0f) * 255.0f), 0, (1<<8)-1);
1157				const deUint16	ref2	= (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].z(), 0.0f, 1.0f) * 255.0f), 0, (1<<8)-1);
1158				const deUint16	ref3	= (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].w(), 0.0f, 1.0f) * 255.0f), 0, (1<<8)-1);
1159				const deUint32	ref		= (deUint32(ref3) << 24) | (deUint32(ref2) << 16) | (deUint32(ref1) << 8) | deUint32(ref0);
1160				const deUint32	res		= outputs[valNdx];
1161				const deUint16	res0	= (deUint8)(res & 0xff);
1162				const deUint16	res1	= (deUint8)((res >> 8) & 0xff);
1163				const deUint16	res2	= (deUint8)((res >> 16) & 0xff);
1164				const deUint16	res3	= (deUint8)((res >> 24) & 0xff);
1165				const int		diff0	= de::abs((int)ref0 - (int)res0);
1166				const int		diff1	= de::abs((int)ref1 - (int)res1);
1167				const int		diff2	= de::abs((int)ref2 - (int)res2);
1168				const int		diff3	= de::abs((int)ref3 - (int)res3);
1169
1170				if (diff0 > maxDiff || diff1 > maxDiff || diff2 > maxDiff || diff3 > maxDiff)
1171				{
1172					if (numFailed < maxPrints)
1173					{
1174						m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
1175															   << ", expected packUnorm4x8(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
1176															   << ", got " << tcu::toHex(res)
1177															   << "\n  diffs = " << tcu::IVec4(diff0, diff1, diff2, diff3) << ", max diff = " << maxDiff
1178										   << TestLog::EndMessage;
1179					}
1180					else if (numFailed == maxPrints)
1181						m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
1182
1183					numFailed += 1;
1184				}
1185			}
1186
1187			m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
1188
1189			if (numFailed == 0)
1190				return tcu::TestStatus::pass("Pass");
1191			else
1192				return tcu::TestStatus::fail("Result comparison failed");
1193
1194		}
1195	}
1196
1197private:
1198	const glu::Precision m_precision;
1199};
1200
1201class PackUnorm4x8Case : public ShaderPackingFunctionCase
1202{
1203public:
1204	PackUnorm4x8Case (tcu::TestContext& testCtx, glu::ShaderType shaderType, glu::Precision precision)
1205		: ShaderPackingFunctionCase	(testCtx, (string("packunorm4x8") + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType)).c_str(), "packUnorm4x8", shaderType)
1206		, m_precision				(precision)
1207	{
1208		m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC4, precision)));
1209		m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
1210
1211		m_spec.source = "out0 = packUnorm4x8(in0);";
1212	}
1213
1214	TestInstance* createInstance (Context& ctx) const
1215	{
1216		return new PackUnorm4x8CaseInstance(ctx, m_shaderType, m_spec, m_precision, getName());
1217	}
1218
1219private:
1220	const glu::Precision m_precision;
1221};
1222
1223class UnpackUnorm4x8CaseInstance : public ShaderPackingFunctionTestInstance
1224{
1225public:
1226	UnpackUnorm4x8CaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, const char* name)
1227		: ShaderPackingFunctionTestInstance	(context, shaderType, spec, name)
1228	{
1229	}
1230
1231	tcu::TestStatus iterate (void)
1232	{
1233		const deUint32				maxDiff		= 1; // Rounding error.
1234		de::Random					rnd			(deStringHash(m_name) ^ 0x776002);
1235		std::vector<deUint32>		inputs;
1236		std::vector<tcu::Vec4>		outputs;
1237
1238		inputs.push_back(0x00000000u);
1239		inputs.push_back(0x7fff8000u);
1240		inputs.push_back(0x80007fffu);
1241		inputs.push_back(0xffffffffu);
1242		inputs.push_back(0x0001fffeu);
1243
1244		// Random values.
1245		for (int ndx = 0; ndx < 95; ndx++)
1246			inputs.push_back(rnd.getUint32());
1247
1248		outputs.resize(inputs.size());
1249
1250		m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
1251
1252		{
1253			const void*	in	= &inputs[0];
1254			void*		out	= &outputs[0];
1255
1256			m_executor->execute((int)inputs.size(), &in, &out);
1257		}
1258
1259		// Verify
1260		{
1261			const int	numValues	= (int)inputs.size();
1262			const int	maxPrints	= 10;
1263			int			numFailed	= 0;
1264
1265			for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
1266			{
1267				const deUint8	in0		= (deUint8)(inputs[valNdx] & 0xff);
1268				const deUint8	in1		= (deUint8)((inputs[valNdx] >> 8) & 0xff);
1269				const deUint8	in2		= (deUint8)((inputs[valNdx] >> 16) & 0xff);
1270				const deUint8	in3		= (deUint8)(inputs[valNdx] >> 24);
1271				const float		ref0	= de::clamp(float(in0) / 255.f, 0.0f, 1.0f);
1272				const float		ref1	= de::clamp(float(in1) / 255.f, 0.0f, 1.0f);
1273				const float		ref2	= de::clamp(float(in2) / 255.f, 0.0f, 1.0f);
1274				const float		ref3	= de::clamp(float(in3) / 255.f, 0.0f, 1.0f);
1275				const float		res0	= outputs[valNdx].x();
1276				const float		res1	= outputs[valNdx].y();
1277				const float		res2	= outputs[valNdx].z();
1278				const float		res3	= outputs[valNdx].w();
1279
1280				const deUint32	diff0	= getUlpDiff(ref0, res0);
1281				const deUint32	diff1	= getUlpDiff(ref1, res1);
1282				const deUint32	diff2	= getUlpDiff(ref2, res2);
1283				const deUint32	diff3	= getUlpDiff(ref3, res3);
1284
1285				if (diff0 > maxDiff || diff1 > maxDiff || diff2 > maxDiff || diff3 > maxDiff)
1286				{
1287					if (numFailed < maxPrints)
1288					{
1289						m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
1290															   << "  expected unpackUnorm4x8(" << tcu::toHex(inputs[valNdx]) << ") = "
1291															   << "vec4(" << HexFloat(ref0) << ", " << HexFloat(ref1) << ", " << HexFloat(ref2) << ", " << HexFloat(ref3) << ")"
1292															   << ", got vec4(" << HexFloat(res0) << ", " << HexFloat(res1) << ", " << HexFloat(res2) << ", " << HexFloat(res3) << ")"
1293															   << "\n  ULP diffs = (" << diff0 << ", " << diff1 << ", " << diff2 << ", " << diff3 << "), max diff = " << maxDiff
1294										   << TestLog::EndMessage;
1295					}
1296					else if (numFailed == maxPrints)
1297						m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
1298
1299					numFailed += 1;
1300				}
1301			}
1302
1303			m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
1304
1305			if (numFailed == 0)
1306				return tcu::TestStatus::pass("Pass");
1307			else
1308				return tcu::TestStatus::fail("Result comparison failed");
1309
1310		}
1311	}
1312};
1313
1314class UnpackUnorm4x8Case : public ShaderPackingFunctionCase
1315{
1316public:
1317	UnpackUnorm4x8Case (tcu::TestContext& testCtx, glu::ShaderType shaderType)
1318		: ShaderPackingFunctionCase	(testCtx, (string("unpackunorm4x8") + getShaderTypePostfix(shaderType)).c_str(), "unpackUnorm4x8", shaderType)
1319	{
1320		m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
1321		m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP)));
1322
1323		m_spec.source = "out0 = unpackUnorm4x8(in0);";
1324	}
1325
1326	TestInstance* createInstance (Context& ctx) const
1327	{
1328		return new UnpackUnorm4x8CaseInstance(ctx, m_shaderType, m_spec, getName());
1329	}
1330
1331};
1332
1333ShaderPackingFunctionTests::ShaderPackingFunctionTests (tcu::TestContext& testCtx)
1334	: tcu::TestCaseGroup	(testCtx, "pack_unpack", "Floating-point pack and unpack function tests")
1335{
1336}
1337
1338ShaderPackingFunctionTests::~ShaderPackingFunctionTests (void)
1339{
1340}
1341
1342void ShaderPackingFunctionTests::init (void)
1343{
1344	// New built-in functions in GLES 3.1
1345	{
1346		const glu::ShaderType allShaderTypes[] =
1347		{
1348			glu::SHADERTYPE_VERTEX,
1349			glu::SHADERTYPE_TESSELLATION_CONTROL,
1350			glu::SHADERTYPE_TESSELLATION_EVALUATION,
1351			glu::SHADERTYPE_GEOMETRY,
1352			glu::SHADERTYPE_FRAGMENT,
1353			glu::SHADERTYPE_COMPUTE
1354		};
1355
1356		// packSnorm4x8
1357		for (int prec = glu::PRECISION_MEDIUMP; prec < glu::PRECISION_LAST; prec++)
1358		{
1359			for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(allShaderTypes); shaderTypeNdx++)
1360				addChild(new PackSnorm4x8Case(m_testCtx, allShaderTypes[shaderTypeNdx], glu::Precision(prec)));
1361		}
1362
1363		// unpackSnorm4x8
1364		for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(allShaderTypes); shaderTypeNdx++)
1365			addChild(new UnpackSnorm4x8Case(m_testCtx, allShaderTypes[shaderTypeNdx]));
1366
1367		// packUnorm4x8
1368		for (int prec = glu::PRECISION_MEDIUMP; prec < glu::PRECISION_LAST; prec++)
1369		{
1370			for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(allShaderTypes); shaderTypeNdx++)
1371				addChild(new PackUnorm4x8Case(m_testCtx, allShaderTypes[shaderTypeNdx], glu::Precision(prec)));
1372		}
1373
1374		// unpackUnorm4x8
1375		for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(allShaderTypes); shaderTypeNdx++)
1376			addChild(new UnpackUnorm4x8Case(m_testCtx, allShaderTypes[shaderTypeNdx]));
1377	}
1378
1379	// GLES 3 functions in new shader types.
1380	{
1381		const glu::ShaderType newShaderTypes[] =
1382		{
1383			glu::SHADERTYPE_GEOMETRY,
1384			glu::SHADERTYPE_COMPUTE
1385		};
1386
1387		// packSnorm2x16
1388		for (int prec = glu::PRECISION_MEDIUMP; prec < glu::PRECISION_LAST; prec++)
1389		{
1390			for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
1391				addChild(new PackSnorm2x16Case(m_testCtx, newShaderTypes[shaderTypeNdx], glu::Precision(prec)));
1392		}
1393
1394		// unpackSnorm2x16
1395		for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
1396			addChild(new UnpackSnorm2x16Case(m_testCtx, newShaderTypes[shaderTypeNdx]));
1397
1398		// packUnorm2x16
1399		for (int prec = glu::PRECISION_MEDIUMP; prec < glu::PRECISION_LAST; prec++)
1400		{
1401			for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
1402				addChild(new PackUnorm2x16Case(m_testCtx, newShaderTypes[shaderTypeNdx], glu::Precision(prec)));
1403		}
1404
1405		// unpackUnorm2x16
1406		for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
1407			addChild(new UnpackUnorm2x16Case(m_testCtx, newShaderTypes[shaderTypeNdx]));
1408
1409		// packHalf2x16
1410		for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
1411			addChild(new PackHalf2x16Case(m_testCtx, newShaderTypes[shaderTypeNdx]));
1412
1413		// unpackHalf2x16
1414		for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
1415			addChild(new UnpackHalf2x16Case(m_testCtx, newShaderTypes[shaderTypeNdx]));
1416	}
1417}
1418
1419} // shaderexecutor
1420} // vkt
1421