• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 3.1 Module
3  * -------------------------------------------------
4  *
5  * Copyright 2015 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 Primitive bounding box tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es31fPrimitiveBoundingBoxTests.hpp"
25 
26 #include "tcuTestLog.hpp"
27 #include "tcuRenderTarget.hpp"
28 #include "tcuSurface.hpp"
29 #include "tcuTextureUtil.hpp"
30 #include "tcuVectorUtil.hpp"
31 #include "gluCallLogWrapper.hpp"
32 #include "gluContextInfo.hpp"
33 #include "gluRenderContext.hpp"
34 #include "gluStrUtil.hpp"
35 #include "gluShaderProgram.hpp"
36 #include "gluObjectWrapper.hpp"
37 #include "gluPixelTransfer.hpp"
38 #include "glsStateQueryUtil.hpp"
39 #include "glwFunctions.hpp"
40 #include "glwEnums.hpp"
41 #include "deRandom.hpp"
42 #include "deUniquePtr.hpp"
43 #include "deStringUtil.hpp"
44 
45 #include <vector>
46 #include <sstream>
47 #include <algorithm>
48 
49 namespace deqp
50 {
51 namespace gles31
52 {
53 namespace Functional
54 {
55 namespace
56 {
57 
58 namespace StateQueryUtil = ::deqp::gls::StateQueryUtil;
59 
60 struct BoundingBox
61 {
62 	tcu::Vec4 min;
63 	tcu::Vec4 max;
64 
65 	/*--------------------------------------------------------------------*//*!
66 	 * Get component by index of a 8-component vector constructed by
67 	 * concatenating 4-component min and max vectors.
68 	 *//*--------------------------------------------------------------------*/
69 	float&			getComponentAccess	(int ndx);
70 	const float&	getComponentAccess	(int ndx) const;
71 };
72 
getComponentAccess(int ndx)73 float& BoundingBox::getComponentAccess (int ndx)
74 {
75 	DE_ASSERT(ndx >= 0 && ndx < 8);
76 	if (ndx < 4)
77 		return min[ndx];
78 	else
79 		return max[ndx-4];
80 }
81 
getComponentAccess(int ndx) const82 const float& BoundingBox::getComponentAccess (int ndx) const
83 {
84 	return const_cast<BoundingBox*>(this)->getComponentAccess(ndx);
85 }
86 
87 struct ProjectedBBox
88 {
89 	tcu::Vec3	min;
90 	tcu::Vec3	max;
91 };
92 
projectBoundingBox(const BoundingBox & bbox)93 static ProjectedBBox projectBoundingBox (const BoundingBox& bbox)
94 {
95 	const float		wMin	= de::max(0.0f, bbox.min.w()); // clamp to w=0 as extension requires
96 	const float		wMax	= de::max(0.0f, bbox.max.w());
97 	ProjectedBBox	retVal;
98 
99 	retVal.min = tcu::min(bbox.min.swizzle(0, 1, 2) / wMin,
100 						  bbox.min.swizzle(0, 1, 2) / wMax);
101 	retVal.max = tcu::max(bbox.max.swizzle(0, 1, 2) / wMin,
102 						  bbox.max.swizzle(0, 1, 2) / wMax);
103 	return retVal;
104 }
105 
getViewportBoundingBoxArea(const ProjectedBBox & bbox,const tcu::IVec2 & viewportSize,float size=0.0f)106 static tcu::IVec4 getViewportBoundingBoxArea (const ProjectedBBox& bbox, const tcu::IVec2& viewportSize, float size = 0.0f)
107 {
108 	tcu::Vec4	vertexBox;
109 	tcu::IVec4	pixelBox;
110 
111 	vertexBox.x() = (bbox.min.x() * 0.5f + 0.5f) * (float)viewportSize.x();
112 	vertexBox.y() = (bbox.min.y() * 0.5f + 0.5f) * (float)viewportSize.y();
113 	vertexBox.z() = (bbox.max.x() * 0.5f + 0.5f) * (float)viewportSize.x();
114 	vertexBox.w() = (bbox.max.y() * 0.5f + 0.5f) * (float)viewportSize.y();
115 
116 	pixelBox.x() = deFloorFloatToInt32(vertexBox.x() - size/2.0f);
117 	pixelBox.y() = deFloorFloatToInt32(vertexBox.y() - size/2.0f);
118 	pixelBox.z() = deCeilFloatToInt32(vertexBox.z() + size/2.0f);
119 	pixelBox.w() = deCeilFloatToInt32(vertexBox.w() + size/2.0f);
120 	return pixelBox;
121 }
122 
123 
124 class InitialValueCase : public TestCase
125 {
126 public:
127 					InitialValueCase	(Context& context, const char* name, const char* desc);
128 
129 	void			init				(void);
130 	IterateResult	iterate				(void);
131 };
132 
InitialValueCase(Context & context,const char * name,const char * desc)133 InitialValueCase::InitialValueCase (Context& context, const char* name, const char* desc)
134 	: TestCase(context, name, desc)
135 {
136 }
137 
init(void)138 void InitialValueCase::init (void)
139 {
140 	if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
141 		throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
142 }
143 
iterate(void)144 InitialValueCase::IterateResult InitialValueCase::iterate (void)
145 {
146 	StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLfloat[8]>	state;
147 	glu::CallLogWrapper											gl		(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
148 
149 	gl.enableLogging(true);
150 
151 	m_testCtx.getLog()
152 		<< tcu::TestLog::Message
153 		<< "Querying GL_PRIMITIVE_BOUNDING_BOX_EXT, expecting (-1, -1, -1, 1) (1, 1, 1, 1)"
154 		<< tcu::TestLog::EndMessage;
155 
156 	gl.glGetFloatv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
157 	GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
158 
159 	if (!state.verifyValidity(m_testCtx))
160 		return STOP;
161 
162 	m_testCtx.getLog()
163 		<< tcu::TestLog::Message
164 		<< "Got " << tcu::formatArray(&state[0], &state[8])
165 		<< tcu::TestLog::EndMessage;
166 
167 	if ((state[0] != -1.0f) || (state[1] != -1.0f) || (state[2] != -1.0f) || (state[3] != 1.0f) ||
168 		(state[4] !=  1.0f) || (state[5] !=  1.0f) || (state[6] !=  1.0f) || (state[7] != 1.0f))
169 	{
170 		m_testCtx.getLog()
171 			<< tcu::TestLog::Message
172 			<< "Error, unexpected value"
173 			<< tcu::TestLog::EndMessage;
174 
175 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid initial value");
176 	}
177 	else
178 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
179 
180 	return STOP;
181 }
182 
183 class QueryCase : public TestCase
184 {
185 public:
186 	enum QueryMethod
187 	{
188 		QUERY_FLOAT = 0,
189 		QUERY_BOOLEAN,
190 		QUERY_INT,
191 		QUERY_INT64,
192 
193 		QUERY_LAST
194 	};
195 
196 						QueryCase	(Context& context, const char* name, const char* desc, QueryMethod method);
197 
198 private:
199 	void				init		(void);
200 	IterateResult		iterate		(void);
201 
202 	bool				verifyState	(glu::CallLogWrapper& gl, const BoundingBox& bbox) const;
203 
204 	const QueryMethod	m_method;
205 };
206 
QueryCase(Context & context,const char * name,const char * desc,QueryMethod method)207 QueryCase::QueryCase (Context& context, const char* name, const char* desc, QueryMethod method)
208 	: TestCase	(context, name, desc)
209 	, m_method	(method)
210 {
211 	DE_ASSERT(method < QUERY_LAST);
212 }
213 
init(void)214 void QueryCase::init (void)
215 {
216 	if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
217 		throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
218 }
219 
iterate(void)220 QueryCase::IterateResult QueryCase::iterate (void)
221 {
222 	static const BoundingBox fixedCases[] =
223 	{
224 		{ tcu::Vec4( 0.0f,  0.0f,  0.0f,  0.0f), tcu::Vec4( 0.0f,  0.0f,  0.0f,  0.0f) },
225 		{ tcu::Vec4(-0.0f, -0.0f, -0.0f, -0.0f), tcu::Vec4( 0.0f,  0.0f,  0.0f, -0.0f) },
226 		{ tcu::Vec4( 0.0f,  0.0f,  0.0f,  0.0f), tcu::Vec4( 1.0f,  1.0f,  1.0f, -1.0f) },
227 		{ tcu::Vec4( 2.0f,  2.0f,  2.0f,  2.0f), tcu::Vec4( 1.5f,  1.5f,  1.5f,  1.0f) },
228 		{ tcu::Vec4( 1.0f,  1.0f,  1.0f,  1.0f), tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f) },
229 		{ tcu::Vec4( 1.0f,  1.0f,  1.0f,  0.3f), tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.2f) },
230 	};
231 
232 	const int					numRandomCases	= 9;
233 	glu::CallLogWrapper			gl				(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
234 	de::Random					rnd				(0xDE3210);
235 	std::vector<BoundingBox>	cases;
236 
237 	cases.insert(cases.begin(), DE_ARRAY_BEGIN(fixedCases), DE_ARRAY_END(fixedCases));
238 	for (int ndx = 0; ndx < numRandomCases; ++ndx)
239 	{
240 		BoundingBox	boundingBox;
241 
242 		// parameter evaluation order is not guaranteed, cannot just do "max = (rand(), rand(), ...)
243 		for (int coordNdx = 0; coordNdx < 8; ++coordNdx)
244 			boundingBox.getComponentAccess(coordNdx) = rnd.getFloat(-4.0f, 4.0f);
245 
246 		cases.push_back(boundingBox);
247 	}
248 
249 	gl.enableLogging(true);
250 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
251 
252 	for (int caseNdx = 0; caseNdx < (int)cases.size(); ++caseNdx)
253 	{
254 		const tcu::ScopedLogSection	section		(m_testCtx.getLog(), "Iteration", "Iteration " + de::toString(caseNdx+1));
255 		const BoundingBox&			boundingBox	= cases[caseNdx];
256 
257 		gl.glPrimitiveBoundingBox(boundingBox.min.x(), boundingBox.min.y(), boundingBox.min.z(), boundingBox.min.w(),
258 								  boundingBox.max.x(), boundingBox.max.y(), boundingBox.max.z(), boundingBox.max.w());
259 
260 		if (!verifyState(gl, boundingBox))
261 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Unexpected query result");
262 	}
263 
264 	return STOP;
265 }
266 
verifyState(glu::CallLogWrapper & gl,const BoundingBox & bbox) const267 bool QueryCase::verifyState (glu::CallLogWrapper& gl, const BoundingBox& bbox) const
268 {
269 	switch (m_method)
270 	{
271 		case QUERY_FLOAT:
272 		{
273 			StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLfloat[8]>	state;
274 			bool														error = false;
275 
276 			gl.glGetFloatv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
277 			GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
278 
279 			if (!state.verifyValidity(m_testCtx))
280 				return false;
281 
282 			m_testCtx.getLog()
283 					<< tcu::TestLog::Message
284 					<< "glGetFloatv returned " << tcu::formatArray(&state[0], &state[8])
285 					<< tcu::TestLog::EndMessage;
286 
287 			for (int ndx = 0; ndx < 8; ++ndx)
288 				if (state[ndx] != bbox.getComponentAccess(ndx))
289 					error = true;
290 
291 			if (error)
292 			{
293 				m_testCtx.getLog()
294 					<< tcu::TestLog::Message
295 					<< "Error, unexpected value\n"
296 					<< "Expected ["
297 					<< bbox.min.x() << ", " << bbox.min.y() << ", " << bbox.min.z() << ", " << bbox.min.w() << ", "
298 					<< bbox.max.x() << ", " << bbox.max.y() << ", " << bbox.max.z() << ", " << bbox.max.w() << "]"
299 					<< tcu::TestLog::EndMessage;
300 				return false;
301 			}
302 			return true;
303 		}
304 
305 		case QUERY_INT:
306 		{
307 			StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLint[8]>	state;
308 			bool														error = false;
309 
310 			gl.glGetIntegerv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
311 			GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
312 
313 			if (!state.verifyValidity(m_testCtx))
314 				return false;
315 
316 			m_testCtx.getLog()
317 					<< tcu::TestLog::Message
318 					<< "glGetIntegerv returned " << tcu::formatArray(&state[0], &state[8])
319 					<< tcu::TestLog::EndMessage;
320 
321 			for (int ndx = 0; ndx < 8; ++ndx)
322 				if (state[ndx] != StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint>(bbox.getComponentAccess(ndx)) &&
323 					state[ndx] != StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint>(bbox.getComponentAccess(ndx)))
324 					error = true;
325 
326 			if (error)
327 			{
328 				tcu::MessageBuilder builder(&m_testCtx.getLog());
329 
330 				builder	<< "Error, unexpected value\n"
331 						<< "Expected [";
332 
333 				for (int ndx = 0; ndx < 8; ++ndx)
334 				{
335 					const glw::GLint roundDown	= StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint>(bbox.getComponentAccess(ndx));
336 					const glw::GLint roundUp	= StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint>(bbox.getComponentAccess(ndx));
337 
338 					if (ndx != 0)
339 						builder << ", ";
340 
341 					if (roundDown == roundUp)
342 						builder << roundDown;
343 					else
344 						builder << "{" << roundDown << ", " << roundUp << "}";
345 				}
346 
347 				builder	<< "]"
348 						<< tcu::TestLog::EndMessage;
349 				return false;
350 			}
351 			return true;
352 		}
353 
354 		case QUERY_INT64:
355 		{
356 			StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLint64[8]>	state;
357 			bool																error = false;
358 
359 			gl.glGetInteger64v(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
360 			GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
361 
362 			if (!state.verifyValidity(m_testCtx))
363 				return false;
364 
365 			m_testCtx.getLog()
366 					<< tcu::TestLog::Message
367 					<< "glGetInteger64v returned " << tcu::formatArray(&state[0], &state[8])
368 					<< tcu::TestLog::EndMessage;
369 
370 			for (int ndx = 0; ndx < 8; ++ndx)
371 				if (state[ndx] != StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint64>(bbox.getComponentAccess(ndx)) &&
372 					state[ndx] != StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint64>(bbox.getComponentAccess(ndx)))
373 					error = true;
374 
375 			if (error)
376 			{
377 				tcu::MessageBuilder builder(&m_testCtx.getLog());
378 
379 				builder	<< "Error, unexpected value\n"
380 						<< "Expected [";
381 
382 				for (int ndx = 0; ndx < 8; ++ndx)
383 				{
384 					const glw::GLint64 roundDown	= StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint64>(bbox.getComponentAccess(ndx));
385 					const glw::GLint64 roundUp		= StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint64>(bbox.getComponentAccess(ndx));
386 
387 					if (ndx != 0)
388 						builder << ", ";
389 
390 					if (roundDown == roundUp)
391 						builder << roundDown;
392 					else
393 						builder << "{" << roundDown << ", " << roundUp << "}";
394 				}
395 
396 				builder	<< "]"
397 						<< tcu::TestLog::EndMessage;
398 				return false;
399 			}
400 			return true;
401 		}
402 
403 		case QUERY_BOOLEAN:
404 		{
405 			StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLboolean[8]>	state;
406 			bool															error = false;
407 
408 			gl.glGetBooleanv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
409 			GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
410 
411 			if (!state.verifyValidity(m_testCtx))
412 				return false;
413 
414 			m_testCtx.getLog()
415 					<< tcu::TestLog::Message
416 					<< "glGetBooleanv returned ["
417 					<< glu::getBooleanStr(state[0]) << ", " << glu::getBooleanStr(state[1]) << ", " << glu::getBooleanStr(state[2]) << ", " << glu::getBooleanStr(state[3]) << ", "
418 					<< glu::getBooleanStr(state[4]) << ", " << glu::getBooleanStr(state[5]) << ", " << glu::getBooleanStr(state[6]) << ", " << glu::getBooleanStr(state[7]) << "]\n"
419 					<< tcu::TestLog::EndMessage;
420 
421 			for (int ndx = 0; ndx < 8; ++ndx)
422 				if (state[ndx] != ((bbox.getComponentAccess(ndx) != 0.0f) ? (GL_TRUE) : (GL_FALSE)))
423 					error = true;
424 
425 			if (error)
426 			{
427 				tcu::MessageBuilder builder(&m_testCtx.getLog());
428 
429 				builder	<< "Error, unexpected value\n"
430 						<< "Expected [";
431 
432 				for (int ndx = 0; ndx < 8; ++ndx)
433 				{
434 					if (ndx != 0)
435 						builder << ", ";
436 
437 					builder << ((bbox.getComponentAccess(ndx) != 0.0f) ? ("GL_TRUE") : ("GL_FALSE"));
438 				}
439 
440 				builder	<< "]"
441 						<< tcu::TestLog::EndMessage;
442 				return false;
443 			}
444 			return true;
445 		}
446 
447 		default:
448 			DE_ASSERT(false);
449 			return true;
450 	}
451 }
452 
453 class BBoxRenderCase : public TestCase
454 {
455 public:
456 	enum
457 	{
458 		FLAG_RENDERTARGET_DEFAULT	= 1u << 0, //!< render to default renderbuffer
459 		FLAG_RENDERTARGET_FBO		= 1u << 1, //!< render to framebuffer object
460 
461 		FLAG_BBOXSIZE_EQUAL			= 1u << 2, //!< set tight primitive bounding box
462 		FLAG_BBOXSIZE_LARGER		= 1u << 3, //!< set padded primitive bounding box
463 		FLAG_BBOXSIZE_SMALLER		= 1u << 4, //!< set too small primitive bounding box
464 
465 		FLAG_TESSELLATION			= 1u << 5, //!< use tessellation shader
466 		FLAG_GEOMETRY				= 1u << 6, //!< use geometry shader
467 
468 		FLAG_SET_BBOX_STATE			= 1u << 7, //!< set primitive bounding box using global state
469 		FLAG_SET_BBOX_OUTPUT		= 1u << 8, //!< set primitive bounding box using tessellation output
470 		FLAG_PER_PRIMITIVE_BBOX		= 1u << 9, //!< set primitive bounding per primitive
471 
472 		FLAGBIT_USER_BIT			= 10u //!< bits N and and up are reserved for subclasses
473 	};
474 
475 									BBoxRenderCase					(Context& context, const char* name, const char* description, int numIterations, deUint32 flags);
476 									~BBoxRenderCase					(void);
477 
478 protected:
479 	enum RenderTarget
480 	{
481 		RENDERTARGET_DEFAULT,
482 		RENDERTARGET_FBO,
483 	};
484 	enum BBoxSize
485 	{
486 		BBOXSIZE_EQUAL,
487 		BBOXSIZE_LARGER,
488 		BBOXSIZE_SMALLER,
489 	};
490 
491 	enum
492 	{
493 		RENDER_TARGET_MIN_SIZE	= 256,
494 		FBO_SIZE				= 512,
495 		MIN_VIEWPORT_SIZE		= 256,
496 		MAX_VIEWPORT_SIZE		= 512,
497 	};
498 	DE_STATIC_ASSERT(MIN_VIEWPORT_SIZE <= RENDER_TARGET_MIN_SIZE);
499 
500 	enum
501 	{
502 		VA_POS_VEC_NDX		= 0,
503 		VA_COL_VEC_NDX		= 1,
504 		VA_NUM_ATTRIB_VECS	= 2,
505 	};
506 
507 	enum AABBRoundDirection
508 	{
509 		ROUND_INWARDS = 0,
510 		ROUND_OUTWARDS
511 	};
512 
513 	struct IterationConfig
514 	{
515 		tcu::IVec2	viewportPos;
516 		tcu::IVec2	viewportSize;
517 		tcu::Vec2	patternPos;		//!< in NDC
518 		tcu::Vec2	patternSize;	//!< in NDC
519 		BoundingBox	bbox;
520 	};
521 
522 	virtual void					init							(void);
523 	virtual void					deinit							(void);
524 	IterateResult					iterate							(void);
525 
526 	virtual std::string				genVertexSource					(void) const = 0;
527 	virtual std::string				genFragmentSource				(void) const = 0;
528 	virtual std::string				genTessellationControlSource	(void) const = 0;
529 	virtual std::string				genTessellationEvaluationSource	(void) const = 0;
530 	virtual std::string				genGeometrySource				(void) const = 0;
531 
532 	virtual IterationConfig			generateConfig					(int iteration, const tcu::IVec2& renderTargetSize) const = 0;
533 	virtual void					getAttributeData				(std::vector<tcu::Vec4>& data) const = 0;
534 	virtual void					renderTestPattern				(const IterationConfig& config) = 0;
535 	virtual void					verifyRenderResult				(const IterationConfig& config) = 0;
536 
537 	IterationConfig					generateRandomConfig			(int seed, const tcu::IVec2& renderTargetSize) const;
538 	tcu::IVec4						getViewportPatternArea			(const tcu::Vec2& patternPos, const tcu::Vec2& patternSize, const tcu::IVec2& viewportSize, AABBRoundDirection roundDir) const;
539 
540 	void							setupRender						(const IterationConfig& config);
541 
542 	enum ShaderFunction
543 	{
544 		SHADER_FUNC_MIRROR_X,
545 		SHADER_FUNC_MIRROR_Y,
546 		SHADER_FUNC_INSIDE_BBOX,
547 	};
548 
549 	const char*						genShaderFunction				(ShaderFunction func) const;
550 
551 	const RenderTarget				m_renderTarget;
552 	const BBoxSize					m_bboxSize;
553 	const bool						m_hasTessellationStage;
554 	const bool						m_hasGeometryStage;
555 	const bool						m_useGlobalState;
556 	const bool						m_calcPerPrimitiveBBox;
557 	const int						m_numIterations;
558 
559 	de::MovePtr<glu::ShaderProgram>	m_program;
560 	de::MovePtr<glu::Buffer>		m_vbo;
561 	de::MovePtr<glu::Framebuffer>	m_fbo;
562 
563 private:
564 	std::vector<IterationConfig>	m_iterationConfigs;
565 	int								m_iteration;
566 };
567 
BBoxRenderCase(Context & context,const char * name,const char * description,int numIterations,deUint32 flags)568 BBoxRenderCase::BBoxRenderCase (Context& context, const char* name, const char* description, int numIterations, deUint32 flags)
569 	: TestCase					(context, name, description)
570 	, m_renderTarget			((flags & FLAG_RENDERTARGET_DEFAULT) ? (RENDERTARGET_DEFAULT) : (RENDERTARGET_FBO))
571 	, m_bboxSize				((flags & FLAG_BBOXSIZE_EQUAL) ? (BBOXSIZE_EQUAL) : (flags & FLAG_BBOXSIZE_SMALLER) ? (BBOXSIZE_SMALLER) : (BBOXSIZE_LARGER))
572 	, m_hasTessellationStage	((flags & FLAG_TESSELLATION) != 0)
573 	, m_hasGeometryStage		((flags & FLAG_GEOMETRY) != 0)
574 	, m_useGlobalState			((flags & FLAG_SET_BBOX_STATE) != 0)
575 	, m_calcPerPrimitiveBBox	((flags & FLAG_PER_PRIMITIVE_BBOX) != 0)
576 	, m_numIterations			(numIterations)
577 	, m_iteration				(0)
578 {
579 	// validate flags
580 	DE_ASSERT((((m_renderTarget == RENDERTARGET_DEFAULT)	?	(FLAG_RENDERTARGET_DEFAULT)	: (0)) |
581 			   ((m_renderTarget == RENDERTARGET_FBO)		?	(FLAG_RENDERTARGET_FBO)		: (0)) |
582 			   ((m_bboxSize == BBOXSIZE_EQUAL)				?	(FLAG_BBOXSIZE_EQUAL)		: (0)) |
583 			   ((m_bboxSize == BBOXSIZE_LARGER)				?	(FLAG_BBOXSIZE_LARGER)		: (0)) |
584 			   ((m_bboxSize == BBOXSIZE_SMALLER)			?	(FLAG_BBOXSIZE_SMALLER)		: (0)) |
585 			   ((m_hasTessellationStage)					?	(FLAG_TESSELLATION)			: (0)) |
586 			   ((m_hasGeometryStage)						?	(FLAG_GEOMETRY)				: (0)) |
587 			   ((m_useGlobalState)							?	(FLAG_SET_BBOX_STATE)		: (0)) |
588 			   ((!m_useGlobalState)							?	(FLAG_SET_BBOX_OUTPUT)		: (0)) |
589 			   ((m_calcPerPrimitiveBBox)					?	(FLAG_PER_PRIMITIVE_BBOX)	: (0))) == (flags & ((1u << FLAGBIT_USER_BIT) - 1)));
590 
591 	DE_ASSERT(m_useGlobalState || m_hasTessellationStage); // using non-global state requires tessellation
592 
593 	if (m_calcPerPrimitiveBBox)
594 	{
595 		DE_ASSERT(!m_useGlobalState); // per-primitive test requires per-primitive (non-global) state
596 		DE_ASSERT(m_bboxSize == BBOXSIZE_EQUAL); // smaller is hard to verify, larger not interesting
597 	}
598 }
599 
~BBoxRenderCase(void)600 BBoxRenderCase::~BBoxRenderCase (void)
601 {
602 	deinit();
603 }
604 
init(void)605 void BBoxRenderCase::init (void)
606 {
607 	const glw::Functions&	gl					= m_context.getRenderContext().getFunctions();
608 	const tcu::IVec2		renderTargetSize	= (m_renderTarget == RENDERTARGET_DEFAULT) ?
609 													(tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) :
610 													(tcu::IVec2(FBO_SIZE, FBO_SIZE));
611 
612 	// requirements
613 	if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
614 		throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
615 	if (m_hasTessellationStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
616 		throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
617 	if (m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader"))
618 		throw tcu::NotSupportedError("Test requires GL_EXT_geometry_shader extension");
619 	if (m_renderTarget == RENDERTARGET_DEFAULT && (renderTargetSize.x() < RENDER_TARGET_MIN_SIZE || renderTargetSize.y() < RENDER_TARGET_MIN_SIZE))
620 		throw tcu::NotSupportedError(std::string() + "Test requires " + de::toString<int>(RENDER_TARGET_MIN_SIZE) + "x" + de::toString<int>(RENDER_TARGET_MIN_SIZE) + " default framebuffer");
621 
622 	// log case specifics
623 	m_testCtx.getLog()
624 		<< tcu::TestLog::Message
625 		<< "Setting primitive bounding box "
626 			<< ((m_calcPerPrimitiveBBox)         ? ("to exactly cover each generated primitive")
627 			  : (m_bboxSize == BBOXSIZE_EQUAL)   ? ("to exactly cover rendered grid")
628 			  : (m_bboxSize == BBOXSIZE_LARGER)  ? ("to cover the grid and include some padding")
629 			  : (m_bboxSize == BBOXSIZE_SMALLER) ? ("to cover only a subset of the grid")
630 			  : (DE_NULL))
631 			<< ".\n"
632 		<< "Rendering with vertex"
633 			<< ((m_hasTessellationStage) ? ("-tessellation{ctrl,eval}") : (""))
634 			<< ((m_hasGeometryStage) ? ("-geometry") : (""))
635 			<< "-fragment program.\n"
636 		<< "Set bounding box using "
637 			<< ((m_useGlobalState) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output"))
638 			<< "\n"
639 		<< "Verifying rendering results are valid within the bounding box."
640 		<< tcu::TestLog::EndMessage;
641 
642 	// resources
643 
644 	{
645 		glu::ProgramSources sources;
646 		sources << glu::VertexSource(genVertexSource());
647 		sources << glu::FragmentSource(genFragmentSource());
648 
649 		if (m_hasTessellationStage)
650 			sources << glu::TessellationControlSource(genTessellationControlSource())
651 					<< glu::TessellationEvaluationSource(genTessellationEvaluationSource());
652 		if (m_hasGeometryStage)
653 			sources << glu::GeometrySource(genGeometrySource());
654 
655 		m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), sources));
656 		GLU_EXPECT_NO_ERROR(gl.getError(), "build program");
657 
658 		{
659 			const tcu::ScopedLogSection section(m_testCtx.getLog(), "ShaderProgram", "Shader program");
660 			m_testCtx.getLog() << *m_program;
661 		}
662 
663 		if (!m_program->isOk())
664 			throw tcu::TestError("failed to build program");
665 	}
666 
667 	if (m_renderTarget == RENDERTARGET_FBO)
668 	{
669 		glu::Texture colorAttachment(m_context.getRenderContext());
670 
671 		gl.bindTexture(GL_TEXTURE_2D, *colorAttachment);
672 		gl.texStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, FBO_SIZE, FBO_SIZE);
673 		GLU_EXPECT_NO_ERROR(gl.getError(), "gen tex");
674 
675 		m_fbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext()));
676 		gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, **m_fbo);
677 		gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *colorAttachment, 0);
678 		GLU_EXPECT_NO_ERROR(gl.getError(), "attach");
679 
680 		// unbind to prevent texture name deletion from removing it from current fbo attachments
681 		gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
682 	}
683 
684 	{
685 		std::vector<tcu::Vec4> data;
686 
687 		getAttributeData(data);
688 
689 		m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
690 		gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
691 		gl.bufferData(GL_ARRAY_BUFFER, (int)(data.size() * sizeof(tcu::Vec4)), &data[0], GL_STATIC_DRAW);
692 		GLU_EXPECT_NO_ERROR(gl.getError(), "create vbo");
693 	}
694 
695 	// Iterations
696 	for (int iterationNdx = 0; iterationNdx < m_numIterations; ++iterationNdx)
697 		m_iterationConfigs.push_back(generateConfig(iterationNdx, renderTargetSize));
698 }
699 
deinit(void)700 void BBoxRenderCase::deinit (void)
701 {
702 	m_program.clear();
703 	m_vbo.clear();
704 	m_fbo.clear();
705 }
706 
iterate(void)707 BBoxRenderCase::IterateResult BBoxRenderCase::iterate (void)
708 {
709 	const tcu::ScopedLogSection	section		(m_testCtx.getLog(),
710 											 std::string() + "Iteration" + de::toString((int)m_iteration),
711 											 std::string() + "Iteration " + de::toString((int)m_iteration+1) + "/" + de::toString((int)m_iterationConfigs.size()));
712 	const IterationConfig&		config		= m_iterationConfigs[m_iteration];
713 
714 	// default
715 	if (m_iteration == 0)
716 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
717 
718 	renderTestPattern(config);
719 	verifyRenderResult(config);
720 
721 	if (++m_iteration < (int)m_iterationConfigs.size())
722 		return CONTINUE;
723 
724 	return STOP;
725 }
726 
generateRandomConfig(int seed,const tcu::IVec2 & renderTargetSize) const727 BBoxRenderCase::IterationConfig BBoxRenderCase::generateRandomConfig (int seed, const tcu::IVec2& renderTargetSize) const
728 {
729 	de::Random		rnd		(seed);
730 	IterationConfig	config;
731 
732 	// viewport config
733 	config.viewportSize.x()	= rnd.getInt(MIN_VIEWPORT_SIZE, de::min<int>(renderTargetSize.x(), MAX_VIEWPORT_SIZE));
734 	config.viewportSize.y()	= rnd.getInt(MIN_VIEWPORT_SIZE, de::min<int>(renderTargetSize.y(), MAX_VIEWPORT_SIZE));
735 	config.viewportPos.x()	= rnd.getInt(0, renderTargetSize.x() - config.viewportSize.x());
736 	config.viewportPos.y()	= rnd.getInt(0, renderTargetSize.y() - config.viewportSize.y());
737 
738 	// pattern location inside viewport
739 	config.patternSize.x()	= rnd.getFloat(0.4f, 1.4f);
740 	config.patternSize.y()	= rnd.getFloat(0.4f, 1.4f);
741 	config.patternPos.x()	= rnd.getFloat(-1.0f, 1.0f - config.patternSize.x());
742 	config.patternPos.y()	= rnd.getFloat(-1.0f, 1.0f - config.patternSize.y());
743 
744 	// accurate bounding box
745 	config.bbox.min			= tcu::Vec4(config.patternPos.x(), config.patternPos.y(), 0.0f, 1.0f);
746 	config.bbox.max			= tcu::Vec4(config.patternPos.x() + config.patternSize.x(), config.patternPos.y() + config.patternSize.y(), 0.0f, 1.0f);
747 
748 	if (m_bboxSize == BBOXSIZE_LARGER)
749 	{
750 		// increase bbox size
751 		config.bbox.min.x() -= rnd.getFloat() * 0.5f;
752 		config.bbox.min.y() -= rnd.getFloat() * 0.5f;
753 		config.bbox.min.z() -= rnd.getFloat() * 0.5f;
754 
755 		config.bbox.max.x() += rnd.getFloat() * 0.5f;
756 		config.bbox.max.y() += rnd.getFloat() * 0.5f;
757 		config.bbox.max.z() += rnd.getFloat() * 0.5f;
758 	}
759 	else if (m_bboxSize == BBOXSIZE_SMALLER)
760 	{
761 		// reduce bbox size
762 		config.bbox.min.x() += rnd.getFloat() * 0.4f * config.patternSize.x();
763 		config.bbox.min.y() += rnd.getFloat() * 0.4f * config.patternSize.y();
764 
765 		config.bbox.max.x() -= rnd.getFloat() * 0.4f * config.patternSize.x();
766 		config.bbox.max.y() -= rnd.getFloat() * 0.4f * config.patternSize.y();
767 	}
768 
769 	return config;
770 }
771 
getViewportPatternArea(const tcu::Vec2 & patternPos,const tcu::Vec2 & patternSize,const tcu::IVec2 & viewportSize,AABBRoundDirection roundDir) const772 tcu::IVec4 BBoxRenderCase::getViewportPatternArea (const tcu::Vec2& patternPos, const tcu::Vec2& patternSize, const tcu::IVec2& viewportSize, AABBRoundDirection roundDir) const
773 {
774 	const float	halfPixel	= 0.5f;
775 	tcu::Vec4	vertexBox;
776 	tcu::IVec4	pixelBox;
777 
778 	vertexBox.x() = (patternPos.x() * 0.5f + 0.5f) * (float)viewportSize.x();
779 	vertexBox.y() = (patternPos.y() * 0.5f + 0.5f) * (float)viewportSize.y();
780 	vertexBox.z() = ((patternPos.x() + patternSize.x()) * 0.5f + 0.5f) * (float)viewportSize.x();
781 	vertexBox.w() = ((patternPos.y() + patternSize.y()) * 0.5f + 0.5f) * (float)viewportSize.y();
782 
783 	if (roundDir == ROUND_INWARDS)
784 	{
785 		pixelBox.x() = (int)deFloatCeil(vertexBox.x()+halfPixel);
786 		pixelBox.y() = (int)deFloatCeil(vertexBox.y()+halfPixel);
787 		pixelBox.z() = (int)deFloatFloor(vertexBox.z()-halfPixel);
788 		pixelBox.w() = (int)deFloatFloor(vertexBox.w()-halfPixel);
789 	}
790 	else
791 	{
792 		pixelBox.x() = (int)deFloatFloor(vertexBox.x()-halfPixel);
793 		pixelBox.y() = (int)deFloatFloor(vertexBox.y()-halfPixel);
794 		pixelBox.z() = (int)deFloatCeil(vertexBox.z()+halfPixel);
795 		pixelBox.w() = (int)deFloatCeil(vertexBox.w()+halfPixel);
796 	}
797 
798 	return pixelBox;
799 }
800 
setupRender(const IterationConfig & config)801 void BBoxRenderCase::setupRender (const IterationConfig& config)
802 {
803 	const glw::Functions&	gl					= m_context.getRenderContext().getFunctions();
804 	const glw::GLint		posLocation			= gl.getAttribLocation(m_program->getProgram(), "a_position");
805 	const glw::GLint		colLocation			= gl.getAttribLocation(m_program->getProgram(), "a_color");
806 	const glw::GLint		posScaleLocation	= gl.getUniformLocation(m_program->getProgram(), "u_posScale");
807 
808 	TCU_CHECK(posLocation != -1);
809 	TCU_CHECK(colLocation != -1);
810 	TCU_CHECK(posScaleLocation != -1);
811 
812 	m_testCtx.getLog()
813 		<< tcu::TestLog::Message
814 		<< "Setting viewport to ("
815 			<< "x: " << config.viewportPos.x() << ", "
816 			<< "y: " << config.viewportPos.y() << ", "
817 			<< "w: " << config.viewportSize.x() << ", "
818 			<< "h: " << config.viewportSize.y() << ")\n"
819 		<< "Vertex coordinates are in range:\n"
820 			<< "\tx: [" << config.patternPos.x() << ", " << (config.patternPos.x() + config.patternSize.x()) << "]\n"
821 			<< "\ty: [" << config.patternPos.y() << ", " << (config.patternPos.y() + config.patternSize.y()) << "]\n"
822 		<< tcu::TestLog::EndMessage;
823 
824 	if (!m_calcPerPrimitiveBBox)
825 		m_testCtx.getLog()
826 			<< tcu::TestLog::Message
827 			<< "Setting primitive bounding box to:\n"
828 				<< "\t" << config.bbox.min << "\n"
829 				<< "\t" << config.bbox.max << "\n"
830 			<< tcu::TestLog::EndMessage;
831 
832 	if (m_useGlobalState)
833 		gl.primitiveBoundingBox(config.bbox.min.x(), config.bbox.min.y(), config.bbox.min.z(), config.bbox.min.w(),
834 								config.bbox.max.x(), config.bbox.max.y(), config.bbox.max.z(), config.bbox.max.w());
835 	else
836 		// state is overriden by the tessellation output, set bbox to invisible area to imitiate dirty state left by application
837 		gl.primitiveBoundingBox(-2.0f, -2.0f, 0.0f, 1.0f,
838 								-1.7f, -1.7f, 0.0f, 1.0f);
839 
840 	if (m_fbo)
841 		gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, **m_fbo);
842 
843 	gl.viewport(config.viewportPos.x(), config.viewportPos.y(), config.viewportSize.x(), config.viewportSize.y());
844 	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
845 	gl.clear(GL_COLOR_BUFFER_BIT);
846 
847 	gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
848 	gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, (int)(VA_NUM_ATTRIB_VECS * sizeof(float[4])), (const float*)DE_NULL + 4 * VA_POS_VEC_NDX);
849 	gl.vertexAttribPointer(colLocation, 4, GL_FLOAT, GL_FALSE, (int)(VA_NUM_ATTRIB_VECS * sizeof(float[4])), (const float*)DE_NULL + 4 * VA_COL_VEC_NDX);
850 	gl.enableVertexAttribArray(posLocation);
851 	gl.enableVertexAttribArray(colLocation);
852 	gl.useProgram(m_program->getProgram());
853 	gl.uniform4f(posScaleLocation, config.patternPos.x(), config.patternPos.y(), config.patternSize.x(), config.patternSize.y());
854 
855 	{
856 		const glw::GLint bboxMinPos = gl.getUniformLocation(m_program->getProgram(), "u_primitiveBBoxMin");
857 		const glw::GLint bboxMaxPos = gl.getUniformLocation(m_program->getProgram(), "u_primitiveBBoxMax");
858 
859 		gl.uniform4f(bboxMinPos, config.bbox.min.x(), config.bbox.min.y(), config.bbox.min.z(), config.bbox.min.w());
860 		gl.uniform4f(bboxMaxPos, config.bbox.max.x(), config.bbox.max.y(), config.bbox.max.z(), config.bbox.max.w());
861 	}
862 
863 	gl.uniform2i(gl.getUniformLocation(m_program->getProgram(), "u_viewportPos"), config.viewportPos.x(), config.viewportPos.y());
864 	gl.uniform2i(gl.getUniformLocation(m_program->getProgram(), "u_viewportSize"), config.viewportSize.x(), config.viewportSize.y());
865 
866 	GLU_EXPECT_NO_ERROR(gl.getError(), "setup");
867 }
868 
genShaderFunction(ShaderFunction func) const869 const char* BBoxRenderCase::genShaderFunction (ShaderFunction func) const
870 {
871 	switch (func)
872 	{
873 		case SHADER_FUNC_MIRROR_X:
874 			return	"vec4 mirrorX(in highp vec4 p)\n"
875 					"{\n"
876 					"	highp vec2 patternOffset = u_posScale.xy;\n"
877 					"	highp vec2 patternScale = u_posScale.zw;\n"
878 					"	highp vec2 patternCenter = patternOffset + patternScale * 0.5;\n"
879 					"	return vec4(2.0 * patternCenter.x - p.x, p.y, p.z, p.w);\n"
880 					"}\n";
881 
882 		case SHADER_FUNC_MIRROR_Y:
883 			return	"vec4 mirrorY(in highp vec4 p)\n"
884 					"{\n"
885 					"	highp vec2 patternOffset = u_posScale.xy;\n"
886 					"	highp vec2 patternScale = u_posScale.zw;\n"
887 					"	highp vec2 patternCenter = patternOffset + patternScale * 0.5;\n"
888 					"	return vec4(p.x, 2.0 * patternCenter.y - p.y, p.z, p.w);\n"
889 					"}\n";
890 
891 		case SHADER_FUNC_INSIDE_BBOX:
892 			return	"uniform highp ivec2 u_viewportPos;\n"
893 					"uniform highp ivec2 u_viewportSize;\n"
894 					"flat in highp float v_bbox_expansionSize;\n"
895 					"flat in highp vec3 v_bbox_clipMin;\n"
896 					"flat in highp vec3 v_bbox_clipMax;\n"
897 					"\n"
898 					"bool fragmentInsideTheBBox(in highp float depth)\n"
899 					"{\n"
900 					"	highp vec4 wc = vec4(floor((v_bbox_clipMin.x * 0.5 + 0.5) * float(u_viewportSize.x) - v_bbox_expansionSize/2.0),\n"
901 					"	                     floor((v_bbox_clipMin.y * 0.5 + 0.5) * float(u_viewportSize.y) - v_bbox_expansionSize/2.0),\n"
902 					"	                     ceil((v_bbox_clipMax.x * 0.5 + 0.5) * float(u_viewportSize.x) + v_bbox_expansionSize/2.0),\n"
903 					"	                     ceil((v_bbox_clipMax.y * 0.5 + 0.5) * float(u_viewportSize.y) + v_bbox_expansionSize/2.0));\n"
904 					"	if (gl_FragCoord.x < float(u_viewportPos.x) + wc.x || gl_FragCoord.x > float(u_viewportPos.x) + wc.z ||\n"
905 					"	    gl_FragCoord.y < float(u_viewportPos.y) + wc.y || gl_FragCoord.y > float(u_viewportPos.y) + wc.w)\n"
906 					"	    return false;\n"
907 					"	const highp float dEpsilon = 0.001;\n"
908 					"	if (depth*2.0-1.0 < v_bbox_clipMin.z - dEpsilon || depth*2.0-1.0 > v_bbox_clipMax.z + dEpsilon)\n"
909 					"	    return false;\n"
910 					"	return true;\n"
911 					"}\n";
912 		default:
913 			DE_ASSERT(false);
914 			return "";
915 	}
916 }
917 
918 class GridRenderCase : public BBoxRenderCase
919 {
920 public:
921 					GridRenderCase					(Context& context, const char* name, const char* description, deUint32 flags);
922 					~GridRenderCase					(void);
923 
924 private:
925 	void			init							(void);
926 
927 	std::string		genVertexSource					(void) const;
928 	std::string		genFragmentSource				(void) const;
929 	std::string		genTessellationControlSource	(void) const;
930 	std::string		genTessellationEvaluationSource	(void) const;
931 	std::string		genGeometrySource				(void) const;
932 
933 	IterationConfig	generateConfig					(int iteration, const tcu::IVec2& renderTargetSize) const;
934 	void			getAttributeData				(std::vector<tcu::Vec4>& data) const;
935 	void			renderTestPattern				(const IterationConfig& config);
936 	void			verifyRenderResult				(const IterationConfig& config);
937 
938 	const int		m_gridSize;
939 };
940 
GridRenderCase(Context & context,const char * name,const char * description,deUint32 flags)941 GridRenderCase::GridRenderCase (Context& context, const char* name, const char* description, deUint32 flags)
942 	: BBoxRenderCase	(context, name, description, 12, flags)
943 	, m_gridSize		(24)
944 {
945 }
946 
~GridRenderCase(void)947 GridRenderCase::~GridRenderCase (void)
948 {
949 }
950 
init(void)951 void GridRenderCase::init (void)
952 {
953 	m_testCtx.getLog()
954 		<< tcu::TestLog::Message
955 		<< "Rendering yellow-green grid to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n"
956 		<< "Grid cells are in random order, varying grid size and location for each iteration.\n"
957 		<< "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated blue channel."
958 		<< tcu::TestLog::EndMessage;
959 
960 	BBoxRenderCase::init();
961 }
962 
genVertexSource(void) const963 std::string GridRenderCase::genVertexSource (void) const
964 {
965 	std::ostringstream	buf;
966 
967 	buf <<	"#version 310 es\n"
968 			"in highp vec4 a_position;\n"
969 			"in highp vec4 a_color;\n"
970 			"out highp vec4 vtx_color;\n"
971 			"uniform highp vec4 u_posScale;\n"
972 			"\n";
973 	if (!m_hasTessellationStage)
974 	{
975 		DE_ASSERT(m_useGlobalState);
976 		buf <<	"uniform highp vec4 u_primitiveBBoxMin;\n"
977 				"uniform highp vec4 u_primitiveBBoxMax;\n"
978 				"\n"
979 				"flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
980 				"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
981 				"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
982 				"\n";
983 	}
984 
985 	buf <<	"void main()\n"
986 			"{\n"
987 			"	highp vec2 patternOffset = u_posScale.xy;\n"
988 			"	highp vec2 patternScale = u_posScale.zw;\n"
989 			"	gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n"
990 			"	vtx_color = a_color;\n";
991 
992 	if (!m_hasTessellationStage)
993 	{
994 		DE_ASSERT(m_useGlobalState);
995 		buf <<	"\n"
996 				"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = 0.0;\n"
997 				"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n"
998 				"	    min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n"
999 				"	        vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n"
1000 				"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n"
1001 				"	    min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n"
1002 				"	        vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n";
1003 	}
1004 
1005 	buf<<	"}\n";
1006 
1007 	return buf.str();
1008 }
1009 
genFragmentSource(void) const1010 std::string GridRenderCase::genFragmentSource (void) const
1011 {
1012 	const char* const	colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
1013 	std::ostringstream	buf;
1014 
1015 	buf <<	"#version 310 es\n"
1016 			"in mediump vec4 " << colorInputName << ";\n"
1017 			"layout(location = 0) out mediump vec4 o_color;\n"
1018 		<<	genShaderFunction(SHADER_FUNC_INSIDE_BBOX)
1019 		<<	"\n"
1020 			"void main()\n"
1021 			"{\n"
1022 			"	mediump vec4 baseColor = " << colorInputName << ";\n"
1023 			"	mediump float blueChannel;\n"
1024 			"	if (fragmentInsideTheBBox(gl_FragCoord.z))\n"
1025 			"		blueChannel = 0.0;\n"
1026 			"	else\n"
1027 			"		blueChannel = 1.0;\n"
1028 			"	o_color = vec4(baseColor.r, baseColor.g, blueChannel, baseColor.a);\n"
1029 			"}\n";
1030 
1031 	return buf.str();
1032 }
1033 
genTessellationControlSource(void) const1034 std::string GridRenderCase::genTessellationControlSource (void) const
1035 {
1036 	std::ostringstream	buf;
1037 
1038 	buf <<	"#version 310 es\n"
1039 			"#extension GL_EXT_tessellation_shader : require\n"
1040 			"#extension GL_EXT_primitive_bounding_box : require\n"
1041 			"layout(vertices=3) out;\n"
1042 			"\n"
1043 			"in highp vec4 vtx_color[];\n"
1044 			"out highp vec4 tess_ctrl_color[];\n"
1045 			"uniform highp float u_tessellationLevel;\n"
1046 			"uniform highp vec4 u_posScale;\n";
1047 
1048 	if (!m_calcPerPrimitiveBBox)
1049 	{
1050 		buf <<	"uniform highp vec4 u_primitiveBBoxMin;\n"
1051 				"uniform highp vec4 u_primitiveBBoxMax;\n";
1052 	}
1053 
1054 	buf <<	"patch out highp float vp_bbox_expansionSize;\n"
1055 			"patch out highp vec3 vp_bbox_clipMin;\n"
1056 			"patch out highp vec3 vp_bbox_clipMax;\n";
1057 
1058 	if (m_calcPerPrimitiveBBox)
1059 	{
1060 		buf <<	"\n";
1061 		if (m_hasGeometryStage)
1062 			buf << genShaderFunction(SHADER_FUNC_MIRROR_X);
1063 		buf << genShaderFunction(SHADER_FUNC_MIRROR_Y);
1064 
1065 		buf <<	"vec4 transformVec(in highp vec4 p)\n"
1066 				"{\n"
1067 				"	return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\n"
1068 				"}\n";
1069 	}
1070 
1071 	buf <<	"\n"
1072 			"void main()\n"
1073 			"{\n"
1074 			"	// convert to nonsensical coordinates, just in case\n"
1075 			"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n"
1076 			"	tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n"
1077 			"\n"
1078 			"	gl_TessLevelOuter[0] = u_tessellationLevel;\n"
1079 			"	gl_TessLevelOuter[1] = u_tessellationLevel;\n"
1080 			"	gl_TessLevelOuter[2] = u_tessellationLevel;\n"
1081 			"	gl_TessLevelInner[0] = u_tessellationLevel;\n";
1082 
1083 	if (m_calcPerPrimitiveBBox)
1084 	{
1085 		buf <<	"\n"
1086 				"	highp vec4 bboxMin = min(min(transformVec(gl_in[0].gl_Position),\n"
1087 				"	                             transformVec(gl_in[1].gl_Position)),\n"
1088 				"	                         transformVec(gl_in[2].gl_Position));\n"
1089 				"	highp vec4 bboxMax = max(max(transformVec(gl_in[0].gl_Position),\n"
1090 				"	                             transformVec(gl_in[1].gl_Position)),\n"
1091 				"	                         transformVec(gl_in[2].gl_Position));\n";
1092 	}
1093 	else
1094 	{
1095 		buf <<	"\n"
1096 				"	highp vec4 bboxMin = u_primitiveBBoxMin;\n"
1097 				"	highp vec4 bboxMax = u_primitiveBBoxMax;\n";
1098 	}
1099 
1100 	if (!m_useGlobalState)
1101 		buf <<	"\n"
1102 				"	gl_BoundingBoxEXT[0] = bboxMin;\n"
1103 				"	gl_BoundingBoxEXT[1] = bboxMax;\n";
1104 
1105 	buf <<	"	vp_bbox_expansionSize = 0.0;\n"
1106 			"	vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n"
1107 			"	                      vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n"
1108 			"	vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n"
1109 			"	                      vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n"
1110 			"}\n";
1111 
1112 	return buf.str();
1113 }
1114 
genTessellationEvaluationSource(void) const1115 std::string GridRenderCase::genTessellationEvaluationSource (void) const
1116 {
1117 	std::ostringstream	buf;
1118 
1119 	buf <<	"#version 310 es\n"
1120 			"#extension GL_EXT_tessellation_shader : require\n"
1121 			"#extension GL_EXT_gpu_shader5 : require\n"
1122 			"layout(triangles) in;\n"
1123 			"\n"
1124 			"in highp vec4 tess_ctrl_color[];\n"
1125 			"out highp vec4 tess_color;\n"
1126 			"uniform highp vec4 u_posScale;\n"
1127 			"patch in highp float vp_bbox_expansionSize;\n"
1128 			"patch in highp vec3 vp_bbox_clipMin;\n"
1129 			"patch in highp vec3 vp_bbox_clipMax;\n"
1130 			"flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
1131 			"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
1132 			"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
1133 			"\n"
1134 			"precise gl_Position;\n"
1135 			"\n"
1136 		<<	genShaderFunction(SHADER_FUNC_MIRROR_Y)
1137 		<<	"void main()\n"
1138 			"{\n"
1139 			"	// non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n"
1140 			"	gl_Position = mirrorY(gl_TessCoord.x * gl_in[0].gl_Position.zwyx +\n"
1141 			"	                      gl_TessCoord.y * gl_in[1].gl_Position.zwyx +\n"
1142 			"	                      gl_TessCoord.z * gl_in[2].gl_Position.zwyx);\n"
1143 			"	tess_color = tess_ctrl_color[0];\n"
1144 			"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = vp_bbox_expansionSize;\n"
1145 			"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin = vp_bbox_clipMin;\n"
1146 			"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax = vp_bbox_clipMax;\n"
1147 			"}\n";
1148 
1149 	return buf.str();
1150 }
1151 
genGeometrySource(void) const1152 std::string GridRenderCase::genGeometrySource (void) const
1153 {
1154 	const char* const	colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
1155 	std::ostringstream	buf;
1156 
1157 	buf <<	"#version 310 es\n"
1158 			"#extension GL_EXT_geometry_shader : require\n"
1159 			"layout(triangles) in;\n"
1160 			"layout(max_vertices=9, triangle_strip) out;\n"
1161 			"\n"
1162 			"in highp vec4 " << colorInputName << "[3];\n"
1163 			"out highp vec4 geo_color;\n"
1164 			"uniform highp vec4 u_posScale;\n"
1165 			"\n"
1166 			"flat in highp float v_geo_bbox_expansionSize[3];\n"
1167 			"flat in highp vec3 v_geo_bbox_clipMin[3];\n"
1168 			"flat in highp vec3 v_geo_bbox_clipMax[3];\n"
1169 			"flat out highp vec3 v_bbox_clipMin;\n"
1170 			"flat out highp vec3 v_bbox_clipMax;\n"
1171 			"flat out highp float v_bbox_expansionSize;\n"
1172 		<<	genShaderFunction(SHADER_FUNC_MIRROR_X)
1173 		<<	"\n"
1174 			"void setVisualizationVaryings()\n"
1175 			"{\n"
1176 			"	v_bbox_expansionSize = v_geo_bbox_expansionSize[0];\n"
1177 			"	v_bbox_clipMin = v_geo_bbox_clipMin[0];\n"
1178 			"	v_bbox_clipMax = v_geo_bbox_clipMax[0];\n"
1179 			"}\n"
1180 			"void main()\n"
1181 			"{\n"
1182 			"	// Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n"
1183 			"	highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n"
1184 			"	highp vec4 p1 = mirrorX(gl_in[1].gl_Position);\n"
1185 			"	highp vec4 p2 = mirrorX(gl_in[2].gl_Position);\n"
1186 			"	highp vec4 pCentroid = vec4((p0.xyz + p1.xyz + p2.xyz) / 3.0, 1.0);\n"
1187 			"	highp vec4 triangleColor = " << colorInputName << "[0];\n"
1188 			"\n"
1189 			"	gl_Position = p0; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1190 			"	gl_Position = p1; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1191 			"	gl_Position = pCentroid; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1192 			"	EndPrimitive();\n"
1193 			"\n"
1194 			"	gl_Position = p1; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1195 			"	gl_Position = p2; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1196 			"	gl_Position = pCentroid; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1197 			"	EndPrimitive();\n"
1198 			"\n"
1199 			"	gl_Position = p2; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1200 			"	gl_Position = p0; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1201 			"	gl_Position = pCentroid; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1202 			"	EndPrimitive();\n"
1203 			"}\n";
1204 
1205 	return buf.str();
1206 }
1207 
generateConfig(int iteration,const tcu::IVec2 & renderTargetSize) const1208 GridRenderCase::IterationConfig GridRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const
1209 {
1210 	return generateRandomConfig(0xDEDEDEu * (deUint32)iteration, renderTargetSize);
1211 }
1212 
getAttributeData(std::vector<tcu::Vec4> & data) const1213 void GridRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const
1214 {
1215 	const tcu::Vec4		green				(0.0f, 1.0f, 0.0f, 1.0f);
1216 	const tcu::Vec4		yellow				(1.0f, 1.0f, 0.0f, 1.0f);
1217 	std::vector<int>	cellOrder			(m_gridSize * m_gridSize);
1218 	de::Random			rnd					(0xDE56789);
1219 
1220 	// generate grid with cells in random order
1221 	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
1222 		cellOrder[ndx] = ndx;
1223 	rnd.shuffle(cellOrder.begin(), cellOrder.end());
1224 
1225 	data.resize(m_gridSize * m_gridSize * 6 * 2);
1226 	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
1227 	{
1228 		const int			cellNdx		= cellOrder[ndx];
1229 		const int			cellX		= cellNdx % m_gridSize;
1230 		const int			cellY		= cellNdx / m_gridSize;
1231 		const tcu::Vec4&	cellColor	= ((cellX+cellY)%2 == 0) ? (green) : (yellow);
1232 
1233 		data[(ndx * 6 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+0) / float(m_gridSize), float(cellY+0) / float(m_gridSize), 0.0f, 1.0f);
1234 		data[(ndx * 6 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1235 		data[(ndx * 6 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+1) / float(m_gridSize), float(cellY+1) / float(m_gridSize), 0.0f, 1.0f);
1236 		data[(ndx * 6 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1237 		data[(ndx * 6 + 2) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+0) / float(m_gridSize), float(cellY+1) / float(m_gridSize), 0.0f, 1.0f);
1238 		data[(ndx * 6 + 2) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1239 		data[(ndx * 6 + 3) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+0) / float(m_gridSize), float(cellY+0) / float(m_gridSize), 0.0f, 1.0f);
1240 		data[(ndx * 6 + 3) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1241 		data[(ndx * 6 + 4) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+1) / float(m_gridSize), float(cellY+0) / float(m_gridSize), 0.0f, 1.0f);
1242 		data[(ndx * 6 + 4) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1243 		data[(ndx * 6 + 5) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+1) / float(m_gridSize), float(cellY+1) / float(m_gridSize), 0.0f, 1.0f);
1244 		data[(ndx * 6 + 5) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1245 	}
1246 }
1247 
renderTestPattern(const IterationConfig & config)1248 void GridRenderCase::renderTestPattern (const IterationConfig& config)
1249 {
1250 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
1251 
1252 	setupRender(config);
1253 
1254 	if (m_hasTessellationStage)
1255 	{
1256 		const glw::GLint	tessLevelPos	= gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel");
1257 		const glw::GLfloat	tessLevel		= 2.8f; // will be rounded up
1258 
1259 		TCU_CHECK(tessLevelPos != -1);
1260 
1261 		m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage;
1262 
1263 		gl.uniform1f(tessLevelPos, tessLevel);
1264 		gl.patchParameteri(GL_PATCH_VERTICES, 3);
1265 		GLU_EXPECT_NO_ERROR(gl.getError(), "patch param");
1266 	}
1267 
1268 	m_testCtx.getLog() << tcu::TestLog::Message << "Rendering grid." << tcu::TestLog::EndMessage;
1269 
1270 	gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_TRIANGLES), 0, m_gridSize * m_gridSize * 6);
1271 	GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
1272 }
1273 
verifyRenderResult(const IterationConfig & config)1274 void GridRenderCase::verifyRenderResult (const IterationConfig& config)
1275 {
1276 	const glw::Functions&	gl						= m_context.getRenderContext().getFunctions();
1277 	const ProjectedBBox		projectedBBox			= projectBoundingBox(config.bbox);
1278 	const tcu::IVec4		viewportBBoxArea		= getViewportBoundingBoxArea(projectedBBox, config.viewportSize);
1279 	const tcu::IVec4		viewportGridOuterArea	= getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_OUTWARDS);
1280 	const tcu::IVec4		viewportGridInnerArea	= getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_INWARDS);
1281 	tcu::Surface			viewportSurface			(config.viewportSize.x(), config.viewportSize.y());
1282 	tcu::Surface			errorMask				(config.viewportSize.x(), config.viewportSize.y());
1283 	bool					anyError				= false;
1284 
1285 	if (!m_calcPerPrimitiveBBox)
1286 		m_testCtx.getLog()
1287 			<< tcu::TestLog::Message
1288 			<< "Projected bounding box: (clip space)\n"
1289 				<< "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n"
1290 				<< "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n"
1291 				<< "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n"
1292 			<< "In viewport coordinates:\n"
1293 				<< "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n"
1294 				<< "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n"
1295 			<< "Verifying render results within the bounding box.\n"
1296 			<< tcu::TestLog::EndMessage;
1297 	else
1298 		m_testCtx.getLog()
1299 			<< tcu::TestLog::Message
1300 			<< "Verifying render result."
1301 			<< tcu::TestLog::EndMessage;
1302 
1303 	if (m_fbo)
1304 		gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo);
1305 	glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess());
1306 
1307 	tcu::clear(errorMask.getAccess(), tcu::IVec4(0,0,0,255));
1308 
1309 	for (int y = de::max(viewportBBoxArea.y(), 0); y < de::min(viewportBBoxArea.w(), config.viewportSize.y()); ++y)
1310 	for (int x = de::max(viewportBBoxArea.x(), 0); x < de::min(viewportBBoxArea.z(), config.viewportSize.x()); ++x)
1311 	{
1312 		const tcu::RGBA	pixel		= viewportSurface.getPixel(x, y);
1313 		const bool		outsideGrid	= x < viewportGridOuterArea.x() ||
1314 									  y < viewportGridOuterArea.y() ||
1315 									  x > viewportGridOuterArea.z() ||
1316 									  y > viewportGridOuterArea.w();
1317 		const bool		insideGrid	= x > viewportGridInnerArea.x() &&
1318 									  y > viewportGridInnerArea.y() &&
1319 									  x < viewportGridInnerArea.z() &&
1320 									  y < viewportGridInnerArea.w();
1321 
1322 		bool			error		= false;
1323 
1324 		if (outsideGrid)
1325 		{
1326 			// expect black
1327 			if (pixel.getRed() != 0 || pixel.getGreen() != 0 || pixel.getBlue() != 0)
1328 				error = true;
1329 		}
1330 
1331 		else if (insideGrid)
1332 		{
1333 			// expect green, yellow or a combination of these
1334 			if (pixel.getGreen() != 255 || pixel.getBlue() != 0)
1335 				error = true;
1336 		}
1337 		else
1338 		{
1339 			// boundary, allow anything
1340 		}
1341 
1342 		if (error)
1343 		{
1344 			errorMask.setPixel(x, y, tcu::RGBA::red());
1345 			anyError = true;
1346 		}
1347 	}
1348 
1349 	if (anyError)
1350 	{
1351 		m_testCtx.getLog()
1352 			<< tcu::TestLog::Message
1353 			<< "Image verification failed."
1354 			<< tcu::TestLog::EndMessage
1355 			<< tcu::TestLog::ImageSet("Images", "Image verification")
1356 			<< tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
1357 			<< tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
1358 			<< tcu::TestLog::EndImageSet;
1359 
1360 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
1361 	}
1362 	else
1363 	{
1364 		m_testCtx.getLog()
1365 			<< tcu::TestLog::Message
1366 			<< "Result image ok."
1367 			<< tcu::TestLog::EndMessage
1368 			<< tcu::TestLog::ImageSet("Images", "Image verification")
1369 			<< tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
1370 			<< tcu::TestLog::EndImageSet;
1371 	}
1372 }
1373 
1374 class LineRenderCase : public BBoxRenderCase
1375 {
1376 public:
1377 	enum
1378 	{
1379 		LINEFLAG_WIDE = 1u << FLAGBIT_USER_BIT,	//!< use wide lines
1380 	};
1381 
1382 					LineRenderCase					(Context& context, const char* name, const char* description, deUint32 flags);
1383 					~LineRenderCase					(void);
1384 
1385 private:
1386 	enum
1387 	{
1388 		GREEN_COMPONENT_NDX = 1,
1389 		BLUE_COMPONENT_NDX = 2,
1390 
1391 		SCAN_ROW_COMPONENT_NDX = GREEN_COMPONENT_NDX, // \note: scans are orthogonal to the line
1392 		SCAN_COL_COMPONENT_NDX = BLUE_COMPONENT_NDX,
1393 	};
1394 
1395 	enum QueryDirection
1396 	{
1397 		DIRECTION_HORIZONTAL = 0,
1398 		DIRECTION_VERTICAL,
1399 	};
1400 
1401 	enum ScanResult
1402 	{
1403 		SCANRESULT_NUM_LINES_OK_BIT		= (1 << 0),
1404 		SCANRESULT_LINE_WIDTH_OK_BIT	= (1 << 1),
1405 		SCANRESULT_LINE_WIDTH_WARN_BIT	= (1 << 2),
1406 		SCANRESULT_LINE_WIDTH_ERR_BIT	= (1 << 3),
1407 	};
1408 
1409 	void				init							(void);
1410 
1411 	std::string			genVertexSource					(void) const;
1412 	std::string			genFragmentSource				(void) const;
1413 	std::string			genTessellationControlSource	(void) const;
1414 	std::string			genTessellationEvaluationSource	(void) const;
1415 	std::string			genGeometrySource				(void) const;
1416 
1417 	IterationConfig		generateConfig					(int iteration, const tcu::IVec2& renderTargetSize) const;
1418 	void				getAttributeData				(std::vector<tcu::Vec4>& data) const;
1419 	void				renderTestPattern				(const IterationConfig& config);
1420 	void				verifyRenderResult				(const IterationConfig& config);
1421 
1422 	tcu::IVec2			getNumberOfLinesRange			(int queryAreaBegin, int queryAreaEnd, float patternStart, float patternSize, int viewportArea, QueryDirection queryDir) const;
1423 	deUint8				scanRow							(const tcu::ConstPixelBufferAccess& access, int row, int rowBegin, int rowEnd, const tcu::IVec2& numLines, int& floodCounter) const;
1424 	deUint8				scanColumn						(const tcu::ConstPixelBufferAccess& access, int column, int columnBegin, int columnEnd, const tcu::IVec2& numLines, int& floodCounter) const;
1425 	bool				checkAreaNumLines				(const tcu::ConstPixelBufferAccess& access, const tcu::IVec4& area, int& floodCounter, int componentNdx, const tcu::IVec2& numLines) const;
1426 	tcu::IVec2			getNumMinimaMaxima				(const tcu::ConstPixelBufferAccess& access, int componentNdx) const;
1427 	deUint8				checkLineWidths					(const tcu::ConstPixelBufferAccess& access, const tcu::IVec2& begin, const tcu::IVec2& end, int componentNdx, int& floodCounter) const;
1428 	void				printLineWidthError				(const tcu::IVec2& pos, int detectedLineWidth, const tcu::IVec2& lineWidthRange, bool isHorizontal, int& floodCounter) const;
1429 
1430 	const int			m_patternSide;
1431 	const bool			m_isWideLineCase;
1432 	const int			m_wideLineLineWidth;
1433 };
1434 
LineRenderCase(Context & context,const char * name,const char * description,deUint32 flags)1435 LineRenderCase::LineRenderCase (Context& context, const char* name, const char* description, deUint32 flags)
1436 	: BBoxRenderCase		(context, name, description, 12, flags)
1437 	, m_patternSide			(12)
1438 	, m_isWideLineCase		((flags & LINEFLAG_WIDE) != 0)
1439 	, m_wideLineLineWidth	(5)
1440 {
1441 }
1442 
~LineRenderCase(void)1443 LineRenderCase::~LineRenderCase (void)
1444 {
1445 }
1446 
init(void)1447 void LineRenderCase::init (void)
1448 {
1449 	m_testCtx.getLog()
1450 		<< tcu::TestLog::Message
1451 		<< "Rendering line pattern to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n"
1452 		<< "Vertical lines are green, horizontal lines blue. Using additive blending.\n"
1453 		<< "Line segments are in random order, varying pattern size and location for each iteration.\n"
1454 		<< "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated red channel."
1455 		<< tcu::TestLog::EndMessage;
1456 
1457 	if (m_isWideLineCase)
1458 	{
1459 		glw::GLfloat lineWidthRange[2] = {0.0f, 0.0f};
1460 		m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_LINE_WIDTH_RANGE, lineWidthRange);
1461 
1462 		if (lineWidthRange[1] < (float)m_wideLineLineWidth)
1463 			throw tcu::NotSupportedError("Test requires line width " + de::toString(m_wideLineLineWidth));
1464 	}
1465 
1466 	BBoxRenderCase::init();
1467 }
1468 
genVertexSource(void) const1469 std::string LineRenderCase::genVertexSource (void) const
1470 {
1471 	std::ostringstream	buf;
1472 
1473 	buf <<	"#version 310 es\n"
1474 			"in highp vec4 a_position;\n"
1475 			"in highp vec4 a_color;\n"
1476 			"out highp vec4 vtx_color;\n"
1477 			"uniform highp vec4 u_posScale;\n"
1478 			"uniform highp float u_lineWidth;\n"
1479 			"\n";
1480 	if (!m_hasTessellationStage)
1481 	{
1482 		DE_ASSERT(m_useGlobalState);
1483 		buf <<	"uniform highp vec4 u_primitiveBBoxMin;\n"
1484 				"uniform highp vec4 u_primitiveBBoxMax;\n"
1485 				"\n"
1486 				"flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
1487 				"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
1488 				"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
1489 				"\n";
1490 	}
1491 	buf <<	"void main()\n"
1492 			"{\n"
1493 			"	highp vec2 patternOffset = u_posScale.xy;\n"
1494 			"	highp vec2 patternScale = u_posScale.zw;\n"
1495 			"	gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n"
1496 			"	vtx_color = a_color;\n";
1497 	if (!m_hasTessellationStage)
1498 	{
1499 		DE_ASSERT(m_useGlobalState);
1500 		buf <<	"\n"
1501 				"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = u_lineWidth;\n"
1502 				"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n"
1503 				"	    min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n"
1504 				"	        vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n"
1505 				"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n"
1506 				"	    min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n"
1507 				"	        vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n";
1508 	}
1509 	buf <<	"}\n";
1510 
1511 	return buf.str();
1512 }
1513 
genFragmentSource(void) const1514 std::string LineRenderCase::genFragmentSource (void) const
1515 {
1516 	const char* const	colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
1517 	std::ostringstream	buf;
1518 
1519 	buf <<	"#version 310 es\n"
1520 			"in mediump vec4 " << colorInputName << ";\n"
1521 			"layout(location = 0) out mediump vec4 o_color;\n"
1522 		<<	genShaderFunction(SHADER_FUNC_INSIDE_BBOX)
1523 		<<	"\n"
1524 			"void main()\n"
1525 			"{\n"
1526 			"	mediump vec4 baseColor = " << colorInputName << ";\n"
1527 			"	mediump float redChannel;\n"
1528 			"	if (fragmentInsideTheBBox(gl_FragCoord.z))\n"
1529 			"		redChannel = 0.0;\n"
1530 			"	else\n"
1531 			"		redChannel = 1.0;\n"
1532 			"	o_color = vec4(redChannel, baseColor.g, baseColor.b, baseColor.a);\n"
1533 			"}\n";
1534 
1535 	return buf.str();
1536 }
1537 
genTessellationControlSource(void) const1538 std::string LineRenderCase::genTessellationControlSource (void) const
1539 {
1540 	std::ostringstream	buf;
1541 
1542 	buf <<	"#version 310 es\n"
1543 			"#extension GL_EXT_tessellation_shader : require\n"
1544 			"#extension GL_EXT_primitive_bounding_box : require\n"
1545 			"layout(vertices=2) out;"
1546 			"\n"
1547 			"in highp vec4 vtx_color[];\n"
1548 			"out highp vec4 tess_ctrl_color[];\n"
1549 			"uniform highp float u_tessellationLevel;\n"
1550 			"uniform highp vec4 u_posScale;\n"
1551 			"uniform highp float u_lineWidth;\n";
1552 
1553 	if (!m_calcPerPrimitiveBBox)
1554 	{
1555 		buf <<	"uniform highp vec4 u_primitiveBBoxMin;\n"
1556 				"uniform highp vec4 u_primitiveBBoxMax;\n";
1557 	}
1558 
1559 	buf <<	"patch out highp float vp_bbox_expansionSize;\n"
1560 			"patch out highp vec3 vp_bbox_clipMin;\n"
1561 			"patch out highp vec3 vp_bbox_clipMax;\n";
1562 
1563 	if (m_calcPerPrimitiveBBox)
1564 	{
1565 		buf <<	"\n";
1566 		if (m_hasGeometryStage)
1567 			buf << genShaderFunction(SHADER_FUNC_MIRROR_X);
1568 		buf << genShaderFunction(SHADER_FUNC_MIRROR_Y);
1569 
1570 		buf <<	"vec4 transformVec(in highp vec4 p)\n"
1571 				"{\n"
1572 				"	return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\n"
1573 				"}\n";
1574 	}
1575 
1576 	buf <<	"\n"
1577 			"void main()\n"
1578 			"{\n"
1579 			"	// convert to nonsensical coordinates, just in case\n"
1580 			"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n"
1581 			"	tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n"
1582 			"\n"
1583 			"	gl_TessLevelOuter[0] = 0.8; // will be rounded up to 1\n"
1584 			"	gl_TessLevelOuter[1] = u_tessellationLevel;\n";
1585 
1586 	if (m_calcPerPrimitiveBBox)
1587 	{
1588 		buf <<	"\n"
1589 				"	highp vec4 bboxMin = min(transformVec(gl_in[0].gl_Position),\n"
1590 				"	                         transformVec(gl_in[1].gl_Position));\n"
1591 				"	highp vec4 bboxMax = max(transformVec(gl_in[0].gl_Position),\n"
1592 				"	                         transformVec(gl_in[1].gl_Position));\n";
1593 	}
1594 	else
1595 	{
1596 		buf <<	"\n"
1597 				"	highp vec4 bboxMin = u_primitiveBBoxMin;\n"
1598 				"	highp vec4 bboxMax = u_primitiveBBoxMax;\n";
1599 	}
1600 
1601 	if (!m_useGlobalState)
1602 		buf <<	"\n"
1603 				"	gl_BoundingBoxEXT[0] = bboxMin;\n"
1604 				"	gl_BoundingBoxEXT[1] = bboxMax;\n";
1605 
1606 	buf <<	"	vp_bbox_expansionSize = u_lineWidth;\n"
1607 			"	vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n"
1608 			"	                      vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n"
1609 			"	vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n"
1610 			"	                      vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n"
1611 			"}\n";
1612 
1613 	return buf.str();
1614 }
1615 
genTessellationEvaluationSource(void) const1616 std::string LineRenderCase::genTessellationEvaluationSource (void) const
1617 {
1618 	std::ostringstream	buf;
1619 
1620 	buf <<	"#version 310 es\n"
1621 			"#extension GL_EXT_tessellation_shader : require\n"
1622 			"layout(isolines) in;"
1623 			"\n"
1624 			"in highp vec4 tess_ctrl_color[];\n"
1625 			"out highp vec4 tess_color;\n"
1626 			"uniform highp vec4 u_posScale;\n"
1627 			"\n"
1628 			"patch in highp float vp_bbox_expansionSize;\n"
1629 			"patch in highp vec3 vp_bbox_clipMin;\n"
1630 			"patch in highp vec3 vp_bbox_clipMax;\n"
1631 			"flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
1632 			"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
1633 			"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
1634 		<<	genShaderFunction(SHADER_FUNC_MIRROR_Y)
1635 		<<	"void main()\n"
1636 			"{\n"
1637 			"	// non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n"
1638 			"	gl_Position = mirrorY(mix(gl_in[0].gl_Position.zwyx, gl_in[1].gl_Position.zwyx, gl_TessCoord.x));\n"
1639 			"	tess_color = tess_ctrl_color[0];\n"
1640 			"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = vp_bbox_expansionSize;\n"
1641 			"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin = vp_bbox_clipMin;\n"
1642 			"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax = vp_bbox_clipMax;\n"
1643 			"}\n";
1644 
1645 	return buf.str();
1646 }
1647 
genGeometrySource(void) const1648 std::string LineRenderCase::genGeometrySource (void) const
1649 {
1650 	const char* const	colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
1651 	std::ostringstream	buf;
1652 
1653 	buf <<	"#version 310 es\n"
1654 			"#extension GL_EXT_geometry_shader : require\n"
1655 			"layout(lines) in;\n"
1656 			"layout(max_vertices=5, line_strip) out;\n"
1657 			"\n"
1658 			"in highp vec4 " << colorInputName << "[2];\n"
1659 			"out highp vec4 geo_color;\n"
1660 			"uniform highp vec4 u_posScale;\n"
1661 			"\n"
1662 			"\n"
1663 			"flat in highp float v_geo_bbox_expansionSize[2];\n"
1664 			"flat in highp vec3 v_geo_bbox_clipMin[2];\n"
1665 			"flat in highp vec3 v_geo_bbox_clipMax[2];\n"
1666 			"flat out highp vec3 v_bbox_clipMin;\n"
1667 			"flat out highp vec3 v_bbox_clipMax;\n"
1668 			"flat out highp float v_bbox_expansionSize;\n"
1669 		<<	genShaderFunction(SHADER_FUNC_MIRROR_X)
1670 		<<	"\n"
1671 			"void setVisualizationVaryings()\n"
1672 			"{\n"
1673 			"	v_bbox_expansionSize = v_geo_bbox_expansionSize[0];\n"
1674 			"	v_bbox_clipMin = v_geo_bbox_clipMin[0];\n"
1675 			"	v_bbox_clipMax = v_geo_bbox_clipMax[0];\n"
1676 			"}\n"
1677 			"void main()\n"
1678 			"{\n"
1679 			"	// Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n"
1680 			"	highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n"
1681 			"	highp vec4 p1 = mirrorX(gl_in[1].gl_Position);\n"
1682 			"	highp vec4 lineColor = " << colorInputName << "[0];\n"
1683 			"\n"
1684 			"	// output two separate primitives, just in case\n"
1685 			"	gl_Position = mix(p0, p1, 0.00); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1686 			"	gl_Position = mix(p0, p1, 0.33); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1687 			"	EndPrimitive();\n"
1688 			"\n"
1689 			"	gl_Position = mix(p0, p1, 0.33); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1690 			"	gl_Position = mix(p0, p1, 0.67); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1691 			"	gl_Position = mix(p0, p1, 1.00); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1692 			"	EndPrimitive();\n"
1693 			"}\n";
1694 
1695 	return buf.str();
1696 }
1697 
generateConfig(int iteration,const tcu::IVec2 & renderTargetSize) const1698 LineRenderCase::IterationConfig LineRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const
1699 {
1700 	const int numMaxAttempts = 128;
1701 
1702 	// Avoid too narrow viewports, lines could merge together. Require viewport is at least 2.5x the size of the line bodies.
1703 	for (int attemptNdx = 0; attemptNdx < numMaxAttempts; ++attemptNdx)
1704 	{
1705 		const IterationConfig& config = generateRandomConfig((0xDEDEDEu * (deUint32)iteration) ^ (0xABAB13 * attemptNdx), renderTargetSize);
1706 
1707 		if ((float)config.viewportSize.x() * (config.patternSize.x() * 0.5f) > 2.5f * (float)m_patternSide * (float)m_wideLineLineWidth &&
1708 			(float)config.viewportSize.y() * (config.patternSize.y() * 0.5f) > 2.5f * (float)m_patternSide * (float)m_wideLineLineWidth)
1709 		{
1710 			return config;
1711 		}
1712 	}
1713 
1714 	DE_ASSERT(false);
1715 	return IterationConfig();
1716 }
1717 
getAttributeData(std::vector<tcu::Vec4> & data) const1718 void LineRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const
1719 {
1720 	const tcu::Vec4		green		(0.0f, 1.0f, 0.0f, 1.0f);
1721 	const tcu::Vec4		blue		(0.0f, 0.0f, 1.0f, 1.0f);
1722 	std::vector<int>	cellOrder	(m_patternSide * m_patternSide * 2);
1723 	de::Random			rnd			(0xDE12345);
1724 
1725 	// generate crosshatch pattern with segments in random order
1726 	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
1727 		cellOrder[ndx] = ndx;
1728 	rnd.shuffle(cellOrder.begin(), cellOrder.end());
1729 
1730 	data.resize(cellOrder.size() * 4);
1731 	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
1732 	{
1733 		const int segmentID		= cellOrder[ndx];
1734 		const int direction		= segmentID & 0x01;
1735 		const int majorCoord	= (segmentID >> 1) / m_patternSide;
1736 		const int minorCoord	= (segmentID >> 1) % m_patternSide;
1737 
1738 		if (direction)
1739 		{
1740 			data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(minorCoord) / float(m_patternSide), float(majorCoord) / float(m_patternSide), 0.0f, 1.0f);
1741 			data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green;
1742 			data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(minorCoord) / float(m_patternSide), float(majorCoord + 1) / float(m_patternSide), 0.0f, 1.0f);
1743 			data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green;
1744 		}
1745 		else
1746 		{
1747 			data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(majorCoord) / float(m_patternSide), float(minorCoord) / float(m_patternSide), 0.0f, 1.0f);
1748 			data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue;
1749 			data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(majorCoord + 1) / float(m_patternSide), float(minorCoord) / float(m_patternSide), 0.0f, 1.0f);
1750 			data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue;
1751 		}
1752 	}
1753 }
1754 
renderTestPattern(const IterationConfig & config)1755 void LineRenderCase::renderTestPattern (const IterationConfig& config)
1756 {
1757 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
1758 
1759 	setupRender(config);
1760 
1761 	if (m_hasTessellationStage)
1762 	{
1763 		const glw::GLint	tessLevelPos	= gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel");
1764 		const glw::GLfloat	tessLevel		= 2.8f; // will be rounded up
1765 
1766 		TCU_CHECK(tessLevelPos != -1);
1767 
1768 		m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage;
1769 
1770 		gl.uniform1f(tessLevelPos, tessLevel);
1771 		gl.patchParameteri(GL_PATCH_VERTICES, 2);
1772 		GLU_EXPECT_NO_ERROR(gl.getError(), "patch param");
1773 	}
1774 
1775 	if (m_isWideLineCase)
1776 		gl.lineWidth((float)m_wideLineLineWidth);
1777 
1778 	gl.uniform1f(gl.getUniformLocation(m_program->getProgram(), "u_lineWidth"), (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f));
1779 
1780 	m_testCtx.getLog() << tcu::TestLog::Message << "Rendering pattern." << tcu::TestLog::EndMessage;
1781 
1782 	gl.enable(GL_BLEND);
1783 	gl.blendFunc(GL_ONE, GL_ONE);
1784 	gl.blendEquation(GL_FUNC_ADD);
1785 
1786 	gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_LINES), 0, m_patternSide * m_patternSide * 2 * 2);
1787 	GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
1788 }
1789 
verifyRenderResult(const IterationConfig & config)1790 void LineRenderCase::verifyRenderResult (const IterationConfig& config)
1791 {
1792 	const glw::Functions&	gl							= m_context.getRenderContext().getFunctions();
1793 	const bool				isMsaa						= m_context.getRenderTarget().getNumSamples() > 1;
1794 	const ProjectedBBox		projectedBBox				= projectBoundingBox(config.bbox);
1795 	const float				lineWidth					= (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f);
1796 	const tcu::IVec4		viewportBBoxArea			= getViewportBoundingBoxArea(projectedBBox, config.viewportSize, lineWidth);
1797 	const tcu::IVec4		viewportPatternArea			= getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_INWARDS);
1798 	const tcu::IVec2		expectedHorizontalLines		= getNumberOfLinesRange(viewportBBoxArea.y(), viewportBBoxArea.w(), config.patternPos.y(), config.patternSize.y(), config.viewportSize.y(), DIRECTION_VERTICAL);
1799 	const tcu::IVec2		expectedVerticalLines		= getNumberOfLinesRange(viewportBBoxArea.x(), viewportBBoxArea.z(), config.patternPos.x(), config.patternSize.x(), config.viewportSize.x(), DIRECTION_HORIZONTAL);
1800 	const tcu::IVec4		verificationArea			= tcu::IVec4(de::max(viewportBBoxArea.x(), 0),
1801 																	 de::max(viewportBBoxArea.y(), 0),
1802 																	 de::min(viewportBBoxArea.z(), config.viewportSize.x()),
1803 																	 de::min(viewportBBoxArea.w(), config.viewportSize.y()));
1804 
1805 	tcu::Surface			viewportSurface				(config.viewportSize.x(), config.viewportSize.y());
1806 	bool					anyError					= false;
1807 	bool					msaaRelaxationRequired		= false;
1808 	bool					hwIssueRelaxationRequired	= false;
1809 	int						messageLimitCounter			= 8;
1810 
1811 	if (!m_calcPerPrimitiveBBox)
1812 		m_testCtx.getLog()
1813 			<< tcu::TestLog::Message
1814 			<< "Projected bounding box: (clip space)\n"
1815 				<< "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n"
1816 				<< "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n"
1817 				<< "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n"
1818 			<< "In viewport coordinates:\n"
1819 				<< "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n"
1820 				<< "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n"
1821 			<< "Verifying render results within the bounding box:\n"
1822 			<< tcu::TestLog::EndMessage;
1823 	else
1824 		m_testCtx.getLog()
1825 			<< tcu::TestLog::Message
1826 			<< "Verifying render result:"
1827 			<< tcu::TestLog::EndMessage;
1828 
1829 	m_testCtx.getLog()
1830 		<< tcu::TestLog::Message
1831 			<< "\tCalculating number of horizontal and vertical lines within the bounding box, expecting:\n"
1832 			<< "\t[" << expectedHorizontalLines.x() << ", " << expectedHorizontalLines.y() << "] horizontal lines.\n"
1833 			<< "\t[" << expectedVerticalLines.x() << ", " << expectedVerticalLines.y() << "] vertical lines.\n"
1834 		<< tcu::TestLog::EndMessage;
1835 
1836 	if (m_fbo)
1837 		gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo);
1838 	glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess());
1839 
1840 	// scan rows
1841 	for (int y = de::max(verificationArea.y(), viewportPatternArea.y()); y < de::min(verificationArea.w(), viewportPatternArea.w()); ++y)
1842 	{
1843 		const deUint8 result = scanRow(viewportSurface.getAccess(),
1844 									   y,
1845 									   verificationArea.x(),
1846 									   verificationArea.z(),
1847 									   expectedVerticalLines,
1848 									   messageLimitCounter);
1849 
1850 		if ((result & SCANRESULT_NUM_LINES_OK_BIT) == 0)
1851 			anyError = true;
1852 		else if ((result & SCANRESULT_LINE_WIDTH_OK_BIT) == 0)
1853 		{
1854 			if (m_isWideLineCase && isMsaa)
1855 			{
1856 				// multisampled wide lines might not be supported
1857 				msaaRelaxationRequired = true;
1858 			}
1859 			else if ((result & SCANRESULT_LINE_WIDTH_ERR_BIT) == 0 &&
1860 					 (result & SCANRESULT_LINE_WIDTH_WARN_BIT) != 0)
1861 			{
1862 				hwIssueRelaxationRequired = true;
1863 			}
1864 			else
1865 				anyError = true;
1866 		}
1867 	}
1868 
1869 	// scan columns
1870 	for (int x = de::max(verificationArea.x(), viewportPatternArea.x()); x < de::min(verificationArea.z(), viewportPatternArea.z()); ++x)
1871 	{
1872 		const deUint8 result = scanColumn(viewportSurface.getAccess(),
1873 										  x,
1874 										  verificationArea.y(),
1875 										  verificationArea.w(),
1876 										  expectedHorizontalLines,
1877 										  messageLimitCounter);
1878 
1879 		if ((result & SCANRESULT_NUM_LINES_OK_BIT) == 0)
1880 			anyError = true;
1881 		else if ((result & SCANRESULT_LINE_WIDTH_OK_BIT) == 0)
1882 		{
1883 			if (m_isWideLineCase && isMsaa)
1884 			{
1885 				// multisampled wide lines might not be supported
1886 				msaaRelaxationRequired = true;
1887 			}
1888 			else if ((result & SCANRESULT_LINE_WIDTH_WARN_BIT) != 0)
1889 			{
1890 				hwIssueRelaxationRequired = true;
1891 			}
1892 			else
1893 				anyError = true;
1894 		}
1895 	}
1896 
1897 	if (anyError || msaaRelaxationRequired || hwIssueRelaxationRequired)
1898 	{
1899 		if (messageLimitCounter < 0)
1900 			m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (-messageLimitCounter) << " row/column error descriptions." << tcu::TestLog::EndMessage;
1901 
1902 		m_testCtx.getLog()
1903 			<< tcu::TestLog::Message
1904 			<< "Image verification failed."
1905 			<< tcu::TestLog::EndMessage
1906 			<< tcu::TestLog::ImageSet("Images", "Image verification")
1907 			<< tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
1908 			<< tcu::TestLog::EndImageSet;
1909 
1910 		if (anyError)
1911 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
1912 		else if (hwIssueRelaxationRequired)
1913 		{
1914 			// Line width hw issue
1915 			m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Line width verification failed");
1916 		}
1917 		else
1918 		{
1919 			// MSAA wide lines are optional
1920 			m_testCtx.setTestResult(QP_TEST_RESULT_COMPATIBILITY_WARNING, "Multisampled wide line verification failed");
1921 		}
1922 	}
1923 	else
1924 	{
1925 		m_testCtx.getLog()
1926 			<< tcu::TestLog::Message
1927 			<< "Result image ok."
1928 			<< tcu::TestLog::EndMessage
1929 			<< tcu::TestLog::ImageSet("Images", "Image verification")
1930 			<< tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
1931 			<< tcu::TestLog::EndImageSet;
1932 	}
1933 }
1934 
getNumberOfLinesRange(int queryAreaBegin,int queryAreaEnd,float patternStart,float patternSize,int viewportArea,QueryDirection queryDir) const1935 tcu::IVec2 LineRenderCase::getNumberOfLinesRange (int queryAreaBegin, int queryAreaEnd, float patternStart, float patternSize, int viewportArea, QueryDirection queryDir) const
1936 {
1937 	// pattern is not symmetric due to mirroring
1938 	const int	patternStartNdx	= (queryDir == DIRECTION_HORIZONTAL) ? ((m_hasGeometryStage) ? (1) : (0)) : ((m_hasTessellationStage) ? (1) : (0));
1939 	const int	patternEndNdx	= patternStartNdx + m_patternSide;
1940 
1941 	int			numLinesMin		= 0;
1942 	int			numLinesMax		= 0;
1943 
1944 	for (int lineNdx = patternStartNdx; lineNdx < patternEndNdx; ++lineNdx)
1945 	{
1946 		const float linePos		= (patternStart + (float(lineNdx) / float(m_patternSide)) * patternSize) * 0.5f + 0.5f;
1947 		const float lineWidth	= (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f);
1948 
1949 		if (linePos * (float)viewportArea > (float)queryAreaBegin + 1.0f &&
1950 			linePos * (float)viewportArea < (float)queryAreaEnd   - 1.0f)
1951 		{
1952 			// line center is within the area
1953 			++numLinesMin;
1954 			++numLinesMax;
1955 		}
1956 		else if (linePos * (float)viewportArea > (float)queryAreaBegin - lineWidth*0.5f - 1.0f &&
1957 		         linePos * (float)viewportArea < (float)queryAreaEnd   + lineWidth*0.5f + 1.0f)
1958 		{
1959 			// line could leak into area
1960 			++numLinesMax;
1961 		}
1962 	}
1963 
1964 	return tcu::IVec2(numLinesMin, numLinesMax);
1965 }
1966 
scanRow(const tcu::ConstPixelBufferAccess & access,int row,int rowBegin,int rowEnd,const tcu::IVec2 & numLines,int & messageLimitCounter) const1967 deUint8 LineRenderCase::scanRow (const tcu::ConstPixelBufferAccess& access, int row, int rowBegin, int rowEnd, const tcu::IVec2& numLines, int& messageLimitCounter) const
1968 {
1969 	const bool		numLinesOk		= checkAreaNumLines(access, tcu::IVec4(rowBegin, row, rowEnd - rowBegin, 1), messageLimitCounter, SCAN_ROW_COMPONENT_NDX, numLines);
1970 	const deUint8	lineWidthRes	= checkLineWidths(access, tcu::IVec2(rowBegin, row), tcu::IVec2(rowEnd, row), SCAN_ROW_COMPONENT_NDX, messageLimitCounter);
1971 	deUint8			result			= 0;
1972 
1973 	if (numLinesOk)
1974 		result |= (deUint8)SCANRESULT_NUM_LINES_OK_BIT;
1975 
1976 	if (lineWidthRes == 0)
1977 		result |= (deUint8)SCANRESULT_LINE_WIDTH_OK_BIT;
1978 	else
1979 		result |= lineWidthRes;
1980 
1981 	return result;
1982 }
1983 
scanColumn(const tcu::ConstPixelBufferAccess & access,int column,int columnBegin,int columnEnd,const tcu::IVec2 & numLines,int & messageLimitCounter) const1984 deUint8 LineRenderCase::scanColumn (const tcu::ConstPixelBufferAccess& access, int column, int columnBegin, int columnEnd, const tcu::IVec2& numLines, int& messageLimitCounter) const
1985 {
1986 	const bool		numLinesOk		= checkAreaNumLines(access, tcu::IVec4(column, columnBegin, 1, columnEnd - columnBegin), messageLimitCounter, SCAN_COL_COMPONENT_NDX, numLines);
1987 	const deUint8	lineWidthRes	= checkLineWidths(access, tcu::IVec2(column, columnBegin), tcu::IVec2(column, columnEnd), SCAN_COL_COMPONENT_NDX, messageLimitCounter);
1988 	deUint8			result			= 0;
1989 
1990 	if (numLinesOk)
1991 		result |= (deUint8)SCANRESULT_NUM_LINES_OK_BIT;
1992 
1993 	if (lineWidthRes == 0)
1994 		result |= (deUint8)SCANRESULT_LINE_WIDTH_OK_BIT;
1995 	else
1996 		result |= lineWidthRes;
1997 
1998 	return result;
1999 }
2000 
checkAreaNumLines(const tcu::ConstPixelBufferAccess & access,const tcu::IVec4 & area,int & messageLimitCounter,int componentNdx,const tcu::IVec2 & numLines) const2001 bool LineRenderCase::checkAreaNumLines (const tcu::ConstPixelBufferAccess& access, const tcu::IVec4& area, int& messageLimitCounter, int componentNdx, const tcu::IVec2& numLines) const
2002 {
2003 	// Num maxima == num lines
2004 	const tcu::ConstPixelBufferAccess	subAccess		= tcu::getSubregion(access, area.x(), area.y(), 0, area.z(), area.w(), 1);
2005 	const tcu::IVec2					numMinimaMaxima	= getNumMinimaMaxima(subAccess, componentNdx);
2006 	const int							numMaxima		= numMinimaMaxima.y();
2007 
2008 	// In valid range
2009 	if (numMaxima >= numLines.x() && numMaxima <= numLines.y())
2010 		return true;
2011 
2012 	if (--messageLimitCounter < 0)
2013 		return false;
2014 
2015 	if (area.z() == 1)
2016 		m_testCtx.getLog()
2017 			<< tcu::TestLog::Message
2018 			<< "On column " << area.x() << ", y: [" << area.y() << "," << (area.y()+area.w()) << "):\n"
2019 				<< "\tExpected [" << numLines.x() << ", " << numLines.y() << "] lines but the number of lines in the area is " << numMaxima
2020 			<< tcu::TestLog::EndMessage;
2021 	else
2022 		m_testCtx.getLog()
2023 			<< tcu::TestLog::Message
2024 			<< "On row " << area.y() << ", x: [" << area.x() << "," << (area.x()+area.z()) << "):\n"
2025 				<< "\tExpected [" << numLines.x() << ", " << numLines.y() << "] lines but the number of lines in the area is " << numMaxima
2026 			<< tcu::TestLog::EndMessage;
2027 
2028 	return false;
2029 }
2030 
getNumMinimaMaxima(const tcu::ConstPixelBufferAccess & access,int componentNdx) const2031 tcu::IVec2 LineRenderCase::getNumMinimaMaxima (const tcu::ConstPixelBufferAccess& access, int componentNdx) const
2032 {
2033 	DE_ASSERT(access.getWidth() == 1 || access.getHeight() == 1);
2034 
2035 	int previousValue	= -1;
2036 	int previousSign	= 0;
2037 	int numMinima		= 0;
2038 	int numMaxima		= 0;
2039 
2040 	for (int y = 0; y < access.getHeight(); ++y)
2041 	for (int x = 0; x < access.getWidth(); ++x)
2042 	{
2043 		const int componentValue = access.getPixelInt(x, y)[componentNdx];
2044 
2045 		if (previousValue != -1)
2046 		{
2047 			const int sign = (componentValue > previousValue) ? (+1) : (componentValue < previousValue) ? (-1) : (0);
2048 
2049 			// local minima/maxima in sign changes (zero signless)
2050 			if (sign != 0 && sign == -previousSign)
2051 			{
2052 				previousSign = sign;
2053 
2054 				if (sign > 0)
2055 					++numMinima;
2056 				else
2057 					++numMaxima;
2058 			}
2059 			else if (sign != 0 && previousSign == 0)
2060 			{
2061 				previousSign = sign;
2062 
2063 				// local extreme at the start boundary
2064 				if (sign > 0)
2065 					++numMinima;
2066 				else
2067 					++numMaxima;
2068 			}
2069 		}
2070 
2071 		previousValue = componentValue;
2072 	}
2073 
2074 	// local extreme at the end boundary
2075 	if (previousSign > 0)
2076 		++numMaxima;
2077 	else if (previousSign < 0)
2078 		++numMinima;
2079 	else
2080 	{
2081 		++numMaxima;
2082 		++numMinima;
2083 	}
2084 
2085 	return tcu::IVec2(numMinima, numMaxima);
2086 }
2087 
checkLineWidths(const tcu::ConstPixelBufferAccess & access,const tcu::IVec2 & begin,const tcu::IVec2 & end,int componentNdx,int & messageLimitCounter) const2088 deUint8 LineRenderCase::checkLineWidths (const tcu::ConstPixelBufferAccess& access, const tcu::IVec2& begin, const tcu::IVec2& end, int componentNdx, int& messageLimitCounter) const
2089 {
2090 	const bool			multisample				= m_context.getRenderTarget().getNumSamples() > 1;
2091 	const int			lineRenderWidth			= (m_isWideLineCase) ? (m_wideLineLineWidth) : 1;
2092 	const tcu::IVec2	lineWidthRange			= (multisample)
2093 													? (tcu::IVec2(lineRenderWidth, lineRenderWidth+1))	// multisampled "smooth" lines may spread to neighboring pixel
2094 													: (tcu::IVec2(lineRenderWidth, lineRenderWidth));
2095 	const tcu::IVec2	relaxedLineWidthRange	= (tcu::IVec2(lineRenderWidth-1, lineRenderWidth+1));
2096 
2097 	int					lineWidth				= 0;
2098 	bool				bboxLimitedLine			= false;
2099 	deUint8				errorMask				= 0;
2100 
2101 	const tcu::IVec2	advance					= (begin.x() == end.x()) ? (tcu::IVec2(0, 1)) : (tcu::IVec2(1, 0));
2102 
2103 	// fragments before begin?
2104 	if (access.getPixelInt(begin.x(), begin.y())[componentNdx] != 0)
2105 	{
2106 		bboxLimitedLine = true;
2107 
2108 		for (tcu::IVec2 cursor = begin - advance;; cursor -= advance)
2109 		{
2110 			if (cursor.x() < 0 || cursor.y() < 0)
2111 			{
2112 				break;
2113 			}
2114 			else if (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0)
2115 			{
2116 				++lineWidth;
2117 			}
2118 			else
2119 				break;
2120 		}
2121 	}
2122 
2123 	for (tcu::IVec2 cursor = begin; cursor != end; cursor += advance)
2124 	{
2125 		const bool hit = (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0);
2126 
2127 		if (hit)
2128 			++lineWidth;
2129 		else if (lineWidth)
2130 		{
2131 			// Line is allowed to be be thinner if it borders the bbox boundary (since part of the line might have been discarded).
2132 			const bool incorrectLineWidth = (lineWidth < lineWidthRange.x() && !bboxLimitedLine) || (lineWidth > lineWidthRange.y());
2133 
2134 			if (incorrectLineWidth)
2135 			{
2136 				const bool incorrectRelaxedLineWidth = (lineWidth < relaxedLineWidthRange.x() && !bboxLimitedLine) || (lineWidth > relaxedLineWidthRange.y());
2137 
2138 				if (incorrectRelaxedLineWidth)
2139 					errorMask |= SCANRESULT_LINE_WIDTH_ERR_BIT;
2140 				else
2141 					errorMask |= SCANRESULT_LINE_WIDTH_WARN_BIT;
2142 
2143 				printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter);
2144 			}
2145 
2146 			lineWidth = 0;
2147 			bboxLimitedLine = false;
2148 		}
2149 	}
2150 
2151 	// fragments after end?
2152 	if (lineWidth)
2153 	{
2154 		for (tcu::IVec2 cursor = end;; cursor += advance)
2155 		{
2156 			if (cursor.x() >= access.getWidth() || cursor.y() >= access.getHeight())
2157 			{
2158 				if (lineWidth > lineWidthRange.y())
2159 				{
2160 					if (lineWidth > relaxedLineWidthRange.y())
2161 						errorMask |= SCANRESULT_LINE_WIDTH_ERR_BIT;
2162 					else
2163 						errorMask |= SCANRESULT_LINE_WIDTH_WARN_BIT;
2164 
2165 					printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter);
2166 				}
2167 
2168 				break;
2169 			}
2170 			else if (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0)
2171 			{
2172 				++lineWidth;
2173 			}
2174 			else if (lineWidth)
2175 			{
2176 				// only check that line width is not larger than expected. Line width may be smaller
2177 				// since the scanning 'cursor' is now outside the bounding box.
2178 				const bool incorrectLineWidth = (lineWidth > lineWidthRange.y());
2179 
2180 				if (incorrectLineWidth)
2181 				{
2182 					const bool incorrectRelaxedLineWidth = (lineWidth > relaxedLineWidthRange.y());
2183 
2184 					if (incorrectRelaxedLineWidth)
2185 						errorMask |= SCANRESULT_LINE_WIDTH_ERR_BIT;
2186 					else
2187 						errorMask |= SCANRESULT_LINE_WIDTH_WARN_BIT;
2188 
2189 					printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter);
2190 				}
2191 
2192 				lineWidth = 0;
2193 			}
2194 		}
2195 	}
2196 
2197 	return errorMask;
2198 }
2199 
printLineWidthError(const tcu::IVec2 & pos,int detectedLineWidth,const tcu::IVec2 & lineWidthRange,bool isHorizontal,int & messageLimitCounter) const2200 void LineRenderCase::printLineWidthError (const tcu::IVec2& pos, int detectedLineWidth, const tcu::IVec2& lineWidthRange, bool isHorizontal, int& messageLimitCounter) const
2201 {
2202 	if (--messageLimitCounter < 0)
2203 		return;
2204 
2205 	m_testCtx.getLog()
2206 		<< tcu::TestLog::Message
2207 		<< "Found incorrect line width near " << pos << ": (" << ((isHorizontal) ? ("horizontal") : ("vertical")) << " line)\n"
2208 			<< "\tExpected line width in range [" << lineWidthRange.x() << ", " << lineWidthRange.y() << "] but found " << detectedLineWidth
2209 		<< tcu::TestLog::EndMessage;
2210 }
2211 
2212 class PointRenderCase : public BBoxRenderCase
2213 {
2214 public:
2215 	enum
2216 	{
2217 		POINTFLAG_WIDE = 1u << FLAGBIT_USER_BIT,	//!< use wide points
2218 	};
2219 	struct GeneratedPoint
2220 	{
2221 		tcu::Vec2	center;
2222 		int			size;
2223 		bool		even;
2224 	};
2225 
2226 							PointRenderCase					(Context& context, const char* name, const char* description, deUint32 flags);
2227 							~PointRenderCase				(void);
2228 
2229 private:
2230 	enum ResultPointType
2231 	{
2232 		POINT_FULL = 0,
2233 		POINT_PARTIAL
2234 	};
2235 
2236 	void					init							(void);
2237 	void					deinit							(void);
2238 
2239 	std::string				genVertexSource					(void) const;
2240 	std::string				genFragmentSource				(void) const;
2241 	std::string				genTessellationControlSource	(void) const;
2242 	std::string				genTessellationEvaluationSource	(void) const;
2243 	std::string				genGeometrySource				(void) const;
2244 
2245 	IterationConfig			generateConfig					(int iteration, const tcu::IVec2& renderTargetSize) const;
2246 	void					generateAttributeData			(void);
2247 	void					getAttributeData				(std::vector<tcu::Vec4>& data) const;
2248 	void					renderTestPattern				(const IterationConfig& config);
2249 	void					verifyRenderResult				(const IterationConfig& config);
2250 
2251 	void					genReferencePointData			(const IterationConfig& config, std::vector<GeneratedPoint>& data) const;
2252 	bool					verifyNarrowPointPattern		(const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter);
2253 	bool					verifyWidePointPattern			(const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter);
2254 	bool					verifyWidePoint					(const tcu::Surface& viewport, const GeneratedPoint& refPoint, const ProjectedBBox& bbox, ResultPointType pointType, int& logFloodCounter);
2255 	bool					verifyWidePointAt				(const tcu::IVec2& pointPos, const tcu::Surface& viewport, const GeneratedPoint& refPoint, const tcu::IVec4& bbox, ResultPointType pointType, int componentNdx, int& logFloodCounter);
2256 	tcu::IVec2				scanPointWidthAt				(const tcu::IVec2& pointPos, const tcu::Surface& viewport, int expectedPointSize, int componentNdx) const;
2257 
2258 	const int				m_numStripes;
2259 	const bool				m_isWidePointCase;
2260 	std::vector<tcu::Vec4>	m_attribData;
2261 };
2262 
PointRenderCase(Context & context,const char * name,const char * description,deUint32 flags)2263 PointRenderCase::PointRenderCase (Context& context, const char* name, const char* description, deUint32 flags)
2264 	: BBoxRenderCase	(context, name, description, 12, flags)
2265 	, m_numStripes		(4)
2266 	, m_isWidePointCase	((flags & POINTFLAG_WIDE) != 0)
2267 {
2268 }
2269 
~PointRenderCase(void)2270 PointRenderCase::~PointRenderCase (void)
2271 {
2272 }
2273 
init(void)2274 void PointRenderCase::init (void)
2275 {
2276 	if (m_isWidePointCase)
2277 	{
2278 		// extensions
2279 		if (m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_point_size"))
2280 			throw tcu::NotSupportedError("Test requires GL_EXT_geometry_point_size extension");
2281 		if (m_hasTessellationStage && !m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_point_size"))
2282 			throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_point_size extension");
2283 
2284 		// point size range
2285 		{
2286 			glw::GLfloat pointSizeRange[2] = {0.0f, 0.0f};
2287 			m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
2288 
2289 			if (pointSizeRange[1] < 5.0f)
2290 				throw tcu::NotSupportedError("Test requires point size 5.0");
2291 		}
2292 	}
2293 
2294 	m_testCtx.getLog()
2295 		<< tcu::TestLog::Message
2296 		<< "Rendering point pattern to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n"
2297 		<< "Half of the points are green, half blue. Using additive blending.\n"
2298 		<< "Points are in random order, varying pattern size and location for each iteration.\n"
2299 		<< "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated red channel."
2300 		<< tcu::TestLog::EndMessage;
2301 
2302 	generateAttributeData();
2303 
2304 	BBoxRenderCase::init();
2305 }
2306 
deinit(void)2307 void PointRenderCase::deinit (void)
2308 {
2309 	// clear data
2310 	m_attribData = std::vector<tcu::Vec4>();
2311 
2312 	// deinit parent
2313 	BBoxRenderCase::deinit();
2314 }
2315 
genVertexSource(void) const2316 std::string PointRenderCase::genVertexSource (void) const
2317 {
2318 	std::ostringstream	buf;
2319 
2320 	buf <<	"#version 310 es\n"
2321 			"in highp vec4 a_position;\n"
2322 			"in highp vec4 a_color;\n"
2323 			"out highp vec4 vtx_color;\n"
2324 			"uniform highp vec4 u_posScale;\n"
2325 			"\n";
2326 	if (!m_hasTessellationStage)
2327 	{
2328 		DE_ASSERT(m_useGlobalState);
2329 		buf <<	"uniform highp vec4 u_primitiveBBoxMin;\n"
2330 				"uniform highp vec4 u_primitiveBBoxMax;\n"
2331 				"\n"
2332 				"flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
2333 				"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
2334 				"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
2335 				"\n";
2336 	}
2337 
2338 	buf <<	"void main()\n"
2339 			"{\n"
2340 			"	highp vec2 patternOffset = u_posScale.xy;\n"
2341 			"	highp vec2 patternScale = u_posScale.zw;\n"
2342 			"	highp float pointSize = "
2343 					<< ((m_isWidePointCase && !m_hasTessellationStage && !m_hasGeometryStage) ? ("(a_color.g > 0.0) ? (5.0) : (3.0)") : ("1.0"))
2344 					<< ";\n"
2345 		<<	"	gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n"
2346 			"	gl_PointSize = pointSize;\n"
2347 			"	vtx_color = a_color;\n";
2348 
2349 	if (!m_hasTessellationStage)
2350 	{
2351 		DE_ASSERT(m_useGlobalState);
2352 		buf <<	"\n"
2353 				"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = pointSize;\n"
2354 				"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n"
2355 				"	    min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n"
2356 				"	        vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n"
2357 				"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n"
2358 				"	    min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n"
2359 				"	        vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n";
2360 	}
2361 
2362 	buf <<	"}\n";
2363 	return buf.str();
2364 }
2365 
genFragmentSource(void) const2366 std::string PointRenderCase::genFragmentSource (void) const
2367 {
2368 	const char* const	colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
2369 	std::ostringstream	buf;
2370 
2371 	buf <<	"#version 310 es\n"
2372 			"in mediump vec4 " << colorInputName << ";\n"
2373 			"layout(location = 0) out mediump vec4 o_color;\n"
2374 		<<	genShaderFunction(SHADER_FUNC_INSIDE_BBOX)
2375 		<<	"\n"
2376 			"void main()\n"
2377 			"{\n"
2378 			"	mediump vec4 baseColor = " << colorInputName << ";\n"
2379 			"	mediump float redChannel;\n"
2380 			"	if (fragmentInsideTheBBox(gl_FragCoord.z))\n"
2381 			"		redChannel = 0.0;\n"
2382 			"	else\n"
2383 			"		redChannel = 1.0;\n"
2384 			"	o_color = vec4(redChannel, baseColor.g, baseColor.b, baseColor.a);\n"
2385 			"}\n";
2386 
2387 	return buf.str();
2388 }
2389 
genTessellationControlSource(void) const2390 std::string PointRenderCase::genTessellationControlSource (void) const
2391 {
2392 	const bool			tessellationWidePoints = (m_isWidePointCase) && (!m_hasGeometryStage);
2393 	std::ostringstream	buf;
2394 
2395 	buf <<	"#version 310 es\n"
2396 			"#extension GL_EXT_tessellation_shader : require\n"
2397 			"#extension GL_EXT_primitive_bounding_box : require\n"
2398 		<<	((tessellationWidePoints) ? ("#extension GL_EXT_tessellation_point_size : require\n") : (""))
2399 		<<	"layout(vertices=1) out;"
2400 			"\n"
2401 			"in highp vec4 vtx_color[];\n"
2402 			"out highp vec4 tess_ctrl_color[];\n"
2403 			"uniform highp float u_tessellationLevel;\n"
2404 			"uniform highp vec4 u_posScale;\n";
2405 
2406 	if (!m_calcPerPrimitiveBBox)
2407 	{
2408 		buf <<	"uniform highp vec4 u_primitiveBBoxMin;\n"
2409 				"uniform highp vec4 u_primitiveBBoxMax;\n";
2410 	}
2411 
2412 	buf <<	"patch out highp vec3 vp_bbox_clipMin;\n"
2413 			"patch out highp vec3 vp_bbox_clipMax;\n";
2414 
2415 	if (m_calcPerPrimitiveBBox)
2416 	{
2417 		buf <<	"\n";
2418 		if (m_hasGeometryStage)
2419 			buf << genShaderFunction(SHADER_FUNC_MIRROR_X);
2420 		buf << genShaderFunction(SHADER_FUNC_MIRROR_Y);
2421 
2422 		buf <<	"vec4 transformVec(in highp vec4 p)\n"
2423 				"{\n"
2424 				"	return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\n"
2425 				"}\n";
2426 	}
2427 
2428 	buf <<	"\n"
2429 			"void main()\n"
2430 			"{\n"
2431 			"	// convert to nonsensical coordinates, just in case\n"
2432 			"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n"
2433 			"	tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n"
2434 			"\n"
2435 			"	gl_TessLevelOuter[0] = u_tessellationLevel;\n"
2436 			"	gl_TessLevelOuter[1] = u_tessellationLevel;\n"
2437 			"	gl_TessLevelOuter[2] = u_tessellationLevel;\n"
2438 			"	gl_TessLevelOuter[3] = u_tessellationLevel;\n"
2439 			"	gl_TessLevelInner[0] = 0.8; // will be rounded up to 1\n"
2440 			"	gl_TessLevelInner[1] = 0.8; // will be rounded up to 1\n";
2441 
2442 	if (m_calcPerPrimitiveBBox)
2443 	{
2444 		buf <<	"\n";
2445 
2446 		if (m_hasGeometryStage)
2447 			buf <<	"	const vec2 minExpansion = vec2(0.07 + 0.05, 0.07 + 0.02); // eval and geometry shader\n"
2448 					"	const vec2 maxExpansion = vec2(0.07 + 0.05, 0.07 + 0.03); // eval and geometry shader\n";
2449 		else
2450 			buf <<	"	const vec2 minExpansion = vec2(0.07, 0.07); // eval shader\n"
2451 					"	const vec2 maxExpansion = vec2(0.07, 0.07); // eval shader\n";
2452 
2453 		buf <<	"	highp vec2 patternScale = u_posScale.zw;\n"
2454 				"	highp vec4 bboxMin = transformVec(gl_in[0].gl_Position) - vec4(minExpansion * patternScale, 0.0, 0.0);\n"
2455 				"	highp vec4 bboxMax = transformVec(gl_in[0].gl_Position) + vec4(maxExpansion * patternScale, 0.0, 0.0);\n";
2456 	}
2457 	else
2458 	{
2459 		buf <<	"\n"
2460 				"	highp vec4 bboxMin = u_primitiveBBoxMin;\n"
2461 				"	highp vec4 bboxMax = u_primitiveBBoxMax;\n";
2462 	}
2463 	if (!m_useGlobalState)
2464 		buf <<	"\n"
2465 				"	gl_BoundingBoxEXT[0] = bboxMin;\n"
2466 				"	gl_BoundingBoxEXT[1] = bboxMax;\n";
2467 
2468 	buf <<	"	vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n"
2469 			"	                      vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n"
2470 			"	vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n"
2471 			"	                      vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n"
2472 			"}\n";
2473 
2474 	return buf.str();
2475 }
2476 
genTessellationEvaluationSource(void) const2477 std::string PointRenderCase::genTessellationEvaluationSource (void) const
2478 {
2479 	const bool			tessellationWidePoints = (m_isWidePointCase) && (!m_hasGeometryStage);
2480 	std::ostringstream	buf;
2481 
2482 	buf <<	"#version 310 es\n"
2483 			"#extension GL_EXT_tessellation_shader : require\n"
2484 		<<	((tessellationWidePoints) ? ("#extension GL_EXT_tessellation_point_size : require\n") : (""))
2485 		<<	"layout(quads, point_mode) in;"
2486 			"\n"
2487 			"in highp vec4 tess_ctrl_color[];\n"
2488 			"out highp vec4 tess_color;\n"
2489 			"uniform highp vec4 u_posScale;\n"
2490 			"\n"
2491 			"patch in highp vec3 vp_bbox_clipMin;\n"
2492 			"patch in highp vec3 vp_bbox_clipMax;\n"
2493 		<<	((!m_hasGeometryStage) ? ("flat out highp float v_bbox_expansionSize;\n") : (""))
2494 		<<	"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
2495 			"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
2496 			"\n"
2497 		<<	genShaderFunction(SHADER_FUNC_MIRROR_Y)
2498 		<<	"void main()\n"
2499 			"{\n"
2500 			"	// non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n"
2501 			"	highp vec2 patternScale = u_posScale.zw;\n"
2502 			"	highp vec4 offset = vec4((gl_TessCoord.xy * 2.0 - vec2(1.0)) * 0.07 * patternScale, 0.0, 0.0);\n"
2503 			"	highp float pointSize = " << ((tessellationWidePoints) ? ("(tess_ctrl_color[0].g > 0.0) ? (5.0) : (3.0)") : ("1.0")) << ";\n"
2504 			"	gl_Position = mirrorY(gl_in[0].gl_Position.zwyx + offset);\n";
2505 
2506 	if (tessellationWidePoints)
2507 		buf << "	gl_PointSize = pointSize;\n";
2508 
2509 	buf <<	"	tess_color = tess_ctrl_color[0];\n"
2510 		<<	((!m_hasGeometryStage) ? ("v_bbox_expansionSize = pointSize;\n") : (""))
2511 		<<	"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin = vp_bbox_clipMin;\n"
2512 			"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax = vp_bbox_clipMax;\n"
2513 			"}\n";
2514 
2515 	return buf.str();
2516 }
2517 
genGeometrySource(void) const2518 std::string PointRenderCase::genGeometrySource (void) const
2519 {
2520 	const char* const	colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
2521 	std::ostringstream	buf;
2522 
2523 	buf <<	"#version 310 es\n"
2524 			"#extension GL_EXT_geometry_shader : require\n"
2525 		<<	((m_isWidePointCase) ? ("#extension GL_EXT_geometry_point_size : require\n") : (""))
2526 		<<	"layout(points) in;\n"
2527 			"layout(max_vertices=3, points) out;\n"
2528 			"\n"
2529 			"in highp vec4 " << colorInputName << "[1];\n"
2530 			"out highp vec4 geo_color;\n"
2531 			"uniform highp vec4 u_posScale;\n"
2532 			"\n"
2533 			"flat in highp vec3 v_geo_bbox_clipMin[1];\n"
2534 			"flat in highp vec3 v_geo_bbox_clipMax[1];\n"
2535 			"flat out highp vec3 v_bbox_clipMin;\n"
2536 			"flat out highp vec3 v_bbox_clipMax;\n"
2537 			"flat out highp float v_bbox_expansionSize;\n"
2538 			"\n"
2539 		<<	genShaderFunction(SHADER_FUNC_MIRROR_X)
2540 		<<	"\n"
2541 			"void main()\n"
2542 			"{\n"
2543 			"	// Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n"
2544 			"	highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n"
2545 			"	highp vec4 pointColor = " << colorInputName << "[0];\n"
2546 			"	highp vec2 patternScale = u_posScale.zw;\n"
2547 			"	highp float pointSize = "
2548 				<< (m_isWidePointCase ? ("(pointColor.g > 0.0) ? (5.0) : (3.0)") : ("1.0"))
2549 				<< ";\n"
2550 			"\n"
2551 			"	highp vec4 offsets[3] =\n"
2552 			"		vec4[3](\n"
2553 			"			vec4( 0.05 * patternScale.x, 0.03 * patternScale.y, 0.0, 0.0),\n"
2554 			"			vec4(-0.01 * patternScale.x,-0.02 * patternScale.y, 0.0, 0.0),\n"
2555 			"			vec4(-0.05 * patternScale.x, 0.02 * patternScale.y, 0.0, 0.0)\n"
2556 			"		);\n"
2557 			"	for (int ndx = 0; ndx < 3; ++ndx)\n"
2558 			"	{\n"
2559 			"		gl_Position = p0 + offsets[ndx];\n";
2560 
2561 	if (m_isWidePointCase)
2562 		buf <<	"		gl_PointSize = pointSize;\n";
2563 
2564 	buf <<	"		v_bbox_clipMin = v_geo_bbox_clipMin[0];\n"
2565 			"		v_bbox_clipMax = v_geo_bbox_clipMax[0];\n"
2566 			"		v_bbox_expansionSize = pointSize;\n"
2567 			"		geo_color = pointColor;\n"
2568 			"		EmitVertex();\n"
2569 			"	}\n"
2570 			"}\n";
2571 
2572 	return buf.str();
2573 }
2574 
generateConfig(int iteration,const tcu::IVec2 & renderTargetSize) const2575 PointRenderCase::IterationConfig PointRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const
2576 {
2577 	IterationConfig config = generateRandomConfig(0xDEDEDEu * (deUint32)iteration, renderTargetSize);
2578 
2579 	// equal or larger -> expand according to shader expansion
2580 	if (m_bboxSize == BBOXSIZE_EQUAL || m_bboxSize == BBOXSIZE_LARGER)
2581 	{
2582 		const tcu::Vec2 patternScale = config.patternSize;
2583 
2584 		if (m_hasTessellationStage)
2585 		{
2586 			config.bbox.min -= tcu::Vec4(0.07f * patternScale.x(), 0.07f * patternScale.y(), 0.0f, 0.0f);
2587 			config.bbox.max += tcu::Vec4(0.07f * patternScale.x(), 0.07f * patternScale.y(), 0.0f, 0.0f);
2588 		}
2589 		if (m_hasGeometryStage)
2590 		{
2591 			config.bbox.min -= tcu::Vec4(0.05f * patternScale.x(), 0.02f * patternScale.y(), 0.0f, 0.0f);
2592 			config.bbox.max += tcu::Vec4(0.05f * patternScale.x(), 0.03f * patternScale.y(), 0.0f, 0.0f);
2593 		}
2594 	}
2595 
2596 	return config;
2597 }
2598 
generateAttributeData(void)2599 void PointRenderCase::generateAttributeData (void)
2600 {
2601 	const tcu::Vec4		green		(0.0f, 1.0f, 0.0f, 1.0f);
2602 	const tcu::Vec4		blue		(0.0f, 0.0f, 1.0f, 1.0f);
2603 	std::vector<int>	cellOrder	(m_numStripes * m_numStripes * 2);
2604 	de::Random			rnd			(0xDE22446);
2605 
2606 	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
2607 		cellOrder[ndx] = ndx;
2608 	rnd.shuffle(cellOrder.begin(), cellOrder.end());
2609 
2610 	m_attribData.resize(cellOrder.size() * 2);
2611 	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
2612 	{
2613 		const int pointID		= cellOrder[ndx];
2614 		const int direction		= pointID & 0x01;
2615 		const int majorCoord	= (pointID >> 1) / m_numStripes;
2616 		const int minorCoord	= (pointID >> 1) % m_numStripes;
2617 
2618 		if (direction)
2619 		{
2620 			m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(minorCoord) / float(m_numStripes), float(majorCoord) / float(m_numStripes), 0.0f, 1.0f);
2621 			m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green;
2622 		}
2623 		else
2624 		{
2625 			m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(((float)majorCoord + 0.5f) / float(m_numStripes), ((float)minorCoord + 0.5f) / float(m_numStripes), 0.0f, 1.0f);
2626 			m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue;
2627 		}
2628 	}
2629 }
2630 
getAttributeData(std::vector<tcu::Vec4> & data) const2631 void PointRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const
2632 {
2633 	data = m_attribData;
2634 }
2635 
renderTestPattern(const IterationConfig & config)2636 void PointRenderCase::renderTestPattern (const IterationConfig& config)
2637 {
2638 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
2639 
2640 	setupRender(config);
2641 
2642 	if (m_hasTessellationStage)
2643 	{
2644 		const glw::GLint	tessLevelPos	= gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel");
2645 		const glw::GLfloat	tessLevel		= 0.8f; // will be rounded up
2646 
2647 		TCU_CHECK(tessLevelPos != -1);
2648 
2649 		m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage;
2650 
2651 		gl.uniform1f(tessLevelPos, tessLevel);
2652 		gl.patchParameteri(GL_PATCH_VERTICES, 1);
2653 		GLU_EXPECT_NO_ERROR(gl.getError(), "patch param");
2654 	}
2655 
2656 	m_testCtx.getLog() << tcu::TestLog::Message << "Rendering pattern." << tcu::TestLog::EndMessage;
2657 
2658 	gl.enable(GL_BLEND);
2659 	gl.blendFunc(GL_ONE, GL_ONE);
2660 	gl.blendEquation(GL_FUNC_ADD);
2661 
2662 	gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_POINTS), 0, m_numStripes * m_numStripes * 2);
2663 	GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
2664 }
2665 
verifyRenderResult(const IterationConfig & config)2666 void PointRenderCase::verifyRenderResult (const IterationConfig& config)
2667 {
2668 	const glw::Functions&		gl						= m_context.getRenderContext().getFunctions();
2669 	const ProjectedBBox			projectedBBox			= projectBoundingBox(config.bbox);
2670 	const tcu::IVec4			viewportBBoxArea		= getViewportBoundingBoxArea(projectedBBox, config.viewportSize);
2671 
2672 	tcu::Surface				viewportSurface			(config.viewportSize.x(), config.viewportSize.y());
2673 	int							logFloodCounter			= 8;
2674 	bool						anyError;
2675 	std::vector<GeneratedPoint>	refPoints;
2676 
2677 	if (!m_calcPerPrimitiveBBox)
2678 		m_testCtx.getLog()
2679 			<< tcu::TestLog::Message
2680 			<< "Projected bounding box: (clip space)\n"
2681 				<< "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n"
2682 				<< "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n"
2683 				<< "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n"
2684 			<< "In viewport coordinates:\n"
2685 				<< "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n"
2686 				<< "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n"
2687 			<< "Verifying render results within the bounding box:\n"
2688 			<< tcu::TestLog::EndMessage;
2689 	else
2690 		m_testCtx.getLog()
2691 			<< tcu::TestLog::Message
2692 			<< "Verifying render result:"
2693 			<< tcu::TestLog::EndMessage;
2694 
2695 	if (m_fbo)
2696 		gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo);
2697 	glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess());
2698 
2699 	genReferencePointData(config, refPoints);
2700 
2701 	if (m_isWidePointCase)
2702 		anyError = verifyWidePointPattern(viewportSurface, refPoints, projectedBBox, logFloodCounter);
2703 	else
2704 		anyError = verifyNarrowPointPattern(viewportSurface, refPoints, projectedBBox, logFloodCounter);
2705 
2706 	if (anyError)
2707 	{
2708 		if (logFloodCounter < 0)
2709 			m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (-logFloodCounter) << " error descriptions." << tcu::TestLog::EndMessage;
2710 
2711 		m_testCtx.getLog()
2712 			<< tcu::TestLog::Message
2713 			<< "Image verification failed."
2714 			<< tcu::TestLog::EndMessage
2715 			<< tcu::TestLog::ImageSet("Images", "Image verification")
2716 			<< tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
2717 			<< tcu::TestLog::EndImageSet;
2718 
2719 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
2720 	}
2721 	else
2722 	{
2723 		m_testCtx.getLog()
2724 			<< tcu::TestLog::Message
2725 			<< "Result image ok."
2726 			<< tcu::TestLog::EndMessage
2727 			<< tcu::TestLog::ImageSet("Images", "Image verification")
2728 			<< tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
2729 			<< tcu::TestLog::EndImageSet;
2730 	}
2731 }
2732 
2733 struct PointSorter
2734 {
operator ()deqp::gles31::Functional::__anonc63ed1f10111::PointSorter2735 	bool operator() (const PointRenderCase::GeneratedPoint& a, const PointRenderCase::GeneratedPoint& b) const
2736 	{
2737 		if (a.center.y() < b.center.y())
2738 			return true;
2739 		else if (a.center.y() > b.center.y())
2740 			return false;
2741 		else
2742 			return (a.center.x() < b.center.x());
2743 	}
2744 };
2745 
genReferencePointData(const IterationConfig & config,std::vector<GeneratedPoint> & data) const2746 void PointRenderCase::genReferencePointData (const IterationConfig& config, std::vector<GeneratedPoint>& data) const
2747 {
2748 	std::vector<GeneratedPoint> currentPoints;
2749 
2750 	// vertex shader
2751 	currentPoints.resize(m_attribData.size() / 2);
2752 	for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
2753 	{
2754 		currentPoints[ndx].center	= m_attribData[ndx*2].swizzle(0, 1);
2755 		currentPoints[ndx].even		= (m_attribData[ndx*2 + 1].y() == 1.0f); // is green
2756 		currentPoints[ndx].size		= ((m_isWidePointCase) ? ((currentPoints[ndx].even) ? 5 : 3) : 1);
2757 	}
2758 
2759 	// tessellation
2760 	if (m_hasTessellationStage)
2761 	{
2762 		std::vector<GeneratedPoint> tessellatedPoints;
2763 
2764 		tessellatedPoints.resize(currentPoints.size() * 4);
2765 		for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
2766 		{
2767 			const tcu::Vec2 position = tcu::Vec2(currentPoints[ndx].center.x(), 1.0f - currentPoints[ndx].center.y()); // mirror Y
2768 
2769 			tessellatedPoints[4 * ndx + 0].center	= position + tcu::Vec2(-0.07f, -0.07f);
2770 			tessellatedPoints[4 * ndx + 0].size		= currentPoints[ndx].size;
2771 			tessellatedPoints[4 * ndx + 0].even		= currentPoints[ndx].even;
2772 
2773 			tessellatedPoints[4 * ndx + 1].center	= position + tcu::Vec2( 0.07f, -0.07f);
2774 			tessellatedPoints[4 * ndx + 1].size		= currentPoints[ndx].size;
2775 			tessellatedPoints[4 * ndx + 1].even		= currentPoints[ndx].even;
2776 
2777 			tessellatedPoints[4 * ndx + 2].center	= position + tcu::Vec2( 0.07f,  0.07f);
2778 			tessellatedPoints[4 * ndx + 2].size		= currentPoints[ndx].size;
2779 			tessellatedPoints[4 * ndx + 2].even		= currentPoints[ndx].even;
2780 
2781 			tessellatedPoints[4 * ndx + 3].center	= position + tcu::Vec2(-0.07f,  0.07f);
2782 			tessellatedPoints[4 * ndx + 3].size		= currentPoints[ndx].size;
2783 			tessellatedPoints[4 * ndx + 3].even		= currentPoints[ndx].even;
2784 		}
2785 
2786 		currentPoints.swap(tessellatedPoints);
2787 	}
2788 
2789 	// geometry
2790 	if (m_hasGeometryStage)
2791 	{
2792 		std::vector<GeneratedPoint> geometryShadedPoints;
2793 
2794 		geometryShadedPoints.resize(currentPoints.size() * 3);
2795 		for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
2796 		{
2797 			const tcu::Vec2 position = tcu::Vec2(1.0f - currentPoints[ndx].center.x(), currentPoints[ndx].center.y()); // mirror X
2798 
2799 			geometryShadedPoints[3 * ndx + 0].center	= position + tcu::Vec2( 0.05f,  0.03f);
2800 			geometryShadedPoints[3 * ndx + 0].size		= currentPoints[ndx].size;
2801 			geometryShadedPoints[3 * ndx + 0].even		= currentPoints[ndx].even;
2802 
2803 			geometryShadedPoints[3 * ndx + 1].center	= position + tcu::Vec2(-0.01f, -0.02f);
2804 			geometryShadedPoints[3 * ndx + 1].size		= currentPoints[ndx].size;
2805 			geometryShadedPoints[3 * ndx + 1].even		= currentPoints[ndx].even;
2806 
2807 			geometryShadedPoints[3 * ndx + 2].center	= position + tcu::Vec2(-0.05f,  0.02f);
2808 			geometryShadedPoints[3 * ndx + 2].size		= currentPoints[ndx].size;
2809 			geometryShadedPoints[3 * ndx + 2].even		= currentPoints[ndx].even;
2810 		}
2811 
2812 		currentPoints.swap(geometryShadedPoints);
2813 	}
2814 
2815 	// sort from left to right, top to bottom
2816 	std::sort(currentPoints.begin(), currentPoints.end(), PointSorter());
2817 
2818 	// map to pattern space
2819 	for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
2820 		currentPoints[ndx].center = currentPoints[ndx].center * config.patternSize + config.patternPos;
2821 
2822 	currentPoints.swap(data);
2823 }
2824 
verifyNarrowPointPattern(const tcu::Surface & viewport,const std::vector<GeneratedPoint> & refPoints,const ProjectedBBox & bbox,int & logFloodCounter)2825 bool PointRenderCase::verifyNarrowPointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter)
2826 {
2827 	bool anyError = false;
2828 
2829 	// check that there is something near each sample
2830 	for (int pointNdx = 0; pointNdx < (int)refPoints.size(); ++pointNdx)
2831 	{
2832 		const float				epsilon		= 1.0e-6f;
2833 		const GeneratedPoint&	refPoint	= refPoints[pointNdx];
2834 
2835 		// skip points not in the the bbox, treat boundary as "in"
2836 		if (refPoint.center.x() < bbox.min.x() - epsilon ||
2837 			refPoint.center.y() < bbox.min.y() - epsilon ||
2838 			refPoint.center.x() > bbox.max.x() + epsilon ||
2839 			refPoint.center.y() > bbox.max.y() + epsilon)
2840 			continue;
2841 		else
2842 		{
2843 			// transform to viewport coords
2844 			const tcu::IVec2 pixelCenter(deRoundFloatToInt32((refPoint.center.x() * 0.5f + 0.5f) * (float)viewport.getWidth()),
2845 										 deRoundFloatToInt32((refPoint.center.y() * 0.5f + 0.5f) * (float)viewport.getHeight()));
2846 
2847 			// find rasterized point in the result
2848 			if (pixelCenter.x() < 1 || pixelCenter.y() < 1 || pixelCenter.x() >= viewport.getWidth()-1 || pixelCenter.y() >= viewport.getHeight()-1)
2849 			{
2850 				// viewport boundary, assume point is fine
2851 			}
2852 			else
2853 			{
2854 				const int	componentNdx	= (refPoint.even) ? (1) : (2); // analyze either green or blue channel
2855 				bool		foundResult		= false;
2856 
2857 				// check neighborhood
2858 				for (int dy = -1; dy < 2 && !foundResult; ++dy)
2859 				for (int dx = -1; dx < 2 && !foundResult; ++dx)
2860 				{
2861 					const tcu::IVec2	testPos	(pixelCenter.x() + dx, pixelCenter.y() + dy);
2862 					const tcu::RGBA		color	= viewport.getPixel(testPos.x(), testPos.y());
2863 
2864 					if (color.toIVec()[componentNdx] > 0)
2865 						foundResult = true;
2866 				}
2867 
2868 				if (!foundResult)
2869 				{
2870 					anyError = true;
2871 
2872 					if (--logFloodCounter >= 0)
2873 					{
2874 						m_testCtx.getLog()
2875 							<< tcu::TestLog::Message
2876 							<< "Missing point near " << pixelCenter << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "."
2877 							<< tcu::TestLog::EndMessage;
2878 					}
2879 				}
2880 			}
2881 		}
2882 	}
2883 
2884 	return anyError;
2885 }
2886 
verifyWidePointPattern(const tcu::Surface & viewport,const std::vector<GeneratedPoint> & refPoints,const ProjectedBBox & bbox,int & logFloodCounter)2887 bool PointRenderCase::verifyWidePointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter)
2888 {
2889 	bool anyError = false;
2890 
2891 	// check that there is something near each sample
2892 	for (int pointNdx = 0; pointNdx < (int)refPoints.size(); ++pointNdx)
2893 	{
2894 		const GeneratedPoint& refPoint = refPoints[pointNdx];
2895 
2896 		if (refPoint.center.x() >= bbox.min.x() &&
2897 			refPoint.center.y() >= bbox.min.y() &&
2898 			refPoint.center.x() <= bbox.max.x() &&
2899 			refPoint.center.y() <= bbox.max.y())
2900 		{
2901 			// point fully in the bounding box
2902 			anyError |= !verifyWidePoint(viewport, refPoint, bbox, POINT_FULL, logFloodCounter);
2903 		}
2904 		else if (refPoint.center.x() >= bbox.min.x() + (float)refPoint.size / 2.0f &&
2905 				 refPoint.center.y() >= bbox.min.y() - (float)refPoint.size / 2.0f &&
2906 				 refPoint.center.x() <= bbox.max.x() + (float)refPoint.size / 2.0f &&
2907 				 refPoint.center.y() <= bbox.max.y() - (float)refPoint.size / 2.0f)
2908 		{
2909 			// point leaks into bounding box
2910 			anyError |= !verifyWidePoint(viewport, refPoint, bbox, POINT_PARTIAL, logFloodCounter);
2911 		}
2912 	}
2913 
2914 	return anyError;
2915 }
2916 
verifyWidePoint(const tcu::Surface & viewport,const GeneratedPoint & refPoint,const ProjectedBBox & bbox,ResultPointType pointType,int & logFloodCounter)2917 bool PointRenderCase::verifyWidePoint (const tcu::Surface& viewport, const GeneratedPoint& refPoint, const ProjectedBBox& bbox, ResultPointType pointType, int& logFloodCounter)
2918 {
2919 	const int			componentNdx		= (refPoint.even) ? (1) : (2);
2920 	const int			halfPointSizeCeil	= (refPoint.size + 1) / 2;
2921 	const int			halfPointSizeFloor	= (refPoint.size + 1) / 2;
2922 	const tcu::IVec4	viewportBBoxArea	= getViewportBoundingBoxArea(bbox, tcu::IVec2(viewport.getWidth(), viewport.getHeight()), (float)refPoint.size);
2923 	const tcu::IVec4	verificationArea	= tcu::IVec4(de::max(viewportBBoxArea.x(), 0),
2924 														 de::max(viewportBBoxArea.y(), 0),
2925 														 de::min(viewportBBoxArea.z(), viewport.getWidth()),
2926 														 de::min(viewportBBoxArea.w(), viewport.getHeight()));
2927 	const tcu::IVec2	pointPos			= tcu::IVec2(deRoundFloatToInt32((refPoint.center.x()*0.5f + 0.5f) * (float)viewport.getWidth()),
2928 														 deRoundFloatToInt32((refPoint.center.y()*0.5f + 0.5f) * (float)viewport.getHeight()));
2929 
2930 	// find any fragment within the point that is inside the bbox, start search at the center
2931 
2932 	if (pointPos.x() >= verificationArea.x() &&
2933 		pointPos.y() >= verificationArea.y() &&
2934 		pointPos.x() < verificationArea.z() &&
2935 		pointPos.y() < verificationArea.w())
2936 	{
2937 		if (viewport.getPixel(pointPos.x(), pointPos.y()).toIVec()[componentNdx])
2938 			return verifyWidePointAt(pointPos, viewport, refPoint, verificationArea, pointType, componentNdx, logFloodCounter);
2939 	}
2940 
2941 	for (int dy = -halfPointSizeCeil; dy <= halfPointSizeCeil; ++dy)
2942 	for (int dx = -halfPointSizeCeil; dx <= halfPointSizeCeil; ++dx)
2943 	{
2944 		const tcu::IVec2 testPos = pointPos + tcu::IVec2(dx, dy);
2945 
2946 		if (dx == 0 && dy == 0)
2947 			continue;
2948 
2949 		if (testPos.x() >= verificationArea.x() &&
2950 			testPos.y() >= verificationArea.y() &&
2951 			testPos.x() < verificationArea.z() &&
2952 			testPos.y() < verificationArea.w())
2953 		{
2954 			if (viewport.getPixel(testPos.x(), testPos.y()).toIVec()[componentNdx])
2955 				return verifyWidePointAt(testPos, viewport, refPoint, verificationArea, pointType, componentNdx, logFloodCounter);
2956 		}
2957 	}
2958 
2959 	// could not find point, this is only ok near boundaries
2960 	if (pointPos.x() + halfPointSizeFloor <  verificationArea.x() - 1 ||
2961 		pointPos.y() + halfPointSizeFloor <  verificationArea.y() - 1 ||
2962 		pointPos.x() - halfPointSizeFloor >= verificationArea.z() - 1 ||
2963 		pointPos.y() - halfPointSizeFloor >= verificationArea.w() - 1)
2964 		return true;
2965 
2966 	if (--logFloodCounter >= 0)
2967 	{
2968 		m_testCtx.getLog()
2969 			<< tcu::TestLog::Message
2970 			<< "Missing wide point near " << pointPos << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "."
2971 			<< tcu::TestLog::EndMessage;
2972 	}
2973 
2974 	return false;
2975 }
2976 
verifyWidePointAt(const tcu::IVec2 & pointPos,const tcu::Surface & viewport,const GeneratedPoint & refPoint,const tcu::IVec4 & bbox,ResultPointType pointType,int componentNdx,int & logFloodCounter)2977 bool PointRenderCase::verifyWidePointAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, const GeneratedPoint& refPoint, const tcu::IVec4& bbox, ResultPointType pointType, int componentNdx, int& logFloodCounter)
2978 {
2979 	const int				expectedPointSize		= refPoint.size;
2980 	bool					viewportClippedTop		= false;
2981 	bool					viewportClippedBottom	= false;
2982 	bool					primitiveClippedTop		= false;
2983 	bool					primitiveClippedBottom	= false;
2984 	std::vector<tcu::IVec2>	widthsUpwards;
2985 	std::vector<tcu::IVec2>	widthsDownwards;
2986 	std::vector<tcu::IVec2>	widths;
2987 
2988 	// search upwards
2989 	for (int y = pointPos.y();; --y)
2990 	{
2991 		if (y < bbox.y() || y < 0)
2992 		{
2993 			if (y < bbox.y())
2994 				primitiveClippedTop = true;
2995 			if (y < 0)
2996 				viewportClippedTop = true;
2997 			break;
2998 		}
2999 		else if (pointPos.y() - y > expectedPointSize)
3000 		{
3001 			// no need to go further than point height
3002 			break;
3003 		}
3004 		else if (viewport.getPixel(pointPos.x(), y).toIVec()[componentNdx] == 0)
3005 		{
3006 			break;
3007 		}
3008 		else
3009 		{
3010 			widthsUpwards.push_back(scanPointWidthAt(tcu::IVec2(pointPos.x(), y), viewport, expectedPointSize, componentNdx));
3011 		}
3012 	}
3013 
3014 	// top is clipped
3015 	if ((viewportClippedTop || (pointType == POINT_PARTIAL && primitiveClippedTop)) && !widthsUpwards.empty())
3016 	{
3017 		const tcu::IVec2&	range			= widthsUpwards.back();
3018 		const bool			squareFits		= (range.y() - range.x() + 1) >= expectedPointSize;
3019 		const bool			widthClipped	= (pointType == POINT_PARTIAL) && (range.x() <= bbox.x() || range.y() >= bbox.z());
3020 
3021 		if (squareFits || widthClipped)
3022 			return true;
3023 	}
3024 
3025 	// and downwards
3026 	for (int y = pointPos.y()+1;; ++y)
3027 	{
3028 		if (y >= bbox.w() || y >= viewport.getHeight())
3029 		{
3030 			if (y >= bbox.w())
3031 				primitiveClippedBottom = true;
3032 			if (y >= viewport.getHeight())
3033 				viewportClippedBottom = true;
3034 			break;
3035 		}
3036 		else if (y - pointPos.y() > expectedPointSize)
3037 		{
3038 			// no need to go further than point height
3039 			break;
3040 		}
3041 		else if (viewport.getPixel(pointPos.x(), y).toIVec()[componentNdx] == 0)
3042 		{
3043 			break;
3044 		}
3045 		else
3046 		{
3047 			widthsDownwards.push_back(scanPointWidthAt(tcu::IVec2(pointPos.x(), y), viewport, expectedPointSize, componentNdx));
3048 		}
3049 	}
3050 
3051 	// bottom is clipped
3052 	if ((viewportClippedBottom || (pointType == POINT_PARTIAL && primitiveClippedBottom)) && !(widthsDownwards.empty() && widthsUpwards.empty()))
3053 	{
3054 		const tcu::IVec2&	range			= (widthsDownwards.empty()) ? (widthsUpwards.front()) : (widthsDownwards.back());
3055 		const bool			squareFits		= (range.y() - range.x() + 1) >= expectedPointSize;
3056 		const bool			bboxClipped		= (pointType == POINT_PARTIAL) && (range.x() <= bbox.x() || range.y() >= bbox.z()-1);
3057 		const bool			viewportClipped	= range.x() <= 0 || range.y() >= viewport.getWidth()-1;
3058 
3059 		if (squareFits || bboxClipped || viewportClipped)
3060 			return true;
3061 	}
3062 
3063 	// would square point would fit into the rasterized area
3064 
3065 	for (int ndx = 0; ndx < (int)widthsUpwards.size(); ++ndx)
3066 		widths.push_back(widthsUpwards[(int)widthsUpwards.size() - ndx - 1]);
3067 	for (int ndx = 0; ndx < (int)widthsDownwards.size(); ++ndx)
3068 		widths.push_back(widthsDownwards[ndx]);
3069 	DE_ASSERT(!widths.empty());
3070 
3071 	for (int y = 0; y < (int)widths.size() - expectedPointSize + 1; ++y)
3072 	{
3073 		tcu::IVec2 unionRange = widths[y];
3074 
3075 		for (int dy = 1; dy < expectedPointSize; ++dy)
3076 		{
3077 			unionRange.x() = de::max(unionRange.x(), widths[y+dy].x());
3078 			unionRange.y() = de::min(unionRange.y(), widths[y+dy].y());
3079 		}
3080 
3081 		// would a N x N block fit here?
3082 		{
3083 			const bool squareFits		= (unionRange.y() - unionRange.x() + 1) >= expectedPointSize;
3084 			const bool bboxClipped		= (pointType == POINT_PARTIAL) && (unionRange.x() <= bbox.x() || unionRange.y() >= bbox.z()-1);
3085 			const bool viewportClipped	= unionRange.x() <= 0 || unionRange.y() >= viewport.getWidth()-1;
3086 
3087 			if (squareFits || bboxClipped || viewportClipped)
3088 				return true;
3089 		}
3090 	}
3091 
3092 	if (--logFloodCounter >= 0)
3093 	{
3094 		m_testCtx.getLog()
3095 			<< tcu::TestLog::Message
3096 			<< "Missing " << expectedPointSize << "x" << expectedPointSize << " point near " << pointPos << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "."
3097 			<< tcu::TestLog::EndMessage;
3098 	}
3099 	return false;
3100 }
3101 
scanPointWidthAt(const tcu::IVec2 & pointPos,const tcu::Surface & viewport,int expectedPointSize,int componentNdx) const3102 tcu::IVec2 PointRenderCase::scanPointWidthAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, int expectedPointSize, int componentNdx) const
3103 {
3104 	int minX = pointPos.x();
3105 	int maxX = pointPos.x();
3106 
3107 	// search horizontally for a point edges
3108 	for (int x = pointPos.x()-1; x >= 0; --x)
3109 	{
3110 		if (viewport.getPixel(x, pointPos.y()).toIVec()[componentNdx] == 0)
3111 			break;
3112 
3113 		// no need to go further than point width
3114 		if (pointPos.x() - x > expectedPointSize)
3115 			break;
3116 
3117 		minX = x;
3118 	}
3119 	for (int x = pointPos.x()+1; x < viewport.getWidth(); ++x)
3120 	{
3121 		if (viewport.getPixel(x, pointPos.y()).toIVec()[componentNdx] == 0)
3122 			break;
3123 
3124 		// no need to go further than point width
3125 		if (x - pointPos.x() > expectedPointSize)
3126 			break;
3127 
3128 		maxX = x;
3129 	}
3130 
3131 	return tcu::IVec2(minX, maxX);
3132 }
3133 
3134 class BlitFboCase : public TestCase
3135 {
3136 public:
3137 	enum RenderTarget
3138 	{
3139 		TARGET_DEFAULT = 0,
3140 		TARGET_FBO,
3141 
3142 		TARGET_LAST
3143 	};
3144 
3145 							BlitFboCase						(Context& context, const char* name, const char* description, RenderTarget src, RenderTarget dst);
3146 							~BlitFboCase					(void);
3147 
3148 private:
3149 	enum
3150 	{
3151 		FBO_SIZE = 256,
3152 	};
3153 
3154 	struct BlitArgs
3155 	{
3156 		tcu::IVec4	src;
3157 		tcu::IVec4	dst;
3158 		tcu::Vec4	bboxMin;
3159 		tcu::Vec4	bboxMax;
3160 		bool		linear;
3161 	};
3162 
3163 	void							init					(void);
3164 	void							deinit					(void);
3165 	IterateResult					iterate					(void);
3166 
3167 	void							fillSourceWithPattern	(void);
3168 	bool							verifyImage				(const BlitArgs& args);
3169 
3170 	const RenderTarget				m_src;
3171 	const RenderTarget				m_dst;
3172 
3173 	std::vector<BlitArgs>			m_iterations;
3174 	int								m_iteration;
3175 	de::MovePtr<glu::Framebuffer>	m_srcFbo;
3176 	de::MovePtr<glu::Framebuffer>	m_dstFbo;
3177 	de::MovePtr<glu::Renderbuffer>	m_srcRbo;
3178 	de::MovePtr<glu::Renderbuffer>	m_dstRbo;
3179 	de::MovePtr<glu::ShaderProgram>	m_program;
3180 	de::MovePtr<glu::Buffer>		m_vbo;
3181 };
3182 
BlitFboCase(Context & context,const char * name,const char * description,RenderTarget src,RenderTarget dst)3183 BlitFboCase::BlitFboCase (Context& context, const char* name, const char* description, RenderTarget src, RenderTarget dst)
3184 	: TestCase		(context, name, description)
3185 	, m_src			(src)
3186 	, m_dst			(dst)
3187 	, m_iteration	(0)
3188 {
3189 	DE_ASSERT(src < TARGET_LAST);
3190 	DE_ASSERT(dst < TARGET_LAST);
3191 }
3192 
~BlitFboCase(void)3193 BlitFboCase::~BlitFboCase (void)
3194 {
3195 	deinit();
3196 }
3197 
init(void)3198 void BlitFboCase::init (void)
3199 {
3200 	const int				numIterations			= 12;
3201 	const bool				defaultFBMultisampled	= (m_context.getRenderTarget().getNumSamples() > 1);
3202 	const glw::Functions&	gl						= m_context.getRenderContext().getFunctions();
3203 	de::Random				rnd						(0xABC123);
3204 
3205 	m_testCtx.getLog()
3206 		<< tcu::TestLog::Message
3207 		<< "Using BlitFramebuffer to blit area from "
3208 			<< ((m_src == TARGET_DEFAULT) ? ("default fb") : ("fbo"))
3209 			<< " to "
3210 			<< ((m_dst == TARGET_DEFAULT) ? ("default fb") : ("fbo"))
3211 			<< ".\n"
3212 		<< "Varying blit arguments and primitive bounding box between iterations.\n"
3213 		<< "Expecting bounding box to have no effect on blitting.\n"
3214 		<< "Source framebuffer is filled with green-yellow grid.\n"
3215 		<< tcu::TestLog::EndMessage;
3216 
3217 	if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
3218 		throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
3219 	if (m_dst == TARGET_DEFAULT && defaultFBMultisampled)
3220 		throw tcu::NotSupportedError("Test requires non-multisampled default framebuffer");
3221 
3222 	// resources
3223 
3224 	if (m_src == TARGET_FBO)
3225 	{
3226 		m_srcRbo = de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(m_context.getRenderContext()));
3227 		gl.bindRenderbuffer(GL_RENDERBUFFER, **m_srcRbo);
3228 		gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, FBO_SIZE, FBO_SIZE);
3229 		GLU_EXPECT_NO_ERROR(gl.getError(), "src rbo");
3230 
3231 		m_srcFbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext()));
3232 		gl.bindFramebuffer(GL_FRAMEBUFFER, **m_srcFbo);
3233 		gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **m_srcRbo);
3234 		GLU_EXPECT_NO_ERROR(gl.getError(), "src fbo");
3235 	}
3236 
3237 	if (m_dst == TARGET_FBO)
3238 	{
3239 		m_dstRbo = de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(m_context.getRenderContext()));
3240 		gl.bindRenderbuffer(GL_RENDERBUFFER, **m_dstRbo);
3241 		gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, FBO_SIZE, FBO_SIZE);
3242 		GLU_EXPECT_NO_ERROR(gl.getError(), "dst rbo");
3243 
3244 		m_dstFbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext()));
3245 		gl.bindFramebuffer(GL_FRAMEBUFFER, **m_dstFbo);
3246 		gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **m_dstRbo);
3247 		GLU_EXPECT_NO_ERROR(gl.getError(), "dst fbo");
3248 	}
3249 
3250 	{
3251 		static const char* const s_vertexSource =	"#version 310 es\n"
3252 													"in highp vec4 a_position;\n"
3253 													"out highp vec4 v_position;\n"
3254 													"void main()\n"
3255 													"{\n"
3256 													"	gl_Position = a_position;\n"
3257 													"	v_position = a_position;\n"
3258 													"}\n";
3259 		static const char* const s_fragmentSource =	"#version 310 es\n"
3260 													"in mediump vec4 v_position;\n"
3261 													"layout(location=0) out mediump vec4 dEQP_FragColor;\n"
3262 													"void main()\n"
3263 													"{\n"
3264 													"	const mediump vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
3265 													"	const mediump vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
3266 													"	dEQP_FragColor = (step(0.1, mod(v_position.x, 0.2)) == step(0.1, mod(v_position.y, 0.2))) ? (green) : (yellow);\n"
3267 													"}\n";
3268 
3269 		m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(s_vertexSource) << glu::FragmentSource(s_fragmentSource)));
3270 
3271 		if (!m_program->isOk())
3272 		{
3273 			m_testCtx.getLog() << *m_program;
3274 			throw tcu::TestError("failed to build program");
3275 		}
3276 	}
3277 
3278 	{
3279 		static const tcu::Vec4 s_quadCoords[] =
3280 		{
3281 			tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f),
3282 			tcu::Vec4(-1.0f,  1.0f, 0.0f, 1.0f),
3283 			tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f),
3284 			tcu::Vec4( 1.0f,  1.0f, 0.0f, 1.0f),
3285 		};
3286 
3287 		m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
3288 
3289 		gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
3290 		gl.bufferData(GL_ARRAY_BUFFER, sizeof(s_quadCoords), s_quadCoords, GL_STATIC_DRAW);
3291 		GLU_EXPECT_NO_ERROR(gl.getError(), "set buf");
3292 	}
3293 
3294 	// gen iterations
3295 
3296 	{
3297 		const tcu::IVec2 srcSize = (m_src == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3298 		const tcu::IVec2 dstSize = (m_dst == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3299 
3300 		m_testCtx.getLog()
3301 			<< tcu::TestLog::Message
3302 			<< "srcSize = " << srcSize << "\n"
3303 			<< "dstSize = " << dstSize << "\n"
3304 			<< tcu::TestLog::EndMessage;
3305 
3306 		for (int ndx = 0; ndx < numIterations; ++ndx)
3307 		{
3308 			BlitArgs args;
3309 
3310 			if (m_src == TARGET_DEFAULT && defaultFBMultisampled)
3311 			{
3312 				const tcu::IVec2	unionSize	= tcu::IVec2(de::min(srcSize.x(), dstSize.x()), de::min(srcSize.y(), dstSize.y()));
3313 				const int			srcWidth	= rnd.getInt(1, unionSize.x());
3314 				const int			srcHeight	= rnd.getInt(1, unionSize.y());
3315 				const int			srcX		= rnd.getInt(0, unionSize.x() - srcWidth);
3316 				const int			srcY		= rnd.getInt(0, unionSize.y() - srcHeight);
3317 
3318 				args.src.x() = srcX;
3319 				args.src.y() = srcY;
3320 				args.src.z() = srcX + srcWidth;
3321 				args.src.w() = srcY + srcHeight;
3322 
3323 				args.dst = args.src;
3324 			}
3325 			else
3326 			{
3327 				const int	srcWidth	= rnd.getInt(1, srcSize.x());
3328 				const int	srcHeight	= rnd.getInt(1, srcSize.y());
3329 				const int	srcX		= rnd.getInt(0, srcSize.x() - srcWidth);
3330 				const int	srcY		= rnd.getInt(0, srcSize.y() - srcHeight);
3331 				const int	dstWidth	= rnd.getInt(1, dstSize.x());
3332 				const int	dstHeight	= rnd.getInt(1, dstSize.y());
3333 				const int	dstX		= rnd.getInt(-(dstWidth / 2), dstSize.x() - (dstWidth+1) / 2);		// allow dst go out of bounds
3334 				const int	dstY		= rnd.getInt(-(dstHeight / 2), dstSize.y() - (dstHeight+1)  / 2);
3335 
3336 				args.src.x() = srcX;
3337 				args.src.y() = srcY;
3338 				args.src.z() = srcX + srcWidth;
3339 				args.src.w() = srcY + srcHeight;
3340 				args.dst.x() = dstX;
3341 				args.dst.y() = dstY;
3342 				args.dst.z() = dstX + dstWidth;
3343 				args.dst.w() = dstY + dstHeight;
3344 			}
3345 
3346 			args.bboxMin.x() = rnd.getFloat(-1.1f, 1.1f);
3347 			args.bboxMin.y() = rnd.getFloat(-1.1f, 1.1f);
3348 			args.bboxMin.z() = rnd.getFloat(-1.1f, 1.1f);
3349 			args.bboxMin.w() = rnd.getFloat( 0.9f, 1.1f);
3350 
3351 			args.bboxMax.x() = rnd.getFloat(-1.1f, 1.1f);
3352 			args.bboxMax.y() = rnd.getFloat(-1.1f, 1.1f);
3353 			args.bboxMax.z() = rnd.getFloat(-1.1f, 1.1f);
3354 			args.bboxMax.w() = rnd.getFloat( 0.9f, 1.1f);
3355 
3356 			if (args.bboxMin.x() / args.bboxMin.w() > args.bboxMax.x() / args.bboxMax.w())
3357 				std::swap(args.bboxMin.x(), args.bboxMax.x());
3358 			if (args.bboxMin.y() / args.bboxMin.w() > args.bboxMax.y() / args.bboxMax.w())
3359 				std::swap(args.bboxMin.y(), args.bboxMax.y());
3360 			if (args.bboxMin.z() / args.bboxMin.w() > args.bboxMax.z() / args.bboxMax.w())
3361 				std::swap(args.bboxMin.z(), args.bboxMax.z());
3362 
3363 			args.linear = rnd.getBool();
3364 
3365 			m_iterations.push_back(args);
3366 		}
3367 	}
3368 }
3369 
deinit(void)3370 void BlitFboCase::deinit (void)
3371 {
3372 	m_srcFbo.clear();
3373 	m_srcRbo.clear();
3374 	m_dstFbo.clear();
3375 	m_dstRbo.clear();
3376 	m_program.clear();
3377 	m_vbo.clear();
3378 }
3379 
iterate(void)3380 BlitFboCase::IterateResult BlitFboCase::iterate (void)
3381 {
3382 	const tcu::ScopedLogSection	section		(m_testCtx.getLog(), "Iteration" + de::toString(m_iteration), "Iteration " + de::toString(m_iteration+1) + " / " + de::toString((int)m_iterations.size()));
3383 	const BlitArgs&				blitCfg		= m_iterations[m_iteration];
3384 	const glw::Functions&		gl			= m_context.getRenderContext().getFunctions();
3385 
3386 	if (m_iteration == 0)
3387 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3388 
3389 	// fill source with test pattern. Default fb must be filled for each iteration because contents might not survive the swap
3390 	if (m_src == TARGET_DEFAULT || m_iteration == 0)
3391 		fillSourceWithPattern();
3392 
3393 	m_testCtx.getLog()
3394 		<< tcu::TestLog::Message
3395 		<< "Set bounding box:\n"
3396 		<< "\tmin:" << blitCfg.bboxMin << "\n"
3397 		<< "\tmax:" << blitCfg.bboxMax << "\n"
3398 		<< "Blit:\n"
3399 		<<	"\tsrc: " << blitCfg.src << "\n"
3400 		<<	"\tdst: " << blitCfg.dst << "\n"
3401 		<<	"\tfilter: " << ((blitCfg.linear) ? ("linear") : ("nearest"))
3402 		<< tcu::TestLog::EndMessage;
3403 
3404 	gl.primitiveBoundingBox(blitCfg.bboxMin.x(), blitCfg.bboxMin.y(), blitCfg.bboxMin.z(), blitCfg.bboxMin.w(),
3405 							blitCfg.bboxMax.x(), blitCfg.bboxMax.y(), blitCfg.bboxMax.z(), blitCfg.bboxMax.w());
3406 
3407 	gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, (m_dst == TARGET_FBO) ? (**m_dstFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3408 	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3409 	gl.clear(GL_COLOR_BUFFER_BIT);
3410 
3411 	gl.bindFramebuffer(GL_READ_FRAMEBUFFER, (m_src == TARGET_FBO) ? (**m_srcFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3412 	gl.blitFramebuffer(blitCfg.src.x(), blitCfg.src.y(), blitCfg.src.z(), blitCfg.src.w(),
3413 					   blitCfg.dst.x(), blitCfg.dst.y(), blitCfg.dst.z(), blitCfg.dst.w(),
3414 					   GL_COLOR_BUFFER_BIT,
3415 					   ((blitCfg.linear) ? (GL_LINEAR) : (GL_NEAREST)));
3416 	GLU_EXPECT_NO_ERROR(gl.getError(), "blit");
3417 
3418 	if (!verifyImage(blitCfg))
3419 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected blit result");
3420 
3421 	return (++m_iteration == (int)m_iterations.size()) ? (STOP) : (CONTINUE);
3422 }
3423 
verifyImage(const BlitArgs & args)3424 bool BlitFboCase::verifyImage (const BlitArgs& args)
3425 {
3426 	const int				colorThreshold	= 4; //!< this test case is not about how color is preserved, allow almost anything
3427 	const tcu::IVec2		dstSize			= (m_dst == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3428 	const glw::Functions&	gl				= m_context.getRenderContext().getFunctions();
3429 	tcu::Surface			viewport		(dstSize.x(), dstSize.y());
3430 	tcu::Surface			errorMask		(dstSize.x(), dstSize.y());
3431 	bool					anyError		= false;
3432 
3433 	m_testCtx.getLog()
3434 		<< tcu::TestLog::Message
3435 		<< "Verifying blit result"
3436 		<< tcu::TestLog::EndMessage;
3437 
3438 	gl.bindFramebuffer(GL_READ_FRAMEBUFFER, (m_dst == TARGET_FBO) ? (**m_dstFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3439 	glu::readPixels(m_context.getRenderContext(), 0, 0, viewport.getAccess());
3440 
3441 	tcu::clear(errorMask.getAccess(), tcu::IVec4(0, 0, 0, 255));
3442 
3443 	for (int y = 0; y < dstSize.y(); ++y)
3444 	for (int x = 0; x < dstSize.x(); ++x)
3445 	{
3446 		const tcu::RGBA color	= viewport.getPixel(x, y);
3447 		const bool		inside	= (x >= args.dst.x() && x < args.dst.z() && y >= args.dst.y() && y < args.dst.w());
3448 		const bool		error	= (inside) ? (color.getGreen() < 255 - colorThreshold || color.getBlue() > colorThreshold)
3449 										   : (color.getRed() > colorThreshold || color.getGreen() > colorThreshold || color.getBlue() > colorThreshold);
3450 
3451 		if (error)
3452 		{
3453 			anyError = true;
3454 			errorMask.setPixel(x, y, tcu::RGBA::red());
3455 		}
3456 	}
3457 
3458 	if (anyError)
3459 	{
3460 		m_testCtx.getLog()
3461 			<< tcu::TestLog::Message
3462 			<< "Image verification failed."
3463 			<< tcu::TestLog::EndMessage
3464 			<< tcu::TestLog::ImageSet("Images", "Image verification")
3465 			<< tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3466 			<< tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
3467 			<< tcu::TestLog::EndImageSet;
3468 		return false;
3469 	}
3470 	else
3471 	{
3472 		m_testCtx.getLog()
3473 			<< tcu::TestLog::Message
3474 			<< "Result image ok."
3475 			<< tcu::TestLog::EndMessage
3476 			<< tcu::TestLog::ImageSet("Images", "Image verification")
3477 			<< tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3478 			<< tcu::TestLog::EndImageSet;
3479 		return true;
3480 	}
3481 }
3482 
fillSourceWithPattern(void)3483 void BlitFboCase::fillSourceWithPattern (void)
3484 {
3485 	const glw::Functions&	gl			= m_context.getRenderContext().getFunctions();
3486 	const tcu::IVec2		srcSize		= (m_src == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3487 	const int				posLocation	= gl.getAttribLocation(m_program->getProgram(), "a_position");
3488 
3489 	gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, (m_src == TARGET_FBO) ? (**m_srcFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3490 	gl.viewport(0, 0, srcSize.x(), srcSize.y());
3491 	gl.useProgram(m_program->getProgram());
3492 
3493 	gl.clearColor(0.0f, 0.0f, 1.0f, 1.0f);
3494 	gl.clear(GL_COLOR_BUFFER_BIT);
3495 
3496 	gl.enableVertexAttribArray(posLocation);
3497 	gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 4 * (int)sizeof(float), NULL);
3498 	gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
3499 	GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
3500 }
3501 
3502 class DepthDrawCase : public TestCase
3503 {
3504 public:
3505 	enum DepthType
3506 	{
3507 		DEPTH_BUILTIN = 0,
3508 		DEPTH_USER_DEFINED,
3509 
3510 		DEPTH_LAST
3511 	};
3512 	enum BBoxState
3513 	{
3514 		STATE_GLOBAL = 0,
3515 		STATE_PER_PRIMITIVE,
3516 
3517 		STATE_LAST
3518 	};
3519 	enum BBoxSize
3520 	{
3521 		BBOX_EQUAL = 0,
3522 		BBOX_LARGER,
3523 
3524 		BBOX_LAST
3525 	};
3526 
3527 									DepthDrawCase					(Context& context, const char* name, const char* description, DepthType depthType, BBoxState state, BBoxSize bboxSize);
3528 									~DepthDrawCase					(void);
3529 
3530 private:
3531 	void							init							(void);
3532 	void							deinit							(void);
3533 	IterateResult					iterate							(void);
3534 
3535 	std::string						genVertexSource					(void) const;
3536 	std::string						genFragmentSource				(void) const;
3537 	std::string						genTessellationControlSource	(void) const;
3538 	std::string						genTessellationEvaluationSource	(void) const;
3539 	void							generateAttributeData			(std::vector<tcu::Vec4>& data) const;
3540 	bool							verifyImage						(const tcu::Surface& viewport) const;
3541 
3542 	enum
3543 	{
3544 		RENDER_AREA_SIZE = 256,
3545 	};
3546 
3547 	struct LayerInfo
3548 	{
3549 		float		zOffset;
3550 		float		zScale;
3551 		tcu::Vec4	color1;
3552 		tcu::Vec4	color2;
3553 	};
3554 
3555 	const int						m_numLayers;
3556 	const int						m_gridSize;
3557 
3558 	const DepthType					m_depthType;
3559 	const BBoxState					m_state;
3560 	const BBoxSize					m_bboxSize;
3561 
3562 	de::MovePtr<glu::ShaderProgram>	m_program;
3563 	de::MovePtr<glu::Buffer>		m_vbo;
3564 	std::vector<LayerInfo>			m_layers;
3565 };
3566 
DepthDrawCase(Context & context,const char * name,const char * description,DepthType depthType,BBoxState state,BBoxSize bboxSize)3567 DepthDrawCase::DepthDrawCase (Context& context, const char* name, const char* description, DepthType depthType, BBoxState state, BBoxSize bboxSize)
3568 	: TestCase		(context, name, description)
3569 	, m_numLayers	(14)
3570 	, m_gridSize	(24)
3571 	, m_depthType	(depthType)
3572 	, m_state		(state)
3573 	, m_bboxSize	(bboxSize)
3574 {
3575 	DE_ASSERT(depthType < DEPTH_LAST);
3576 	DE_ASSERT(state < STATE_LAST);
3577 	DE_ASSERT(bboxSize < BBOX_LAST);
3578 }
3579 
~DepthDrawCase(void)3580 DepthDrawCase::~DepthDrawCase (void)
3581 {
3582 	deinit();
3583 }
3584 
init(void)3585 void DepthDrawCase::init (void)
3586 {
3587 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
3588 
3589 	// requirements
3590 
3591 	if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
3592 		throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
3593 	if (m_state == STATE_PER_PRIMITIVE && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
3594 		throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
3595 	if (m_context.getRenderTarget().getDepthBits() == 0)
3596 		throw tcu::NotSupportedError("Test requires depth buffer");
3597 	if (m_context.getRenderTarget().getWidth() < RENDER_AREA_SIZE || m_context.getRenderTarget().getHeight() < RENDER_AREA_SIZE)
3598 		throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_AREA_SIZE) + "x" + de::toString<int>(RENDER_AREA_SIZE) + " viewport");
3599 
3600 	// log
3601 	m_testCtx.getLog()
3602 		<< tcu::TestLog::Message
3603 		<< "Rendering multiple triangle grids with with different z coordinates.\n"
3604 		<< "Topmost grid is green-yellow, other grids are blue-red.\n"
3605 		<< "Expecting only the green-yellow grid to be visible.\n"
3606 		<< "Setting primitive bounding box "
3607 			<< ((m_bboxSize == BBOX_EQUAL) ? ("to exactly cover") : ("to cover"))
3608 			<< ((m_state == STATE_GLOBAL) ? (" each grid") : (" each triangle"))
3609 			<< ((m_bboxSize == BBOX_EQUAL) ? (".") : (" and include some padding."))
3610 			<< "\n"
3611 		<< "Set bounding box using "
3612 			<< ((m_state == STATE_GLOBAL) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output"))
3613 			<< "\n"
3614 		<< ((m_depthType == DEPTH_USER_DEFINED) ? ("Fragment depth is set in the fragment shader") : (""))
3615 		<< tcu::TestLog::EndMessage;
3616 
3617 	// resources
3618 
3619 	{
3620 		glu::ProgramSources sources;
3621 		sources << glu::VertexSource(genVertexSource());
3622 		sources << glu::FragmentSource(genFragmentSource());
3623 
3624 		if (m_state == STATE_PER_PRIMITIVE)
3625 			sources << glu::TessellationControlSource(genTessellationControlSource())
3626 					<< glu::TessellationEvaluationSource(genTessellationEvaluationSource());
3627 
3628 		m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), sources));
3629 		GLU_EXPECT_NO_ERROR(gl.getError(), "build program");
3630 
3631 		{
3632 			const tcu::ScopedLogSection section(m_testCtx.getLog(), "ShaderProgram", "Shader program");
3633 			m_testCtx.getLog() << *m_program;
3634 		}
3635 
3636 		if (!m_program->isOk())
3637 			throw tcu::TestError("failed to build program");
3638 	}
3639 
3640 	{
3641 		std::vector<tcu::Vec4> data;
3642 
3643 		generateAttributeData(data);
3644 
3645 		m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
3646 		gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
3647 		gl.bufferData(GL_ARRAY_BUFFER, (int)(sizeof(tcu::Vec4) * data.size()), &data[0], GL_STATIC_DRAW);
3648 		GLU_EXPECT_NO_ERROR(gl.getError(), "buf upload");
3649 	}
3650 
3651 	// gen layers
3652 	{
3653 		de::Random rnd(0x12345);
3654 
3655 		m_layers.resize(m_numLayers);
3656 		for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx)
3657 		{
3658 			m_layers[layerNdx].zOffset	= ((float)layerNdx / (float)m_numLayers) * 2.0f - 1.0f;
3659 			m_layers[layerNdx].zScale	= (2.0f / (float)m_numLayers);
3660 			m_layers[layerNdx].color1	= (layerNdx == 0) ? (tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f)) : (tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
3661 			m_layers[layerNdx].color2	= (layerNdx == 0) ? (tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f)) : (tcu::Vec4(1.0f, 0.0f, 1.0f, 1.0f));
3662 		}
3663 		rnd.shuffle(m_layers.begin(), m_layers.end());
3664 	}
3665 }
3666 
deinit(void)3667 void DepthDrawCase::deinit (void)
3668 {
3669 	m_program.clear();
3670 	m_vbo.clear();
3671 }
3672 
iterate(void)3673 DepthDrawCase::IterateResult DepthDrawCase::iterate (void)
3674 {
3675 	const bool				hasTessellation		= (m_state == STATE_PER_PRIMITIVE);
3676 	const glw::Functions&	gl					= m_context.getRenderContext().getFunctions();
3677 	const glw::GLint		posLocation			= gl.getAttribLocation(m_program->getProgram(), "a_position");
3678 	const glw::GLint		colLocation			= gl.getAttribLocation(m_program->getProgram(), "a_colorMix");
3679 	const glw::GLint		depthBiasLocation	= gl.getUniformLocation(m_program->getProgram(), "u_depthBias");
3680 	const glw::GLint		depthScaleLocation	= gl.getUniformLocation(m_program->getProgram(), "u_depthScale");
3681 	const glw::GLint		color1Location		= gl.getUniformLocation(m_program->getProgram(), "u_color1");
3682 	const glw::GLint		color2Location		= gl.getUniformLocation(m_program->getProgram(), "u_color2");
3683 
3684 	tcu::Surface			viewport			(RENDER_AREA_SIZE, RENDER_AREA_SIZE);
3685 	de::Random				rnd					(0x213237);
3686 
3687 	TCU_CHECK(posLocation != -1);
3688 	TCU_CHECK(colLocation != -1);
3689 	TCU_CHECK(depthBiasLocation != -1);
3690 	TCU_CHECK(depthScaleLocation != -1);
3691 	TCU_CHECK(color1Location != -1);
3692 	TCU_CHECK(color2Location != -1);
3693 
3694 	gl.viewport(0, 0, RENDER_AREA_SIZE, RENDER_AREA_SIZE);
3695 	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3696 	gl.clearDepthf(1.0f);
3697 	gl.depthFunc(GL_LESS);
3698 	gl.enable(GL_DEPTH_TEST);
3699 	gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
3700 	GLU_EXPECT_NO_ERROR(gl.getError(), "setup viewport");
3701 
3702 	gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
3703 	gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, (int)(8 * sizeof(float)), (const float*)DE_NULL);
3704 	gl.vertexAttribPointer(colLocation, 4, GL_FLOAT, GL_FALSE, (int)(8 * sizeof(float)), (const float*)DE_NULL + 4);
3705 	gl.enableVertexAttribArray(posLocation);
3706 	gl.enableVertexAttribArray(colLocation);
3707 	gl.useProgram(m_program->getProgram());
3708 	GLU_EXPECT_NO_ERROR(gl.getError(), "setup va");
3709 
3710 	if (hasTessellation)
3711 		gl.patchParameteri(GL_PATCH_VERTICES, 3);
3712 
3713 	for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx)
3714 	{
3715 		gl.uniform1f(depthBiasLocation, m_layers[layerNdx].zOffset);
3716 		gl.uniform1f(depthScaleLocation, m_layers[layerNdx].zScale);
3717 		gl.uniform4fv(color1Location, 1, m_layers[layerNdx].color1.getPtr());
3718 		gl.uniform4fv(color2Location, 1, m_layers[layerNdx].color2.getPtr());
3719 
3720 		if (m_state == STATE_GLOBAL)
3721 		{
3722 			const float negPadding = (m_bboxSize == BBOX_EQUAL) ? (0.0f) : (rnd.getFloat() * 0.3f);
3723 			const float posPadding = (m_bboxSize == BBOX_EQUAL) ? (0.0f) : (rnd.getFloat() * 0.3f);
3724 
3725 			gl.primitiveBoundingBox(-1.0f, -1.0f, m_layers[layerNdx].zOffset - negPadding, 1.0f,
3726 									1.0f,  1.0f, (m_layers[layerNdx].zOffset + m_layers[layerNdx].zScale + posPadding), 1.0f);
3727 		}
3728 
3729 		gl.drawArrays((hasTessellation) ? (GL_PATCHES) : (GL_TRIANGLES), 0, m_gridSize * m_gridSize * 6);
3730 	}
3731 
3732 	glu::readPixels(m_context.getRenderContext(), 0, 0, viewport.getAccess());
3733 	GLU_EXPECT_NO_ERROR(gl.getError(), "render and read");
3734 
3735 	if (verifyImage(viewport))
3736 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3737 	else
3738 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
3739 
3740 	return STOP;
3741 }
3742 
genVertexSource(void) const3743 std::string DepthDrawCase::genVertexSource (void) const
3744 {
3745 	const bool			hasTessellation	= (m_state == STATE_PER_PRIMITIVE);
3746 	std::ostringstream	buf;
3747 
3748 	buf <<	"#version 310 es\n"
3749 			"in highp vec4 a_position;\n"
3750 			"in highp vec4 a_colorMix;\n"
3751 			"out highp vec4 vtx_colorMix;\n";
3752 
3753 	if (!hasTessellation && m_depthType == DEPTH_USER_DEFINED)
3754 		buf << "out highp float v_fragDepth;\n";
3755 
3756 	if (!hasTessellation)
3757 		buf <<	"uniform highp float u_depthBias;\n"
3758 				"uniform highp float u_depthScale;\n";
3759 
3760 	buf <<	"\n"
3761 			"void main()\n"
3762 			"{\n";
3763 
3764 	if (hasTessellation)
3765 		buf << "	gl_Position = a_position;\n";
3766 	else if (m_depthType == DEPTH_USER_DEFINED)
3767 		buf <<	"	highp float dummyZ = a_position.z;\n"
3768 				"	highp float writtenZ = a_position.w;\n"
3769 				"	gl_Position = vec4(a_position.xy, dummyZ, 1.0);\n"
3770 				"	v_fragDepth = writtenZ * u_depthScale + u_depthBias;\n";
3771 	else
3772 		buf <<	"	highp float writtenZ = a_position.w;\n"
3773 				"	gl_Position = vec4(a_position.xy, writtenZ * u_depthScale + u_depthBias, 1.0);\n";
3774 
3775 	buf <<	"	vtx_colorMix = a_colorMix;\n"
3776 			"}\n";
3777 
3778 	return buf.str();
3779 }
3780 
genFragmentSource(void) const3781 std::string DepthDrawCase::genFragmentSource (void) const
3782 {
3783 	const bool			hasTessellation	= (m_state == STATE_PER_PRIMITIVE);
3784 	const char* const	colorMixName	= (hasTessellation) ? ("tess_eval_colorMix") : ("vtx_colorMix");
3785 	std::ostringstream	buf;
3786 
3787 	buf <<	"#version 310 es\n"
3788 			"in mediump vec4 " << colorMixName << ";\n";
3789 
3790 	if (m_depthType == DEPTH_USER_DEFINED)
3791 		buf << "in mediump float v_fragDepth;\n";
3792 
3793 	buf <<	"layout(location = 0) out mediump vec4 o_color;\n"
3794 			"uniform highp vec4 u_color1;\n"
3795 			"uniform highp vec4 u_color2;\n"
3796 			"\n"
3797 			"void main()\n"
3798 			"{\n"
3799 			"	o_color = mix(u_color1, u_color2, " << colorMixName << ");\n";
3800 
3801 	if (m_depthType == DEPTH_USER_DEFINED)
3802 		buf << "	gl_FragDepth = v_fragDepth * 0.5 + 0.5;\n";
3803 
3804 	buf <<	"}\n";
3805 
3806 	return buf.str();
3807 }
3808 
genTessellationControlSource(void) const3809 std::string DepthDrawCase::genTessellationControlSource (void) const
3810 {
3811 	std::ostringstream	buf;
3812 
3813 	buf <<	"#version 310 es\n"
3814 			"#extension GL_EXT_tessellation_shader : require\n"
3815 			"#extension GL_EXT_primitive_bounding_box : require\n"
3816 			"layout(vertices=3) out;\n"
3817 			"\n"
3818 			"uniform highp float u_depthBias;\n"
3819 			"uniform highp float u_depthScale;\n"
3820 			"\n"
3821 			"in highp vec4 vtx_colorMix[];\n"
3822 			"out highp vec4 tess_ctrl_colorMix[];\n"
3823 			"\n"
3824 			"void main()\n"
3825 			"{\n"
3826 			"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
3827 			"	tess_ctrl_colorMix[gl_InvocationID] = vtx_colorMix[0];\n"
3828 			"\n"
3829 			"	gl_TessLevelOuter[0] = 2.8;\n"
3830 			"	gl_TessLevelOuter[1] = 2.8;\n"
3831 			"	gl_TessLevelOuter[2] = 2.8;\n"
3832 			"	gl_TessLevelInner[0] = 2.8;\n"
3833 			"\n"
3834 			"	// real Z stored in w component\n"
3835 			"	highp vec4 minBound = vec4(min(min(vec3(gl_in[0].gl_Position.xy, gl_in[0].gl_Position.w * u_depthScale + u_depthBias),\n"
3836 			"	                                   vec3(gl_in[1].gl_Position.xy, gl_in[1].gl_Position.w * u_depthScale + u_depthBias)),\n"
3837 			"	                               vec3(gl_in[2].gl_Position.xy, gl_in[2].gl_Position.w * u_depthScale + u_depthBias)), 1.0);\n"
3838 			"	highp vec4 maxBound = vec4(max(max(vec3(gl_in[0].gl_Position.xy, gl_in[0].gl_Position.w * u_depthScale + u_depthBias),\n"
3839 			"	                                   vec3(gl_in[1].gl_Position.xy, gl_in[1].gl_Position.w * u_depthScale + u_depthBias)),\n"
3840 			"	                               vec3(gl_in[2].gl_Position.xy, gl_in[2].gl_Position.w * u_depthScale + u_depthBias)), 1.0);\n";
3841 
3842 	if (m_bboxSize == BBOX_EQUAL)
3843 		buf <<	"	gl_BoundingBoxEXT[0] = minBound;\n"
3844 				"	gl_BoundingBoxEXT[1] = maxBound;\n";
3845 	else
3846 		buf <<	"	highp float nedPadding = mod(gl_in[0].gl_Position.z, 0.3);\n"
3847 				"	highp float posPadding = mod(gl_in[1].gl_Position.z, 0.3);\n"
3848 				"	gl_BoundingBoxEXT[0] = minBound - vec4(0.0, 0.0, nedPadding, 0.0);\n"
3849 				"	gl_BoundingBoxEXT[1] = maxBound + vec4(0.0, 0.0, posPadding, 0.0);\n";
3850 
3851 	buf <<	"}\n";
3852 
3853 	return buf.str();
3854 }
3855 
genTessellationEvaluationSource(void) const3856 std::string DepthDrawCase::genTessellationEvaluationSource (void) const
3857 {
3858 	std::ostringstream	buf;
3859 
3860 	buf <<	"#version 310 es\n"
3861 			"#extension GL_EXT_tessellation_shader : require\n"
3862 			"#extension GL_EXT_gpu_shader5 : require\n"
3863 			"layout(triangles) in;\n"
3864 			"\n"
3865 			"in highp vec4 tess_ctrl_colorMix[];\n"
3866 			"out highp vec4 tess_eval_colorMix;\n";
3867 
3868 	if (m_depthType == DEPTH_USER_DEFINED)
3869 		buf << "out highp float v_fragDepth;\n";
3870 
3871 	buf <<	"uniform highp float u_depthBias;\n"
3872 			"uniform highp float u_depthScale;\n"
3873 			"\n"
3874 			"precise gl_Position;\n"
3875 			"\n"
3876 			"void main()\n"
3877 			"{\n"
3878 			"	highp vec4 tessellatedPos = gl_TessCoord.x * gl_in[0].gl_Position + gl_TessCoord.y * gl_in[1].gl_Position + gl_TessCoord.z * gl_in[2].gl_Position;\n";
3879 
3880 	if (m_depthType == DEPTH_USER_DEFINED)
3881 		buf <<	"	highp float dummyZ = tessellatedPos.z;\n"
3882 				"	highp float writtenZ = tessellatedPos.w;\n"
3883 				"	gl_Position = vec4(tessellatedPos.xy, dummyZ, 1.0);\n"
3884 				"	v_fragDepth = writtenZ * u_depthScale + u_depthBias;\n";
3885 	else
3886 		buf <<	"	highp float writtenZ = tessellatedPos.w;\n"
3887 				"	gl_Position = vec4(tessellatedPos.xy, writtenZ * u_depthScale + u_depthBias, 1.0);\n";
3888 
3889 	buf <<	"	tess_eval_colorMix = tess_ctrl_colorMix[0];\n"
3890 			"}\n";
3891 
3892 	return buf.str();
3893 }
3894 
generateAttributeData(std::vector<tcu::Vec4> & data) const3895 void DepthDrawCase::generateAttributeData (std::vector<tcu::Vec4>& data) const
3896 {
3897 	const tcu::Vec4		color1				(0.0f, 0.0f, 0.0f, 0.0f); // mix weights
3898 	const tcu::Vec4		color2				(1.0f, 1.0f, 1.0f, 1.0f);
3899 	std::vector<int>	cellOrder			(m_gridSize * m_gridSize);
3900 	de::Random			rnd					(0xAB54321);
3901 
3902 	// generate grid with cells in random order
3903 	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
3904 		cellOrder[ndx] = ndx;
3905 	rnd.shuffle(cellOrder.begin(), cellOrder.end());
3906 
3907 	data.resize(m_gridSize * m_gridSize * 6 * 2);
3908 	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
3909 	{
3910 		const int			cellNdx		= cellOrder[ndx];
3911 		const int			cellX		= cellNdx % m_gridSize;
3912 		const int			cellY		= cellNdx / m_gridSize;
3913 		const tcu::Vec4&	cellColor	= ((cellX+cellY)%2 == 0) ? (color1) : (color2);
3914 
3915 		data[ndx * 6 * 2 +  0] = tcu::Vec4(float(cellX+0) / float(m_gridSize) * 2.0f - 1.0f, float(cellY+0) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f);	data[ndx * 6 * 2 +  1] = cellColor;
3916 		data[ndx * 6 * 2 +  2] = tcu::Vec4(float(cellX+1) / float(m_gridSize) * 2.0f - 1.0f, float(cellY+1) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f);	data[ndx * 6 * 2 +  3] = cellColor;
3917 		data[ndx * 6 * 2 +  4] = tcu::Vec4(float(cellX+0) / float(m_gridSize) * 2.0f - 1.0f, float(cellY+1) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f);	data[ndx * 6 * 2 +  5] = cellColor;
3918 		data[ndx * 6 * 2 +  6] = tcu::Vec4(float(cellX+0) / float(m_gridSize) * 2.0f - 1.0f, float(cellY+0) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f);	data[ndx * 6 * 2 +  7] = cellColor;
3919 		data[ndx * 6 * 2 +  8] = tcu::Vec4(float(cellX+1) / float(m_gridSize) * 2.0f - 1.0f, float(cellY+0) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f);	data[ndx * 6 * 2 +  9] = cellColor;
3920 		data[ndx * 6 * 2 + 10] = tcu::Vec4(float(cellX+1) / float(m_gridSize) * 2.0f - 1.0f, float(cellY+1) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f);	data[ndx * 6 * 2 + 11] = cellColor;
3921 
3922 		// Fill Z with random values (fake Z)
3923 		for (int vtxNdx = 0; vtxNdx < 6; ++vtxNdx)
3924 			data[ndx * 6 * 2 + 2*vtxNdx].z() = rnd.getFloat(0.0f, 1.0);
3925 
3926 		// Fill W with other random values (written Z)
3927 		for (int vtxNdx = 0; vtxNdx < 6; ++vtxNdx)
3928 			data[ndx * 6 * 2 + 2*vtxNdx].w() = rnd.getFloat(0.0f, 1.0);
3929 	}
3930 }
3931 
verifyImage(const tcu::Surface & viewport) const3932 bool DepthDrawCase::verifyImage (const tcu::Surface& viewport) const
3933 {
3934 	tcu::Surface	errorMask	(viewport.getWidth(), viewport.getHeight());
3935 	bool			anyError	= false;
3936 
3937 	tcu::clear(errorMask.getAccess(), tcu::IVec4(0,0,0,255));
3938 
3939 	for (int y = 0; y < viewport.getHeight(); ++y)
3940 	for (int x = 0; x < viewport.getWidth(); ++x)
3941 	{
3942 		const tcu::RGBA	pixel		= viewport.getPixel(x, y);
3943 		bool			error		= false;
3944 
3945 		// expect green, yellow or a combination of these
3946 		if (pixel.getGreen() != 255 || pixel.getBlue() != 0)
3947 			error = true;
3948 
3949 		if (error)
3950 		{
3951 			errorMask.setPixel(x, y, tcu::RGBA::red());
3952 			anyError = true;
3953 		}
3954 	}
3955 
3956 	if (anyError)
3957 		m_testCtx.getLog()
3958 			<< tcu::TestLog::Message
3959 			<< "Image verification failed."
3960 			<< tcu::TestLog::EndMessage
3961 			<< tcu::TestLog::ImageSet("Images", "Image verification")
3962 			<< tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3963 			<< tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
3964 			<< tcu::TestLog::EndImageSet;
3965 	else
3966 		m_testCtx.getLog()
3967 			<< tcu::TestLog::Message
3968 			<< "Result image ok."
3969 			<< tcu::TestLog::EndMessage
3970 			<< tcu::TestLog::ImageSet("Images", "Image verification")
3971 			<< tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3972 			<< tcu::TestLog::EndImageSet;
3973 
3974 	return !anyError;
3975 }
3976 
3977 class ClearCase : public TestCase
3978 {
3979 public:
3980 	enum
3981 	{
3982 		SCISSOR_CLEAR_BIT		= 1 << 0,
3983 		DRAW_TRIANGLE_BIT		= 1 << 1,
3984 		PER_PRIMITIVE_BBOX_BIT	= 1 << 2,
3985 		FULLSCREEN_SCISSOR_BIT	= 1 << 3,
3986 	};
3987 
3988 									ClearCase						(Context& context, const char* name, const char* description, deUint32 flags);
3989 									~ClearCase						(void);
3990 
3991 private:
3992 	struct DrawObject
3993 	{
3994 		int firstNdx;
3995 		int numVertices;
3996 	};
3997 
3998 	void							init							(void);
3999 	void							deinit							(void);
4000 	IterateResult					iterate							(void);
4001 
4002 	void							createVbo						(void);
4003 	void							createProgram					(void);
4004 	void							renderTo						(tcu::Surface& dst, bool useBBox);
4005 	bool							verifyImagesEqual				(const tcu::PixelBufferAccess& withoutBBox, const tcu::PixelBufferAccess& withBBox);
4006 	bool							verifyImageResultValid			(const tcu::PixelBufferAccess& result);
4007 
4008 	std::string						genVertexSource					(void) const;
4009 	std::string						genFragmentSource				(void) const;
4010 	std::string						genTessellationControlSource	(bool setBBox) const;
4011 	std::string						genTessellationEvaluationSource	(void) const;
4012 
4013 	const bool						m_scissoredClear;
4014 	const bool						m_fullscreenScissor;
4015 	const bool						m_drawTriangles;
4016 	const bool						m_useGlobalState;
4017 
4018 	de::MovePtr<glu::Buffer>		m_vbo;
4019 	de::MovePtr<glu::ShaderProgram>	m_perPrimitiveProgram;
4020 	de::MovePtr<glu::ShaderProgram>	m_basicProgram;
4021 	std::vector<DrawObject>			m_drawObjects;
4022 	std::vector<tcu::Vec4>			m_objectVertices;
4023 };
4024 
ClearCase(Context & context,const char * name,const char * description,deUint32 flags)4025 ClearCase::ClearCase (Context& context, const char* name, const char* description, deUint32 flags)
4026 	: TestCase				(context, name, description)
4027 	, m_scissoredClear		((flags & SCISSOR_CLEAR_BIT) != 0)
4028 	, m_fullscreenScissor	((flags & FULLSCREEN_SCISSOR_BIT) != 0)
4029 	, m_drawTriangles		((flags & DRAW_TRIANGLE_BIT) != 0)
4030 	, m_useGlobalState		((flags & PER_PRIMITIVE_BBOX_BIT) == 0)
4031 {
4032 	DE_ASSERT(m_useGlobalState || m_drawTriangles); // per-triangle bbox requires triangles
4033 	DE_ASSERT(!m_fullscreenScissor || m_scissoredClear); // fullscreenScissor requires scissoredClear
4034 }
4035 
~ClearCase(void)4036 ClearCase::~ClearCase (void)
4037 {
4038 	deinit();
4039 }
4040 
init(void)4041 void ClearCase::init (void)
4042 {
4043 	if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
4044 		throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
4045 	if (m_drawTriangles && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
4046 		throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
4047 
4048 	m_testCtx.getLog()
4049 		<< tcu::TestLog::Message
4050 		<< "Doing multiple"
4051 			<< ((m_scissoredClear) ? (" scissored") : (""))
4052 			<< " color buffer clears"
4053 			<< ((m_drawTriangles) ? (" and drawing some geometry between them") : (""))
4054 			<< ".\n"
4055 		<< ((m_scissoredClear && m_fullscreenScissor) ? ("Setting scissor area to cover entire viewport.\n") : (""))
4056 		<< "Rendering with and without setting the bounding box.\n"
4057 		<< "Expecting bounding box to have no effect on clears (i.e. results are constant).\n"
4058 		<< "Set bounding box using "
4059 			<< ((m_useGlobalState) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output"))
4060 			<< ".\n"
4061 		<< "Clear color is green with yellowish shades.\n"
4062 		<< ((m_drawTriangles) ? ("Primitive color is yellow with greenish shades.\n") : (""))
4063 		<< tcu::TestLog::EndMessage;
4064 
4065 	if (m_drawTriangles)
4066 	{
4067 		createVbo();
4068 		createProgram();
4069 	}
4070 }
4071 
deinit(void)4072 void ClearCase::deinit (void)
4073 {
4074 	m_vbo.clear();
4075 	m_perPrimitiveProgram.clear();
4076 	m_basicProgram.clear();
4077 	m_drawObjects = std::vector<DrawObject>();
4078 	m_objectVertices = std::vector<tcu::Vec4>();
4079 }
4080 
iterate(void)4081 ClearCase::IterateResult ClearCase::iterate (void)
4082 {
4083 	const tcu::IVec2	renderTargetSize	(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight());
4084 	tcu::Surface		resultWithoutBBox	(renderTargetSize.x(), renderTargetSize.y());
4085 	tcu::Surface		resultWithBBox		(renderTargetSize.x(), renderTargetSize.y());
4086 
4087 	// render with and without bbox set
4088 	for (int passNdx = 0; passNdx < 2; ++passNdx)
4089 	{
4090 		const bool		useBBox			= (passNdx == 1);
4091 		tcu::Surface&	destination		= (useBBox) ? (resultWithBBox) : (resultWithoutBBox);
4092 
4093 		renderTo(destination, useBBox);
4094 	}
4095 
4096 	// Verify images are equal and that the image does not contain (trivially detectable) garbage
4097 
4098 	if (!verifyImagesEqual(resultWithoutBBox.getAccess(), resultWithBBox.getAccess()))
4099 	{
4100 		// verifyImagesEqual will print out the image and error mask
4101 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
4102 	}
4103 	else if (!verifyImageResultValid(resultWithBBox.getAccess()))
4104 	{
4105 		// verifyImageResultValid will print out the image and error mask
4106 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result verification failed");
4107 	}
4108 	else
4109 	{
4110 		m_testCtx.getLog()
4111 			<< tcu::TestLog::Message
4112 			<< "Image comparison passed."
4113 			<< tcu::TestLog::EndMessage
4114 			<< tcu::TestLog::ImageSet("Images", "Image verification")
4115 			<< tcu::TestLog::Image("Result", "Result", resultWithBBox.getAccess())
4116 			<< tcu::TestLog::EndImageSet;
4117 
4118 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4119 	}
4120 
4121 	return STOP;
4122 }
4123 
createVbo(void)4124 void ClearCase::createVbo (void)
4125 {
4126 	const int				numObjects	= 16;
4127 	const glw::Functions&	gl			= m_context.getRenderContext().getFunctions();
4128 	de::Random				rnd			(deStringHash(getName()));
4129 
4130 	m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
4131 
4132 	for (int objectNdx = 0; objectNdx < numObjects; ++objectNdx)
4133 	{
4134 		const int	numTriangles	= rnd.getInt(1, 4);
4135 		const float	minX			= rnd.getFloat(-1.2f, 0.8f);
4136 		const float	minY			= rnd.getFloat(-1.2f, 0.8f);
4137 		const float	maxX			= minX + rnd.getFloat(0.2f, 1.0f);
4138 		const float	maxY			= minY + rnd.getFloat(0.2f, 1.0f);
4139 
4140 		DrawObject	drawObject;
4141 		drawObject.firstNdx = (int)m_objectVertices.size();
4142 		drawObject.numVertices = numTriangles * 3;
4143 
4144 		m_drawObjects.push_back(drawObject);
4145 
4146 		for (int triangleNdx = 0; triangleNdx < numTriangles; ++triangleNdx)
4147 		for (int vertexNdx = 0; vertexNdx < 3; ++vertexNdx)
4148 		{
4149 			const float posX = rnd.getFloat(minX, maxX);
4150 			const float posY = rnd.getFloat(minY, maxY);
4151 			const float posZ = rnd.getFloat(-0.7f, 0.7f);
4152 			const float posW = rnd.getFloat(0.9f, 1.1f);
4153 
4154 			m_objectVertices.push_back(tcu::Vec4(posX, posY, posZ, posW));
4155 		}
4156 	}
4157 
4158 	gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
4159 	gl.bufferData(GL_ARRAY_BUFFER, (int)(m_objectVertices.size() * sizeof(tcu::Vec4)), &m_objectVertices[0], GL_STATIC_DRAW);
4160 	GLU_EXPECT_NO_ERROR(gl.getError(), "buffer upload");
4161 }
4162 
createProgram(void)4163 void ClearCase::createProgram (void)
4164 {
4165 	m_basicProgram = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(),
4166 																			glu::ProgramSources()
4167 																				<< glu::VertexSource(genVertexSource())
4168 																				<< glu::FragmentSource(genFragmentSource())
4169 																				<< glu::TessellationControlSource(genTessellationControlSource(false))
4170 																				<< glu::TessellationEvaluationSource(genTessellationEvaluationSource())));
4171 
4172 	m_testCtx.getLog()
4173 		<< tcu::TestLog::Section("Program", "Shader program")
4174 		<< *m_basicProgram
4175 		<< tcu::TestLog::EndSection;
4176 
4177 	if (!m_basicProgram->isOk())
4178 		throw tcu::TestError("shader build failed");
4179 
4180 	if (!m_useGlobalState)
4181 	{
4182 		m_perPrimitiveProgram = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(),
4183 																					   glu::ProgramSources()
4184 																							<< glu::VertexSource(genVertexSource())
4185 																							<< glu::FragmentSource(genFragmentSource())
4186 																							<< glu::TessellationControlSource(genTessellationControlSource(true))
4187 																							<< glu::TessellationEvaluationSource(genTessellationEvaluationSource())));
4188 
4189 		m_testCtx.getLog()
4190 			<< tcu::TestLog::Section("PerPrimitiveProgram", "Shader program that sets the bounding box")
4191 			<< *m_perPrimitiveProgram
4192 			<< tcu::TestLog::EndSection;
4193 
4194 		if (!m_perPrimitiveProgram->isOk())
4195 			throw tcu::TestError("shader build failed");
4196 	}
4197 }
4198 
renderTo(tcu::Surface & dst,bool useBBox)4199 void ClearCase::renderTo (tcu::Surface& dst, bool useBBox)
4200 {
4201 	const int				numOps				= 45;
4202 	const tcu::Vec4			yellow				(1.0f, 1.0f, 0.0f, 1.0f);
4203 	const tcu::Vec4			green				(0.0f, 1.0f, 0.0f, 1.0f);
4204 	const tcu::IVec2		renderTargetSize	(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight());
4205 	const glw::Functions&	gl					= m_context.getRenderContext().getFunctions();
4206 	de::Random				rnd					(deStringHash(getName()));
4207 	glu::VertexArray		vao					(m_context.getRenderContext());
4208 
4209 	// always do the initial clear
4210 	gl.disable(GL_SCISSOR_TEST);
4211 	gl.viewport(0, 0, renderTargetSize.x(), renderTargetSize.y());
4212 	gl.clearColor(yellow.x(), yellow.y(), yellow.z(), yellow.w());
4213 	gl.clear(GL_COLOR_BUFFER_BIT);
4214 	gl.finish();
4215 
4216 	// prepare draw
4217 	if (m_scissoredClear)
4218 		gl.enable(GL_SCISSOR_TEST);
4219 
4220 	if (m_drawTriangles)
4221 	{
4222 		const deUint32	programHandle		= (m_useGlobalState || !useBBox) ? (m_basicProgram->getProgram()) : (m_perPrimitiveProgram->getProgram());
4223 		const int		positionAttribLoc	= gl.getAttribLocation(programHandle, "a_position");
4224 
4225 		TCU_CHECK(positionAttribLoc != -1);
4226 
4227 		gl.useProgram(programHandle);
4228 		gl.bindVertexArray(*vao);
4229 		gl.enableVertexAttribArray(positionAttribLoc);
4230 		gl.vertexAttribPointer(positionAttribLoc, 4, GL_FLOAT, GL_FALSE, (int)sizeof(tcu::Vec4), DE_NULL);
4231 		gl.patchParameteri(GL_PATCH_VERTICES, 3);
4232 	}
4233 
4234 	// do random scissor/clearldraw operations
4235 	for (int opNdx = 0; opNdx < numOps; ++opNdx)
4236 	{
4237 		const int	drawObjNdx				= (m_drawTriangles) ? (rnd.getInt(0, (int)m_drawObjects.size() - 1)) : (0);
4238 		const int	objectVertexStartNdx	= (m_drawTriangles) ? (m_drawObjects[drawObjNdx].firstNdx) : (0);
4239 		const int	objectVertexLength		= (m_drawTriangles) ? (m_drawObjects[drawObjNdx].numVertices) : (0);
4240 		tcu::Vec4	bboxMin;
4241 		tcu::Vec4	bboxMax;
4242 
4243 		if (m_drawTriangles)
4244 		{
4245 			bboxMin = tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f);
4246 			bboxMax = tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f);
4247 
4248 			// calc bbox
4249 			for (int vertexNdx = objectVertexStartNdx; vertexNdx < objectVertexStartNdx + objectVertexLength; ++vertexNdx)
4250 			for (int componentNdx = 0; componentNdx < 4; ++componentNdx)
4251 			{
4252 				bboxMin[componentNdx] = de::min(bboxMin[componentNdx], m_objectVertices[vertexNdx][componentNdx]);
4253 				bboxMax[componentNdx] = de::max(bboxMax[componentNdx], m_objectVertices[vertexNdx][componentNdx]);
4254 			}
4255 		}
4256 		else
4257 		{
4258 			// no geometry, just random something
4259 			bboxMin.x() = rnd.getFloat(-1.2f, 1.0f);
4260 			bboxMin.y() = rnd.getFloat(-1.2f, 1.0f);
4261 			bboxMin.z() = rnd.getFloat(-1.2f, 1.0f);
4262 			bboxMin.w() = 1.0f;
4263 			bboxMax.x() = bboxMin.x() + rnd.getFloat(0.2f, 1.0f);
4264 			bboxMax.y() = bboxMin.y() + rnd.getFloat(0.2f, 1.0f);
4265 			bboxMax.z() = bboxMin.z() + rnd.getFloat(0.2f, 1.0f);
4266 			bboxMax.w() = 1.0f;
4267 		}
4268 
4269 		if (m_scissoredClear)
4270 		{
4271 			const int scissorX = (m_fullscreenScissor) ? (0)					: rnd.getInt(0, renderTargetSize.x()-1);
4272 			const int scissorY = (m_fullscreenScissor) ? (0)					: rnd.getInt(0, renderTargetSize.y()-1);
4273 			const int scissorW = (m_fullscreenScissor) ? (renderTargetSize.x())	: rnd.getInt(0, renderTargetSize.x()-scissorX);
4274 			const int scissorH = (m_fullscreenScissor) ? (renderTargetSize.y())	: rnd.getInt(0, renderTargetSize.y()-scissorY);
4275 
4276 			gl.scissor(scissorX, scissorY, scissorW, scissorH);
4277 		}
4278 
4279 		{
4280 			const tcu::Vec4 color = tcu::mix(green, yellow, rnd.getFloat() * 0.4f); // greenish
4281 			gl.clearColor(color.x(), color.y(), color.z(), color.w());
4282 			gl.clear(GL_COLOR_BUFFER_BIT);
4283 		}
4284 
4285 		if (useBBox)
4286 		{
4287 			DE_ASSERT(m_useGlobalState || m_drawTriangles); // !m_useGlobalState -> m_drawTriangles
4288 			if (m_useGlobalState)
4289 				gl.primitiveBoundingBox(bboxMin.x(), bboxMin.y(), bboxMin.z(), bboxMin.w(),
4290 										bboxMax.x(), bboxMax.y(), bboxMax.z(), bboxMax.w());
4291 		}
4292 
4293 		if (m_drawTriangles)
4294 			gl.drawArrays(GL_PATCHES, objectVertexStartNdx, objectVertexLength);
4295 	}
4296 
4297 	GLU_EXPECT_NO_ERROR(gl.getError(), "post draw");
4298 	glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess());
4299 }
4300 
verifyImagesEqual(const tcu::PixelBufferAccess & withoutBBox,const tcu::PixelBufferAccess & withBBox)4301 bool ClearCase::verifyImagesEqual (const tcu::PixelBufferAccess& withoutBBox, const tcu::PixelBufferAccess& withBBox)
4302 {
4303 	DE_ASSERT(withoutBBox.getWidth() == withBBox.getWidth());
4304 	DE_ASSERT(withoutBBox.getHeight() == withBBox.getHeight());
4305 
4306 	tcu::Surface	errorMask	(withoutBBox.getWidth(), withoutBBox.getHeight());
4307 	bool			anyError	= false;
4308 
4309 	tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec());
4310 
4311 	for (int y = 0; y < withoutBBox.getHeight(); ++y)
4312 	for (int x = 0; x < withoutBBox.getWidth(); ++x)
4313 	{
4314 		if (withoutBBox.getPixelInt(x, y) != withBBox.getPixelInt(x, y))
4315 		{
4316 			errorMask.setPixel(x, y, tcu::RGBA::red());
4317 			anyError = true;
4318 		}
4319 	}
4320 
4321 	if (anyError)
4322 	{
4323 		m_testCtx.getLog()
4324 			<< tcu::TestLog::Message
4325 			<< "Image comparison failed."
4326 			<< tcu::TestLog::EndMessage
4327 			<< tcu::TestLog::ImageSet("Images", "Image comparison")
4328 			<< tcu::TestLog::Image("WithoutBBox", "Result with bounding box not set", withoutBBox)
4329 			<< tcu::TestLog::Image("WithBBox", "Result with bounding box set", withBBox)
4330 			<< tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
4331 			<< tcu::TestLog::EndImageSet;
4332 	}
4333 
4334 	return !anyError;
4335 }
4336 
verifyImageResultValid(const tcu::PixelBufferAccess & result)4337 bool ClearCase::verifyImageResultValid (const tcu::PixelBufferAccess& result)
4338 {
4339 	tcu::Surface	errorMask	(result.getWidth(), result.getHeight());
4340 	bool			anyError	= false;
4341 
4342 	tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec());
4343 
4344 	for (int y = 0; y < result.getHeight(); ++y)
4345 	for (int x = 0; x < result.getWidth(); ++x)
4346 	{
4347 		const tcu::IVec4 pixel = result.getPixelInt(x, y);
4348 
4349 		// allow green, yellow and any shade between
4350 		if (pixel[1] != 255 || pixel[2] != 0)
4351 		{
4352 			errorMask.setPixel(x, y, tcu::RGBA::red());
4353 			anyError = true;
4354 		}
4355 	}
4356 
4357 	if (anyError)
4358 	{
4359 		m_testCtx.getLog()
4360 			<< tcu::TestLog::Message
4361 			<< "Image verification failed."
4362 			<< tcu::TestLog::EndMessage
4363 			<< tcu::TestLog::ImageSet("Images", "Image verification")
4364 			<< tcu::TestLog::Image("ResultImage", "Result image", result)
4365 			<< tcu::TestLog::Image("ErrorMask", "Error mask", errorMask)
4366 			<< tcu::TestLog::EndImageSet;
4367 	}
4368 
4369 	return !anyError;
4370 }
4371 
4372 static const char* const s_yellowishPosOnlyVertexSource =	"#version 310 es\n"
4373 															"in highp vec4 a_position;\n"
4374 															"out highp vec4 v_vertex_color;\n"
4375 															"void main()\n"
4376 															"{\n"
4377 															"	gl_Position = a_position;\n"
4378 															"	// yellowish shade\n"
4379 															"	highp float redComponent = 0.5 + float(gl_VertexID % 5) / 8.0;\n"
4380 															"	v_vertex_color = vec4(redComponent, 1.0, 0.0, 1.0);\n"
4381 															"}\n";
4382 
4383 static const char* const s_basicColorFragmentSource =	"#version 310 es\n"
4384 														"in mediump vec4 v_color;\n"
4385 														"layout(location = 0) out mediump vec4 o_color;\n"
4386 														"void main()\n"
4387 														"{\n"
4388 														"	o_color = v_color;\n"
4389 														"}\n";
4390 
4391 
4392 static const char* const s_basicColorTessEvalSource =	"#version 310 es\n"
4393 														"#extension GL_EXT_tessellation_shader : require\n"
4394 														"#extension GL_EXT_gpu_shader5 : require\n"
4395 														"layout(triangles) in;\n"
4396 														"in highp vec4 v_tess_eval_color[];\n"
4397 														"out highp vec4 v_color;\n"
4398 														"precise gl_Position;\n"
4399 														"void main()\n"
4400 														"{\n"
4401 														"	gl_Position = gl_TessCoord.x * gl_in[0].gl_Position\n"
4402 														"	            + gl_TessCoord.y * gl_in[1].gl_Position\n"
4403 														"	            + gl_TessCoord.z * gl_in[2].gl_Position;\n"
4404 														"	v_color = gl_TessCoord.x * v_tess_eval_color[0]\n"
4405 														"	        + gl_TessCoord.y * v_tess_eval_color[1]\n"
4406 														"	        + gl_TessCoord.z * v_tess_eval_color[2];\n"
4407 														"}\n";
4408 
genVertexSource(void) const4409 std::string ClearCase::genVertexSource (void) const
4410 {
4411 	return	s_yellowishPosOnlyVertexSource;
4412 }
4413 
genFragmentSource(void) const4414 std::string ClearCase::genFragmentSource (void) const
4415 {
4416 	return s_basicColorFragmentSource;
4417 }
4418 
genTessellationControlSource(bool setBBox) const4419 std::string ClearCase::genTessellationControlSource (bool setBBox) const
4420 {
4421 	std::ostringstream buf;
4422 
4423 	buf <<	"#version 310 es\n"
4424 			"#extension GL_EXT_tessellation_shader : require\n";
4425 
4426 	if (setBBox)
4427 		buf << "#extension GL_EXT_primitive_bounding_box : require\n";
4428 
4429 	buf <<	"layout(vertices=3) out;\n"
4430 			"in highp vec4 v_vertex_color[];\n"
4431 			"out highp vec4 v_tess_eval_color[];\n"
4432 			"void main()\n"
4433 			"{\n"
4434 			"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
4435 			"	v_tess_eval_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
4436 			"	gl_TessLevelOuter[0] = 2.8;\n"
4437 			"	gl_TessLevelOuter[1] = 2.8;\n"
4438 			"	gl_TessLevelOuter[2] = 2.8;\n"
4439 			"	gl_TessLevelInner[0] = 2.8;\n";
4440 
4441 	if (setBBox)
4442 	{
4443 		buf <<	"\n"
4444 		"	gl_BoundingBoxEXT[0] = min(min(gl_in[0].gl_Position,\n"
4445 		"	                               gl_in[1].gl_Position),\n"
4446 		"	                           gl_in[2].gl_Position);\n"
4447 		"	gl_BoundingBoxEXT[1] = max(max(gl_in[0].gl_Position,\n"
4448 		"	                               gl_in[1].gl_Position),\n"
4449 		"	                           gl_in[2].gl_Position);\n";
4450 	}
4451 
4452 	buf << "}\n";
4453 	return buf.str();
4454 }
4455 
genTessellationEvaluationSource(void) const4456 std::string ClearCase::genTessellationEvaluationSource (void) const
4457 {
4458 	return s_basicColorTessEvalSource;
4459 }
4460 
4461 class ViewportCallOrderCase : public TestCase
4462 {
4463 public:
4464 	enum CallOrder
4465 	{
4466 		VIEWPORT_FIRST = 0,
4467 		BBOX_FIRST,
4468 
4469 		ORDER_LAST
4470 	};
4471 
4472 									ViewportCallOrderCase			(Context& context, const char* name, const char* description, CallOrder callOrder);
4473 									~ViewportCallOrderCase			(void);
4474 
4475 private:
4476 	void							init							(void);
4477 	void							deinit							(void);
4478 	IterateResult					iterate							(void);
4479 
4480 	void							genVbo							(void);
4481 	void							genProgram						(void);
4482 	bool							verifyImage						(const tcu::PixelBufferAccess& result);
4483 
4484 	std::string						genVertexSource					(void) const;
4485 	std::string						genFragmentSource				(void) const;
4486 	std::string						genTessellationControlSource	(void) const;
4487 	std::string						genTessellationEvaluationSource	(void) const;
4488 
4489 	const CallOrder					m_callOrder;
4490 
4491 	de::MovePtr<glu::Buffer>		m_vbo;
4492 	de::MovePtr<glu::ShaderProgram>	m_program;
4493 	int								m_numVertices;
4494 };
4495 
ViewportCallOrderCase(Context & context,const char * name,const char * description,CallOrder callOrder)4496 ViewportCallOrderCase::ViewportCallOrderCase (Context& context, const char* name, const char* description, CallOrder callOrder)
4497 	: TestCase		(context, name, description)
4498 	, m_callOrder	(callOrder)
4499 	, m_numVertices	(-1)
4500 {
4501 	DE_ASSERT(m_callOrder < ORDER_LAST);
4502 }
4503 
~ViewportCallOrderCase(void)4504 ViewportCallOrderCase::~ViewportCallOrderCase (void)
4505 {
4506 	deinit();
4507 }
4508 
init(void)4509 void ViewportCallOrderCase::init (void)
4510 {
4511 	if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
4512 		throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
4513 
4514 	if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
4515 		throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
4516 
4517 	m_testCtx.getLog()
4518 		<< tcu::TestLog::Message
4519 		<< "Testing call order of state setting functions have no effect on the rendering.\n"
4520 		<< "Setting viewport and bounding box in the following order:\n"
4521 			<< ((m_callOrder == VIEWPORT_FIRST)
4522 				? ("\tFirst viewport with glViewport function.\n")
4523 				: ("\tFirst bounding box with glPrimitiveBoundingBoxEXT function.\n"))
4524 			<< ((m_callOrder == VIEWPORT_FIRST)
4525 				? ("\tThen bounding box with glPrimitiveBoundingBoxEXT function.\n")
4526 				: ("\tThen viewport with glViewport function.\n"))
4527 		<< "Verifying rendering result."
4528 		<< tcu::TestLog::EndMessage;
4529 
4530 	// resources
4531 	genVbo();
4532 	genProgram();
4533 }
4534 
deinit(void)4535 void ViewportCallOrderCase::deinit (void)
4536 {
4537 	m_vbo.clear();
4538 	m_program.clear();
4539 }
4540 
iterate(void)4541 ViewportCallOrderCase::IterateResult ViewportCallOrderCase::iterate (void)
4542 {
4543 	const glw::Functions&	gl				= m_context.getRenderContext().getFunctions();
4544 	const tcu::IVec2		viewportSize	= tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight());
4545 	const glw::GLint		posLocation		= gl.getAttribLocation(m_program->getProgram(), "a_position");
4546 	tcu::Surface			resultSurface	(viewportSize.x(), viewportSize.y());
4547 
4548 	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
4549 	gl.clear(GL_COLOR_BUFFER_BIT);
4550 
4551 	// set state
4552 	for (int orderNdx = 0; orderNdx < 2; ++orderNdx)
4553 	{
4554 		if ((orderNdx == 0 && m_callOrder == VIEWPORT_FIRST) ||
4555 			(orderNdx == 1 && m_callOrder == BBOX_FIRST))
4556 		{
4557 			m_testCtx.getLog()
4558 				<< tcu::TestLog::Message
4559 				<< "Setting viewport to cover the left half of the render target.\n"
4560 				<< "\t(0, 0, " << (viewportSize.x()/2) << ", " << viewportSize.y() << ")"
4561 				<< tcu::TestLog::EndMessage;
4562 
4563 			gl.viewport(0, 0, viewportSize.x()/2, viewportSize.y());
4564 		}
4565 		else
4566 		{
4567 			m_testCtx.getLog()
4568 				<< tcu::TestLog::Message
4569 				<< "Setting bounding box to cover the right half of the clip space.\n"
4570 				<< "\t(0.0, -1.0, -1.0, 1.0) .. (1.0, 1.0, 1.0f, 1.0)"
4571 				<< tcu::TestLog::EndMessage;
4572 
4573 			gl.primitiveBoundingBox(0.0f, -1.0f, -1.0f, 1.0f,
4574 									1.0f,  1.0f,  1.0f, 1.0f);
4575 		}
4576 	}
4577 
4578 	m_testCtx.getLog()
4579 		<< tcu::TestLog::Message
4580 		<< "Rendering mesh covering the right half of the clip space."
4581 		<< tcu::TestLog::EndMessage;
4582 
4583 	gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
4584 	gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, sizeof(float[4]), (const float*)DE_NULL);
4585 	gl.enableVertexAttribArray(posLocation);
4586 	gl.useProgram(m_program->getProgram());
4587 	gl.patchParameteri(GL_PATCH_VERTICES, 3);
4588 	gl.drawArrays(GL_PATCHES, 0, m_numVertices);
4589 	GLU_EXPECT_NO_ERROR(gl.getError(), "post-draw");
4590 
4591 	m_testCtx.getLog()
4592 		<< tcu::TestLog::Message
4593 		<< "Verifying image"
4594 		<< tcu::TestLog::EndMessage;
4595 	glu::readPixels(m_context.getRenderContext(), 0, 0, resultSurface.getAccess());
4596 
4597 	if (!verifyImage(resultSurface.getAccess()))
4598 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
4599 	else
4600 	{
4601 		m_testCtx.getLog()
4602 			<< tcu::TestLog::Message
4603 			<< "Result ok."
4604 			<< tcu::TestLog::EndMessage
4605 			<< tcu::TestLog::ImageSet("Images", "Image verification")
4606 			<< tcu::TestLog::Image("Result", "Result", resultSurface.getAccess())
4607 			<< tcu::TestLog::EndImageSet;
4608 
4609 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4610 	}
4611 	return STOP;
4612 }
4613 
genVbo(void)4614 void ViewportCallOrderCase::genVbo (void)
4615 {
4616 	const int				gridSize	= 6;
4617 	const glw::Functions&	gl			= m_context.getRenderContext().getFunctions();
4618 	std::vector<tcu::Vec4>	data		(gridSize * gridSize * 2 * 3);
4619 	std::vector<int>		cellOrder	(gridSize * gridSize * 2);
4620 	de::Random				rnd			(0x55443322);
4621 
4622 	// generate grid with triangles in random order
4623 	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
4624 		cellOrder[ndx] = ndx;
4625 	rnd.shuffle(cellOrder.begin(), cellOrder.end());
4626 
4627 	// generate grid filling the right half of the clip space: (x: 0.0, y: -1.0) .. (x: 1.0, y: 1.0)
4628 	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
4629 	{
4630 		const int			cellNdx		= cellOrder[ndx];
4631 		const bool			cellSide	= ((cellNdx % 2) == 0);
4632 		const int			cellX		= (cellNdx / 2) % gridSize;
4633 		const int			cellY		= (cellNdx / 2) / gridSize;
4634 
4635 		if (cellSide)
4636 		{
4637 			data[ndx * 3 + 0] = tcu::Vec4(float(cellX+0) / float(gridSize), (float(cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4638 			data[ndx * 3 + 1] = tcu::Vec4(float(cellX+1) / float(gridSize), (float(cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4639 			data[ndx * 3 + 2] = tcu::Vec4(float(cellX+0) / float(gridSize), (float(cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4640 		}
4641 		else
4642 		{
4643 			data[ndx * 3 + 0] = tcu::Vec4(float(cellX+0) / float(gridSize), (float(cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4644 			data[ndx * 3 + 1] = tcu::Vec4(float(cellX+1) / float(gridSize), (float(cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4645 			data[ndx * 3 + 2] = tcu::Vec4(float(cellX+1) / float(gridSize), (float(cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4646 		}
4647 	}
4648 
4649 	m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
4650 	gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
4651 	gl.bufferData(GL_ARRAY_BUFFER, (int)(data.size() * sizeof(tcu::Vec4)), &data[0], GL_STATIC_DRAW);
4652 	GLU_EXPECT_NO_ERROR(gl.getError(), "create vbo");
4653 
4654 	m_numVertices = (int)data.size();
4655 }
4656 
genProgram(void)4657 void ViewportCallOrderCase::genProgram (void)
4658 {
4659 	m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(),
4660 																	   glu::ProgramSources()
4661 																			<< glu::VertexSource(genVertexSource())
4662 																			<< glu::FragmentSource(genFragmentSource())
4663 																			<< glu::TessellationControlSource(genTessellationControlSource())
4664 																			<< glu::TessellationEvaluationSource(genTessellationEvaluationSource())));
4665 
4666 	m_testCtx.getLog()
4667 		<< tcu::TestLog::Section("Program", "Shader program")
4668 		<< *m_program
4669 		<< tcu::TestLog::EndSection;
4670 
4671 	if (!m_program->isOk())
4672 		throw tcu::TestError("shader build failed");
4673 }
4674 
verifyImage(const tcu::PixelBufferAccess & result)4675 bool ViewportCallOrderCase::verifyImage (const tcu::PixelBufferAccess& result)
4676 {
4677 	const tcu::IVec2	insideBorder	(deCeilFloatToInt32(0.25f * (float)result.getWidth()) + 1, deFloorFloatToInt32(0.5f * (float)result.getWidth()) - 1);
4678 	const tcu::IVec2	outsideBorder	(deFloorFloatToInt32(0.25f * (float)result.getWidth()) - 1, deCeilFloatToInt32(0.5f * (float)result.getWidth()) + 1);
4679 	tcu::Surface		errorMask		(result.getWidth(), result.getHeight());
4680 	bool				anyError		= false;
4681 
4682 	tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec());
4683 
4684 	for (int y = 0; y < result.getHeight(); ++y)
4685 	for (int x = 0; x < result.getWidth(); ++x)
4686 	{
4687 		const tcu::IVec4	pixel			= result.getPixelInt(x, y);
4688 		const bool			insideMeshArea	= x >= insideBorder.x() && x <= insideBorder.x();
4689 		const bool			outsideMeshArea = x <= outsideBorder.x() && x >= outsideBorder.x();
4690 
4691 		// inside mesh, allow green, yellow and any shade between
4692 		// outside mesh, allow background (black) only
4693 		// in the border area, allow anything
4694 		if ((insideMeshArea && (pixel[1] != 255 || pixel[2] != 0)) ||
4695 			(outsideMeshArea && (pixel[0] != 0 || pixel[1] != 0 || pixel[2] != 0)))
4696 		{
4697 			errorMask.setPixel(x, y, tcu::RGBA::red());
4698 			anyError = true;
4699 		}
4700 	}
4701 
4702 	if (anyError)
4703 	{
4704 		m_testCtx.getLog()
4705 			<< tcu::TestLog::Message
4706 			<< "Image verification failed."
4707 			<< tcu::TestLog::EndMessage
4708 			<< tcu::TestLog::ImageSet("Images", "Image verification")
4709 			<< tcu::TestLog::Image("ResultImage", "Result image", result)
4710 			<< tcu::TestLog::Image("ErrorMask", "Error mask", errorMask)
4711 			<< tcu::TestLog::EndImageSet;
4712 	}
4713 
4714 	return !anyError;
4715 }
4716 
genVertexSource(void) const4717 std::string ViewportCallOrderCase::genVertexSource (void) const
4718 {
4719 	return	s_yellowishPosOnlyVertexSource;
4720 }
4721 
genFragmentSource(void) const4722 std::string ViewportCallOrderCase::genFragmentSource (void) const
4723 {
4724 	return s_basicColorFragmentSource;
4725 }
4726 
genTessellationControlSource(void) const4727 std::string ViewportCallOrderCase::genTessellationControlSource (void) const
4728 {
4729 	return	"#version 310 es\n"
4730 			"#extension GL_EXT_tessellation_shader : require\n"
4731 			"layout(vertices=3) out;\n"
4732 			"in highp vec4 v_vertex_color[];\n"
4733 			"out highp vec4 v_tess_eval_color[];\n"
4734 			"void main()\n"
4735 			"{\n"
4736 			"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
4737 			"	v_tess_eval_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
4738 			"	gl_TessLevelOuter[0] = 2.8;\n"
4739 			"	gl_TessLevelOuter[1] = 2.8;\n"
4740 			"	gl_TessLevelOuter[2] = 2.8;\n"
4741 			"	gl_TessLevelInner[0] = 2.8;\n"
4742 			"}\n";
4743 }
4744 
genTessellationEvaluationSource(void) const4745 std::string ViewportCallOrderCase::genTessellationEvaluationSource (void) const
4746 {
4747 	return s_basicColorTessEvalSource;
4748 }
4749 
4750 } // anonymous
4751 
PrimitiveBoundingBoxTests(Context & context)4752 PrimitiveBoundingBoxTests::PrimitiveBoundingBoxTests (Context& context)
4753 	: TestCaseGroup(context, "primitive_bounding_box", "Tests for EXT_primitive_bounding_box")
4754 {
4755 }
4756 
~PrimitiveBoundingBoxTests(void)4757 PrimitiveBoundingBoxTests::~PrimitiveBoundingBoxTests (void)
4758 {
4759 }
4760 
init(void)4761 void PrimitiveBoundingBoxTests::init (void)
4762 {
4763 	static const struct
4764 	{
4765 		const char*	name;
4766 		const char*	description;
4767 		deUint32	methodFlags;
4768 	} stateSetMethods[] =
4769 	{
4770 		{
4771 			"global_state",
4772 			"Set bounding box using PRIMITIVE_BOUNDING_BOX_EXT state",
4773 			BBoxRenderCase::FLAG_SET_BBOX_STATE,
4774 		},
4775 		{
4776 			"tessellation_set_per_draw",
4777 			"Set bounding box using gl_BoundingBoxEXT, use same value for all primitives",
4778 			BBoxRenderCase::FLAG_SET_BBOX_OUTPUT,
4779 		},
4780 		{
4781 			"tessellation_set_per_primitive",
4782 			"Set bounding box using gl_BoundingBoxEXT, use per-primitive bounding box",
4783 			BBoxRenderCase::FLAG_SET_BBOX_OUTPUT | BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4784 		},
4785 	};
4786 	static const struct
4787 	{
4788 		const char*	name;
4789 		const char*	description;
4790 		deUint32	stageFlags;
4791 	} pipelineConfigs[] =
4792 	{
4793 		{
4794 			"vertex_fragment",
4795 			"Render with vertex-fragment program",
4796 			0u
4797 		},
4798 		{
4799 			"vertex_tessellation_fragment",
4800 			"Render with vertex-tessellation{ctrl,eval}-fragment program",
4801 			BBoxRenderCase::FLAG_TESSELLATION
4802 		},
4803 		{
4804 			"vertex_geometry_fragment",
4805 			"Render with vertex-tessellation{ctrl,eval}-geometry-fragment program",
4806 			BBoxRenderCase::FLAG_GEOMETRY
4807 		},
4808 		{
4809 			"vertex_tessellation_geometry_fragment",
4810 			"Render with vertex-geometry-fragment program",
4811 			BBoxRenderCase::FLAG_TESSELLATION | BBoxRenderCase::FLAG_GEOMETRY
4812 		},
4813 	};
4814 	static const struct
4815 	{
4816 		const char*	name;
4817 		const char*	description;
4818 		deUint32	flags;
4819 		deUint32	invalidFlags;
4820 		deUint32	requiredFlags;
4821 	} usageConfigs[] =
4822 	{
4823 		{
4824 			"default_framebuffer_bbox_equal",
4825 			"Render to default framebuffer, set tight bounding box",
4826 			BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
4827 			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4828 			0
4829 		},
4830 		{
4831 			"default_framebuffer_bbox_larger",
4832 			"Render to default framebuffer, set padded bounding box",
4833 			BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_LARGER,
4834 			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4835 			0
4836 		},
4837 		{
4838 			"default_framebuffer_bbox_smaller",
4839 			"Render to default framebuffer, set too small bounding box",
4840 			BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_SMALLER,
4841 			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4842 			0
4843 		},
4844 		{
4845 			"fbo_bbox_equal",
4846 			"Render to texture, set tight bounding box",
4847 			BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
4848 			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4849 			0
4850 		},
4851 		{
4852 			"fbo_bbox_larger",
4853 			"Render to texture, set padded bounding box",
4854 			BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_LARGER,
4855 			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4856 			0
4857 		},
4858 		{
4859 			"fbo_bbox_smaller",
4860 			"Render to texture, set too small bounding box",
4861 			BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_SMALLER,
4862 			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4863 			0
4864 		},
4865 		{
4866 			"default_framebuffer",
4867 			"Render to default framebuffer, set tight bounding box",
4868 			BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
4869 			0,
4870 			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX
4871 		},
4872 		{
4873 			"fbo",
4874 			"Render to texture, set tight bounding box",
4875 			BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
4876 			0,
4877 			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX
4878 		},
4879 	};
4880 	enum PrimitiveRenderType
4881 	{
4882 		TYPE_TRIANGLE,
4883 		TYPE_LINE,
4884 		TYPE_POINT,
4885 	};
4886 	const struct
4887 	{
4888 		const char*			name;
4889 		const char*			description;
4890 		PrimitiveRenderType	type;
4891 		deUint32			flags;
4892 	} primitiveTypes[] =
4893 	{
4894 		{
4895 			"triangles",
4896 			"Triangle render tests",
4897 			TYPE_TRIANGLE,
4898 			0
4899 		},
4900 		{
4901 			"lines",
4902 			"Line render tests",
4903 			TYPE_LINE,
4904 			0
4905 		},
4906 		{
4907 			"points",
4908 			"Point render tests",
4909 			TYPE_POINT,
4910 			0
4911 		},
4912 		{
4913 			"wide_lines",
4914 			"Wide line render tests",
4915 			TYPE_LINE,
4916 			LineRenderCase::LINEFLAG_WIDE
4917 		},
4918 		{
4919 			"wide_points",
4920 			"Wide point render tests",
4921 			TYPE_POINT,
4922 			PointRenderCase::POINTFLAG_WIDE
4923 		},
4924 	};
4925 
4926 	// .state_query
4927 	{
4928 		tcu::TestCaseGroup* const stateQueryGroup = new tcu::TestCaseGroup(m_testCtx, "state_query", "State queries");
4929 		addChild(stateQueryGroup);
4930 
4931 		stateQueryGroup->addChild(new InitialValueCase	(m_context,	"initial_value",	"Initial value case"));
4932 		stateQueryGroup->addChild(new QueryCase			(m_context,	"getfloat",			"getFloatv",			QueryCase::QUERY_FLOAT));
4933 		stateQueryGroup->addChild(new QueryCase			(m_context,	"getboolean",		"getBooleanv",			QueryCase::QUERY_BOOLEAN));
4934 		stateQueryGroup->addChild(new QueryCase			(m_context,	"getinteger",		"getIntegerv",			QueryCase::QUERY_INT));
4935 		stateQueryGroup->addChild(new QueryCase			(m_context,	"getinteger64",		"getInteger64v",		QueryCase::QUERY_INT64));
4936 	}
4937 
4938 	// .triangles
4939 	// .(wide_)lines
4940 	// .(wide_)points
4941 	for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); ++primitiveTypeNdx)
4942 	{
4943 		tcu::TestCaseGroup* const primitiveGroup = new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, primitiveTypes[primitiveTypeNdx].description);
4944 		addChild(primitiveGroup);
4945 
4946 		for (int stateSetMethodNdx = 0; stateSetMethodNdx < DE_LENGTH_OF_ARRAY(stateSetMethods); ++stateSetMethodNdx)
4947 		{
4948 			tcu::TestCaseGroup* const methodGroup = new tcu::TestCaseGroup(m_testCtx, stateSetMethods[stateSetMethodNdx].name, stateSetMethods[stateSetMethodNdx].description);
4949 			primitiveGroup->addChild(methodGroup);
4950 
4951 			for (int pipelineConfigNdx = 0; pipelineConfigNdx < DE_LENGTH_OF_ARRAY(pipelineConfigs); ++pipelineConfigNdx)
4952 			{
4953 				if ((stateSetMethods[stateSetMethodNdx].methodFlags & BBoxRenderCase::FLAG_SET_BBOX_OUTPUT) != 0 &&
4954 					(pipelineConfigs[pipelineConfigNdx].stageFlags  & BBoxRenderCase::FLAG_TESSELLATION)    == 0)
4955 				{
4956 					// invalid config combination
4957 				}
4958 				else
4959 				{
4960 					tcu::TestCaseGroup* const pipelineGroup = new tcu::TestCaseGroup(m_testCtx, pipelineConfigs[pipelineConfigNdx].name, pipelineConfigs[pipelineConfigNdx].description);
4961 					methodGroup->addChild(pipelineGroup);
4962 
4963 					for (int usageNdx = 0; usageNdx < DE_LENGTH_OF_ARRAY(usageConfigs); ++usageNdx)
4964 					{
4965 						const deUint32 flags = primitiveTypes[primitiveTypeNdx].flags         |
4966 											   stateSetMethods[stateSetMethodNdx].methodFlags |
4967 											   pipelineConfigs[pipelineConfigNdx].stageFlags  |
4968 											   usageConfigs[usageNdx].flags;
4969 
4970 						if (usageConfigs[usageNdx].invalidFlags && (flags & usageConfigs[usageNdx].invalidFlags) != 0)
4971 							continue;
4972 						if (usageConfigs[usageNdx].requiredFlags && (flags & usageConfigs[usageNdx].requiredFlags) == 0)
4973 							continue;
4974 
4975 						switch (primitiveTypes[primitiveTypeNdx].type)
4976 						{
4977 							case TYPE_TRIANGLE:
4978 								pipelineGroup->addChild(new GridRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags));
4979 								break;
4980 							case TYPE_LINE:
4981 								pipelineGroup->addChild(new LineRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags));
4982 								break;
4983 							case TYPE_POINT:
4984 								pipelineGroup->addChild(new PointRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags));
4985 								break;
4986 							default:
4987 								DE_ASSERT(false);
4988 						}
4989 					}
4990 				}
4991 			}
4992 		}
4993 	}
4994 
4995 	// .depth
4996 	{
4997 		static const struct
4998 		{
4999 			const char*					name;
5000 			const char*					description;
5001 			DepthDrawCase::DepthType	depthMethod;
5002 		} depthMethods[] =
5003 		{
5004 			{
5005 				"builtin_depth",
5006 				"Fragment depth not modified in fragment shader",
5007 				DepthDrawCase::DEPTH_BUILTIN
5008 			},
5009 			{
5010 				"user_defined_depth",
5011 				"Fragment depth is defined in the fragment shader",
5012 				DepthDrawCase::DEPTH_USER_DEFINED
5013 			},
5014 		};
5015 		static const struct
5016 		{
5017 			const char*					name;
5018 			const char*					description;
5019 			DepthDrawCase::BBoxState	bboxState;
5020 			DepthDrawCase::BBoxSize		bboxSize;
5021 		} depthCases[] =
5022 		{
5023 			{
5024 				"global_state_bbox_equal",
5025 				"Test tight bounding box with global bbox state",
5026 				DepthDrawCase::STATE_GLOBAL,
5027 				DepthDrawCase::BBOX_EQUAL,
5028 			},
5029 			{
5030 				"global_state_bbox_larger",
5031 				"Test padded bounding box with global bbox state",
5032 				DepthDrawCase::STATE_GLOBAL,
5033 				DepthDrawCase::BBOX_LARGER,
5034 			},
5035 			{
5036 				"per_primitive_bbox_equal",
5037 				"Test tight bounding box with tessellation output bbox",
5038 				DepthDrawCase::STATE_PER_PRIMITIVE,
5039 				DepthDrawCase::BBOX_EQUAL,
5040 			},
5041 			{
5042 				"per_primitive_bbox_larger",
5043 				"Test padded bounding box with tessellation output bbox",
5044 				DepthDrawCase::STATE_PER_PRIMITIVE,
5045 				DepthDrawCase::BBOX_LARGER,
5046 			},
5047 		};
5048 
5049 		tcu::TestCaseGroup* const depthGroup = new tcu::TestCaseGroup(m_testCtx, "depth", "Test bounding box depth component");
5050 		addChild(depthGroup);
5051 
5052 		// .builtin_depth
5053 		// .user_defined_depth
5054 		for (int depthNdx = 0; depthNdx < DE_LENGTH_OF_ARRAY(depthMethods); ++depthNdx)
5055 		{
5056 			tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, depthMethods[depthNdx].name, depthMethods[depthNdx].description);
5057 			depthGroup->addChild(group);
5058 
5059 			for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(depthCases); ++caseNdx)
5060 				group->addChild(new DepthDrawCase(m_context, depthCases[caseNdx].name, depthCases[caseNdx].description, depthMethods[depthNdx].depthMethod, depthCases[caseNdx].bboxState, depthCases[caseNdx].bboxSize));
5061 		}
5062 	}
5063 
5064 	// .blit_fbo
5065 	{
5066 		tcu::TestCaseGroup* const blitFboGroup = new tcu::TestCaseGroup(m_testCtx, "blit_fbo", "Test bounding box does not affect blitting");
5067 		addChild(blitFboGroup);
5068 
5069 		blitFboGroup->addChild(new BlitFboCase(m_context, "blit_default_to_fbo", "Blit from default fb to fbo", BlitFboCase::TARGET_DEFAULT, BlitFboCase::TARGET_FBO));
5070 		blitFboGroup->addChild(new BlitFboCase(m_context, "blit_fbo_to_default", "Blit from fbo to default fb", BlitFboCase::TARGET_FBO,     BlitFboCase::TARGET_DEFAULT));
5071 		blitFboGroup->addChild(new BlitFboCase(m_context, "blit_fbo_to_fbo",     "Blit from fbo to fbo",        BlitFboCase::TARGET_FBO,     BlitFboCase::TARGET_FBO));
5072 	}
5073 
5074 	// .clear
5075 	{
5076 		tcu::TestCaseGroup* const clearGroup = new tcu::TestCaseGroup(m_testCtx, "clear", "Test bounding box does not clears");
5077 		addChild(clearGroup);
5078 
5079 		clearGroup->addChild(new ClearCase(m_context, "full_clear",                                             "Do full clears",                                               0));
5080 		clearGroup->addChild(new ClearCase(m_context, "full_clear_with_triangles",                              "Do full clears and render some geometry",                      ClearCase::DRAW_TRIANGLE_BIT));
5081 		clearGroup->addChild(new ClearCase(m_context, "full_clear_with_triangles_per_primitive_bbox",           "Do full clears and render some geometry",                      ClearCase::DRAW_TRIANGLE_BIT | ClearCase::PER_PRIMITIVE_BBOX_BIT));
5082 		clearGroup->addChild(new ClearCase(m_context, "scissored_clear",                                        "Do scissored clears",                                          ClearCase::SCISSOR_CLEAR_BIT));
5083 		clearGroup->addChild(new ClearCase(m_context, "scissored_clear_with_triangles",                         "Do scissored clears and render some geometry",                 ClearCase::SCISSOR_CLEAR_BIT | ClearCase::DRAW_TRIANGLE_BIT));
5084 		clearGroup->addChild(new ClearCase(m_context, "scissored_clear_with_triangles_per_primitive_bbox",      "Do scissored clears and render some geometry",                 ClearCase::SCISSOR_CLEAR_BIT | ClearCase::DRAW_TRIANGLE_BIT | ClearCase::PER_PRIMITIVE_BBOX_BIT));
5085 		clearGroup->addChild(new ClearCase(m_context, "scissored_full_clear",                                   "Do full clears with enabled scissor",                          ClearCase::FULLSCREEN_SCISSOR_BIT | ClearCase::SCISSOR_CLEAR_BIT));
5086 		clearGroup->addChild(new ClearCase(m_context, "scissored_full_clear_with_triangles",                    "Do full clears with enabled scissor and render some geometry", ClearCase::FULLSCREEN_SCISSOR_BIT | ClearCase::SCISSOR_CLEAR_BIT | ClearCase::DRAW_TRIANGLE_BIT));
5087 		clearGroup->addChild(new ClearCase(m_context, "scissored_full_clear_with_triangles_per_primitive_bbox", "Do full clears with enabled scissor and render some geometry", ClearCase::FULLSCREEN_SCISSOR_BIT | ClearCase::SCISSOR_CLEAR_BIT | ClearCase::DRAW_TRIANGLE_BIT | ClearCase::PER_PRIMITIVE_BBOX_BIT));
5088 	}
5089 
5090 	// .call_order (Khronos bug #13262)
5091 	{
5092 		tcu::TestCaseGroup* const callOrderGroup = new tcu::TestCaseGroup(m_testCtx, "call_order", "Test viewport and bounding box calls have no effect");
5093 		addChild(callOrderGroup);
5094 
5095 		callOrderGroup->addChild(new ViewportCallOrderCase(m_context, "viewport_first_bbox_second", "Set up viewport first and bbox after", ViewportCallOrderCase::VIEWPORT_FIRST));
5096 		callOrderGroup->addChild(new ViewportCallOrderCase(m_context, "bbox_first_viewport_second", "Set up bbox first and viewport after", ViewportCallOrderCase::BBOX_FIRST));
5097 	}
5098 }
5099 
5100 } // Functional
5101 } // gles31
5102 } // deqp
5103