• 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	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 
getInteger(const glw::Functions & gl,deUint32 pname)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 
getRange(const glw::Functions & gl,deUint32 pname)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 
drawRandomPrimitives(const glu::RenderContext & renderCtx,deUint32 program,PrimitiveType primType,int numPrimitives,de::Random * rnd)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 
FboHelper(const glu::RenderContext & renderCtx,int width,int height,deUint32 format,int numSamples)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 
~FboHelper(void)254 FboHelper::~FboHelper (void)
255 {
256 }
257 
bindForRendering(void)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 
readPixels(int x,int y,const tcu::PixelBufferAccess & dst)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 
HelperInvocationValueCase(Context & context,const char * name,const char * description,PrimitiveType primType,int numSamples)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 
~HelperInvocationValueCase(void)324 HelperInvocationValueCase::~HelperInvocationValueCase (void)
325 {
326 	deinit();
327 }
328 
init(void)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 
deinit(void)375 void HelperInvocationValueCase::deinit (void)
376 {
377 	m_program.clear();
378 	m_fbo.clear();
379 }
380 
verifyHelperInvocationValue(TestLog & log,const tcu::Surface & result,bool isMultiSample)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 
iterate(void)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 
HelperInvocationDerivateCase(Context & context,const char * name,const char * description,PrimitiveType primType,int numSamples,const char * derivateFunc,bool checkAbsoluteValue)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 
~HelperInvocationDerivateCase(void)483 HelperInvocationDerivateCase::~HelperInvocationDerivateCase (void)
484 {
485 	deinit();
486 }
487 
init(void)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 
deinit(void)537 void HelperInvocationDerivateCase::deinit (void)
538 {
539 	m_program.clear();
540 	m_fbo.clear();
541 }
542 
hasNeighborWithColor(const tcu::Surface & surface,int x,int y,tcu::RGBA color,tcu::RGBA threshold)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 
verifyHelperInvocationDerivate(TestLog & log,const tcu::Surface & result,bool isMultiSample)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 
iterate(void)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 
ShaderHelperInvocationTests(Context & context)649 ShaderHelperInvocationTests::ShaderHelperInvocationTests (Context& context)
650 	: TestCaseGroup(context, "helper_invocation", "gl_HelperInvocation tests")
651 {
652 }
653 
~ShaderHelperInvocationTests(void)654 ShaderHelperInvocationTests::~ShaderHelperInvocationTests (void)
655 {
656 }
657 
init(void)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