1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.1 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 gl_HelperInvocation tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "es31fShaderHelperInvocationTests.hpp"
25
26#include "gluObjectWrapper.hpp"
27#include "gluShaderProgram.hpp"
28#include "gluDrawUtil.hpp"
29#include "gluPixelTransfer.hpp"
30
31#include "glwFunctions.hpp"
32#include "glwEnums.hpp"
33
34#include "tcuTestLog.hpp"
35#include "tcuVector.hpp"
36#include "tcuSurface.hpp"
37
38#include "deUniquePtr.hpp"
39#include "deStringUtil.hpp"
40#include "deRandom.hpp"
41#include "deString.h"
42
43namespace deqp
44{
45namespace gles31
46{
47namespace Functional
48{
49namespace
50{
51
52using glu::ShaderProgram;
53using tcu::TestLog;
54using tcu::Vec2;
55using tcu::IVec2;
56using de::MovePtr;
57using std::string;
58using std::vector;
59
60enum PrimitiveType
61{
62	PRIMITIVETYPE_TRIANGLE = 0,
63	PRIMITIVETYPE_LINE,
64	PRIMITIVETYPE_WIDE_LINE,
65	PRIMITIVETYPE_POINT,
66	PRIMITIVETYPE_WIDE_POINT,
67
68	PRIMITIVETYPE_LAST
69};
70
71static int getNumVerticesPerPrimitive (PrimitiveType primType)
72{
73	switch (primType)
74	{
75		case PRIMITIVETYPE_TRIANGLE:	return 3;
76		case PRIMITIVETYPE_LINE:		return 2;
77		case PRIMITIVETYPE_WIDE_LINE:	return 2;
78		case PRIMITIVETYPE_POINT:		return 1;
79		case PRIMITIVETYPE_WIDE_POINT:	return 1;
80		default:
81			DE_ASSERT(false);
82			return 0;
83	}
84}
85
86static glu::PrimitiveType getGluPrimitiveType (PrimitiveType primType)
87{
88	switch (primType)
89	{
90		case PRIMITIVETYPE_TRIANGLE:	return glu::PRIMITIVETYPE_TRIANGLES;
91		case PRIMITIVETYPE_LINE:		return glu::PRIMITIVETYPE_LINES;
92		case PRIMITIVETYPE_WIDE_LINE:	return glu::PRIMITIVETYPE_LINES;
93		case PRIMITIVETYPE_POINT:		return glu::PRIMITIVETYPE_POINTS;
94		case PRIMITIVETYPE_WIDE_POINT:	return glu::PRIMITIVETYPE_POINTS;
95		default:
96			DE_ASSERT(false);
97			return glu::PRIMITIVETYPE_LAST;
98	}
99}
100
101static void genVertices (PrimitiveType primType, int numPrimitives, de::Random* rnd, vector<Vec2>* dst)
102{
103	const bool		isTri		= primType == PRIMITIVETYPE_TRIANGLE;
104	const float		minCoord	= isTri ? -1.5f : -1.0f;
105	const float		maxCoord	= isTri ? +1.5f : +1.0f;
106	const int		numVert		= getNumVerticesPerPrimitive(primType)*numPrimitives;
107
108	dst->resize(numVert);
109
110	for (size_t ndx = 0; ndx < dst->size(); ndx++)
111	{
112		(*dst)[ndx][0] = rnd->getFloat(minCoord, maxCoord);
113		(*dst)[ndx][1] = rnd->getFloat(minCoord, maxCoord);
114	}
115}
116
117static int getInteger (const glw::Functions& gl, deUint32 pname)
118{
119	int v = 0;
120	gl.getIntegerv(pname, &v);
121	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv()");
122	return v;
123}
124
125static Vec2 getRange (const glw::Functions& gl, deUint32 pname)
126{
127	Vec2 v(0.0f);
128	gl.getFloatv(pname, v.getPtr());
129	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetFloatv()");
130	return v;
131}
132
133static void drawRandomPrimitives (const glu::RenderContext& renderCtx, deUint32 program, PrimitiveType primType, int numPrimitives, de::Random* rnd)
134{
135	const glw::Functions&			gl				= renderCtx.getFunctions();
136	const float						minPointSize	= 16.0f;
137	const float						maxPointSize	= 32.0f;
138	const float						minLineWidth	= 16.0f;
139	const float						maxLineWidth	= 32.0f;
140	vector<Vec2>					vertices;
141	vector<glu::VertexArrayBinding>	vertexArrays;
142
143	genVertices(primType, numPrimitives, rnd, &vertices);
144
145	vertexArrays.push_back(glu::va::Float("a_position", 2, (int)vertices.size(), 0, (const float*)&vertices[0]));
146
147	gl.useProgram(program);
148
149	// Special state for certain primitives
150	if (primType == PRIMITIVETYPE_POINT || primType == PRIMITIVETYPE_WIDE_POINT)
151	{
152		const Vec2		range			= getRange(gl, GL_ALIASED_POINT_SIZE_RANGE);
153		const bool		isWidePoint		= primType == PRIMITIVETYPE_WIDE_POINT;
154		const float		pointSize		= isWidePoint ? de::min(rnd->getFloat(minPointSize, maxPointSize), range.y()) : 1.0f;
155		const int		pointSizeLoc	= gl.getUniformLocation(program, "u_pointSize");
156
157		gl.uniform1f(pointSizeLoc, pointSize);
158	}
159	else if (primType == PRIMITIVETYPE_WIDE_LINE)
160	{
161		const Vec2		range			= getRange(gl, GL_ALIASED_LINE_WIDTH_RANGE);
162		const float		lineWidth		= de::min(rnd->getFloat(minLineWidth, maxLineWidth), range.y());
163
164		gl.lineWidth(lineWidth);
165	}
166
167	glu::draw(renderCtx, program, (int)vertexArrays.size(), &vertexArrays[0],
168			  glu::PrimitiveList(getGluPrimitiveType(primType), (int)vertices.size()));
169}
170
171class FboHelper
172{
173public:
174								FboHelper			(const glu::RenderContext& renderCtx, int width, int height, deUint32 format, int numSamples);
175								~FboHelper			(void);
176
177	void						bindForRendering	(void);
178	void						readPixels			(int x, int y, const tcu::PixelBufferAccess& dst);
179
180private:
181	const glu::RenderContext&	m_renderCtx;
182	const int					m_numSamples;
183
184	glu::Renderbuffer			m_colorbuffer;
185	glu::Framebuffer			m_framebuffer;
186	glu::Renderbuffer			m_resolveColorbuffer;
187	glu::Framebuffer			m_resolveFramebuffer;
188};
189
190FboHelper::FboHelper (const glu::RenderContext& renderCtx, int width, int height, deUint32 format, int numSamples)
191	: m_renderCtx			(renderCtx)
192	, m_numSamples			(numSamples)
193	, m_colorbuffer			(renderCtx)
194	, m_framebuffer			(renderCtx)
195	, m_resolveColorbuffer	(renderCtx)
196	, m_resolveFramebuffer	(renderCtx)
197{
198	const glw::Functions&	gl			= m_renderCtx.getFunctions();
199	const int				maxSamples	= getInteger(gl, GL_MAX_SAMPLES);
200
201	gl.bindRenderbuffer(GL_RENDERBUFFER, *m_colorbuffer);
202	gl.renderbufferStorageMultisample(GL_RENDERBUFFER, m_numSamples, format, width, height);
203	gl.bindFramebuffer(GL_FRAMEBUFFER, *m_framebuffer);
204	gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *m_colorbuffer);
205
206	if (m_numSamples > maxSamples && gl.checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
207		throw tcu::NotSupportedError("Sample count exceeds GL_MAX_SAMPLES");
208
209	TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
210
211	if (m_numSamples != 0)
212	{
213		gl.bindRenderbuffer(GL_RENDERBUFFER, *m_resolveColorbuffer);
214		gl.renderbufferStorage(GL_RENDERBUFFER, format, width, height);
215		gl.bindFramebuffer(GL_FRAMEBUFFER, *m_resolveFramebuffer);
216		gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *m_resolveColorbuffer);
217		TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
218	}
219
220	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to create framebuffer");
221}
222
223FboHelper::~FboHelper (void)
224{
225}
226
227void FboHelper::bindForRendering (void)
228{
229	const glw::Functions& gl = m_renderCtx.getFunctions();
230	gl.bindFramebuffer(GL_FRAMEBUFFER, *m_framebuffer);
231	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer()");
232}
233
234void FboHelper::readPixels (int x, int y, const tcu::PixelBufferAccess& dst)
235{
236	const glw::Functions&	gl		= m_renderCtx.getFunctions();
237	const int				width	= dst.getWidth();
238	const int				height	= dst.getHeight();
239
240	if (m_numSamples != 0)
241	{
242		gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, *m_resolveFramebuffer);
243		gl.blitFramebuffer(x, y, width, height, x, y, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
244		gl.bindFramebuffer(GL_READ_FRAMEBUFFER, *m_resolveFramebuffer);
245	}
246
247	glu::readPixels(m_renderCtx, x, y, dst);
248}
249
250enum
251{
252	FRAMEBUFFER_WIDTH	= 256,
253	FRAMEBUFFER_HEIGHT	= 256,
254	FRAMEBUFFER_FORMAT	= GL_RGBA8,
255	NUM_SAMPLES_MAX		= -1
256};
257
258//! Verifies that gl_HelperInvocation is false in all rendered pixels.
259class HelperInvocationValueCase : public TestCase
260{
261public:
262							HelperInvocationValueCase	(Context& context, const char* name, const char* description, PrimitiveType primType, int numSamples);
263							~HelperInvocationValueCase	(void);
264
265	void					init						(void);
266	void					deinit						(void);
267	IterateResult			iterate						(void);
268
269private:
270	const PrimitiveType		m_primitiveType;
271	const int				m_numSamples;
272
273	const int				m_numIters;
274	const int				m_numPrimitivesPerIter;
275
276	MovePtr<ShaderProgram>	m_program;
277	MovePtr<FboHelper>		m_fbo;
278	int						m_iterNdx;
279};
280
281HelperInvocationValueCase::HelperInvocationValueCase (Context& context, const char* name, const char* description, PrimitiveType primType, int numSamples)
282	: TestCase					(context, name, description)
283	, m_primitiveType			(primType)
284	, m_numSamples				(numSamples)
285	, m_numIters				(5)
286	, m_numPrimitivesPerIter	(10)
287	, m_iterNdx					(0)
288{
289}
290
291HelperInvocationValueCase::~HelperInvocationValueCase (void)
292{
293	deinit();
294}
295
296void HelperInvocationValueCase::init (void)
297{
298	const glu::RenderContext&	renderCtx		= m_context.getRenderContext();
299	const glw::Functions&		gl				= renderCtx.getFunctions();
300	const int					maxSamples		= getInteger(gl, GL_MAX_SAMPLES);
301	const int					actualSamples	= m_numSamples == NUM_SAMPLES_MAX ? maxSamples : m_numSamples;
302
303	m_program = MovePtr<ShaderProgram>(new ShaderProgram(m_context.getRenderContext(),
304		glu::ProgramSources()
305			<< glu::VertexSource(
306				"#version 310 es\n"
307				"in highp vec2 a_position;\n"
308				"uniform highp float u_pointSize;\n"
309				"void main (void)\n"
310				"{\n"
311				"	gl_Position = vec4(a_position, 0.0, 1.0);\n"
312				"	gl_PointSize = u_pointSize;\n"
313				"}\n")
314			<< glu::FragmentSource(
315				"#version 310 es\n"
316				"out mediump vec4 o_color;\n"
317				"void main (void)\n"
318				"{\n"
319				"	if (gl_HelperInvocation)\n"
320				"		o_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
321				"	else\n"
322				"		o_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
323				"}\n")));
324
325	m_testCtx.getLog() << *m_program;
326
327	if (!m_program->isOk())
328	{
329		m_program.clear();
330		TCU_FAIL("Compile failed");
331	}
332
333	m_testCtx.getLog() << TestLog::Message << "Using GL_RGBA8 framebuffer with "
334					   << actualSamples << " samples" << TestLog::EndMessage;
335
336	m_fbo = MovePtr<FboHelper>(new FboHelper(renderCtx, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT,
337											 FRAMEBUFFER_FORMAT, actualSamples));
338
339	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
340}
341
342void HelperInvocationValueCase::deinit (void)
343{
344	m_program.clear();
345	m_fbo.clear();
346}
347
348static bool verifyHelperInvocationValue (TestLog& log, const tcu::Surface& result, bool isMultiSample)
349{
350	const tcu::RGBA		bgRef				(0, 0, 0, 255);
351	const tcu::RGBA		fgRef				(0, 255, 0, 255);
352	const tcu::RGBA		threshold			(1, isMultiSample ? 254 : 1, 1, 1);
353	int					numInvalidPixels	= 0;
354
355	for (int y = 0; y < result.getHeight(); ++y)
356	{
357		for (int x = 0; x < result.getWidth(); ++x)
358		{
359			const tcu::RGBA	resPix	= result.getPixel(x, y);
360
361			if (!tcu::compareThreshold(resPix, bgRef, threshold) &&
362				!tcu::compareThreshold(resPix, fgRef, threshold))
363				numInvalidPixels += 1;
364		}
365	}
366
367	if (numInvalidPixels > 0)
368	{
369		log << TestLog::Image("Result", "Result image", result);
370		log << TestLog::Message << "ERROR: Found " << numInvalidPixels << " invalid result pixels!" << TestLog::EndMessage;
371	}
372	else
373		log << TestLog::Message << "All result pixels are valid" << TestLog::EndMessage;
374
375	return numInvalidPixels == 0;
376}
377
378HelperInvocationValueCase::IterateResult HelperInvocationValueCase::iterate (void)
379{
380	const glu::RenderContext&		renderCtx	= m_context.getRenderContext();
381	const glw::Functions&			gl			= renderCtx.getFunctions();
382	const string					sectionName	= string("Iteration ") + de::toString(m_iterNdx+1) + " / " + de::toString(m_numIters);
383	const tcu::ScopedLogSection		section		(m_testCtx.getLog(), (string("Iter") + de::toString(m_iterNdx)), sectionName);
384	de::Random						rnd			(deStringHash(getName()) ^ deInt32Hash(m_iterNdx));
385	tcu::Surface					result		(FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT);
386
387	m_fbo->bindForRendering();
388	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
389	gl.clear(GL_COLOR_BUFFER_BIT);
390
391	drawRandomPrimitives(renderCtx, m_program->getProgram(), m_primitiveType, m_numPrimitivesPerIter, &rnd);
392
393	m_fbo->readPixels(0, 0, result.getAccess());
394
395	if (!verifyHelperInvocationValue(m_testCtx.getLog(), result, m_numSamples != 0))
396		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid pixels found");
397
398	m_iterNdx += 1;
399	return (m_iterNdx < m_numIters) ? CONTINUE : STOP;
400}
401
402//! Checks derivates when value depends on gl_HelperInvocation.
403class HelperInvocationDerivateCase : public TestCase
404{
405public:
406							HelperInvocationDerivateCase	(Context& context, const char* name, const char* description, PrimitiveType primType, int numSamples, const char* derivateFunc);
407							~HelperInvocationDerivateCase	(void);
408
409	void					init							(void);
410	void					deinit							(void);
411	IterateResult			iterate							(void);
412
413private:
414	const PrimitiveType		m_primitiveType;
415	const int				m_numSamples;
416	const std::string		m_derivateFunc;
417
418	const int				m_numIters;
419
420	MovePtr<ShaderProgram>	m_program;
421	MovePtr<FboHelper>		m_fbo;
422	int						m_iterNdx;
423};
424
425HelperInvocationDerivateCase::HelperInvocationDerivateCase (Context& context, const char* name, const char* description, PrimitiveType primType, int numSamples, const char* derivateFunc)
426	: TestCase					(context, name, description)
427	, m_primitiveType			(primType)
428	, m_numSamples				(numSamples)
429	, m_derivateFunc			(derivateFunc)
430	, m_numIters				(16)
431	, m_iterNdx					(0)
432{
433}
434
435HelperInvocationDerivateCase::~HelperInvocationDerivateCase (void)
436{
437	deinit();
438}
439
440void HelperInvocationDerivateCase::init (void)
441{
442	const glu::RenderContext&	renderCtx		= m_context.getRenderContext();
443	const glw::Functions&		gl				= renderCtx.getFunctions();
444	const int					maxSamples		= getInteger(gl, GL_MAX_SAMPLES);
445	const int					actualSamples	= m_numSamples == NUM_SAMPLES_MAX ? maxSamples : m_numSamples;
446
447	m_program = MovePtr<ShaderProgram>(new ShaderProgram(m_context.getRenderContext(),
448		glu::ProgramSources()
449			<< glu::VertexSource(
450				"#version 310 es\n"
451				"in highp vec2 a_position;\n"
452				"uniform highp float u_pointSize;\n"
453				"void main (void)\n"
454				"{\n"
455				"	gl_Position = vec4(a_position, 0.0, 1.0);\n"
456				"	gl_PointSize = u_pointSize;\n"
457				"}\n")
458			<< glu::FragmentSource(string(
459				"#version 310 es\n"
460				"out mediump vec4 o_color;\n"
461				"void main (void)\n"
462				"{\n"
463				"	highp float value		= gl_HelperInvocation ? 1.0 : 0.0;\n"
464				"	highp float derivate	= ") + m_derivateFunc + "(value);\n"
465				"	if (gl_HelperInvocation)\n"
466				"		o_color = vec4(1.0, 0.0, derivate, 1.0);\n"
467				"	else\n"
468				"		o_color = vec4(0.0, 1.0, derivate, 1.0);\n"
469				"}\n")));
470
471	m_testCtx.getLog() << *m_program;
472
473	if (!m_program->isOk())
474	{
475		m_program.clear();
476		TCU_FAIL("Compile failed");
477	}
478
479	m_testCtx.getLog() << TestLog::Message << "Using GL_RGBA8 framebuffer with "
480					   << actualSamples << " samples" << TestLog::EndMessage;
481
482	m_fbo = MovePtr<FboHelper>(new FboHelper(renderCtx, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT,
483											 FRAMEBUFFER_FORMAT, actualSamples));
484
485	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
486}
487
488void HelperInvocationDerivateCase::deinit (void)
489{
490	m_program.clear();
491	m_fbo.clear();
492}
493
494static bool hasNeighborWithColor (const tcu::Surface& surface, int x, int y, tcu::RGBA color, tcu::RGBA threshold)
495{
496	static const IVec2 s_neighbors[] =
497	{
498		IVec2(-1, -1),
499		IVec2( 0, -1),
500		IVec2(+1, -1),
501		IVec2(-1,  0),
502		IVec2(+1,  0),
503		IVec2(-1, +1),
504		IVec2( 0, +1),
505		IVec2(+1, +1)
506	};
507
508	const int	w	= surface.getWidth();
509	const int	h	= surface.getHeight();
510
511	for (int sample = 0; sample < DE_LENGTH_OF_ARRAY(s_neighbors); sample++)
512	{
513		const IVec2	pos	= IVec2(x, y) + s_neighbors[sample];
514
515		if (de::inBounds(pos.x(), 0, w) && de::inBounds(pos.y(), 0, h))
516		{
517			const tcu::RGBA neighborColor = surface.getPixel(pos.x(), pos.y());
518
519			if (tcu::compareThreshold(color, neighborColor, threshold))
520				return true;
521		}
522		else
523			return true; // Can't know for certain
524	}
525
526	return false;
527}
528
529static bool verifyHelperInvocationDerivate (TestLog& log, const tcu::Surface& result, bool isMultiSample)
530{
531	const tcu::RGBA		bgRef				(0, 0, 0, 255);
532	const tcu::RGBA		fgRef				(0, 255, 0, 255);
533	const tcu::RGBA		isBgThreshold		(1, isMultiSample ? 254 : 1, 0, 1);
534	const tcu::RGBA		isFgThreshold		(1, isMultiSample ? 254 : 1, 255, 1);
535	int					numInvalidPixels	= 0;
536	int					numNonZeroDeriv		= 0;
537
538	for (int y = 0; y < result.getHeight(); ++y)
539	{
540		for (int x = 0; x < result.getWidth(); ++x)
541		{
542			const tcu::RGBA	resPix			= result.getPixel(x, y);
543			const bool		isBg			= tcu::compareThreshold(resPix, bgRef, isBgThreshold);
544			const bool		isFg			= tcu::compareThreshold(resPix, fgRef, isFgThreshold);
545			const bool		nonZeroDeriv	= resPix.getBlue() > 0;
546			const bool		neighborBg		= nonZeroDeriv ? hasNeighborWithColor(result, x, y, bgRef, isBgThreshold) : false;
547
548			if (nonZeroDeriv)
549				numNonZeroDeriv	+= 1;
550
551			if ((!isBg && !isFg) ||				// Neither of valid colors (ignoring blue channel that has derivate)
552				(nonZeroDeriv && !neighborBg))	// Has non-zero derivate, but sample not at primitive edge
553				numInvalidPixels += 1;
554		}
555	}
556
557	if (numInvalidPixels > 0)
558	{
559		log << TestLog::Image("Result", "Result image", result);
560		log << TestLog::Message << "ERROR: Found " << numInvalidPixels << " invalid result pixels!" << TestLog::EndMessage;
561	}
562	else
563		log << TestLog::Message << "All result pixels are valid" << TestLog::EndMessage;
564
565	log << TestLog::Message << "Found " << numNonZeroDeriv << " pixels with non-zero derivate (neighbor sample has gl_HelperInvocation = true)" << TestLog::EndMessage;
566
567	return numInvalidPixels == 0;
568}
569
570HelperInvocationDerivateCase::IterateResult HelperInvocationDerivateCase::iterate (void)
571{
572	const glu::RenderContext&		renderCtx	= m_context.getRenderContext();
573	const glw::Functions&			gl			= renderCtx.getFunctions();
574	const string					sectionName	= string("Iteration ") + de::toString(m_iterNdx+1) + " / " + de::toString(m_numIters);
575	const tcu::ScopedLogSection		section		(m_testCtx.getLog(), (string("Iter") + de::toString(m_iterNdx)), sectionName);
576	de::Random						rnd			(deStringHash(getName()) ^ deInt32Hash(m_iterNdx));
577	tcu::Surface					result		(FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT);
578
579	m_fbo->bindForRendering();
580	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
581	gl.clear(GL_COLOR_BUFFER_BIT);
582
583	drawRandomPrimitives(renderCtx, m_program->getProgram(), m_primitiveType, 1, &rnd);
584
585	m_fbo->readPixels(0, 0, result.getAccess());
586
587	if (!verifyHelperInvocationDerivate(m_testCtx.getLog(), result, m_numSamples != 0))
588		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid pixels found");
589
590	m_iterNdx += 1;
591	return (m_iterNdx < m_numIters) ? CONTINUE : STOP;
592}
593
594} // anonymous
595
596ShaderHelperInvocationTests::ShaderHelperInvocationTests (Context& context)
597	: TestCaseGroup(context, "helper_invocation", "gl_HelperInvocation tests")
598{
599}
600
601ShaderHelperInvocationTests::~ShaderHelperInvocationTests (void)
602{
603}
604
605void ShaderHelperInvocationTests::init (void)
606{
607	static const struct
608	{
609		const char*		caseName;
610		PrimitiveType	primType;
611	} s_primTypes[] =
612	{
613		{ "triangles",		PRIMITIVETYPE_TRIANGLE		},
614		{ "lines",			PRIMITIVETYPE_LINE			},
615		{ "wide_lines",		PRIMITIVETYPE_WIDE_LINE		},
616		{ "points",			PRIMITIVETYPE_POINT			},
617		{ "wide_points",	PRIMITIVETYPE_WIDE_POINT	}
618	};
619
620	static const struct
621	{
622		const char*		suffix;
623		int				numSamples;
624	} s_sampleCounts[] =
625	{
626		{ "",					0				},
627		{ "_4_samples",			4				},
628		{ "_8_samples",			8				},
629		{ "_max_samples",		NUM_SAMPLES_MAX	}
630	};
631
632	// value
633	{
634		tcu::TestCaseGroup* const valueGroup = new tcu::TestCaseGroup(m_testCtx, "value", "gl_HelperInvocation value in rendered pixels");
635		addChild(valueGroup);
636
637		for (int sampleCountNdx = 0; sampleCountNdx < DE_LENGTH_OF_ARRAY(s_sampleCounts); sampleCountNdx++)
638		{
639			for (int primTypeNdx = 0; primTypeNdx < DE_LENGTH_OF_ARRAY(s_primTypes); primTypeNdx++)
640			{
641				const string		name		= string(s_primTypes[primTypeNdx].caseName) + s_sampleCounts[sampleCountNdx].suffix;
642				const PrimitiveType	primType	= s_primTypes[primTypeNdx].primType;
643				const int			numSamples	= s_sampleCounts[sampleCountNdx].numSamples;
644
645				valueGroup->addChild(new HelperInvocationValueCase(m_context, name.c_str(), "", primType, numSamples));
646			}
647		}
648	}
649
650	// derivate
651	{
652		tcu::TestCaseGroup* const derivateGroup = new tcu::TestCaseGroup(m_testCtx, "derivate", "Derivate of gl_HelperInvocation-dependent value");
653		addChild(derivateGroup);
654
655		for (int sampleCountNdx = 0; sampleCountNdx < DE_LENGTH_OF_ARRAY(s_sampleCounts); sampleCountNdx++)
656		{
657			for (int primTypeNdx = 0; primTypeNdx < DE_LENGTH_OF_ARRAY(s_primTypes); primTypeNdx++)
658			{
659				const string		name		= string(s_primTypes[primTypeNdx].caseName) + s_sampleCounts[sampleCountNdx].suffix;
660				const PrimitiveType	primType	= s_primTypes[primTypeNdx].primType;
661				const int			numSamples	= s_sampleCounts[sampleCountNdx].numSamples;
662
663				derivateGroup->addChild(new HelperInvocationDerivateCase(m_context, (name + "_dfdx").c_str(),	"", primType, numSamples, "dFdx"));
664				derivateGroup->addChild(new HelperInvocationDerivateCase(m_context, (name + "_dfdy").c_str(),	"", primType, numSamples, "dFdy"));
665				derivateGroup->addChild(new HelperInvocationDerivateCase(m_context, (name + "_fwidth").c_str(),	"", primType, numSamples, "fwidth"));
666			}
667		}
668	}
669}
670
671} // Functional
672} // gles31
673} // deqp
674