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