1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.0 Module
3 * -------------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Floating-point packing and unpacking function tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "es3fShaderPackingFunctionTests.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 gles3
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_300_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	};
140	DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
141	return s_postfix[shaderType];
142}
143
144class PackSnorm2x16Case : public ShaderPackingFunctionCase
145{
146public:
147	PackSnorm2x16Case (Context& context, glu::ShaderType shaderType, glu::Precision precision)
148		: ShaderPackingFunctionCase	(context, (string("packsnorm2x16") + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType)).c_str(), "packSnorm2x16", shaderType)
149		, m_precision				(precision)
150	{
151		m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC2, precision)));
152		m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
153
154		m_spec.source = "out0 = packSnorm2x16(in0);";
155	}
156
157	IterateResult iterate (void)
158	{
159		de::Random					rnd			(deStringHash(getName()) ^ 0x776002);
160		std::vector<tcu::Vec2>		inputs;
161		std::vector<deUint32>		outputs;
162		const int					maxDiff		= m_precision == glu::PRECISION_HIGHP	? 1		:		// Rounding only.
163												  m_precision == glu::PRECISION_MEDIUMP	? 33	:		// (2^-10) * (2^15) + 1
164												  m_precision == glu::PRECISION_LOWP	? 129	: 0;	// (2^-8) * (2^15) + 1
165
166		// Special values to check.
167		inputs.push_back(tcu::Vec2(0.0f, 0.0f));
168		inputs.push_back(tcu::Vec2(-1.0f, 1.0f));
169		inputs.push_back(tcu::Vec2(0.5f, -0.5f));
170		inputs.push_back(tcu::Vec2(-1.5f, 1.5f));
171		inputs.push_back(tcu::Vec2(0.25f, -0.75f));
172
173		// Random values, mostly in range.
174		for (int ndx = 0; ndx < 15; ndx++)
175		{
176			const float x = rnd.getFloat()*2.5f - 1.25f;
177			const float y = rnd.getFloat()*2.5f - 1.25f;
178			inputs.push_back(tcu::Vec2(x, y));
179		}
180
181		// Large random values.
182		for (int ndx = 0; ndx < 80; ndx++)
183		{
184			const float x = rnd.getFloat()*1e6f - 0.5e6f;
185			const float y = rnd.getFloat()*1e6f - 0.5e6f;
186			inputs.push_back(tcu::Vec2(x, y));
187		}
188
189		outputs.resize(inputs.size());
190
191		m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
192
193		{
194			const void*	in	= &inputs[0];
195			void*		out	= &outputs[0];
196
197			m_executor->useProgram();
198			m_executor->execute((int)inputs.size(), &in, &out);
199		}
200
201		// Verify
202		{
203			const int	numValues	= (int)inputs.size();
204			const int	maxPrints	= 10;
205			int			numFailed	= 0;
206
207			for (int valNdx = 0; valNdx < numValues; valNdx++)
208			{
209				const deUint16	ref0	= (deUint16)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].x(), -1.0f, 1.0f) * 32767.0f), -(1<<15), (1<<15)-1);
210				const deUint16	ref1	= (deUint16)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].y(), -1.0f, 1.0f) * 32767.0f), -(1<<15), (1<<15)-1);
211				const deUint32	ref		= (ref1 << 16) | ref0;
212				const deUint32	res		= outputs[valNdx];
213				const deUint16	res0	= (deUint16)(res & 0xffff);
214				const deUint16	res1	= (deUint16)(res >> 16);
215				const int		diff0	= de::abs((int)ref0 - (int)res0);
216				const int		diff1	= de::abs((int)ref1 - (int)res1);
217
218				if (diff0 > maxDiff || diff1 > maxDiff)
219				{
220					if (numFailed < maxPrints)
221					{
222						m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
223															   << ", expected packSnorm2x16(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
224															   << ", got " << tcu::toHex(res)
225															   << "\n  diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
226										   << TestLog::EndMessage;
227					}
228					else if (numFailed == maxPrints)
229						m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
230
231					numFailed += 1;
232				}
233			}
234
235			m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
236
237			m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
238									numFailed == 0 ? "Pass"					: "Result comparison failed");
239		}
240
241		return STOP;
242	}
243
244private:
245	glu::Precision m_precision;
246};
247
248class UnpackSnorm2x16Case : public ShaderPackingFunctionCase
249{
250public:
251	UnpackSnorm2x16Case (Context& context, glu::ShaderType shaderType)
252		: ShaderPackingFunctionCase(context, (string("unpacksnorm2x16") + getShaderTypePostfix(shaderType)).c_str(), "unpackSnorm2x16", shaderType)
253	{
254		m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
255		m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_HIGHP)));
256
257		m_spec.source = "out0 = unpackSnorm2x16(in0);";
258	}
259
260	IterateResult iterate (void)
261	{
262		const deUint32				maxDiff		= 1; // Rounding error.
263		de::Random					rnd			(deStringHash(getName()) ^ 0x776002);
264		std::vector<deUint32>		inputs;
265		std::vector<tcu::Vec2>		outputs;
266
267		inputs.push_back(0x00000000u);
268		inputs.push_back(0x7fff8000u);
269		inputs.push_back(0x80007fffu);
270		inputs.push_back(0xffffffffu);
271		inputs.push_back(0x0001fffeu);
272
273		// Random values.
274		for (int ndx = 0; ndx < 95; ndx++)
275			inputs.push_back(rnd.getUint32());
276
277		outputs.resize(inputs.size());
278
279		m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
280
281		{
282			const void*	in	= &inputs[0];
283			void*		out	= &outputs[0];
284
285			m_executor->useProgram();
286			m_executor->execute((int)inputs.size(), &in, &out);
287		}
288
289		// Verify
290		{
291			const int	numValues	= (int)inputs.size();
292			const int	maxPrints	= 10;
293			int			numFailed	= 0;
294
295			for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
296			{
297				const deInt16	in0			= (deInt16)(deUint16)(inputs[valNdx] & 0xffff);
298				const deInt16	in1			= (deInt16)(deUint16)(inputs[valNdx] >> 16);
299				const float		ref0		= de::clamp(float(in0) / 32767.f, -1.0f, 1.0f);
300				const float		ref1		= de::clamp(float(in1) / 32767.f, -1.0f, 1.0f);
301				const float		res0		= outputs[valNdx].x();
302				const float		res1		= outputs[valNdx].y();
303
304				const deUint32	diff0	= getUlpDiff(ref0, res0);
305				const deUint32	diff1	= getUlpDiff(ref1, res1);
306
307				if (diff0 > maxDiff || diff1 > maxDiff)
308				{
309					if (numFailed < maxPrints)
310					{
311						m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
312															   << "  expected unpackSnorm2x16(" << tcu::toHex(inputs[valNdx]) << ") = "
313															   << "vec2(" << HexFloat(ref0) << ", " << HexFloat(ref1) << ")"
314															   << ", got vec2(" << HexFloat(res0) << ", " << HexFloat(res1) << ")"
315															   << "\n  ULP diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
316										   << TestLog::EndMessage;
317					}
318					else if (numFailed == maxPrints)
319						m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
320
321					numFailed += 1;
322				}
323			}
324
325			m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
326
327			m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
328									numFailed == 0 ? "Pass"					: "Result comparison failed");
329		}
330
331		return STOP;
332	}
333};
334
335class PackUnorm2x16Case : public ShaderPackingFunctionCase
336{
337public:
338	PackUnorm2x16Case (Context& context, glu::ShaderType shaderType, glu::Precision precision)
339		: ShaderPackingFunctionCase	(context, (string("packunorm2x16") + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType)).c_str(), "packUnorm2x16", shaderType)
340		, m_precision				(precision)
341	{
342		m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC2, precision)));
343		m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
344
345		m_spec.source = "out0 = packUnorm2x16(in0);";
346	}
347
348	IterateResult iterate (void)
349	{
350		de::Random					rnd			(deStringHash(getName()) ^ 0x776002);
351		std::vector<tcu::Vec2>		inputs;
352		std::vector<deUint32>		outputs;
353		const int					maxDiff		= m_precision == glu::PRECISION_HIGHP	? 1		:		// Rounding only.
354												  m_precision == glu::PRECISION_MEDIUMP	? 65	:		// (2^-10) * (2^16) + 1
355												  m_precision == glu::PRECISION_LOWP	? 257	: 0;	// (2^-8) * (2^16) + 1
356
357		// Special values to check.
358		inputs.push_back(tcu::Vec2(0.0f, 0.0f));
359		inputs.push_back(tcu::Vec2(0.5f, 1.0f));
360		inputs.push_back(tcu::Vec2(1.0f, 0.5f));
361		inputs.push_back(tcu::Vec2(-0.5f, 1.5f));
362		inputs.push_back(tcu::Vec2(0.25f, 0.75f));
363
364		// Random values, mostly in range.
365		for (int ndx = 0; ndx < 15; ndx++)
366		{
367			const float x = rnd.getFloat()*1.25f;
368			const float y = rnd.getFloat()*1.25f;
369			inputs.push_back(tcu::Vec2(x, y));
370		}
371
372		// Large random values.
373		for (int ndx = 0; ndx < 80; ndx++)
374		{
375			const float x = rnd.getFloat()*1e6f - 1e5f;
376			const float y = rnd.getFloat()*1e6f - 1e5f;
377			inputs.push_back(tcu::Vec2(x, y));
378		}
379
380		outputs.resize(inputs.size());
381
382		m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
383
384		{
385			const void*	in	= &inputs[0];
386			void*		out	= &outputs[0];
387
388			m_executor->useProgram();
389			m_executor->execute((int)inputs.size(), &in, &out);
390		}
391
392		// Verify
393		{
394			const int	numValues	= (int)inputs.size();
395			const int	maxPrints	= 10;
396			int			numFailed	= 0;
397
398			for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
399			{
400				const deUint16	ref0	= (deUint16)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].x(), 0.0f, 1.0f) * 65535.0f), 0, (1<<16)-1);
401				const deUint16	ref1	= (deUint16)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].y(), 0.0f, 1.0f) * 65535.0f), 0, (1<<16)-1);
402				const deUint32	ref		= (ref1 << 16) | ref0;
403				const deUint32	res		= outputs[valNdx];
404				const deUint16	res0	= (deUint16)(res & 0xffff);
405				const deUint16	res1	= (deUint16)(res >> 16);
406				const int		diff0	= de::abs((int)ref0 - (int)res0);
407				const int		diff1	= de::abs((int)ref1 - (int)res1);
408
409				if (diff0 > maxDiff || diff1 > maxDiff)
410				{
411					if (numFailed < maxPrints)
412					{
413						m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
414															   << ", expected packUnorm2x16(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
415															   << ", got " << tcu::toHex(res)
416															   << "\n  diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
417										   << TestLog::EndMessage;
418					}
419					else if (numFailed == maxPrints)
420						m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
421
422					numFailed += 1;
423				}
424			}
425
426			m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
427
428			m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
429									numFailed == 0 ? "Pass"					: "Result comparison failed");
430		}
431
432		return STOP;
433	}
434
435private:
436	glu::Precision m_precision;
437};
438
439class UnpackUnorm2x16Case : public ShaderPackingFunctionCase
440{
441public:
442	UnpackUnorm2x16Case (Context& context, glu::ShaderType shaderType)
443		: ShaderPackingFunctionCase(context, (string("unpackunorm2x16") + getShaderTypePostfix(shaderType)).c_str(), "unpackUnorm2x16", shaderType)
444	{
445		m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
446		m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_HIGHP)));
447
448		m_spec.source = "out0 = unpackUnorm2x16(in0);";
449	}
450
451	IterateResult iterate (void)
452	{
453		const deUint32				maxDiff		= 1; // Rounding error.
454		de::Random					rnd			(deStringHash(getName()) ^ 0x776002);
455		std::vector<deUint32>		inputs;
456		std::vector<tcu::Vec2>		outputs;
457
458		inputs.push_back(0x00000000u);
459		inputs.push_back(0x7fff8000u);
460		inputs.push_back(0x80007fffu);
461		inputs.push_back(0xffffffffu);
462		inputs.push_back(0x0001fffeu);
463
464		// Random values.
465		for (int ndx = 0; ndx < 95; ndx++)
466			inputs.push_back(rnd.getUint32());
467
468		outputs.resize(inputs.size());
469
470		m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
471
472		{
473			const void*	in	= &inputs[0];
474			void*		out	= &outputs[0];
475
476			m_executor->useProgram();
477			m_executor->execute((int)inputs.size(), &in, &out);
478		}
479
480		// Verify
481		{
482			const int	numValues	= (int)inputs.size();
483			const int	maxPrints	= 10;
484			int			numFailed	= 0;
485
486			for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
487			{
488				const deUint16	in0			= (deUint16)(inputs[valNdx] & 0xffff);
489				const deUint16	in1			= (deUint16)(inputs[valNdx] >> 16);
490				const float		ref0		= float(in0) / 65535.0f;
491				const float		ref1		= float(in1) / 65535.0f;
492				const float		res0		= outputs[valNdx].x();
493				const float		res1		= outputs[valNdx].y();
494
495				const deUint32	diff0		= getUlpDiff(ref0, res0);
496				const deUint32	diff1		= getUlpDiff(ref1, res1);
497
498				if (diff0 > maxDiff || diff1 > maxDiff)
499				{
500					if (numFailed < maxPrints)
501					{
502						m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
503															   << "  expected unpackUnorm2x16(" << tcu::toHex(inputs[valNdx]) << ") = "
504															   << "vec2(" << HexFloat(ref0) << ", " << HexFloat(ref1) << ")"
505															   << ", got vec2(" << HexFloat(res0) << ", " << HexFloat(res1) << ")"
506															   << "\n  ULP diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
507										   << TestLog::EndMessage;
508					}
509					else if (numFailed == maxPrints)
510						m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
511
512					numFailed += 1;
513				}
514			}
515
516			m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
517
518			m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
519									numFailed == 0 ? "Pass"					: "Result comparison failed");
520		}
521
522		return STOP;
523	}
524};
525
526class PackHalf2x16Case : public ShaderPackingFunctionCase
527{
528public:
529	PackHalf2x16Case (Context& context, glu::ShaderType shaderType)
530		: ShaderPackingFunctionCase(context, (string("packhalf2x16") + getShaderTypePostfix(shaderType)).c_str(), "packHalf2x16", shaderType)
531	{
532		m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_HIGHP)));
533		m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
534
535		m_spec.source = "out0 = packHalf2x16(in0);";
536	}
537
538	IterateResult iterate (void)
539	{
540		const int					maxDiff		= 0; // Values can be represented exactly in mediump.
541		de::Random					rnd			(deStringHash(getName()) ^ 0x776002);
542		std::vector<tcu::Vec2>		inputs;
543		std::vector<deUint32>		outputs;
544
545		// Special values to check.
546		inputs.push_back(tcu::Vec2(0.0f, 0.0f));
547		inputs.push_back(tcu::Vec2(0.5f, 1.0f));
548		inputs.push_back(tcu::Vec2(1.0f, 0.5f));
549		inputs.push_back(tcu::Vec2(-0.5f, 1.5f));
550		inputs.push_back(tcu::Vec2(0.25f, 0.75f));
551
552		// Random values.
553		{
554			const int	minExp	= -14;
555			const int	maxExp	= 15;
556
557			for (int ndx = 0; ndx < 95; ndx++)
558			{
559				tcu::Vec2 v;
560				for (int c = 0; c < 2; c++)
561				{
562					const int		s			= rnd.getBool() ? 1 : -1;
563					const int		exp			= rnd.getInt(minExp, maxExp);
564					const deUint32	mantissa	= rnd.getUint32() & ((1<<23)-1);
565
566					v[c] = tcu::Float32::construct(s, exp ? exp : 1 /* avoid denormals */, (1u<<23) | mantissa).asFloat();
567				}
568				inputs.push_back(v);
569			}
570		}
571
572		// Convert input values to fp16 and back to make sure they can be represented exactly in mediump.
573		for (std::vector<tcu::Vec2>::iterator inVal = inputs.begin(); inVal != inputs.end(); ++inVal)
574			*inVal = tcu::Vec2(tcu::Float16(inVal->x()).asFloat(), tcu::Float16(inVal->y()).asFloat());
575
576		outputs.resize(inputs.size());
577
578		m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
579
580		{
581			const void*	in	= &inputs[0];
582			void*		out	= &outputs[0];
583
584			m_executor->useProgram();
585			m_executor->execute((int)inputs.size(), &in, &out);
586		}
587
588		// Verify
589		{
590			const int	numValues	= (int)inputs.size();
591			const int	maxPrints	= 10;
592			int			numFailed	= 0;
593
594			for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
595			{
596				const deUint16	ref0	= (deUint16)tcu::Float16(inputs[valNdx].x()).bits();
597				const deUint16	ref1	= (deUint16)tcu::Float16(inputs[valNdx].y()).bits();
598				const deUint32	ref		= (ref1 << 16) | ref0;
599				const deUint32	res		= outputs[valNdx];
600				const deUint16	res0	= (deUint16)(res & 0xffff);
601				const deUint16	res1	= (deUint16)(res >> 16);
602				const int		diff0	= de::abs((int)ref0 - (int)res0);
603				const int		diff1	= de::abs((int)ref1 - (int)res1);
604
605				if (diff0 > maxDiff || diff1 > maxDiff)
606				{
607					if (numFailed < maxPrints)
608					{
609						m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
610															   << ", expected packHalf2x16(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
611															   << ", got " << tcu::toHex(res)
612															   << "\n  diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
613										   << TestLog::EndMessage;
614					}
615					else if (numFailed == maxPrints)
616						m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
617
618					numFailed += 1;
619				}
620			}
621
622			m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
623
624			m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
625									numFailed == 0 ? "Pass"					: "Result comparison failed");
626		}
627
628		return STOP;
629	}
630};
631
632class UnpackHalf2x16Case : public ShaderPackingFunctionCase
633{
634public:
635	UnpackHalf2x16Case (Context& context, glu::ShaderType shaderType)
636		: ShaderPackingFunctionCase(context, (string("unpackhalf2x16") + getShaderTypePostfix(shaderType)).c_str(), "unpackHalf2x16", shaderType)
637	{
638		m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
639		m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_MEDIUMP)));
640
641		m_spec.source = "out0 = unpackHalf2x16(in0);";
642	}
643
644	IterateResult iterate (void)
645	{
646		const int					maxDiff		= 0; // All bits must be accurate.
647		de::Random					rnd			(deStringHash(getName()) ^ 0x776002);
648		std::vector<deUint32>		inputs;
649		std::vector<tcu::Vec2>		outputs;
650
651		// Special values.
652		inputs.push_back((tcu::Float16( 0.0f).bits() << 16) | tcu::Float16( 1.0f).bits());
653		inputs.push_back((tcu::Float16( 1.0f).bits() << 16) | tcu::Float16( 0.0f).bits());
654		inputs.push_back((tcu::Float16(-1.0f).bits() << 16) | tcu::Float16( 0.5f).bits());
655		inputs.push_back((tcu::Float16( 0.5f).bits() << 16) | tcu::Float16(-0.5f).bits());
656
657		// Construct random values.
658		{
659			const int	minExp		= -14;
660			const int	maxExp		= 15;
661			const int	mantBits	= 10;
662
663			for (int ndx = 0; ndx < 96; ndx++)
664			{
665				deUint32 inVal = 0;
666				for (int c = 0; c < 2; c++)
667				{
668					const int		s			= rnd.getBool() ? 1 : -1;
669					const int		exp			= rnd.getInt(minExp, maxExp);
670					const deUint32	mantissa	= rnd.getUint32() & ((1<<mantBits)-1);
671					const deUint16	value		= tcu::Float16::construct(s, exp ? exp : 1 /* avoid denorm */, (1u<<10) | mantissa).bits();
672
673					inVal |= value << (16*c);
674				}
675				inputs.push_back(inVal);
676			}
677		}
678
679		outputs.resize(inputs.size());
680
681		m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
682
683		{
684			const void*	in	= &inputs[0];
685			void*		out	= &outputs[0];
686
687			m_executor->useProgram();
688			m_executor->execute((int)inputs.size(), &in, &out);
689		}
690
691		// Verify
692		{
693			const int	numValues	= (int)inputs.size();
694			const int	maxPrints	= 10;
695			int			numFailed	= 0;
696
697			for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
698			{
699				const deUint16	in0			= (deUint16)(inputs[valNdx] & 0xffff);
700				const deUint16	in1			= (deUint16)(inputs[valNdx] >> 16);
701				const float		ref0		= tcu::Float16(in0).asFloat();
702				const float		ref1		= tcu::Float16(in1).asFloat();
703				const float		res0		= outputs[valNdx].x();
704				const float		res1		= outputs[valNdx].y();
705
706				const deUint32	refBits0	= tcu::Float32(ref0).bits();
707				const deUint32	refBits1	= tcu::Float32(ref1).bits();
708				const deUint32	resBits0	= tcu::Float32(res0).bits();
709				const deUint32	resBits1	= tcu::Float32(res1).bits();
710
711				const int		diff0	= de::abs((int)refBits0 - (int)resBits0);
712				const int		diff1	= de::abs((int)refBits1 - (int)resBits1);
713
714				if (diff0 > maxDiff || diff1 > maxDiff)
715				{
716					if (numFailed < maxPrints)
717					{
718						m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
719															   << "  expected unpackHalf2x16(" << tcu::toHex(inputs[valNdx]) << ") = "
720															   << "vec2(" << ref0 << " / " << tcu::toHex(refBits0) << ", " << ref1 << " / " << tcu::toHex(refBits1) << ")"
721															   << ", got vec2(" << res0 << " / " << tcu::toHex(resBits0) << ", " << res1 << " / " << tcu::toHex(resBits1) << ")"
722															   << "\n  ULP diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
723										   << TestLog::EndMessage;
724					}
725					else if (numFailed == maxPrints)
726						m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
727
728					numFailed += 1;
729				}
730			}
731
732			m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
733
734			m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
735									numFailed == 0 ? "Pass"					: "Result comparison failed");
736		}
737
738		return STOP;
739	}
740};
741
742ShaderPackingFunctionTests::ShaderPackingFunctionTests (Context& context)
743	: TestCaseGroup(context, "pack_unpack", "Floating-point pack and unpack function tests")
744{
745}
746
747ShaderPackingFunctionTests::~ShaderPackingFunctionTests (void)
748{
749}
750
751void ShaderPackingFunctionTests::init (void)
752{
753	addChild(new PackSnorm2x16Case	(m_context, glu::SHADERTYPE_VERTEX,		glu::PRECISION_LOWP));
754	addChild(new PackSnorm2x16Case	(m_context, glu::SHADERTYPE_FRAGMENT,	glu::PRECISION_LOWP));
755	addChild(new PackSnorm2x16Case	(m_context, glu::SHADERTYPE_VERTEX,		glu::PRECISION_MEDIUMP));
756	addChild(new PackSnorm2x16Case	(m_context, glu::SHADERTYPE_FRAGMENT,	glu::PRECISION_MEDIUMP));
757	addChild(new PackSnorm2x16Case	(m_context, glu::SHADERTYPE_VERTEX,		glu::PRECISION_HIGHP));
758	addChild(new PackSnorm2x16Case	(m_context, glu::SHADERTYPE_FRAGMENT,	glu::PRECISION_HIGHP));
759
760	addChild(new UnpackSnorm2x16Case(m_context, glu::SHADERTYPE_VERTEX));
761	addChild(new UnpackSnorm2x16Case(m_context, glu::SHADERTYPE_FRAGMENT));
762
763	addChild(new PackUnorm2x16Case	(m_context, glu::SHADERTYPE_VERTEX,		glu::PRECISION_LOWP));
764	addChild(new PackUnorm2x16Case	(m_context, glu::SHADERTYPE_FRAGMENT,	glu::PRECISION_LOWP));
765	addChild(new PackUnorm2x16Case	(m_context, glu::SHADERTYPE_VERTEX,		glu::PRECISION_MEDIUMP));
766	addChild(new PackUnorm2x16Case	(m_context, glu::SHADERTYPE_FRAGMENT,	glu::PRECISION_MEDIUMP));
767	addChild(new PackUnorm2x16Case	(m_context, glu::SHADERTYPE_VERTEX,		glu::PRECISION_HIGHP));
768	addChild(new PackUnorm2x16Case	(m_context, glu::SHADERTYPE_FRAGMENT,	glu::PRECISION_HIGHP));
769
770	addChild(new UnpackUnorm2x16Case(m_context, glu::SHADERTYPE_VERTEX));
771	addChild(new UnpackUnorm2x16Case(m_context, glu::SHADERTYPE_FRAGMENT));
772
773	addChild(new PackHalf2x16Case	(m_context, glu::SHADERTYPE_VERTEX));
774	addChild(new PackHalf2x16Case	(m_context, glu::SHADERTYPE_FRAGMENT));
775
776	addChild(new UnpackHalf2x16Case	(m_context, glu::SHADERTYPE_VERTEX));
777	addChild(new UnpackHalf2x16Case	(m_context, glu::SHADERTYPE_FRAGMENT));
778}
779
780} // Functional
781} // gles3
782} // deqp
783