• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
43 namespace deqp
44 {
45 namespace gles31
46 {
47 namespace Functional
48 {
49 namespace
50 {
51 
52 using glu::ShaderProgram;
53 using tcu::TestLog;
54 using tcu::Vec2;
55 using tcu::IVec2;
56 using de::MovePtr;
57 using std::string;
58 using std::vector;
59 
60 enum 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 
getNumVerticesPerPrimitive(PrimitiveType primType)71 static 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 
getGluPrimitiveType(PrimitiveType primType)86 static 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 
genVertices(PrimitiveType primType,int numPrimitives,de::Random * rnd,vector<Vec2> * dst)101 static 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 
getInteger(const glw::Functions & gl,deUint32 pname)117 static 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 
getRange(const glw::Functions & gl,deUint32 pname)125 static 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 
drawRandomPrimitives(const glu::RenderContext & renderCtx,deUint32 program,PrimitiveType primType,int numPrimitives,de::Random * rnd)133 static 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 
171 class FboHelper
172 {
173 public:
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 
180 private:
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 
FboHelper(const glu::RenderContext & renderCtx,int width,int height,deUint32 format,int numSamples)190 FboHelper::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 
~FboHelper(void)223 FboHelper::~FboHelper (void)
224 {
225 }
226 
bindForRendering(void)227 void 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 
readPixels(int x,int y,const tcu::PixelBufferAccess & dst)234 void 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 
250 enum
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.
259 class HelperInvocationValueCase : public TestCase
260 {
261 public:
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 
269 private:
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 
HelperInvocationValueCase(Context & context,const char * name,const char * description,PrimitiveType primType,int numSamples)281 HelperInvocationValueCase::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 
~HelperInvocationValueCase(void)291 HelperInvocationValueCase::~HelperInvocationValueCase (void)
292 {
293 	deinit();
294 }
295 
init(void)296 void 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 
deinit(void)342 void HelperInvocationValueCase::deinit (void)
343 {
344 	m_program.clear();
345 	m_fbo.clear();
346 }
347 
verifyHelperInvocationValue(TestLog & log,const tcu::Surface & result,bool isMultiSample)348 static 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 
iterate(void)378 HelperInvocationValueCase::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.
403 class HelperInvocationDerivateCase : public TestCase
404 {
405 public:
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 
413 private:
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 
HelperInvocationDerivateCase(Context & context,const char * name,const char * description,PrimitiveType primType,int numSamples,const char * derivateFunc)425 HelperInvocationDerivateCase::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 
~HelperInvocationDerivateCase(void)435 HelperInvocationDerivateCase::~HelperInvocationDerivateCase (void)
436 {
437 	deinit();
438 }
439 
init(void)440 void 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 
deinit(void)488 void HelperInvocationDerivateCase::deinit (void)
489 {
490 	m_program.clear();
491 	m_fbo.clear();
492 }
493 
hasNeighborWithColor(const tcu::Surface & surface,int x,int y,tcu::RGBA color,tcu::RGBA threshold)494 static 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 
verifyHelperInvocationDerivate(TestLog & log,const tcu::Surface & result,bool isMultiSample)529 static 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 
iterate(void)570 HelperInvocationDerivateCase::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 
ShaderHelperInvocationTests(Context & context)596 ShaderHelperInvocationTests::ShaderHelperInvocationTests (Context& context)
597 	: TestCaseGroup(context, "helper_invocation", "gl_HelperInvocation tests")
598 {
599 }
600 
~ShaderHelperInvocationTests(void)601 ShaderHelperInvocationTests::~ShaderHelperInvocationTests (void)
602 {
603 }
604 
init(void)605 void 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