• 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 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 
createFloatPrecisionEvalProgram(const glu::RenderContext & context,glu::Precision precision,const char * evalOp,bool isVertexCase)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 
createIntUintPrecisionEvalProgram(const glu::RenderContext & context,glu::DataType type,glu::Precision precision,const char * evalOp,bool isVertexCase)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 
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)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 
~ShaderFloatPrecisionCase(void)254 ShaderFloatPrecisionCase::~ShaderFloatPrecisionCase (void)
255 {
256 	ShaderFloatPrecisionCase::deinit();
257 }
258 
init(void)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 
deinit(void)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 
compare(float in0,float in1,double reference,float result)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 
iterate(void)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 
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)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 
~ShaderIntPrecisionCase(void)507 ShaderIntPrecisionCase::~ShaderIntPrecisionCase (void)
508 {
509 	ShaderIntPrecisionCase::deinit();
510 }
511 
init(void)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 
deinit(void)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 
extendTo32Bit(int value,int bits)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 
iterate(void)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 
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)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 
~ShaderUintPrecisionCase(void)706 ShaderUintPrecisionCase::~ShaderUintPrecisionCase (void)
707 {
708 	ShaderUintPrecisionCase::deinit();
709 }
710 
init(void)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 
deinit(void)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 
iterate(void)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 
ShaderPrecisionTests(Context & context)844 ShaderPrecisionTests::ShaderPrecisionTests (Context& context)
845 	: TestCaseGroup(context, "precision", "Shader precision requirements validation tests")
846 {
847 }
848 
~ShaderPrecisionTests(void)849 ShaderPrecisionTests::~ShaderPrecisionTests (void)
850 {
851 }
852 
init(void)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