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