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 
33 namespace deqp
34 {
35 namespace gles3
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::gles3::Functional::__anonb1ef44700111::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::GLSL_VERSION_300_ES;
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 
getPrecisionPostfix(glu::Precision precision)120 static 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 
getShaderTypePostfix(glu::ShaderType shaderType)133 static 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 
144 class PackSnorm2x16Case : public ShaderPackingFunctionCase
145 {
146 public:
PackSnorm2x16Case(Context & context,glu::ShaderType shaderType,glu::Precision precision)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 
iterate(void)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 
244 private:
245 	glu::Precision m_precision;
246 };
247 
248 class UnpackSnorm2x16Case : public ShaderPackingFunctionCase
249 {
250 public:
UnpackSnorm2x16Case(Context & context,glu::ShaderType shaderType)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 
iterate(void)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 
335 class PackUnorm2x16Case : public ShaderPackingFunctionCase
336 {
337 public:
PackUnorm2x16Case(Context & context,glu::ShaderType shaderType,glu::Precision precision)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 
iterate(void)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 
435 private:
436 	glu::Precision m_precision;
437 };
438 
439 class UnpackUnorm2x16Case : public ShaderPackingFunctionCase
440 {
441 public:
UnpackUnorm2x16Case(Context & context,glu::ShaderType shaderType)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 
iterate(void)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 
526 class PackHalf2x16Case : public ShaderPackingFunctionCase
527 {
528 public:
PackHalf2x16Case(Context & context,glu::ShaderType shaderType)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 
iterate(void)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 
632 class UnpackHalf2x16Case : public ShaderPackingFunctionCase
633 {
634 public:
UnpackHalf2x16Case(Context & context,glu::ShaderType shaderType)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 
iterate(void)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 */, (deUint16)((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 
ShaderPackingFunctionTests(Context & context)742 ShaderPackingFunctionTests::ShaderPackingFunctionTests (Context& context)
743 	: TestCaseGroup(context, "pack_unpack", "Floating-point pack and unpack function tests")
744 {
745 }
746 
~ShaderPackingFunctionTests(void)747 ShaderPackingFunctionTests::~ShaderPackingFunctionTests (void)
748 {
749 }
750 
init(void)751 void 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