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 Shader precision tests.
     22  *
     23  * \note Floating-point case uses R32UI render target and uses
     24  *		 floatBitsToUint() in shader to write out floating-point value bits.
     25  *		 This is done since ES3 core doesn't support FP render targets.
     26  *//*--------------------------------------------------------------------*/
     27 
     28 #include "es3fShaderPrecisionTests.hpp"
     29 #include "tcuVector.hpp"
     30 #include "tcuTestLog.hpp"
     31 #include "tcuVectorUtil.hpp"
     32 #include "tcuFloat.hpp"
     33 #include "tcuFormatUtil.hpp"
     34 #include "gluRenderContext.hpp"
     35 #include "gluShaderProgram.hpp"
     36 #include "gluShaderUtil.hpp"
     37 #include "gluDrawUtil.hpp"
     38 #include "deRandom.hpp"
     39 #include "deString.h"
     40 
     41 #include "glwEnums.hpp"
     42 #include "glwFunctions.hpp"
     43 
     44 #include <algorithm>
     45 
     46 namespace deqp
     47 {
     48 namespace gles3
     49 {
     50 namespace Functional
     51 {
     52 
     53 using std::string;
     54 using std::vector;
     55 using std::ostringstream;
     56 using tcu::TestLog;
     57 
     58 enum
     59 {
     60 	FRAMEBUFFER_WIDTH	= 32,
     61 	FRAMEBUFFER_HEIGHT	= 32
     62 };
     63 
     64 static glu::ShaderProgram* createFloatPrecisionEvalProgram (const glu::RenderContext& context, glu::Precision precision, const char* evalOp, bool isVertexCase)
     65 {
     66 	glu::DataType	type		= glu::TYPE_FLOAT;
     67 	glu::DataType	outType		= glu::TYPE_UINT;
     68 	const char*		typeName	= glu::getDataTypeName(type);
     69 	const char*		outTypeName	= glu::getDataTypeName(outType);
     70 	const char*		precName	= glu::getPrecisionName(precision);
     71 	ostringstream	vtx;
     72 	ostringstream	frag;
     73 	ostringstream&	op			= isVertexCase ? vtx : frag;
     74 
     75 	vtx << "#version 300 es\n"
     76 		<< "in highp vec4 a_position;\n"
     77 		<< "in " << precName << " " << typeName << " a_in0;\n"
     78 		<< "in " << precName << " " << typeName << " a_in1;\n";
     79 	frag << "#version 300 es\n"
     80 		 << "layout(location = 0) out highp " << outTypeName << " o_out;\n";
     81 
     82 	if (isVertexCase)
     83 	{
     84 		vtx << "flat out " << precName << " " << typeName << " v_out;\n";
     85 		frag << "flat in " << precName << " " << typeName << " v_out;\n";
     86 	}
     87 	else
     88 	{
     89 		vtx << "flat out " << precName << " " << typeName << " v_in0;\n"
     90 			<< "flat out " << precName << " " << typeName << " v_in1;\n";
     91 		frag << "flat in " << precName << " " << typeName << " v_in0;\n"
     92 			 << "flat in " << precName << " " << typeName << " v_in1;\n";
     93 	}
     94 
     95 	vtx << "\nvoid main (void)\n{\n"
     96 		<< "	gl_Position = a_position;\n";
     97 	frag << "\nvoid main (void)\n{\n";
     98 
     99 	op << "\t" << precName << " " << typeName << " in0 = " << (isVertexCase ? "a_" : "v_") << "in0;\n"
    100 	   << "\t" << precName << " " << typeName << " in1 = " << (isVertexCase ? "a_" : "v_") << "in1;\n";
    101 
    102 	if (!isVertexCase)
    103 		op << "\t" << precName << " " << typeName << " res;\n";
    104 
    105 	op << "\t" << (isVertexCase ? "v_out" : "res") << " = " << evalOp << ";\n";
    106 
    107 	if (isVertexCase)
    108 	{
    109 		frag << "	o_out = floatBitsToUint(v_out);\n";
    110 	}
    111 	else
    112 	{
    113 		vtx << "	v_in0 = a_in0;\n"
    114 			<< "	v_in1 = a_in1;\n";
    115 		frag << "	o_out = floatBitsToUint(res);\n";
    116 	}
    117 
    118 	vtx << "}\n";
    119 	frag << "}\n";
    120 
    121 	return new glu::ShaderProgram(context, glu::makeVtxFragSources(vtx.str(), frag.str()));
    122 }
    123 
    124 static glu::ShaderProgram* createIntUintPrecisionEvalProgram (const glu::RenderContext& context, glu::DataType type, glu::Precision precision, const char* evalOp, bool isVertexCase)
    125 {
    126 	const char*		typeName	= glu::getDataTypeName(type);
    127 	const char*		precName	= glu::getPrecisionName(precision);
    128 	ostringstream	vtx;
    129 	ostringstream	frag;
    130 	ostringstream&	op			= isVertexCase ? vtx : frag;
    131 
    132 	vtx << "#version 300 es\n"
    133 		<< "in highp vec4 a_position;\n"
    134 		<< "in " << precName << " " << typeName << " a_in0;\n"
    135 		<< "in " << precName << " " << typeName << " a_in1;\n";
    136 	frag << "#version 300 es\n"
    137 		 << "layout(location = 0) out " << precName << " " << typeName << " o_out;\n";
    138 
    139 	if (isVertexCase)
    140 	{
    141 		vtx << "flat out " << precName << " " << typeName << " v_out;\n";
    142 		frag << "flat in " << precName << " " << typeName << " v_out;\n";
    143 	}
    144 	else
    145 	{
    146 		vtx << "flat out " << precName << " " << typeName << " v_in0;\n"
    147 			<< "flat out " << precName << " " << typeName << " v_in1;\n";
    148 		frag << "flat in " << precName << " " << typeName << " v_in0;\n"
    149 			 << "flat in " << precName << " " << typeName << " v_in1;\n";
    150 	}
    151 
    152 	vtx << "\nvoid main (void)\n{\n"
    153 		<< "	gl_Position = a_position;\n";
    154 	frag << "\nvoid main (void)\n{\n";
    155 
    156 	op << "\t" << precName << " " << typeName << " in0 = " << (isVertexCase ? "a_" : "v_") << "in0;\n"
    157 	   << "\t" << precName << " " << typeName << " in1 = " << (isVertexCase ? "a_" : "v_") << "in1;\n";
    158 
    159 	op << "\t" << (isVertexCase ? "v_" : "o_") << "out = " << evalOp << ";\n";
    160 
    161 	if (isVertexCase)
    162 	{
    163 		frag << "	o_out = v_out;\n";
    164 	}
    165 	else
    166 	{
    167 		vtx << "	v_in0 = a_in0;\n"
    168 			<< "	v_in1 = a_in1;\n";
    169 	}
    170 
    171 	vtx << "}\n";
    172 	frag << "}\n";
    173 
    174 	return new glu::ShaderProgram(context, glu::makeVtxFragSources(vtx.str(), frag.str()));
    175 }
    176 
    177 class ShaderFloatPrecisionCase : public TestCase
    178 {
    179 public:
    180 	typedef double (*EvalFunc) (double in0, double in1);
    181 
    182 								ShaderFloatPrecisionCase	(Context& context, const char* name, const char* desc, const char* op, EvalFunc evalFunc, glu::Precision precision, const tcu::Vec2& rangeA, const tcu::Vec2& rangeB, bool isVertexCase);
    183 								~ShaderFloatPrecisionCase	(void);
    184 
    185 	void						init						(void);
    186 	void						deinit						(void);
    187 	IterateResult				iterate						(void);
    188 
    189 protected:
    190 	bool						compare						(float in0, float in1, double reference, float result)
    191 #if (DE_COMPILER == DE_COMPILER_GCC) && (DE_CPU == DE_CPU_ARM_64)
    192 #	if (__GNUC__ == 4) && (__GNUC_MINOR__ == 9) && (__GNUC_PATCHLEVEL__ == 0)
    193 		// Some prerelease GCC 4.9 versions have a bug in shift right when
    194 		// targeting ARMv8.
    195 		//
    196 		// If compiler wants to perform logical shift by variable/register
    197 		// in fp/vector registers it uses USHL that selects shift direction
    198 		// based on shift operand value. Thus for right shifts the shift
    199 		// operand needs to be negated.
    200 		//
    201 		// The bug is in right shift pattern; it doesn't mark shift operand
    202 		// as clobbered and thus later code using that same register may
    203 		// see the negated value.
    204 		//
    205 		// Workaround is to disable optimization for this function.
    206 		//
    207 		// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61633
    208 		__attribute__((optimize(0)))
    209 #	endif
    210 #endif
    211 	;
    212 
    213 private:
    214 								ShaderFloatPrecisionCase	(const ShaderFloatPrecisionCase& other);
    215 	ShaderFloatPrecisionCase&	operator=					(const ShaderFloatPrecisionCase& other);
    216 
    217 	// Case parameters.
    218 	std::string					m_op;
    219 	EvalFunc					m_evalFunc;
    220 	glu::Precision				m_precision;
    221 	tcu::Vec2					m_rangeA;
    222 	tcu::Vec2					m_rangeB;
    223 	bool						m_isVertexCase;
    224 
    225 	int							m_numTestsPerIter;
    226 	int							m_numIters;
    227 	de::Random					m_rnd;
    228 
    229 	// Iteration state.
    230 	glu::ShaderProgram*			m_program;
    231 	deUint32					m_framebuffer;
    232 	deUint32					m_renderbuffer;
    233 	int							m_iterNdx;
    234 };
    235 
    236 ShaderFloatPrecisionCase::ShaderFloatPrecisionCase (Context& context, const char* name, const char* desc, const char* op, EvalFunc evalFunc, glu::Precision precision, const tcu::Vec2& rangeA, const tcu::Vec2& rangeB, bool isVertexCase)
    237 	: TestCase			(context, name, desc)
    238 	, m_op				(op)
    239 	, m_evalFunc		(evalFunc)
    240 	, m_precision		(precision)
    241 	, m_rangeA			(rangeA)
    242 	, m_rangeB			(rangeB)
    243 	, m_isVertexCase	(isVertexCase)
    244 	, m_numTestsPerIter	(32)
    245 	, m_numIters		(4)
    246 	, m_rnd				(deStringHash(name))
    247 	, m_program			(DE_NULL)
    248 	, m_framebuffer		(0)
    249 	, m_renderbuffer	(0)
    250 	, m_iterNdx			(0)
    251 {
    252 }
    253 
    254 ShaderFloatPrecisionCase::~ShaderFloatPrecisionCase (void)
    255 {
    256 	ShaderFloatPrecisionCase::deinit();
    257 }
    258 
    259 void ShaderFloatPrecisionCase::init (void)
    260 {
    261 	const glw::Functions&	gl	= m_context.getRenderContext().getFunctions();
    262 	TestLog&				log	= m_testCtx.getLog();
    263 
    264 	DE_ASSERT(!m_program && !m_framebuffer && !m_renderbuffer);
    265 
    266 	// Create program.
    267 	m_program = createFloatPrecisionEvalProgram(m_context.getRenderContext(), m_precision, m_op.c_str(), m_isVertexCase);
    268 	log << *m_program;
    269 
    270 	TCU_CHECK(m_program->isOk());
    271 
    272 	// Create framebuffer.
    273 	gl.genFramebuffers(1, &m_framebuffer);
    274 	gl.genRenderbuffers(1, &m_renderbuffer);
    275 
    276 	gl.bindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer);
    277 	gl.renderbufferStorage(GL_RENDERBUFFER, GL_R32UI, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT);
    278 
    279 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
    280 	gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_renderbuffer);
    281 
    282 	GLU_EXPECT_NO_ERROR(gl.getError(), "Post framebuffer setup");
    283 	TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
    284 
    285 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer());
    286 
    287 	// Initialize test result to pass.
    288 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    289 	m_iterNdx = 0;
    290 }
    291 
    292 void ShaderFloatPrecisionCase::deinit (void)
    293 {
    294 	delete m_program;
    295 
    296 	if (m_framebuffer)
    297 		m_context.getRenderContext().getFunctions().deleteFramebuffers(1, &m_framebuffer);
    298 
    299 	if (m_renderbuffer)
    300 		m_context.getRenderContext().getFunctions().deleteRenderbuffers(1, &m_renderbuffer);
    301 
    302 	m_program		= DE_NULL;
    303 	m_framebuffer	= 0;
    304 	m_renderbuffer	= 0;
    305 }
    306 
    307 bool ShaderFloatPrecisionCase::compare (float in0, float in1, double reference, float result)
    308 {
    309 	// Comparison is done using 64-bit reference value to accurately evaluate rounding mode error.
    310 	// If 32-bit reference value is used, 2 bits of rounding error must be allowed.
    311 
    312 	// For mediump and lowp types the comparison currently allows 3 bits of rounding error:
    313 	// two bits from conversions and one from actual operation.
    314 
    315 	// \todo [2013-09-30 pyry] Make this more strict: determine if rounding can actually happen.
    316 
    317 	const int		mantissaBits		= m_precision == glu::PRECISION_HIGHP ? 23 : 10;
    318 	const int		numPrecBits			= 52 - mantissaBits;
    319 
    320 	const int		in0Exp				= tcu::Float32(in0).exponent();
    321 	const int		in1Exp				= tcu::Float32(in1).exponent();
    322 	const int		resExp				= tcu::Float32(result).exponent();
    323 	const int		numLostBits			= de::max(de::max(in0Exp-resExp, in1Exp-resExp), 0); // Lost due to mantissa shift.
    324 
    325 	const int		roundingUlpError	= m_precision == glu::PRECISION_HIGHP ? 1 : 3;
    326 	const int		maskBits			= numLostBits + numPrecBits;
    327 
    328 	m_testCtx.getLog() << TestLog::Message << "Assuming " << mantissaBits << " mantissa bits, " << numLostBits << " bits lost in operation, and " << roundingUlpError << " ULP rounding error."
    329 					   << TestLog::EndMessage;
    330 
    331 	{
    332 		const deUint64	refBits				= tcu::Float64(reference).bits();
    333 		const deUint64	resBits				= tcu::Float64(result).bits();
    334 		const deUint64	accurateRefBits		= refBits >> maskBits;
    335 		const deUint64	accurateResBits		= resBits >> maskBits;
    336 		const deUint64	ulpDiff				= (deUint64)de::abs((deInt64)accurateRefBits - (deInt64)accurateResBits);
    337 
    338 		if (ulpDiff > (deUint64)roundingUlpError)
    339 		{
    340 			m_testCtx.getLog() << TestLog::Message << "ERROR: comparison failed! ULP diff (ignoring lost/undefined bits) = " << ulpDiff << TestLog::EndMessage;
    341 			return false;
    342 		}
    343 		else
    344 			return true;
    345 	}
    346 }
    347 
    348 ShaderFloatPrecisionCase::IterateResult ShaderFloatPrecisionCase::iterate (void)
    349 {
    350 	// Constant data.
    351 	const float position[] =
    352 	{
    353 		-1.0f, -1.0f, 0.0f, 1.0f,
    354 		-1.0f,  1.0f, 0.0f, 1.0f,
    355 		 1.0f, -1.0f, 0.0f, 1.0f,
    356 		 1.0f,  1.0f, 0.0f, 1.0f
    357 	};
    358 	const deUint16					indices[]	= { 0, 1, 2, 2, 1, 3 };
    359 
    360 	const int						numVertices	= 4;
    361 	float							in0Arr[4]	= { 0.0f };
    362 	float							in1Arr[4]	= { 0.0f };
    363 
    364 	TestLog&						log			= m_testCtx.getLog();
    365 	const glw::Functions&			gl			= m_context.getRenderContext().getFunctions();
    366 	vector<glu::VertexArrayBinding>	vertexArrays;
    367 
    368 	// Image read from GL.
    369 	std::vector<float>	pixels		(FRAMEBUFFER_WIDTH*FRAMEBUFFER_HEIGHT*4);
    370 
    371 	// \todo [2012-05-03 pyry] Could be cached.
    372 	deUint32			prog		= m_program->getProgram();
    373 
    374 	gl.useProgram(prog);
    375 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
    376 
    377 	vertexArrays.push_back(glu::va::Float("a_position", 4, numVertices, 0, &position[0]));
    378 	vertexArrays.push_back(glu::va::Float("a_in0", 1, numVertices, 0, &in0Arr[0]));
    379 	vertexArrays.push_back(glu::va::Float("a_in1", 1, numVertices, 0, &in1Arr[0]));
    380 
    381 	GLU_EXPECT_NO_ERROR(gl.getError(), "After program setup");
    382 
    383 	// Compute values and reference.
    384 	for (int testNdx = 0; testNdx < m_numTestsPerIter; testNdx++)
    385 	{
    386 		const float		in0		= m_rnd.getFloat(m_rangeA.x(), m_rangeA.y());
    387 		const float		in1		= m_rnd.getFloat(m_rangeB.x(), m_rangeB.y());
    388 		const double	refD	= m_evalFunc((double)in0, (double)in1);
    389 		const float		refF	= tcu::Float64(refD).asFloat(); // Uses RTE rounding mode.
    390 
    391 		log << TestLog::Message << "iter " << m_iterNdx << ", test " << testNdx << ": "
    392 								<< "in0 = " << in0 << " / " << tcu::toHex(tcu::Float32(in0).bits())
    393 								<< ", in1 = " << in1 << " / " << tcu::toHex(tcu::Float32(in1).bits())
    394 			<< TestLog::EndMessage
    395 			<< TestLog::Message << "  reference = " << refF << " / " << tcu::toHex(tcu::Float32(refF).bits()) << TestLog::EndMessage;
    396 
    397 		std::fill(&in0Arr[0], &in0Arr[0] + DE_LENGTH_OF_ARRAY(in0Arr), in0);
    398 		std::fill(&in1Arr[0], &in1Arr[0] + DE_LENGTH_OF_ARRAY(in1Arr), in1);
    399 
    400 		glu::draw(m_context.getRenderContext(), prog, (int)vertexArrays.size(), &vertexArrays[0],
    401 				  glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
    402 		gl.readPixels(0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, GL_RGBA_INTEGER, GL_UNSIGNED_INT, &pixels[0]);
    403 		GLU_EXPECT_NO_ERROR(gl.getError(), "After render");
    404 
    405 		log << TestLog::Message << "  result = " << pixels[0] << " / " << tcu::toHex(tcu::Float32(pixels[0]).bits()) << TestLog::EndMessage;
    406 
    407 		// Verify results
    408 		{
    409 			const bool firstPixelOk = compare(in0, in1, refD, pixels[0]);
    410 
    411 			if (firstPixelOk)
    412 			{
    413 				// Check that rest of pixels match to first one.
    414 				const deUint32	firstPixelBits	= tcu::Float32(pixels[0]).bits();
    415 				bool			allPixelsOk		= true;
    416 
    417 				for (int y = 0; y < FRAMEBUFFER_HEIGHT; y++)
    418 				{
    419 					for (int x = 0; x < FRAMEBUFFER_WIDTH; x++)
    420 					{
    421 						const deUint32 pixelBits = tcu::Float32(pixels[(y*FRAMEBUFFER_WIDTH + x)*4]).bits();
    422 
    423 						if (pixelBits != firstPixelBits)
    424 						{
    425 							log << TestLog::Message << "ERROR: Inconsistent results, got " << tcu::toHex(pixelBits) << " at (" << x << ", " << y << ")" << TestLog::EndMessage;
    426 							allPixelsOk = false;
    427 						}
    428 					}
    429 
    430 					if (!allPixelsOk)
    431 						break;
    432 				}
    433 
    434 				if (!allPixelsOk)
    435 					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Inconsistent values in framebuffer");
    436 			}
    437 			else
    438 				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result comparison failed");
    439 		}
    440 
    441 		if (m_testCtx.getTestResult() != QP_TEST_RESULT_PASS)
    442 			break;
    443 	}
    444 
    445 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer());
    446 	GLU_EXPECT_NO_ERROR(gl.getError(), "After iteration");
    447 
    448 	m_iterNdx += 1;
    449 	return (m_iterNdx < m_numIters && m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) ? CONTINUE : STOP;
    450 }
    451 
    452 class ShaderIntPrecisionCase : public TestCase
    453 {
    454 public:
    455 	typedef int					(*EvalFunc)					(int a, int b);
    456 
    457 								ShaderIntPrecisionCase		(Context& context, const char* name, const char* desc, const char* op, EvalFunc evalFunc, glu::Precision precision, int bits, const tcu::IVec2& rangeA, const tcu::IVec2& rangeB, bool isVertexCase);
    458 								~ShaderIntPrecisionCase		(void);
    459 
    460 	void						init						(void);
    461 	void						deinit						(void);
    462 	IterateResult				iterate						(void);
    463 
    464 private:
    465 								ShaderIntPrecisionCase		(const ShaderIntPrecisionCase& other);
    466 	ShaderIntPrecisionCase&		operator=					(const ShaderIntPrecisionCase& other);
    467 
    468 	// Case parameters.
    469 	std::string					m_op;
    470 	EvalFunc					m_evalFunc;
    471 	glu::Precision				m_precision;
    472 	int							m_bits;
    473 	tcu::IVec2					m_rangeA;
    474 	tcu::IVec2					m_rangeB;
    475 	bool						m_isVertexCase;
    476 
    477 	int							m_numTestsPerIter;
    478 	int							m_numIters;
    479 	de::Random					m_rnd;
    480 
    481 	// Iteration state.
    482 	glu::ShaderProgram*			m_program;
    483 	deUint32					m_framebuffer;
    484 	deUint32					m_renderbuffer;
    485 	int							m_iterNdx;
    486 };
    487 
    488 ShaderIntPrecisionCase::ShaderIntPrecisionCase (Context& context, const char* name, const char* desc, const char* op, EvalFunc evalFunc, glu::Precision precision, int bits, const tcu::IVec2& rangeA, const tcu::IVec2& rangeB, bool isVertexCase)
    489 	: TestCase			(context, name, desc)
    490 	, m_op				(op)
    491 	, m_evalFunc		(evalFunc)
    492 	, m_precision		(precision)
    493 	, m_bits			(bits)
    494 	, m_rangeA			(rangeA)
    495 	, m_rangeB			(rangeB)
    496 	, m_isVertexCase	(isVertexCase)
    497 	, m_numTestsPerIter	(32)
    498 	, m_numIters		(4)
    499 	, m_rnd				(deStringHash(name))
    500 	, m_program			(DE_NULL)
    501 	, m_framebuffer		(0)
    502 	, m_renderbuffer	(0)
    503 	, m_iterNdx			(0)
    504 {
    505 }
    506 
    507 ShaderIntPrecisionCase::~ShaderIntPrecisionCase (void)
    508 {
    509 	ShaderIntPrecisionCase::deinit();
    510 }
    511 
    512 void ShaderIntPrecisionCase::init (void)
    513 {
    514 	const glw::Functions&	gl	= m_context.getRenderContext().getFunctions();
    515 	TestLog&				log	= m_testCtx.getLog();
    516 
    517 	DE_ASSERT(!m_program && !m_framebuffer && !m_renderbuffer);
    518 
    519 	// Create program.
    520 	m_program = createIntUintPrecisionEvalProgram(m_context.getRenderContext(), glu::TYPE_INT, m_precision, m_op.c_str(), m_isVertexCase);
    521 	log << *m_program;
    522 
    523 	TCU_CHECK(m_program->isOk());
    524 
    525 	// Create framebuffer.
    526 	gl.genFramebuffers(1, &m_framebuffer);
    527 	gl.genRenderbuffers(1, &m_renderbuffer);
    528 
    529 	gl.bindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer);
    530 	gl.renderbufferStorage(GL_RENDERBUFFER, GL_R32I, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT);
    531 
    532 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
    533 	gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_renderbuffer);
    534 
    535 	GLU_EXPECT_NO_ERROR(gl.getError(), "Post framebuffer setup");
    536 	TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
    537 
    538 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer());
    539 
    540 	// Initialize test result to pass.
    541 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    542 	m_iterNdx = 0;
    543 
    544 	log << TestLog::Message << "Number of accurate bits assumed = " << m_bits << TestLog::EndMessage;
    545 }
    546 
    547 void ShaderIntPrecisionCase::deinit (void)
    548 {
    549 	delete m_program;
    550 
    551 	if (m_framebuffer)
    552 		m_context.getRenderContext().getFunctions().deleteFramebuffers(1, &m_framebuffer);
    553 
    554 	if (m_renderbuffer)
    555 		m_context.getRenderContext().getFunctions().deleteRenderbuffers(1, &m_renderbuffer);
    556 
    557 	m_program		= DE_NULL;
    558 	m_framebuffer	= 0;
    559 	m_renderbuffer	= 0;
    560 }
    561 
    562 inline int extendTo32Bit (int value, int bits)
    563 {
    564 	return (value & ((1<<(bits-1))-1)) | (((value & (1<<(bits-1))) << (32-bits)) >> (32-bits));
    565 }
    566 
    567 ShaderIntPrecisionCase::IterateResult ShaderIntPrecisionCase::iterate (void)
    568 {
    569 	// Constant data.
    570 	const float position[] =
    571 	{
    572 		-1.0f, -1.0f, 0.0f, 1.0f,
    573 		-1.0f,  1.0f, 0.0f, 1.0f,
    574 		 1.0f, -1.0f, 0.0f, 1.0f,
    575 		 1.0f,  1.0f, 0.0f, 1.0f
    576 	};
    577 	const deUint16					indices[]	= { 0, 1, 2, 2, 1, 3 };
    578 
    579 	const int						numVertices	= 4;
    580 	int								in0Arr[4]	= { 0 };
    581 	int								in1Arr[4]	= { 0 };
    582 
    583 	TestLog&						log			= m_testCtx.getLog();
    584 	const glw::Functions&			gl			= m_context.getRenderContext().getFunctions();
    585 	deUint32						mask		= m_bits == 32 ? 0xffffffffu : ((1u<<m_bits)-1);
    586 	vector<int>						pixels		(FRAMEBUFFER_WIDTH*FRAMEBUFFER_HEIGHT*4);
    587 	vector<glu::VertexArrayBinding>	vertexArrays;
    588 
    589 	deUint32						prog		= m_program->getProgram();
    590 
    591 	// \todo [2012-05-03 pyry] A bit hacky. getInt() should work fine with ranges like this.
    592 	bool							isMaxRangeA	= m_rangeA.x() == (int)0x80000000 && m_rangeA.y() == (int)0x7fffffff;
    593 	bool							isMaxRangeB	= m_rangeB.x() == (int)0x80000000 && m_rangeB.y() == (int)0x7fffffff;
    594 
    595 	gl.useProgram(prog);
    596 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
    597 
    598 	vertexArrays.push_back(glu::va::Float("a_position", 4, numVertices, 0, &position[0]));
    599 	vertexArrays.push_back(glu::va::Int32("a_in0", 1, numVertices, 0, &in0Arr[0]));
    600 	vertexArrays.push_back(glu::va::Int32("a_in1", 1, numVertices, 0, &in1Arr[0]));
    601 
    602 	GLU_EXPECT_NO_ERROR(gl.getError(), "After program setup");
    603 
    604 	// Compute values and reference.
    605 	for (int testNdx = 0; testNdx < m_numTestsPerIter; testNdx++)
    606 	{
    607 		int		in0			= extendTo32Bit(((isMaxRangeA ? (int)m_rnd.getUint32() : m_rnd.getInt(m_rangeA.x(), m_rangeA.y())) & mask), m_bits);
    608 		int		in1			= extendTo32Bit(((isMaxRangeB ? (int)m_rnd.getUint32() : m_rnd.getInt(m_rangeB.x(), m_rangeB.y())) & mask), m_bits);
    609 		int		refMasked	= m_evalFunc(in0, in1) & mask;
    610 		int		refOut		= extendTo32Bit(refMasked, m_bits);
    611 
    612 		log << TestLog::Message << "iter " << m_iterNdx << ", test " << testNdx << ": "
    613 								<< "in0 = " << in0 << ", in1 = " << in1 << ", ref out = " << refOut << " / " << tcu::toHex(refMasked)
    614 			<< TestLog::EndMessage;
    615 
    616 		std::fill(&in0Arr[0], &in0Arr[0] + DE_LENGTH_OF_ARRAY(in0Arr), in0);
    617 		std::fill(&in1Arr[0], &in1Arr[0] + DE_LENGTH_OF_ARRAY(in1Arr), in1);
    618 
    619 		glu::draw(m_context.getRenderContext(), prog, (int)vertexArrays.size(), &vertexArrays[0],
    620 				  glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
    621 		gl.readPixels(0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, GL_RGBA_INTEGER, GL_INT, &pixels[0]);
    622 		GLU_EXPECT_NO_ERROR(gl.getError(), "After render");
    623 
    624 		// Compare pixels.
    625 		for (int y = 0; y < FRAMEBUFFER_HEIGHT; y++)
    626 		{
    627 			for (int x = 0; x < FRAMEBUFFER_WIDTH; x++)
    628 			{
    629 				int			cmpOut		= pixels[(y*FRAMEBUFFER_WIDTH + x)*4];
    630 				int			cmpMasked	= cmpOut & mask;
    631 
    632 				if (cmpMasked != refMasked)
    633 				{
    634 					log << TestLog::Message << "Comparison failed (at " << x << ", " << y << "): "
    635 											<< "got " << cmpOut << " / " << tcu::toHex(cmpOut)
    636 						<< TestLog::EndMessage;
    637 					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
    638 					return STOP;
    639 				}
    640 			}
    641 		}
    642 	}
    643 
    644 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer());
    645 	GLU_EXPECT_NO_ERROR(gl.getError(), "After iteration");
    646 
    647 	m_iterNdx += 1;
    648 	return (m_iterNdx < m_numIters) ? CONTINUE : STOP;
    649 }
    650 
    651 class ShaderUintPrecisionCase : public TestCase
    652 {
    653 public:
    654 	typedef deUint32			(*EvalFunc)					(deUint32 a, deUint32 b);
    655 
    656 								ShaderUintPrecisionCase		(Context& context, const char* name, const char* desc, const char* op, EvalFunc evalFunc, glu::Precision precision, int bits, const tcu::UVec2& rangeA, const tcu::UVec2& rangeB, bool isVertexCase);
    657 								~ShaderUintPrecisionCase	(void);
    658 
    659 	void						init						(void);
    660 	void						deinit						(void);
    661 	IterateResult				iterate						(void);
    662 
    663 private:
    664 								ShaderUintPrecisionCase		(const ShaderUintPrecisionCase& other);
    665 	ShaderUintPrecisionCase&	operator=					(const ShaderUintPrecisionCase& other);
    666 
    667 	// Case parameters.
    668 	std::string					m_op;
    669 	EvalFunc					m_evalFunc;
    670 	glu::Precision				m_precision;
    671 	int							m_bits;
    672 	tcu::UVec2					m_rangeA;
    673 	tcu::UVec2					m_rangeB;
    674 	bool						m_isVertexCase;
    675 
    676 	int							m_numTestsPerIter;
    677 	int							m_numIters;
    678 	de::Random					m_rnd;
    679 
    680 	// Iteration state.
    681 	glu::ShaderProgram*			m_program;
    682 	deUint32					m_framebuffer;
    683 	deUint32					m_renderbuffer;
    684 	int							m_iterNdx;
    685 };
    686 
    687 ShaderUintPrecisionCase::ShaderUintPrecisionCase (Context& context, const char* name, const char* desc, const char* op, EvalFunc evalFunc, glu::Precision precision, int bits, const tcu::UVec2& rangeA, const tcu::UVec2& rangeB, bool isVertexCase)
    688 	: TestCase			(context, name, desc)
    689 	, m_op				(op)
    690 	, m_evalFunc		(evalFunc)
    691 	, m_precision		(precision)
    692 	, m_bits			(bits)
    693 	, m_rangeA			(rangeA)
    694 	, m_rangeB			(rangeB)
    695 	, m_isVertexCase	(isVertexCase)
    696 	, m_numTestsPerIter	(32)
    697 	, m_numIters		(4)
    698 	, m_rnd				(deStringHash(name))
    699 	, m_program			(DE_NULL)
    700 	, m_framebuffer		(0)
    701 	, m_renderbuffer	(0)
    702 	, m_iterNdx			(0)
    703 {
    704 }
    705 
    706 ShaderUintPrecisionCase::~ShaderUintPrecisionCase (void)
    707 {
    708 	ShaderUintPrecisionCase::deinit();
    709 }
    710 
    711 void ShaderUintPrecisionCase::init (void)
    712 {
    713 	const glw::Functions&	gl	= m_context.getRenderContext().getFunctions();
    714 	TestLog&				log	= m_testCtx.getLog();
    715 
    716 	DE_ASSERT(!m_program && !m_framebuffer && !m_renderbuffer);
    717 
    718 	// Create program.
    719 	m_program = createIntUintPrecisionEvalProgram(m_context.getRenderContext(), glu::TYPE_UINT, m_precision, m_op.c_str(), m_isVertexCase);
    720 	log << *m_program;
    721 
    722 	TCU_CHECK(m_program->isOk());
    723 
    724 	// Create framebuffer.
    725 	gl.genFramebuffers(1, &m_framebuffer);
    726 	gl.genRenderbuffers(1, &m_renderbuffer);
    727 
    728 	gl.bindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer);
    729 	gl.renderbufferStorage(GL_RENDERBUFFER, GL_R32UI, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT);
    730 
    731 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
    732 	gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_renderbuffer);
    733 
    734 	GLU_EXPECT_NO_ERROR(gl.getError(), "Post framebuffer setup");
    735 	TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
    736 
    737 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer());
    738 
    739 	// Initialize test result to pass.
    740 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    741 	m_iterNdx = 0;
    742 
    743 	log << TestLog::Message << "Number of accurate bits assumed = " << m_bits << TestLog::EndMessage;
    744 }
    745 
    746 void ShaderUintPrecisionCase::deinit (void)
    747 {
    748 	delete m_program;
    749 
    750 	if (m_framebuffer)
    751 		m_context.getRenderContext().getFunctions().deleteFramebuffers(1, &m_framebuffer);
    752 
    753 	if (m_renderbuffer)
    754 		m_context.getRenderContext().getFunctions().deleteRenderbuffers(1, &m_renderbuffer);
    755 
    756 	m_program		= DE_NULL;
    757 	m_framebuffer	= 0;
    758 	m_renderbuffer	= 0;
    759 }
    760 
    761 ShaderUintPrecisionCase::IterateResult ShaderUintPrecisionCase::iterate (void)
    762 {
    763 	// Constant data.
    764 	const float position[] =
    765 	{
    766 		-1.0f, -1.0f, 0.0f, 1.0f,
    767 		-1.0f,  1.0f, 0.0f, 1.0f,
    768 		 1.0f, -1.0f, 0.0f, 1.0f,
    769 		 1.0f,  1.0f, 0.0f, 1.0f
    770 	};
    771 	const deUint16					indices[]	= { 0, 1, 2, 2, 1, 3 };
    772 
    773 	const int						numVertices	= 4;
    774 	deUint32						in0Arr[4]	= { 0 };
    775 	deUint32						in1Arr[4]	= { 0 };
    776 
    777 	TestLog&						log			= m_testCtx.getLog();
    778 	const glw::Functions&			gl			= m_context.getRenderContext().getFunctions();
    779 	deUint32						mask		= m_bits == 32 ? 0xffffffffu : ((1u<<m_bits)-1);
    780 	vector<deUint32>				pixels		(FRAMEBUFFER_WIDTH*FRAMEBUFFER_HEIGHT*4);
    781 	vector<glu::VertexArrayBinding>	vertexArrays;
    782 
    783 	deUint32						prog		= m_program->getProgram();
    784 
    785 	// \todo [2012-05-03 pyry] A bit hacky.
    786 	bool							isMaxRangeA	= m_rangeA.x() == 0 && m_rangeA.y() == 0xffffffff;
    787 	bool							isMaxRangeB	= m_rangeB.x() == 0 && m_rangeB.y() == 0xffffffff;
    788 
    789 	gl.useProgram(prog);
    790 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
    791 
    792 	vertexArrays.push_back(glu::va::Float("a_position", 4, numVertices, 0, &position[0]));
    793 	vertexArrays.push_back(glu::va::Uint32("a_in0", 1, numVertices, 0, &in0Arr[0]));
    794 	vertexArrays.push_back(glu::va::Uint32("a_in1", 1, numVertices, 0, &in1Arr[0]));
    795 
    796 	GLU_EXPECT_NO_ERROR(gl.getError(), "After program setup");
    797 
    798 	// Compute values and reference.
    799 	for (int testNdx = 0; testNdx < m_numTestsPerIter; testNdx++)
    800 	{
    801 		deUint32	in0		= (isMaxRangeA ? m_rnd.getUint32() : (m_rangeA.x() + m_rnd.getUint32()%(m_rangeA.y()-m_rangeA.x()+1))) & mask;
    802 		deUint32	in1		= (isMaxRangeB ? m_rnd.getUint32() : (m_rangeB.x() + m_rnd.getUint32()%(m_rangeB.y()-m_rangeB.x()+1))) & mask;
    803 		deUint32	refOut	= m_evalFunc(in0, in1) & mask;
    804 
    805 		log << TestLog::Message << "iter " << m_iterNdx << ", test " << testNdx << ": "
    806 								<< "in0 = " << tcu::toHex(in0) << ", in1 = " << tcu::toHex(in1) << ", ref out = " << tcu::toHex(refOut)
    807 			<< TestLog::EndMessage;
    808 
    809 		std::fill(&in0Arr[0], &in0Arr[0] + DE_LENGTH_OF_ARRAY(in0Arr), in0);
    810 		std::fill(&in1Arr[0], &in1Arr[0] + DE_LENGTH_OF_ARRAY(in1Arr), in1);
    811 
    812 		glu::draw(m_context.getRenderContext(), prog, (int)vertexArrays.size(), &vertexArrays[0],
    813 				  glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
    814 		gl.readPixels(0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, GL_RGBA_INTEGER, GL_UNSIGNED_INT, &pixels[0]);
    815 		GLU_EXPECT_NO_ERROR(gl.getError(), "After render");
    816 
    817 		// Compare pixels.
    818 		for (int y = 0; y < FRAMEBUFFER_HEIGHT; y++)
    819 		{
    820 			for (int x = 0; x < FRAMEBUFFER_WIDTH; x++)
    821 			{
    822 				deUint32	cmpOut		= pixels[(y*FRAMEBUFFER_WIDTH + x)*4];
    823 				deUint32	cmpMasked	= cmpOut & mask;
    824 
    825 				if (cmpMasked != refOut)
    826 				{
    827 					log << TestLog::Message << "Comparison failed (at " << x << ", " << y << "): "
    828 											<< "got " << tcu::toHex(cmpOut)
    829 						<< TestLog::EndMessage;
    830 					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
    831 					return STOP;
    832 				}
    833 			}
    834 		}
    835 	}
    836 
    837 	gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
    838 	GLU_EXPECT_NO_ERROR(gl.getError(), "After iteration");
    839 
    840 	m_iterNdx += 1;
    841 	return (m_iterNdx < m_numIters) ? CONTINUE : STOP;
    842 }
    843 
    844 ShaderPrecisionTests::ShaderPrecisionTests (Context& context)
    845 	: TestCaseGroup(context, "precision", "Shader precision requirements validation tests")
    846 {
    847 }
    848 
    849 ShaderPrecisionTests::~ShaderPrecisionTests (void)
    850 {
    851 }
    852 
    853 void ShaderPrecisionTests::init (void)
    854 {
    855 	using tcu::add;
    856 	using tcu::sub;
    857 	using tcu::mul;
    858 	using tcu::div;
    859 	using tcu::Vec2;
    860 	using tcu::IVec2;
    861 	using tcu::UVec2;
    862 
    863 	// Exp = Emax-2, Mantissa = 0
    864 	float		minF32			= tcu::Float32((1u<<31) | (0xfdu<<23) | 0x0u).asFloat();
    865 	float		maxF32			= tcu::Float32((0u<<31) | (0xfdu<<23) | 0x0u).asFloat();
    866 	float		minF16			= tcu::Float16((deUint16)((1u<<15) | (0x1du<<10) | 0x0u)).asFloat();
    867 	float		maxF16			= tcu::Float16((deUint16)((0u<<15) | (0x1du<<10) | 0x0u)).asFloat();
    868 	tcu::Vec2	fullRange32F	(minF32, maxF32);
    869 	tcu::Vec2	fullRange16F	(minF16, maxF16);
    870 	tcu::IVec2	fullRange32I	(0x80000000, 0x7fffffff);
    871 	tcu::IVec2	fullRange16I	(-(1<<15), (1<<15)-1);
    872 	tcu::IVec2	fullRange8I		(-(1<<7), (1<<7)-1);
    873 	tcu::UVec2	fullRange32U	(0u, 0xffffffffu);
    874 	tcu::UVec2	fullRange16U	(0u, 0xffffu);
    875 	tcu::UVec2	fullRange8U		(0u, 0xffu);
    876 
    877 	// \note Right now it is not programmatically verified that the results shouldn't end up being inf/nan but
    878 	//       actual values used are ok.
    879 
    880 	static const struct
    881 	{
    882 		const char*							name;
    883 		const char*							op;
    884 		ShaderFloatPrecisionCase::EvalFunc	evalFunc;
    885 		glu::Precision						precision;
    886 		tcu::Vec2							rangeA;
    887 		tcu::Vec2							rangeB;
    888 	} floatCases[] =
    889 	{
    890 		// Name				Op				Eval			Precision				RangeA				RangeB
    891 		{ "highp_add",		"in0 + in1",	add<double>,	glu::PRECISION_HIGHP,	fullRange32F,		fullRange32F		},
    892 		{ "highp_sub",		"in0 - in1",	sub<double>,	glu::PRECISION_HIGHP,	fullRange32F,		fullRange32F		},
    893 		{ "highp_mul",		"in0 * in1",	mul<double>,	glu::PRECISION_HIGHP,	Vec2(-1e5f, 1e5f),	Vec2(-1e5f, 1e5f)	},
    894 		{ "highp_div",		"in0 / in1",	div<double>,	glu::PRECISION_HIGHP,	Vec2(-1e5f, 1e5f),	Vec2(-1e5f, 1e5f)	},
    895 		{ "mediump_add",	"in0 + in1",	add<double>,	glu::PRECISION_MEDIUMP,	fullRange16F,		fullRange16F		},
    896 		{ "mediump_sub",	"in0 - in1",	sub<double>,	glu::PRECISION_MEDIUMP,	fullRange16F,		fullRange16F		},
    897 		{ "mediump_mul",	"in0 * in1",	mul<double>,	glu::PRECISION_MEDIUMP,	Vec2(-1e2f, 1e2f),	Vec2(-1e2f, 1e2f)	},
    898 		{ "mediump_div",	"in0 / in1",	div<double>,	glu::PRECISION_MEDIUMP,	Vec2(-1e2f, 1e2f),	Vec2(-1e2f, 1e2f)	}
    899 	};
    900 
    901 	static const struct
    902 	{
    903 		const char*							name;
    904 		const char*							op;
    905 		ShaderIntPrecisionCase::EvalFunc	evalFunc;
    906 		glu::Precision						precision;
    907 		int									bits;
    908 		tcu::IVec2							rangeA;
    909 		tcu::IVec2							rangeB;
    910 	} intCases[] =
    911 	{
    912 		// Name				Op				Eval				Precision				Bits	RangeA			RangeB
    913 		{ "highp_add",		"in0 + in1",	add<int>,			glu::PRECISION_HIGHP,	32,		fullRange32I,	fullRange32I },
    914 		{ "highp_sub",		"in0 - in1",	sub<int>,			glu::PRECISION_HIGHP,	32,		fullRange32I,	fullRange32I },
    915 		{ "highp_mul",		"in0 * in1",	mul<int>,			glu::PRECISION_HIGHP,	32,		fullRange32I,	fullRange32I },
    916 		{ "highp_div",		"in0 / in1",	div<int>,			glu::PRECISION_HIGHP,	32,		fullRange32I,	IVec2(-10000, -1) },
    917 		{ "mediump_add",	"in0 + in1",	add<int>,			glu::PRECISION_MEDIUMP,	16,		fullRange16I,	fullRange16I },
    918 		{ "mediump_sub",	"in0 - in1",	sub<int>,			glu::PRECISION_MEDIUMP,	16,		fullRange16I,	fullRange16I },
    919 		{ "mediump_mul",	"in0 * in1",	mul<int>,			glu::PRECISION_MEDIUMP,	16,		fullRange16I,	fullRange16I },
    920 		{ "mediump_div",	"in0 / in1",	div<int>,			glu::PRECISION_MEDIUMP,	16,		fullRange16I,	IVec2(1, 1000) },
    921 		{ "lowp_add",		"in0 + in1",	add<int>,			glu::PRECISION_LOWP,	8,		fullRange8I,	fullRange8I },
    922 		{ "lowp_sub",		"in0 - in1",	sub<int>,			glu::PRECISION_LOWP,	8,		fullRange8I,	fullRange8I },
    923 		{ "lowp_mul",		"in0 * in1",	mul<int>,			glu::PRECISION_LOWP,	8,		fullRange8I,	fullRange8I },
    924 		{ "lowp_div",		"in0 / in1",	div<int>,			glu::PRECISION_LOWP,	8,		fullRange8I,	IVec2(-50, -1) }
    925 	};
    926 
    927 	static const struct
    928 	{
    929 		const char*							name;
    930 		const char*							op;
    931 		ShaderUintPrecisionCase::EvalFunc	evalFunc;
    932 		glu::Precision						precision;
    933 		int									bits;
    934 		tcu::UVec2							rangeA;
    935 		tcu::UVec2							rangeB;
    936 	} uintCases[] =
    937 	{
    938 		// Name				Op				Eval				Precision				Bits	RangeA			RangeB
    939 		{ "highp_add",		"in0 + in1",	add<deUint32>,		glu::PRECISION_HIGHP,	32,		fullRange32U,	fullRange32U },
    940 		{ "highp_sub",		"in0 - in1",	sub<deUint32>,		glu::PRECISION_HIGHP,	32,		fullRange32U,	fullRange32U },
    941 		{ "highp_mul",		"in0 * in1",	mul<deUint32>,		glu::PRECISION_HIGHP,	32,		fullRange32U,	fullRange32U },
    942 		{ "highp_div",		"in0 / in1",	div<deUint32>,		glu::PRECISION_HIGHP,	32,		fullRange32U,	UVec2(1u, 10000u) },
    943 		{ "mediump_add",	"in0 + in1",	add<deUint32>,		glu::PRECISION_MEDIUMP,	16,		fullRange16U,	fullRange16U },
    944 		{ "mediump_sub",	"in0 - in1",	sub<deUint32>,		glu::PRECISION_MEDIUMP,	16,		fullRange16U,	fullRange16U },
    945 		{ "mediump_mul",	"in0 * in1",	mul<deUint32>,		glu::PRECISION_MEDIUMP,	16,		fullRange16U,	fullRange16U },
    946 		{ "mediump_div",	"in0 / in1",	div<deUint32>,		glu::PRECISION_MEDIUMP,	16,		fullRange16U,	UVec2(1, 1000u) },
    947 		{ "lowp_add",		"in0 + in1",	add<deUint32>,		glu::PRECISION_LOWP,	8,		fullRange8U,	fullRange8U },
    948 		{ "lowp_sub",		"in0 - in1",	sub<deUint32>,		glu::PRECISION_LOWP,	8,		fullRange8U,	fullRange8U },
    949 		{ "lowp_mul",		"in0 * in1",	mul<deUint32>,		glu::PRECISION_LOWP,	8,		fullRange8U,	fullRange8U },
    950 		{ "lowp_div",		"in0 / in1",	div<deUint32>,		glu::PRECISION_LOWP,	8,		fullRange8U,	UVec2(1, 50u) }
    951 	};
    952 
    953 	tcu::TestCaseGroup* floatGroup = new tcu::TestCaseGroup(m_testCtx, "float", "Floating-point precision tests");
    954 	addChild(floatGroup);
    955 	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(floatCases); ndx++)
    956 	{
    957 		floatGroup->addChild(new ShaderFloatPrecisionCase(m_context,
    958 														  (string(floatCases[ndx].name) + "_vertex").c_str(), "",
    959 														  floatCases[ndx].op,
    960 														  floatCases[ndx].evalFunc,
    961 														  floatCases[ndx].precision,
    962 														  floatCases[ndx].rangeA,
    963 														  floatCases[ndx].rangeB,
    964 														  true));
    965 		floatGroup->addChild(new ShaderFloatPrecisionCase(m_context,
    966 														  (string(floatCases[ndx].name) + "_fragment").c_str(), "",
    967 														  floatCases[ndx].op,
    968 														  floatCases[ndx].evalFunc,
    969 														  floatCases[ndx].precision,
    970 														  floatCases[ndx].rangeA,
    971 														  floatCases[ndx].rangeB,
    972 														  false));
    973 	}
    974 
    975 	tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "int", "Integer precision tests");
    976 	addChild(intGroup);
    977 	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(intCases); ndx++)
    978 	{
    979 		intGroup->addChild(new ShaderIntPrecisionCase(m_context,
    980 													  (string(intCases[ndx].name) + "_vertex").c_str(), "",
    981 													  intCases[ndx].op,
    982 													  intCases[ndx].evalFunc,
    983 													  intCases[ndx].precision,
    984 													  intCases[ndx].bits,
    985 													  intCases[ndx].rangeA,
    986 													  intCases[ndx].rangeB,
    987 													  true));
    988 		intGroup->addChild(new ShaderIntPrecisionCase(m_context,
    989 													  (string(intCases[ndx].name) + "_fragment").c_str(), "",
    990 													  intCases[ndx].op,
    991 													  intCases[ndx].evalFunc,
    992 													  intCases[ndx].precision,
    993 													  intCases[ndx].bits,
    994 													  intCases[ndx].rangeA,
    995 													  intCases[ndx].rangeB,
    996 													  false));
    997 	}
    998 
    999 	tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uint", "Unsigned integer precision tests");
   1000 	addChild(uintGroup);
   1001 	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(uintCases); ndx++)
   1002 	{
   1003 		uintGroup->addChild(new ShaderUintPrecisionCase(m_context,
   1004 														(string(uintCases[ndx].name) + "_vertex").c_str(), "",
   1005 														uintCases[ndx].op,
   1006 														uintCases[ndx].evalFunc,
   1007 														uintCases[ndx].precision,
   1008 														uintCases[ndx].bits,
   1009 														uintCases[ndx].rangeA,
   1010 														uintCases[ndx].rangeB,
   1011 														true));
   1012 		uintGroup->addChild(new ShaderUintPrecisionCase(m_context,
   1013 														(string(uintCases[ndx].name) + "_fragment").c_str(), "",
   1014 														uintCases[ndx].op,
   1015 														uintCases[ndx].evalFunc,
   1016 														uintCases[ndx].precision,
   1017 														uintCases[ndx].bits,
   1018 														uintCases[ndx].rangeA,
   1019 														uintCases[ndx].rangeB,
   1020 														false));
   1021 	}
   1022 }
   1023 
   1024 } // Functional
   1025 } // gles3
   1026 } // deqp
   1027