• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::__anon95c574d10111::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 
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 
ShaderPackingFunctionTests(Context & context)718 ShaderPackingFunctionTests::ShaderPackingFunctionTests (Context& context)
719 	: TestCaseGroup(context, "pack_unpack", "Floating-point pack and unpack function tests")
720 {
721 }
722 
~ShaderPackingFunctionTests(void)723 ShaderPackingFunctionTests::~ShaderPackingFunctionTests (void)
724 {
725 }
726 
init(void)727 void ShaderPackingFunctionTests::init (void)
728 {
729 	addChild(new PackSnorm2x16Case	(m_context, glu::SHADERTYPE_VERTEX,		glu::PRECISION_LOWP));
730 	addChild(new PackSnorm2x16Case	(m_context, glu::SHADERTYPE_FRAGMENT,	glu::PRECISION_LOWP));
731 	addChild(new PackSnorm2x16Case	(m_context, glu::SHADERTYPE_VERTEX,		glu::PRECISION_MEDIUMP));
732 	addChild(new PackSnorm2x16Case	(m_context, glu::SHADERTYPE_FRAGMENT,	glu::PRECISION_MEDIUMP));
733 	addChild(new PackSnorm2x16Case	(m_context, glu::SHADERTYPE_VERTEX,		glu::PRECISION_HIGHP));
734 	addChild(new PackSnorm2x16Case	(m_context, glu::SHADERTYPE_FRAGMENT,	glu::PRECISION_HIGHP));
735 
736 	addChild(new UnpackSnorm2x16Case(m_context, glu::SHADERTYPE_VERTEX));
737 	addChild(new UnpackSnorm2x16Case(m_context, glu::SHADERTYPE_FRAGMENT));
738 
739 	addChild(new PackUnorm2x16Case	(m_context, glu::SHADERTYPE_VERTEX,		glu::PRECISION_LOWP));
740 	addChild(new PackUnorm2x16Case	(m_context, glu::SHADERTYPE_FRAGMENT,	glu::PRECISION_LOWP));
741 	addChild(new PackUnorm2x16Case	(m_context, glu::SHADERTYPE_VERTEX,		glu::PRECISION_MEDIUMP));
742 	addChild(new PackUnorm2x16Case	(m_context, glu::SHADERTYPE_FRAGMENT,	glu::PRECISION_MEDIUMP));
743 	addChild(new PackUnorm2x16Case	(m_context, glu::SHADERTYPE_VERTEX,		glu::PRECISION_HIGHP));
744 	addChild(new PackUnorm2x16Case	(m_context, glu::SHADERTYPE_FRAGMENT,	glu::PRECISION_HIGHP));
745 
746 	addChild(new UnpackUnorm2x16Case(m_context, glu::SHADERTYPE_VERTEX));
747 	addChild(new UnpackUnorm2x16Case(m_context, glu::SHADERTYPE_FRAGMENT));
748 
749 	addChild(new PackHalf2x16Case	(m_context, glu::SHADERTYPE_VERTEX));
750 	addChild(new PackHalf2x16Case	(m_context, glu::SHADERTYPE_FRAGMENT));
751 
752 	addChild(new UnpackHalf2x16Case	(m_context, glu::SHADERTYPE_VERTEX));
753 	addChild(new UnpackHalf2x16Case	(m_context, glu::SHADERTYPE_FRAGMENT));
754 }
755 
756 } // Functional
757 } // gles3
758 } // deqp
759