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