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