• 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 
192 private:
193 								ShaderFloatPrecisionCase	(const ShaderFloatPrecisionCase& other);
194 	ShaderFloatPrecisionCase&	operator=					(const ShaderFloatPrecisionCase& other);
195 
196 	// Case parameters.
197 	std::string					m_op;
198 	EvalFunc					m_evalFunc;
199 	glu::Precision				m_precision;
200 	tcu::Vec2					m_rangeA;
201 	tcu::Vec2					m_rangeB;
202 	bool						m_isVertexCase;
203 
204 	int							m_numTestsPerIter;
205 	int							m_numIters;
206 	de::Random					m_rnd;
207 
208 	// Iteration state.
209 	glu::ShaderProgram*			m_program;
210 	deUint32					m_framebuffer;
211 	deUint32					m_renderbuffer;
212 	int							m_iterNdx;
213 };
214 
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)215 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)
216 	: TestCase			(context, name, desc)
217 	, m_op				(op)
218 	, m_evalFunc		(evalFunc)
219 	, m_precision		(precision)
220 	, m_rangeA			(rangeA)
221 	, m_rangeB			(rangeB)
222 	, m_isVertexCase	(isVertexCase)
223 	, m_numTestsPerIter	(32)
224 	, m_numIters		(4)
225 	, m_rnd				(deStringHash(name))
226 	, m_program			(DE_NULL)
227 	, m_framebuffer		(0)
228 	, m_renderbuffer	(0)
229 	, m_iterNdx			(0)
230 {
231 }
232 
~ShaderFloatPrecisionCase(void)233 ShaderFloatPrecisionCase::~ShaderFloatPrecisionCase (void)
234 {
235 	ShaderFloatPrecisionCase::deinit();
236 }
237 
init(void)238 void ShaderFloatPrecisionCase::init (void)
239 {
240 	const glw::Functions&	gl	= m_context.getRenderContext().getFunctions();
241 	TestLog&				log	= m_testCtx.getLog();
242 
243 	DE_ASSERT(!m_program && !m_framebuffer && !m_renderbuffer);
244 
245 	// Create program.
246 	m_program = createFloatPrecisionEvalProgram(m_context.getRenderContext(), m_precision, m_op.c_str(), m_isVertexCase);
247 	log << *m_program;
248 
249 	TCU_CHECK(m_program->isOk());
250 
251 	// Create framebuffer.
252 	gl.genFramebuffers(1, &m_framebuffer);
253 	gl.genRenderbuffers(1, &m_renderbuffer);
254 
255 	gl.bindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer);
256 	gl.renderbufferStorage(GL_RENDERBUFFER, GL_R32UI, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT);
257 
258 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
259 	gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_renderbuffer);
260 
261 	GLU_EXPECT_NO_ERROR(gl.getError(), "Post framebuffer setup");
262 	TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
263 
264 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer());
265 
266 	// Initialize test result to pass.
267 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
268 	m_iterNdx = 0;
269 }
270 
deinit(void)271 void ShaderFloatPrecisionCase::deinit (void)
272 {
273 	delete m_program;
274 
275 	if (m_framebuffer)
276 		m_context.getRenderContext().getFunctions().deleteFramebuffers(1, &m_framebuffer);
277 
278 	if (m_renderbuffer)
279 		m_context.getRenderContext().getFunctions().deleteRenderbuffers(1, &m_renderbuffer);
280 
281 	m_program		= DE_NULL;
282 	m_framebuffer	= 0;
283 	m_renderbuffer	= 0;
284 }
285 
compare(float in0,float in1,double reference,float result)286 bool ShaderFloatPrecisionCase::compare (float in0, float in1, double reference, float result)
287 {
288 	// Comparison is done using 64-bit reference value to accurately evaluate rounding mode error.
289 	// If 32-bit reference value is used, 2 bits of rounding error must be allowed.
290 
291 	// For mediump and lowp types the comparison currently allows 3 bits of rounding error:
292 	// two bits from conversions and one from actual operation.
293 
294 	// \todo [2013-09-30 pyry] Make this more strict: determine if rounding can actually happen.
295 
296 	const int		mantissaBits		= m_precision == glu::PRECISION_HIGHP ? 23 : 10;
297 	const int		numPrecBits			= 52 - mantissaBits;
298 
299 	const int		in0Exp				= tcu::Float32(in0).exponent();
300 	const int		in1Exp				= tcu::Float32(in1).exponent();
301 	const int		resExp				= tcu::Float32(result).exponent();
302 	const int		numLostBits			= de::max(de::max(in0Exp-resExp, in1Exp-resExp), 0); // Lost due to mantissa shift.
303 
304 	const int		roundingUlpError	= m_precision == glu::PRECISION_HIGHP ? 1 : 3;
305 	const int		maskBits			= numLostBits + numPrecBits;
306 
307 	m_testCtx.getLog() << TestLog::Message << "Assuming " << mantissaBits << " mantissa bits, " << numLostBits << " bits lost in operation, and " << roundingUlpError << " ULP rounding error."
308 					   << TestLog::EndMessage;
309 
310 	{
311 		const deUint64	refBits				= tcu::Float64(reference).bits();
312 		const deUint64	resBits				= tcu::Float64(result).bits();
313 		const deUint64	accurateRefBits		= maskBits < 64 ? refBits >> (deUint64)maskBits : 0u;
314 		const deUint64	accurateResBits		= maskBits < 64 ? resBits >> (deUint64)maskBits : 0u;
315 		const deUint64	ulpDiff				= (deUint64)de::abs((deInt64)accurateRefBits - (deInt64)accurateResBits);
316 
317 		if (ulpDiff > (deUint64)roundingUlpError)
318 		{
319 			m_testCtx.getLog() << TestLog::Message << "ERROR: comparison failed! ULP diff (ignoring lost/undefined bits) = " << ulpDiff << TestLog::EndMessage;
320 			return false;
321 		}
322 		else
323 			return true;
324 	}
325 }
326 
iterate(void)327 ShaderFloatPrecisionCase::IterateResult ShaderFloatPrecisionCase::iterate (void)
328 {
329 	// Constant data.
330 	const float position[] =
331 	{
332 		-1.0f, -1.0f, 0.0f, 1.0f,
333 		-1.0f,  1.0f, 0.0f, 1.0f,
334 		 1.0f, -1.0f, 0.0f, 1.0f,
335 		 1.0f,  1.0f, 0.0f, 1.0f
336 	};
337 	const deUint16					indices[]	= { 0, 1, 2, 2, 1, 3 };
338 
339 	const int						numVertices	= 4;
340 	float							in0Arr[4]	= { 0.0f };
341 	float							in1Arr[4]	= { 0.0f };
342 
343 	TestLog&						log			= m_testCtx.getLog();
344 	const glw::Functions&			gl			= m_context.getRenderContext().getFunctions();
345 	vector<glu::VertexArrayBinding>	vertexArrays;
346 
347 	// Image read from GL.
348 	std::vector<float>	pixels		(FRAMEBUFFER_WIDTH*FRAMEBUFFER_HEIGHT*4);
349 
350 	// \todo [2012-05-03 pyry] Could be cached.
351 	deUint32			prog		= m_program->getProgram();
352 
353 	gl.useProgram(prog);
354 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
355 
356 	vertexArrays.push_back(glu::va::Float("a_position", 4, numVertices, 0, &position[0]));
357 	vertexArrays.push_back(glu::va::Float("a_in0", 1, numVertices, 0, &in0Arr[0]));
358 	vertexArrays.push_back(glu::va::Float("a_in1", 1, numVertices, 0, &in1Arr[0]));
359 
360 	GLU_EXPECT_NO_ERROR(gl.getError(), "After program setup");
361 
362 	// Compute values and reference.
363 	for (int testNdx = 0; testNdx < m_numTestsPerIter; testNdx++)
364 	{
365 		const float		in0		= m_rnd.getFloat(m_rangeA.x(), m_rangeA.y());
366 		const float		in1		= m_rnd.getFloat(m_rangeB.x(), m_rangeB.y());
367 		const double	refD	= m_evalFunc((double)in0, (double)in1);
368 		const float		refF	= tcu::Float64(refD).asFloat(); // Uses RTE rounding mode.
369 
370 		log << TestLog::Message << "iter " << m_iterNdx << ", test " << testNdx << ": "
371 								<< "in0 = " << in0 << " / " << tcu::toHex(tcu::Float32(in0).bits())
372 								<< ", in1 = " << in1 << " / " << tcu::toHex(tcu::Float32(in1).bits())
373 			<< TestLog::EndMessage
374 			<< TestLog::Message << "  reference = " << refF << " / " << tcu::toHex(tcu::Float32(refF).bits()) << TestLog::EndMessage;
375 
376 		std::fill(&in0Arr[0], &in0Arr[0] + DE_LENGTH_OF_ARRAY(in0Arr), in0);
377 		std::fill(&in1Arr[0], &in1Arr[0] + DE_LENGTH_OF_ARRAY(in1Arr), in1);
378 
379 		glu::draw(m_context.getRenderContext(), prog, (int)vertexArrays.size(), &vertexArrays[0],
380 				  glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
381 		gl.readPixels(0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, GL_RGBA_INTEGER, GL_UNSIGNED_INT, &pixels[0]);
382 		GLU_EXPECT_NO_ERROR(gl.getError(), "After render");
383 
384 		log << TestLog::Message << "  result = " << pixels[0] << " / " << tcu::toHex(tcu::Float32(pixels[0]).bits()) << TestLog::EndMessage;
385 
386 		// Verify results
387 		{
388 			const bool firstPixelOk = compare(in0, in1, refD, pixels[0]);
389 
390 			if (firstPixelOk)
391 			{
392 				// Check that rest of pixels match to first one.
393 				const deUint32	firstPixelBits	= tcu::Float32(pixels[0]).bits();
394 				bool			allPixelsOk		= true;
395 
396 				for (int y = 0; y < FRAMEBUFFER_HEIGHT; y++)
397 				{
398 					for (int x = 0; x < FRAMEBUFFER_WIDTH; x++)
399 					{
400 						const deUint32 pixelBits = tcu::Float32(pixels[(y*FRAMEBUFFER_WIDTH + x)*4]).bits();
401 
402 						if (pixelBits != firstPixelBits)
403 						{
404 							log << TestLog::Message << "ERROR: Inconsistent results, got " << tcu::toHex(pixelBits) << " at (" << x << ", " << y << ")" << TestLog::EndMessage;
405 							allPixelsOk = false;
406 						}
407 					}
408 
409 					if (!allPixelsOk)
410 						break;
411 				}
412 
413 				if (!allPixelsOk)
414 					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Inconsistent values in framebuffer");
415 			}
416 			else
417 				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result comparison failed");
418 		}
419 
420 		if (m_testCtx.getTestResult() != QP_TEST_RESULT_PASS)
421 			break;
422 	}
423 
424 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer());
425 	GLU_EXPECT_NO_ERROR(gl.getError(), "After iteration");
426 
427 	m_iterNdx += 1;
428 	return (m_iterNdx < m_numIters && m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) ? CONTINUE : STOP;
429 }
430 
431 class ShaderIntPrecisionCase : public TestCase
432 {
433 public:
434 	typedef int					(*EvalFunc)					(int a, int b);
435 
436 								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);
437 								~ShaderIntPrecisionCase		(void);
438 
439 	void						init						(void);
440 	void						deinit						(void);
441 	IterateResult				iterate						(void);
442 
443 private:
444 								ShaderIntPrecisionCase		(const ShaderIntPrecisionCase& other);
445 	ShaderIntPrecisionCase&		operator=					(const ShaderIntPrecisionCase& other);
446 
447 	// Case parameters.
448 	std::string					m_op;
449 	EvalFunc					m_evalFunc;
450 	glu::Precision				m_precision;
451 	int							m_bits;
452 	tcu::IVec2					m_rangeA;
453 	tcu::IVec2					m_rangeB;
454 	bool						m_isVertexCase;
455 
456 	int							m_numTestsPerIter;
457 	int							m_numIters;
458 	de::Random					m_rnd;
459 
460 	// Iteration state.
461 	glu::ShaderProgram*			m_program;
462 	deUint32					m_framebuffer;
463 	deUint32					m_renderbuffer;
464 	int							m_iterNdx;
465 };
466 
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)467 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)
468 	: TestCase			(context, name, desc)
469 	, m_op				(op)
470 	, m_evalFunc		(evalFunc)
471 	, m_precision		(precision)
472 	, m_bits			(bits)
473 	, m_rangeA			(rangeA)
474 	, m_rangeB			(rangeB)
475 	, m_isVertexCase	(isVertexCase)
476 	, m_numTestsPerIter	(32)
477 	, m_numIters		(4)
478 	, m_rnd				(deStringHash(name))
479 	, m_program			(DE_NULL)
480 	, m_framebuffer		(0)
481 	, m_renderbuffer	(0)
482 	, m_iterNdx			(0)
483 {
484 }
485 
~ShaderIntPrecisionCase(void)486 ShaderIntPrecisionCase::~ShaderIntPrecisionCase (void)
487 {
488 	ShaderIntPrecisionCase::deinit();
489 }
490 
init(void)491 void ShaderIntPrecisionCase::init (void)
492 {
493 	const glw::Functions&	gl	= m_context.getRenderContext().getFunctions();
494 	TestLog&				log	= m_testCtx.getLog();
495 
496 	DE_ASSERT(!m_program && !m_framebuffer && !m_renderbuffer);
497 
498 	// Create program.
499 	m_program = createIntUintPrecisionEvalProgram(m_context.getRenderContext(), glu::TYPE_INT, m_precision, m_op.c_str(), m_isVertexCase);
500 	log << *m_program;
501 
502 	TCU_CHECK(m_program->isOk());
503 
504 	// Create framebuffer.
505 	gl.genFramebuffers(1, &m_framebuffer);
506 	gl.genRenderbuffers(1, &m_renderbuffer);
507 
508 	gl.bindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer);
509 	gl.renderbufferStorage(GL_RENDERBUFFER, GL_R32I, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT);
510 
511 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
512 	gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_renderbuffer);
513 
514 	GLU_EXPECT_NO_ERROR(gl.getError(), "Post framebuffer setup");
515 	TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
516 
517 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer());
518 
519 	// Initialize test result to pass.
520 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
521 	m_iterNdx = 0;
522 
523 	log << TestLog::Message << "Number of accurate bits assumed = " << m_bits << TestLog::EndMessage;
524 }
525 
deinit(void)526 void ShaderIntPrecisionCase::deinit (void)
527 {
528 	delete m_program;
529 
530 	if (m_framebuffer)
531 		m_context.getRenderContext().getFunctions().deleteFramebuffers(1, &m_framebuffer);
532 
533 	if (m_renderbuffer)
534 		m_context.getRenderContext().getFunctions().deleteRenderbuffers(1, &m_renderbuffer);
535 
536 	m_program		= DE_NULL;
537 	m_framebuffer	= 0;
538 	m_renderbuffer	= 0;
539 }
540 
iterate(void)541 ShaderIntPrecisionCase::IterateResult ShaderIntPrecisionCase::iterate (void)
542 {
543 	// Constant data.
544 	const float position[] =
545 	{
546 		-1.0f, -1.0f, 0.0f, 1.0f,
547 		-1.0f,  1.0f, 0.0f, 1.0f,
548 		 1.0f, -1.0f, 0.0f, 1.0f,
549 		 1.0f,  1.0f, 0.0f, 1.0f
550 	};
551 	const deUint16					indices[]	= { 0, 1, 2, 2, 1, 3 };
552 
553 	const int						numVertices	= 4;
554 	int								in0Arr[4]	= { 0 };
555 	int								in1Arr[4]	= { 0 };
556 
557 	TestLog&						log			= m_testCtx.getLog();
558 	const glw::Functions&			gl			= m_context.getRenderContext().getFunctions();
559 	deUint32						mask		= m_bits == 32 ? 0xffffffffu : ((1u<<m_bits)-1);
560 	vector<int>						pixels		(FRAMEBUFFER_WIDTH*FRAMEBUFFER_HEIGHT*4);
561 	vector<glu::VertexArrayBinding>	vertexArrays;
562 
563 	deUint32						prog		= m_program->getProgram();
564 
565 	// \todo [2012-05-03 pyry] A bit hacky. getInt() should work fine with ranges like this.
566 	bool							isMaxRangeA	= m_rangeA.x() == (int)0x80000000 && m_rangeA.y() == (int)0x7fffffff;
567 	bool							isMaxRangeB	= m_rangeB.x() == (int)0x80000000 && m_rangeB.y() == (int)0x7fffffff;
568 
569 	gl.useProgram(prog);
570 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
571 
572 	vertexArrays.push_back(glu::va::Float("a_position", 4, numVertices, 0, &position[0]));
573 	vertexArrays.push_back(glu::va::Int32("a_in0", 1, numVertices, 0, &in0Arr[0]));
574 	vertexArrays.push_back(glu::va::Int32("a_in1", 1, numVertices, 0, &in1Arr[0]));
575 
576 	GLU_EXPECT_NO_ERROR(gl.getError(), "After program setup");
577 
578 	// Compute values and reference.
579 	for (int testNdx = 0; testNdx < m_numTestsPerIter; testNdx++)
580 	{
581 		int		in0			= deSignExtendTo32(((isMaxRangeA ? (int)m_rnd.getUint32() : m_rnd.getInt(m_rangeA.x(), m_rangeA.y())) & mask), m_bits);
582 		int		in1			= deSignExtendTo32(((isMaxRangeB ? (int)m_rnd.getUint32() : m_rnd.getInt(m_rangeB.x(), m_rangeB.y())) & mask), m_bits);
583 		int		refMasked	= m_evalFunc(in0, in1) & mask;
584 		int		refOut		= deSignExtendTo32(refMasked, m_bits);
585 
586 		log << TestLog::Message << "iter " << m_iterNdx << ", test " << testNdx << ": "
587 								<< "in0 = " << in0 << ", in1 = " << in1 << ", ref out = " << refOut << " / " << tcu::toHex(refMasked)
588 			<< TestLog::EndMessage;
589 
590 		std::fill(&in0Arr[0], &in0Arr[0] + DE_LENGTH_OF_ARRAY(in0Arr), in0);
591 		std::fill(&in1Arr[0], &in1Arr[0] + DE_LENGTH_OF_ARRAY(in1Arr), in1);
592 
593 		glu::draw(m_context.getRenderContext(), prog, (int)vertexArrays.size(), &vertexArrays[0],
594 				  glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
595 		gl.readPixels(0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, GL_RGBA_INTEGER, GL_INT, &pixels[0]);
596 		GLU_EXPECT_NO_ERROR(gl.getError(), "After render");
597 
598 		// Compare pixels.
599 		for (int y = 0; y < FRAMEBUFFER_HEIGHT; y++)
600 		{
601 			for (int x = 0; x < FRAMEBUFFER_WIDTH; x++)
602 			{
603 				int			cmpOut		= pixels[(y*FRAMEBUFFER_WIDTH + x)*4];
604 				int			cmpMasked	= cmpOut & mask;
605 
606 				if (cmpMasked != refMasked)
607 				{
608 					log << TestLog::Message << "Comparison failed (at " << x << ", " << y << "): "
609 											<< "got " << cmpOut << " / " << tcu::toHex(cmpOut)
610 						<< TestLog::EndMessage;
611 					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
612 					return STOP;
613 				}
614 			}
615 		}
616 	}
617 
618 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer());
619 	GLU_EXPECT_NO_ERROR(gl.getError(), "After iteration");
620 
621 	m_iterNdx += 1;
622 	return (m_iterNdx < m_numIters) ? CONTINUE : STOP;
623 }
624 
625 class ShaderUintPrecisionCase : public TestCase
626 {
627 public:
628 	typedef deUint32			(*EvalFunc)					(deUint32 a, deUint32 b);
629 
630 								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);
631 								~ShaderUintPrecisionCase	(void);
632 
633 	void						init						(void);
634 	void						deinit						(void);
635 	IterateResult				iterate						(void);
636 
637 private:
638 								ShaderUintPrecisionCase		(const ShaderUintPrecisionCase& other);
639 	ShaderUintPrecisionCase&	operator=					(const ShaderUintPrecisionCase& other);
640 
641 	// Case parameters.
642 	std::string					m_op;
643 	EvalFunc					m_evalFunc;
644 	glu::Precision				m_precision;
645 	int							m_bits;
646 	tcu::UVec2					m_rangeA;
647 	tcu::UVec2					m_rangeB;
648 	bool						m_isVertexCase;
649 
650 	int							m_numTestsPerIter;
651 	int							m_numIters;
652 	de::Random					m_rnd;
653 
654 	// Iteration state.
655 	glu::ShaderProgram*			m_program;
656 	deUint32					m_framebuffer;
657 	deUint32					m_renderbuffer;
658 	int							m_iterNdx;
659 };
660 
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)661 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)
662 	: TestCase			(context, name, desc)
663 	, m_op				(op)
664 	, m_evalFunc		(evalFunc)
665 	, m_precision		(precision)
666 	, m_bits			(bits)
667 	, m_rangeA			(rangeA)
668 	, m_rangeB			(rangeB)
669 	, m_isVertexCase	(isVertexCase)
670 	, m_numTestsPerIter	(32)
671 	, m_numIters		(4)
672 	, m_rnd				(deStringHash(name))
673 	, m_program			(DE_NULL)
674 	, m_framebuffer		(0)
675 	, m_renderbuffer	(0)
676 	, m_iterNdx			(0)
677 {
678 }
679 
~ShaderUintPrecisionCase(void)680 ShaderUintPrecisionCase::~ShaderUintPrecisionCase (void)
681 {
682 	ShaderUintPrecisionCase::deinit();
683 }
684 
init(void)685 void ShaderUintPrecisionCase::init (void)
686 {
687 	const glw::Functions&	gl	= m_context.getRenderContext().getFunctions();
688 	TestLog&				log	= m_testCtx.getLog();
689 
690 	DE_ASSERT(!m_program && !m_framebuffer && !m_renderbuffer);
691 
692 	// Create program.
693 	m_program = createIntUintPrecisionEvalProgram(m_context.getRenderContext(), glu::TYPE_UINT, m_precision, m_op.c_str(), m_isVertexCase);
694 	log << *m_program;
695 
696 	TCU_CHECK(m_program->isOk());
697 
698 	// Create framebuffer.
699 	gl.genFramebuffers(1, &m_framebuffer);
700 	gl.genRenderbuffers(1, &m_renderbuffer);
701 
702 	gl.bindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer);
703 	gl.renderbufferStorage(GL_RENDERBUFFER, GL_R32UI, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT);
704 
705 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
706 	gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_renderbuffer);
707 
708 	GLU_EXPECT_NO_ERROR(gl.getError(), "Post framebuffer setup");
709 	TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
710 
711 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer());
712 
713 	// Initialize test result to pass.
714 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
715 	m_iterNdx = 0;
716 
717 	log << TestLog::Message << "Number of accurate bits assumed = " << m_bits << TestLog::EndMessage;
718 }
719 
deinit(void)720 void ShaderUintPrecisionCase::deinit (void)
721 {
722 	delete m_program;
723 
724 	if (m_framebuffer)
725 		m_context.getRenderContext().getFunctions().deleteFramebuffers(1, &m_framebuffer);
726 
727 	if (m_renderbuffer)
728 		m_context.getRenderContext().getFunctions().deleteRenderbuffers(1, &m_renderbuffer);
729 
730 	m_program		= DE_NULL;
731 	m_framebuffer	= 0;
732 	m_renderbuffer	= 0;
733 }
734 
iterate(void)735 ShaderUintPrecisionCase::IterateResult ShaderUintPrecisionCase::iterate (void)
736 {
737 	// Constant data.
738 	const float position[] =
739 	{
740 		-1.0f, -1.0f, 0.0f, 1.0f,
741 		-1.0f,  1.0f, 0.0f, 1.0f,
742 		 1.0f, -1.0f, 0.0f, 1.0f,
743 		 1.0f,  1.0f, 0.0f, 1.0f
744 	};
745 	const deUint16					indices[]	= { 0, 1, 2, 2, 1, 3 };
746 
747 	const int						numVertices	= 4;
748 	deUint32						in0Arr[4]	= { 0 };
749 	deUint32						in1Arr[4]	= { 0 };
750 
751 	TestLog&						log			= m_testCtx.getLog();
752 	const glw::Functions&			gl			= m_context.getRenderContext().getFunctions();
753 	deUint32						mask		= m_bits == 32 ? 0xffffffffu : ((1u<<m_bits)-1);
754 	vector<deUint32>				pixels		(FRAMEBUFFER_WIDTH*FRAMEBUFFER_HEIGHT*4);
755 	vector<glu::VertexArrayBinding>	vertexArrays;
756 
757 	deUint32						prog		= m_program->getProgram();
758 
759 	// \todo [2012-05-03 pyry] A bit hacky.
760 	bool							isMaxRangeA	= m_rangeA.x() == 0 && m_rangeA.y() == 0xffffffff;
761 	bool							isMaxRangeB	= m_rangeB.x() == 0 && m_rangeB.y() == 0xffffffff;
762 
763 	gl.useProgram(prog);
764 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
765 
766 	vertexArrays.push_back(glu::va::Float("a_position", 4, numVertices, 0, &position[0]));
767 	vertexArrays.push_back(glu::va::Uint32("a_in0", 1, numVertices, 0, &in0Arr[0]));
768 	vertexArrays.push_back(glu::va::Uint32("a_in1", 1, numVertices, 0, &in1Arr[0]));
769 
770 	GLU_EXPECT_NO_ERROR(gl.getError(), "After program setup");
771 
772 	// Compute values and reference.
773 	for (int testNdx = 0; testNdx < m_numTestsPerIter; testNdx++)
774 	{
775 		deUint32	in0		= (isMaxRangeA ? m_rnd.getUint32() : (m_rangeA.x() + m_rnd.getUint32()%(m_rangeA.y()-m_rangeA.x()+1))) & mask;
776 		deUint32	in1		= (isMaxRangeB ? m_rnd.getUint32() : (m_rangeB.x() + m_rnd.getUint32()%(m_rangeB.y()-m_rangeB.x()+1))) & mask;
777 		deUint32	refOut	= m_evalFunc(in0, in1) & mask;
778 
779 		log << TestLog::Message << "iter " << m_iterNdx << ", test " << testNdx << ": "
780 								<< "in0 = " << tcu::toHex(in0) << ", in1 = " << tcu::toHex(in1) << ", ref out = " << tcu::toHex(refOut)
781 			<< TestLog::EndMessage;
782 
783 		std::fill(&in0Arr[0], &in0Arr[0] + DE_LENGTH_OF_ARRAY(in0Arr), in0);
784 		std::fill(&in1Arr[0], &in1Arr[0] + DE_LENGTH_OF_ARRAY(in1Arr), in1);
785 
786 		glu::draw(m_context.getRenderContext(), prog, (int)vertexArrays.size(), &vertexArrays[0],
787 				  glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
788 		gl.readPixels(0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, GL_RGBA_INTEGER, GL_UNSIGNED_INT, &pixels[0]);
789 		GLU_EXPECT_NO_ERROR(gl.getError(), "After render");
790 
791 		// Compare pixels.
792 		for (int y = 0; y < FRAMEBUFFER_HEIGHT; y++)
793 		{
794 			for (int x = 0; x < FRAMEBUFFER_WIDTH; x++)
795 			{
796 				deUint32	cmpOut		= pixels[(y*FRAMEBUFFER_WIDTH + x)*4];
797 				deUint32	cmpMasked	= cmpOut & mask;
798 
799 				if (cmpMasked != refOut)
800 				{
801 					log << TestLog::Message << "Comparison failed (at " << x << ", " << y << "): "
802 											<< "got " << tcu::toHex(cmpOut)
803 						<< TestLog::EndMessage;
804 					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
805 					return STOP;
806 				}
807 			}
808 		}
809 	}
810 
811 	gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
812 	GLU_EXPECT_NO_ERROR(gl.getError(), "After iteration");
813 
814 	m_iterNdx += 1;
815 	return (m_iterNdx < m_numIters) ? CONTINUE : STOP;
816 }
817 
ShaderPrecisionTests(Context & context)818 ShaderPrecisionTests::ShaderPrecisionTests (Context& context)
819 	: TestCaseGroup(context, "precision", "Shader precision requirements validation tests")
820 {
821 }
822 
~ShaderPrecisionTests(void)823 ShaderPrecisionTests::~ShaderPrecisionTests (void)
824 {
825 }
826 
init(void)827 void ShaderPrecisionTests::init (void)
828 {
829 	using tcu::add;
830 	using tcu::sub;
831 	using tcu::mul;
832 	using tcu::div;
833 	using tcu::Vec2;
834 	using tcu::IVec2;
835 	using tcu::UVec2;
836 
837 	// Exp = Emax-2, Mantissa = 0
838 	float		minF32			= tcu::Float32((1u<<31) | (0xfdu<<23) | 0x0u).asFloat();
839 	float		maxF32			= tcu::Float32((0u<<31) | (0xfdu<<23) | 0x0u).asFloat();
840 	float		minF16			= tcu::Float16((deUint16)((1u<<15) | (0x1du<<10) | 0x0u)).asFloat();
841 	float		maxF16			= tcu::Float16((deUint16)((0u<<15) | (0x1du<<10) | 0x0u)).asFloat();
842 	tcu::Vec2	fullRange32F	(minF32, maxF32);
843 	tcu::Vec2	fullRange16F	(minF16, maxF16);
844 	tcu::IVec2	fullRange32I	(0x80000000, 0x7fffffff);
845 	tcu::IVec2	fullRange16I	(-(1<<15), (1<<15)-1);
846 	tcu::IVec2	fullRange8I		(-(1<<7), (1<<7)-1);
847 	tcu::UVec2	fullRange32U	(0u, 0xffffffffu);
848 	tcu::UVec2	fullRange16U	(0u, 0xffffu);
849 	tcu::UVec2	fullRange8U		(0u, 0xffu);
850 
851 	// \note Right now it is not programmatically verified that the results shouldn't end up being inf/nan but
852 	//       actual values used are ok.
853 
854 	static const struct
855 	{
856 		const char*							name;
857 		const char*							op;
858 		ShaderFloatPrecisionCase::EvalFunc	evalFunc;
859 		glu::Precision						precision;
860 		tcu::Vec2							rangeA;
861 		tcu::Vec2							rangeB;
862 	} floatCases[] =
863 	{
864 		// Name				Op				Eval			Precision				RangeA				RangeB
865 		{ "highp_add",		"in0 + in1",	add<double>,	glu::PRECISION_HIGHP,	fullRange32F,		fullRange32F		},
866 		{ "highp_sub",		"in0 - in1",	sub<double>,	glu::PRECISION_HIGHP,	fullRange32F,		fullRange32F		},
867 		{ "highp_mul",		"in0 * in1",	mul<double>,	glu::PRECISION_HIGHP,	Vec2(-1e5f, 1e5f),	Vec2(-1e5f, 1e5f)	},
868 		{ "highp_div",		"in0 / in1",	div<double>,	glu::PRECISION_HIGHP,	Vec2(-1e5f, 1e5f),	Vec2(-1e5f, 1e5f)	},
869 		{ "mediump_add",	"in0 + in1",	add<double>,	glu::PRECISION_MEDIUMP,	fullRange16F,		fullRange16F		},
870 		{ "mediump_sub",	"in0 - in1",	sub<double>,	glu::PRECISION_MEDIUMP,	fullRange16F,		fullRange16F		},
871 		{ "mediump_mul",	"in0 * in1",	mul<double>,	glu::PRECISION_MEDIUMP,	Vec2(-1e2f, 1e2f),	Vec2(-1e2f, 1e2f)	},
872 		{ "mediump_div",	"in0 / in1",	div<double>,	glu::PRECISION_MEDIUMP,	Vec2(-1e2f, 1e2f),	Vec2(-1e2f, 1e2f)	}
873 	};
874 
875 	static const struct
876 	{
877 		const char*							name;
878 		const char*							op;
879 		ShaderIntPrecisionCase::EvalFunc	evalFunc;
880 		glu::Precision						precision;
881 		int									bits;
882 		tcu::IVec2							rangeA;
883 		tcu::IVec2							rangeB;
884 	} intCases[] =
885 	{
886 		// Name				Op				Eval				Precision				Bits	RangeA			RangeB
887 		{ "highp_add",		"in0 + in1",	add<int>,			glu::PRECISION_HIGHP,	32,		fullRange32I,	fullRange32I },
888 		{ "highp_sub",		"in0 - in1",	sub<int>,			glu::PRECISION_HIGHP,	32,		fullRange32I,	fullRange32I },
889 		{ "highp_mul",		"in0 * in1",	mul<int>,			glu::PRECISION_HIGHP,	32,		fullRange32I,	fullRange32I },
890 		{ "highp_div",		"in0 / in1",	div<int>,			glu::PRECISION_HIGHP,	32,		fullRange32I,	IVec2(-10000, -1) },
891 		{ "mediump_add",	"in0 + in1",	add<int>,			glu::PRECISION_MEDIUMP,	16,		fullRange16I,	fullRange16I },
892 		{ "mediump_sub",	"in0 - in1",	sub<int>,			glu::PRECISION_MEDIUMP,	16,		fullRange16I,	fullRange16I },
893 		{ "mediump_mul",	"in0 * in1",	mul<int>,			glu::PRECISION_MEDIUMP,	16,		fullRange16I,	fullRange16I },
894 		{ "mediump_div",	"in0 / in1",	div<int>,			glu::PRECISION_MEDIUMP,	16,		fullRange16I,	IVec2(1, 1000) },
895 		{ "lowp_add",		"in0 + in1",	add<int>,			glu::PRECISION_LOWP,	8,		fullRange8I,	fullRange8I },
896 		{ "lowp_sub",		"in0 - in1",	sub<int>,			glu::PRECISION_LOWP,	8,		fullRange8I,	fullRange8I },
897 		{ "lowp_mul",		"in0 * in1",	mul<int>,			glu::PRECISION_LOWP,	8,		fullRange8I,	fullRange8I },
898 		{ "lowp_div",		"in0 / in1",	div<int>,			glu::PRECISION_LOWP,	8,		fullRange8I,	IVec2(-50, -1) }
899 	};
900 
901 	static const struct
902 	{
903 		const char*							name;
904 		const char*							op;
905 		ShaderUintPrecisionCase::EvalFunc	evalFunc;
906 		glu::Precision						precision;
907 		int									bits;
908 		tcu::UVec2							rangeA;
909 		tcu::UVec2							rangeB;
910 	} uintCases[] =
911 	{
912 		// Name				Op				Eval				Precision				Bits	RangeA			RangeB
913 		{ "highp_add",		"in0 + in1",	add<deUint32>,		glu::PRECISION_HIGHP,	32,		fullRange32U,	fullRange32U },
914 		{ "highp_sub",		"in0 - in1",	sub<deUint32>,		glu::PRECISION_HIGHP,	32,		fullRange32U,	fullRange32U },
915 		{ "highp_mul",		"in0 * in1",	mul<deUint32>,		glu::PRECISION_HIGHP,	32,		fullRange32U,	fullRange32U },
916 		{ "highp_div",		"in0 / in1",	div<deUint32>,		glu::PRECISION_HIGHP,	32,		fullRange32U,	UVec2(1u, 10000u) },
917 		{ "mediump_add",	"in0 + in1",	add<deUint32>,		glu::PRECISION_MEDIUMP,	16,		fullRange16U,	fullRange16U },
918 		{ "mediump_sub",	"in0 - in1",	sub<deUint32>,		glu::PRECISION_MEDIUMP,	16,		fullRange16U,	fullRange16U },
919 		{ "mediump_mul",	"in0 * in1",	mul<deUint32>,		glu::PRECISION_MEDIUMP,	16,		fullRange16U,	fullRange16U },
920 		{ "mediump_div",	"in0 / in1",	div<deUint32>,		glu::PRECISION_MEDIUMP,	16,		fullRange16U,	UVec2(1, 1000u) },
921 		{ "lowp_add",		"in0 + in1",	add<deUint32>,		glu::PRECISION_LOWP,	8,		fullRange8U,	fullRange8U },
922 		{ "lowp_sub",		"in0 - in1",	sub<deUint32>,		glu::PRECISION_LOWP,	8,		fullRange8U,	fullRange8U },
923 		{ "lowp_mul",		"in0 * in1",	mul<deUint32>,		glu::PRECISION_LOWP,	8,		fullRange8U,	fullRange8U },
924 		{ "lowp_div",		"in0 / in1",	div<deUint32>,		glu::PRECISION_LOWP,	8,		fullRange8U,	UVec2(1, 50u) }
925 	};
926 
927 	tcu::TestCaseGroup* floatGroup = new tcu::TestCaseGroup(m_testCtx, "float", "Floating-point precision tests");
928 	addChild(floatGroup);
929 	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(floatCases); ndx++)
930 	{
931 		floatGroup->addChild(new ShaderFloatPrecisionCase(m_context,
932 														  (string(floatCases[ndx].name) + "_vertex").c_str(), "",
933 														  floatCases[ndx].op,
934 														  floatCases[ndx].evalFunc,
935 														  floatCases[ndx].precision,
936 														  floatCases[ndx].rangeA,
937 														  floatCases[ndx].rangeB,
938 														  true));
939 		floatGroup->addChild(new ShaderFloatPrecisionCase(m_context,
940 														  (string(floatCases[ndx].name) + "_fragment").c_str(), "",
941 														  floatCases[ndx].op,
942 														  floatCases[ndx].evalFunc,
943 														  floatCases[ndx].precision,
944 														  floatCases[ndx].rangeA,
945 														  floatCases[ndx].rangeB,
946 														  false));
947 	}
948 
949 	tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "int", "Integer precision tests");
950 	addChild(intGroup);
951 	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(intCases); ndx++)
952 	{
953 		intGroup->addChild(new ShaderIntPrecisionCase(m_context,
954 													  (string(intCases[ndx].name) + "_vertex").c_str(), "",
955 													  intCases[ndx].op,
956 													  intCases[ndx].evalFunc,
957 													  intCases[ndx].precision,
958 													  intCases[ndx].bits,
959 													  intCases[ndx].rangeA,
960 													  intCases[ndx].rangeB,
961 													  true));
962 		intGroup->addChild(new ShaderIntPrecisionCase(m_context,
963 													  (string(intCases[ndx].name) + "_fragment").c_str(), "",
964 													  intCases[ndx].op,
965 													  intCases[ndx].evalFunc,
966 													  intCases[ndx].precision,
967 													  intCases[ndx].bits,
968 													  intCases[ndx].rangeA,
969 													  intCases[ndx].rangeB,
970 													  false));
971 	}
972 
973 	tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uint", "Unsigned integer precision tests");
974 	addChild(uintGroup);
975 	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(uintCases); ndx++)
976 	{
977 		uintGroup->addChild(new ShaderUintPrecisionCase(m_context,
978 														(string(uintCases[ndx].name) + "_vertex").c_str(), "",
979 														uintCases[ndx].op,
980 														uintCases[ndx].evalFunc,
981 														uintCases[ndx].precision,
982 														uintCases[ndx].bits,
983 														uintCases[ndx].rangeA,
984 														uintCases[ndx].rangeB,
985 														true));
986 		uintGroup->addChild(new ShaderUintPrecisionCase(m_context,
987 														(string(uintCases[ndx].name) + "_fragment").c_str(), "",
988 														uintCases[ndx].op,
989 														uintCases[ndx].evalFunc,
990 														uintCases[ndx].precision,
991 														uintCases[ndx].bits,
992 														uintCases[ndx].rangeA,
993 														uintCases[ndx].rangeB,
994 														false));
995 	}
996 }
997 
998 } // Functional
999 } // gles3
1000 } // deqp
1001