Home | History | Annotate | Download | only in functional
      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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    223 FboHelper::~FboHelper (void)
    224 {
    225 }
    226 
    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 
    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 
    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 
    291 HelperInvocationValueCase::~HelperInvocationValueCase (void)
    292 {
    293 	deinit();
    294 }
    295 
    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 
    342 void HelperInvocationValueCase::deinit (void)
    343 {
    344 	m_program.clear();
    345 	m_fbo.clear();
    346 }
    347 
    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 
    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 
    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 
    435 HelperInvocationDerivateCase::~HelperInvocationDerivateCase (void)
    436 {
    437 	deinit();
    438 }
    439 
    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 
    488 void HelperInvocationDerivateCase::deinit (void)
    489 {
    490 	m_program.clear();
    491 	m_fbo.clear();
    492 }
    493 
    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 
    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 
    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 
    596 ShaderHelperInvocationTests::ShaderHelperInvocationTests (Context& context)
    597 	: TestCaseGroup(context, "helper_invocation", "gl_HelperInvocation tests")
    598 {
    599 }
    600 
    601 ShaderHelperInvocationTests::~ShaderHelperInvocationTests (void)
    602 {
    603 }
    604 
    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