es3fShaderPrecisionTests.cpp revision 8852c82a1ffa4760985c17cc6875d5d521daf343
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
46namespace deqp
47{
48namespace gles3
49{
50namespace Functional
51{
52
53using std::string;
54using std::vector;
55using std::ostringstream;
56using tcu::TestLog;
57
58enum
59{
60	FRAMEBUFFER_WIDTH	= 32,
61	FRAMEBUFFER_HEIGHT	= 32
62};
63
64static 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
124static 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
177class ShaderFloatPrecisionCase : public TestCase
178{
179public:
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
189protected:
190	bool						compare						(float in0, float in1, double reference, float result);
191
192private:
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
215ShaderFloatPrecisionCase::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
233ShaderFloatPrecisionCase::~ShaderFloatPrecisionCase (void)
234{
235	ShaderFloatPrecisionCase::deinit();
236}
237
238void 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
271void 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
286bool 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		= refBits >> maskBits;
314		const deUint64	accurateResBits		= resBits >> maskBits;
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
327ShaderFloatPrecisionCase::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
431class ShaderIntPrecisionCase : public TestCase
432{
433public:
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
443private:
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
467ShaderIntPrecisionCase::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
486ShaderIntPrecisionCase::~ShaderIntPrecisionCase (void)
487{
488	ShaderIntPrecisionCase::deinit();
489}
490
491void 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
526void 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
541inline int extendTo32Bit (int value, int bits)
542{
543	return (value & ((1<<(bits-1))-1)) | (((value & (1<<(bits-1))) << (32-bits)) >> (32-bits));
544}
545
546ShaderIntPrecisionCase::IterateResult ShaderIntPrecisionCase::iterate (void)
547{
548	// Constant data.
549	const float position[] =
550	{
551		-1.0f, -1.0f, 0.0f, 1.0f,
552		-1.0f,  1.0f, 0.0f, 1.0f,
553		 1.0f, -1.0f, 0.0f, 1.0f,
554		 1.0f,  1.0f, 0.0f, 1.0f
555	};
556	const deUint16					indices[]	= { 0, 1, 2, 2, 1, 3 };
557
558	const int						numVertices	= 4;
559	int								in0Arr[4]	= { 0 };
560	int								in1Arr[4]	= { 0 };
561
562	TestLog&						log			= m_testCtx.getLog();
563	const glw::Functions&			gl			= m_context.getRenderContext().getFunctions();
564	deUint32						mask		= m_bits == 32 ? 0xffffffffu : ((1u<<m_bits)-1);
565	vector<int>						pixels		(FRAMEBUFFER_WIDTH*FRAMEBUFFER_HEIGHT*4);
566	vector<glu::VertexArrayBinding>	vertexArrays;
567
568	deUint32						prog		= m_program->getProgram();
569
570	// \todo [2012-05-03 pyry] A bit hacky. getInt() should work fine with ranges like this.
571	bool							isMaxRangeA	= m_rangeA.x() == (int)0x80000000 && m_rangeA.y() == (int)0x7fffffff;
572	bool							isMaxRangeB	= m_rangeB.x() == (int)0x80000000 && m_rangeB.y() == (int)0x7fffffff;
573
574	gl.useProgram(prog);
575	gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
576
577	vertexArrays.push_back(glu::va::Float("a_position", 4, numVertices, 0, &position[0]));
578	vertexArrays.push_back(glu::va::Int32("a_in0", 1, numVertices, 0, &in0Arr[0]));
579	vertexArrays.push_back(glu::va::Int32("a_in1", 1, numVertices, 0, &in1Arr[0]));
580
581	GLU_EXPECT_NO_ERROR(gl.getError(), "After program setup");
582
583	// Compute values and reference.
584	for (int testNdx = 0; testNdx < m_numTestsPerIter; testNdx++)
585	{
586		int		in0			= extendTo32Bit(((isMaxRangeA ? (int)m_rnd.getUint32() : m_rnd.getInt(m_rangeA.x(), m_rangeA.y())) & mask), m_bits);
587		int		in1			= extendTo32Bit(((isMaxRangeB ? (int)m_rnd.getUint32() : m_rnd.getInt(m_rangeB.x(), m_rangeB.y())) & mask), m_bits);
588		int		refMasked	= m_evalFunc(in0, in1) & mask;
589		int		refOut		= extendTo32Bit(refMasked, m_bits);
590
591		log << TestLog::Message << "iter " << m_iterNdx << ", test " << testNdx << ": "
592								<< "in0 = " << in0 << ", in1 = " << in1 << ", ref out = " << refOut << " / " << tcu::toHex(refMasked)
593			<< TestLog::EndMessage;
594
595		std::fill(&in0Arr[0], &in0Arr[0] + DE_LENGTH_OF_ARRAY(in0Arr), in0);
596		std::fill(&in1Arr[0], &in1Arr[0] + DE_LENGTH_OF_ARRAY(in1Arr), in1);
597
598		glu::draw(m_context.getRenderContext(), prog, (int)vertexArrays.size(), &vertexArrays[0],
599				  glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
600		gl.readPixels(0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, GL_RGBA_INTEGER, GL_INT, &pixels[0]);
601		GLU_EXPECT_NO_ERROR(gl.getError(), "After render");
602
603		// Compare pixels.
604		for (int y = 0; y < FRAMEBUFFER_HEIGHT; y++)
605		{
606			for (int x = 0; x < FRAMEBUFFER_WIDTH; x++)
607			{
608				int			cmpOut		= pixels[(y*FRAMEBUFFER_WIDTH + x)*4];
609				int			cmpMasked	= cmpOut & mask;
610
611				if (cmpMasked != refMasked)
612				{
613					log << TestLog::Message << "Comparison failed (at " << x << ", " << y << "): "
614											<< "got " << cmpOut << " / " << tcu::toHex(cmpOut)
615						<< TestLog::EndMessage;
616					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
617					return STOP;
618				}
619			}
620		}
621	}
622
623	gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer());
624	GLU_EXPECT_NO_ERROR(gl.getError(), "After iteration");
625
626	m_iterNdx += 1;
627	return (m_iterNdx < m_numIters) ? CONTINUE : STOP;
628}
629
630class ShaderUintPrecisionCase : public TestCase
631{
632public:
633	typedef deUint32			(*EvalFunc)					(deUint32 a, deUint32 b);
634
635								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);
636								~ShaderUintPrecisionCase	(void);
637
638	void						init						(void);
639	void						deinit						(void);
640	IterateResult				iterate						(void);
641
642private:
643								ShaderUintPrecisionCase		(const ShaderUintPrecisionCase& other);
644	ShaderUintPrecisionCase&	operator=					(const ShaderUintPrecisionCase& other);
645
646	// Case parameters.
647	std::string					m_op;
648	EvalFunc					m_evalFunc;
649	glu::Precision				m_precision;
650	int							m_bits;
651	tcu::UVec2					m_rangeA;
652	tcu::UVec2					m_rangeB;
653	bool						m_isVertexCase;
654
655	int							m_numTestsPerIter;
656	int							m_numIters;
657	de::Random					m_rnd;
658
659	// Iteration state.
660	glu::ShaderProgram*			m_program;
661	deUint32					m_framebuffer;
662	deUint32					m_renderbuffer;
663	int							m_iterNdx;
664};
665
666ShaderUintPrecisionCase::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)
667	: TestCase			(context, name, desc)
668	, m_op				(op)
669	, m_evalFunc		(evalFunc)
670	, m_precision		(precision)
671	, m_bits			(bits)
672	, m_rangeA			(rangeA)
673	, m_rangeB			(rangeB)
674	, m_isVertexCase	(isVertexCase)
675	, m_numTestsPerIter	(32)
676	, m_numIters		(4)
677	, m_rnd				(deStringHash(name))
678	, m_program			(DE_NULL)
679	, m_framebuffer		(0)
680	, m_renderbuffer	(0)
681	, m_iterNdx			(0)
682{
683}
684
685ShaderUintPrecisionCase::~ShaderUintPrecisionCase (void)
686{
687	ShaderUintPrecisionCase::deinit();
688}
689
690void ShaderUintPrecisionCase::init (void)
691{
692	const glw::Functions&	gl	= m_context.getRenderContext().getFunctions();
693	TestLog&				log	= m_testCtx.getLog();
694
695	DE_ASSERT(!m_program && !m_framebuffer && !m_renderbuffer);
696
697	// Create program.
698	m_program = createIntUintPrecisionEvalProgram(m_context.getRenderContext(), glu::TYPE_UINT, m_precision, m_op.c_str(), m_isVertexCase);
699	log << *m_program;
700
701	TCU_CHECK(m_program->isOk());
702
703	// Create framebuffer.
704	gl.genFramebuffers(1, &m_framebuffer);
705	gl.genRenderbuffers(1, &m_renderbuffer);
706
707	gl.bindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer);
708	gl.renderbufferStorage(GL_RENDERBUFFER, GL_R32UI, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT);
709
710	gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
711	gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_renderbuffer);
712
713	GLU_EXPECT_NO_ERROR(gl.getError(), "Post framebuffer setup");
714	TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
715
716	gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer());
717
718	// Initialize test result to pass.
719	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
720	m_iterNdx = 0;
721
722	log << TestLog::Message << "Number of accurate bits assumed = " << m_bits << TestLog::EndMessage;
723}
724
725void ShaderUintPrecisionCase::deinit (void)
726{
727	delete m_program;
728
729	if (m_framebuffer)
730		m_context.getRenderContext().getFunctions().deleteFramebuffers(1, &m_framebuffer);
731
732	if (m_renderbuffer)
733		m_context.getRenderContext().getFunctions().deleteRenderbuffers(1, &m_renderbuffer);
734
735	m_program		= DE_NULL;
736	m_framebuffer	= 0;
737	m_renderbuffer	= 0;
738}
739
740ShaderUintPrecisionCase::IterateResult ShaderUintPrecisionCase::iterate (void)
741{
742	// Constant data.
743	const float position[] =
744	{
745		-1.0f, -1.0f, 0.0f, 1.0f,
746		-1.0f,  1.0f, 0.0f, 1.0f,
747		 1.0f, -1.0f, 0.0f, 1.0f,
748		 1.0f,  1.0f, 0.0f, 1.0f
749	};
750	const deUint16					indices[]	= { 0, 1, 2, 2, 1, 3 };
751
752	const int						numVertices	= 4;
753	deUint32						in0Arr[4]	= { 0 };
754	deUint32						in1Arr[4]	= { 0 };
755
756	TestLog&						log			= m_testCtx.getLog();
757	const glw::Functions&			gl			= m_context.getRenderContext().getFunctions();
758	deUint32						mask		= m_bits == 32 ? 0xffffffffu : ((1u<<m_bits)-1);
759	vector<deUint32>				pixels		(FRAMEBUFFER_WIDTH*FRAMEBUFFER_HEIGHT*4);
760	vector<glu::VertexArrayBinding>	vertexArrays;
761
762	deUint32						prog		= m_program->getProgram();
763
764	// \todo [2012-05-03 pyry] A bit hacky.
765	bool							isMaxRangeA	= m_rangeA.x() == 0 && m_rangeA.y() == 0xffffffff;
766	bool							isMaxRangeB	= m_rangeB.x() == 0 && m_rangeB.y() == 0xffffffff;
767
768	gl.useProgram(prog);
769	gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
770
771	vertexArrays.push_back(glu::va::Float("a_position", 4, numVertices, 0, &position[0]));
772	vertexArrays.push_back(glu::va::Uint32("a_in0", 1, numVertices, 0, &in0Arr[0]));
773	vertexArrays.push_back(glu::va::Uint32("a_in1", 1, numVertices, 0, &in1Arr[0]));
774
775	GLU_EXPECT_NO_ERROR(gl.getError(), "After program setup");
776
777	// Compute values and reference.
778	for (int testNdx = 0; testNdx < m_numTestsPerIter; testNdx++)
779	{
780		deUint32	in0		= (isMaxRangeA ? m_rnd.getUint32() : (m_rangeA.x() + m_rnd.getUint32()%(m_rangeA.y()-m_rangeA.x()+1))) & mask;
781		deUint32	in1		= (isMaxRangeB ? m_rnd.getUint32() : (m_rangeB.x() + m_rnd.getUint32()%(m_rangeB.y()-m_rangeB.x()+1))) & mask;
782		deUint32	refOut	= m_evalFunc(in0, in1) & mask;
783
784		log << TestLog::Message << "iter " << m_iterNdx << ", test " << testNdx << ": "
785								<< "in0 = " << tcu::toHex(in0) << ", in1 = " << tcu::toHex(in1) << ", ref out = " << tcu::toHex(refOut)
786			<< TestLog::EndMessage;
787
788		std::fill(&in0Arr[0], &in0Arr[0] + DE_LENGTH_OF_ARRAY(in0Arr), in0);
789		std::fill(&in1Arr[0], &in1Arr[0] + DE_LENGTH_OF_ARRAY(in1Arr), in1);
790
791		glu::draw(m_context.getRenderContext(), prog, (int)vertexArrays.size(), &vertexArrays[0],
792				  glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
793		gl.readPixels(0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, GL_RGBA_INTEGER, GL_UNSIGNED_INT, &pixels[0]);
794		GLU_EXPECT_NO_ERROR(gl.getError(), "After render");
795
796		// Compare pixels.
797		for (int y = 0; y < FRAMEBUFFER_HEIGHT; y++)
798		{
799			for (int x = 0; x < FRAMEBUFFER_WIDTH; x++)
800			{
801				deUint32	cmpOut		= pixels[(y*FRAMEBUFFER_WIDTH + x)*4];
802				deUint32	cmpMasked	= cmpOut & mask;
803
804				if (cmpMasked != refOut)
805				{
806					log << TestLog::Message << "Comparison failed (at " << x << ", " << y << "): "
807											<< "got " << tcu::toHex(cmpOut)
808						<< TestLog::EndMessage;
809					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
810					return STOP;
811				}
812			}
813		}
814	}
815
816	gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
817	GLU_EXPECT_NO_ERROR(gl.getError(), "After iteration");
818
819	m_iterNdx += 1;
820	return (m_iterNdx < m_numIters) ? CONTINUE : STOP;
821}
822
823ShaderPrecisionTests::ShaderPrecisionTests (Context& context)
824	: TestCaseGroup(context, "precision", "Shader precision requirements validation tests")
825{
826}
827
828ShaderPrecisionTests::~ShaderPrecisionTests (void)
829{
830}
831
832void ShaderPrecisionTests::init (void)
833{
834	using tcu::add;
835	using tcu::sub;
836	using tcu::mul;
837	using tcu::div;
838	using tcu::Vec2;
839	using tcu::IVec2;
840	using tcu::UVec2;
841
842	// Exp = Emax-2, Mantissa = 0
843	float		minF32			= tcu::Float32((1u<<31) | (0xfdu<<23) | 0x0u).asFloat();
844	float		maxF32			= tcu::Float32((0u<<31) | (0xfdu<<23) | 0x0u).asFloat();
845	float		minF16			= tcu::Float16((deUint16)((1u<<15) | (0x1du<<10) | 0x0u)).asFloat();
846	float		maxF16			= tcu::Float16((deUint16)((0u<<15) | (0x1du<<10) | 0x0u)).asFloat();
847	tcu::Vec2	fullRange32F	(minF32, maxF32);
848	tcu::Vec2	fullRange16F	(minF16, maxF16);
849	tcu::IVec2	fullRange32I	(0x80000000, 0x7fffffff);
850	tcu::IVec2	fullRange16I	(-(1<<15), (1<<15)-1);
851	tcu::IVec2	fullRange8I		(-(1<<7), (1<<7)-1);
852	tcu::UVec2	fullRange32U	(0u, 0xffffffffu);
853	tcu::UVec2	fullRange16U	(0u, 0xffffu);
854	tcu::UVec2	fullRange8U		(0u, 0xffu);
855
856	// \note Right now it is not programmatically verified that the results shouldn't end up being inf/nan but
857	//       actual values used are ok.
858
859	static const struct
860	{
861		const char*							name;
862		const char*							op;
863		ShaderFloatPrecisionCase::EvalFunc	evalFunc;
864		glu::Precision						precision;
865		tcu::Vec2							rangeA;
866		tcu::Vec2							rangeB;
867	} floatCases[] =
868	{
869		// Name				Op				Eval			Precision				RangeA				RangeB
870		{ "highp_add",		"in0 + in1",	add<double>,	glu::PRECISION_HIGHP,	fullRange32F,		fullRange32F		},
871		{ "highp_sub",		"in0 - in1",	sub<double>,	glu::PRECISION_HIGHP,	fullRange32F,		fullRange32F		},
872		{ "highp_mul",		"in0 * in1",	mul<double>,	glu::PRECISION_HIGHP,	Vec2(-1e5f, 1e5f),	Vec2(-1e5f, 1e5f)	},
873		{ "highp_div",		"in0 / in1",	div<double>,	glu::PRECISION_HIGHP,	Vec2(-1e5f, 1e5f),	Vec2(-1e5f, 1e5f)	},
874		{ "mediump_add",	"in0 + in1",	add<double>,	glu::PRECISION_MEDIUMP,	fullRange16F,		fullRange16F		},
875		{ "mediump_sub",	"in0 - in1",	sub<double>,	glu::PRECISION_MEDIUMP,	fullRange16F,		fullRange16F		},
876		{ "mediump_mul",	"in0 * in1",	mul<double>,	glu::PRECISION_MEDIUMP,	Vec2(-1e2f, 1e2f),	Vec2(-1e2f, 1e2f)	},
877		{ "mediump_div",	"in0 / in1",	div<double>,	glu::PRECISION_MEDIUMP,	Vec2(-1e2f, 1e2f),	Vec2(-1e2f, 1e2f)	}
878	};
879
880	static const struct
881	{
882		const char*							name;
883		const char*							op;
884		ShaderIntPrecisionCase::EvalFunc	evalFunc;
885		glu::Precision						precision;
886		int									bits;
887		tcu::IVec2							rangeA;
888		tcu::IVec2							rangeB;
889	} intCases[] =
890	{
891		// Name				Op				Eval				Precision				Bits	RangeA			RangeB
892		{ "highp_add",		"in0 + in1",	add<int>,			glu::PRECISION_HIGHP,	32,		fullRange32I,	fullRange32I },
893		{ "highp_sub",		"in0 - in1",	sub<int>,			glu::PRECISION_HIGHP,	32,		fullRange32I,	fullRange32I },
894		{ "highp_mul",		"in0 * in1",	mul<int>,			glu::PRECISION_HIGHP,	32,		fullRange32I,	fullRange32I },
895		{ "highp_div",		"in0 / in1",	div<int>,			glu::PRECISION_HIGHP,	32,		fullRange32I,	IVec2(-10000, -1) },
896		{ "mediump_add",	"in0 + in1",	add<int>,			glu::PRECISION_MEDIUMP,	16,		fullRange16I,	fullRange16I },
897		{ "mediump_sub",	"in0 - in1",	sub<int>,			glu::PRECISION_MEDIUMP,	16,		fullRange16I,	fullRange16I },
898		{ "mediump_mul",	"in0 * in1",	mul<int>,			glu::PRECISION_MEDIUMP,	16,		fullRange16I,	fullRange16I },
899		{ "mediump_div",	"in0 / in1",	div<int>,			glu::PRECISION_MEDIUMP,	16,		fullRange16I,	IVec2(1, 1000) },
900		{ "lowp_add",		"in0 + in1",	add<int>,			glu::PRECISION_LOWP,	8,		fullRange8I,	fullRange8I },
901		{ "lowp_sub",		"in0 - in1",	sub<int>,			glu::PRECISION_LOWP,	8,		fullRange8I,	fullRange8I },
902		{ "lowp_mul",		"in0 * in1",	mul<int>,			glu::PRECISION_LOWP,	8,		fullRange8I,	fullRange8I },
903		{ "lowp_div",		"in0 / in1",	div<int>,			glu::PRECISION_LOWP,	8,		fullRange8I,	IVec2(-50, -1) }
904	};
905
906	static const struct
907	{
908		const char*							name;
909		const char*							op;
910		ShaderUintPrecisionCase::EvalFunc	evalFunc;
911		glu::Precision						precision;
912		int									bits;
913		tcu::UVec2							rangeA;
914		tcu::UVec2							rangeB;
915	} uintCases[] =
916	{
917		// Name				Op				Eval				Precision				Bits	RangeA			RangeB
918		{ "highp_add",		"in0 + in1",	add<deUint32>,		glu::PRECISION_HIGHP,	32,		fullRange32U,	fullRange32U },
919		{ "highp_sub",		"in0 - in1",	sub<deUint32>,		glu::PRECISION_HIGHP,	32,		fullRange32U,	fullRange32U },
920		{ "highp_mul",		"in0 * in1",	mul<deUint32>,		glu::PRECISION_HIGHP,	32,		fullRange32U,	fullRange32U },
921		{ "highp_div",		"in0 / in1",	div<deUint32>,		glu::PRECISION_HIGHP,	32,		fullRange32U,	UVec2(1u, 10000u) },
922		{ "mediump_add",	"in0 + in1",	add<deUint32>,		glu::PRECISION_MEDIUMP,	16,		fullRange16U,	fullRange16U },
923		{ "mediump_sub",	"in0 - in1",	sub<deUint32>,		glu::PRECISION_MEDIUMP,	16,		fullRange16U,	fullRange16U },
924		{ "mediump_mul",	"in0 * in1",	mul<deUint32>,		glu::PRECISION_MEDIUMP,	16,		fullRange16U,	fullRange16U },
925		{ "mediump_div",	"in0 / in1",	div<deUint32>,		glu::PRECISION_MEDIUMP,	16,		fullRange16U,	UVec2(1, 1000u) },
926		{ "lowp_add",		"in0 + in1",	add<deUint32>,		glu::PRECISION_LOWP,	8,		fullRange8U,	fullRange8U },
927		{ "lowp_sub",		"in0 - in1",	sub<deUint32>,		glu::PRECISION_LOWP,	8,		fullRange8U,	fullRange8U },
928		{ "lowp_mul",		"in0 * in1",	mul<deUint32>,		glu::PRECISION_LOWP,	8,		fullRange8U,	fullRange8U },
929		{ "lowp_div",		"in0 / in1",	div<deUint32>,		glu::PRECISION_LOWP,	8,		fullRange8U,	UVec2(1, 50u) }
930	};
931
932	tcu::TestCaseGroup* floatGroup = new tcu::TestCaseGroup(m_testCtx, "float", "Floating-point precision tests");
933	addChild(floatGroup);
934	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(floatCases); ndx++)
935	{
936		floatGroup->addChild(new ShaderFloatPrecisionCase(m_context,
937														  (string(floatCases[ndx].name) + "_vertex").c_str(), "",
938														  floatCases[ndx].op,
939														  floatCases[ndx].evalFunc,
940														  floatCases[ndx].precision,
941														  floatCases[ndx].rangeA,
942														  floatCases[ndx].rangeB,
943														  true));
944		floatGroup->addChild(new ShaderFloatPrecisionCase(m_context,
945														  (string(floatCases[ndx].name) + "_fragment").c_str(), "",
946														  floatCases[ndx].op,
947														  floatCases[ndx].evalFunc,
948														  floatCases[ndx].precision,
949														  floatCases[ndx].rangeA,
950														  floatCases[ndx].rangeB,
951														  false));
952	}
953
954	tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "int", "Integer precision tests");
955	addChild(intGroup);
956	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(intCases); ndx++)
957	{
958		intGroup->addChild(new ShaderIntPrecisionCase(m_context,
959													  (string(intCases[ndx].name) + "_vertex").c_str(), "",
960													  intCases[ndx].op,
961													  intCases[ndx].evalFunc,
962													  intCases[ndx].precision,
963													  intCases[ndx].bits,
964													  intCases[ndx].rangeA,
965													  intCases[ndx].rangeB,
966													  true));
967		intGroup->addChild(new ShaderIntPrecisionCase(m_context,
968													  (string(intCases[ndx].name) + "_fragment").c_str(), "",
969													  intCases[ndx].op,
970													  intCases[ndx].evalFunc,
971													  intCases[ndx].precision,
972													  intCases[ndx].bits,
973													  intCases[ndx].rangeA,
974													  intCases[ndx].rangeB,
975													  false));
976	}
977
978	tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uint", "Unsigned integer precision tests");
979	addChild(uintGroup);
980	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(uintCases); ndx++)
981	{
982		uintGroup->addChild(new ShaderUintPrecisionCase(m_context,
983														(string(uintCases[ndx].name) + "_vertex").c_str(), "",
984														uintCases[ndx].op,
985														uintCases[ndx].evalFunc,
986														uintCases[ndx].precision,
987														uintCases[ndx].bits,
988														uintCases[ndx].rangeA,
989														uintCases[ndx].rangeB,
990														true));
991		uintGroup->addChild(new ShaderUintPrecisionCase(m_context,
992														(string(uintCases[ndx].name) + "_fragment").c_str(), "",
993														uintCases[ndx].op,
994														uintCases[ndx].evalFunc,
995														uintCases[ndx].precision,
996														uintCases[ndx].bits,
997														uintCases[ndx].rangeA,
998														uintCases[ndx].rangeB,
999														false));
1000	}
1001}
1002
1003} // Functional
1004} // gles3
1005} // deqp
1006